diff --git a/.gitattributes b/.gitattributes index 288e6f98a85..7c9ff784df6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,10 +5,9 @@ *.md text eol=lf # testing Windows spaces - https://help.github.com/en/github/using-git/configuring-git-to-handle-line-endings -packages-tests/FileFormatter/ValueObject/Fixture/composer_carriage_return_line_feed.json json eol=crlf +tests/FileFormatter/ValueObject/Fixture/composer_carriage_return_line_feed.json json eol=crlf # for 3rd party packages working with rector/rector-src as dependency rules-tests export-ignore -packages-tests export-ignore tests export-ignore diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index ec5cb99c61d..6aef581916a 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1,13 +1,6 @@ version: 2 updates: - - - package-ecosystem: composer - directory: "/" - open-pull-requests-limit: 5 - schedule: - interval: monthly - - package-ecosystem: github-actions directory: "/" diff --git a/.github/typos.toml b/.github/typos.toml new file mode 100644 index 00000000000..fe1504d9f91 --- /dev/null +++ b/.github/typos.toml @@ -0,0 +1,9 @@ +[default.extend-words] +Symplify = "Symplify" +Invokable = "Invokable" + +# as in 2nd +nd = "nd" + +# plurals +Identicals = "Identicals" diff --git a/.github/workflows/build_scoped_rector.yaml b/.github/workflows/build_scoped_rector.yaml index bfd8c454d7d..80d1624af84 100644 --- a/.github/workflows/build_scoped_rector.yaml +++ b/.github/workflows/build_scoped_rector.yaml @@ -15,57 +15,65 @@ env: jobs: build_scoped_rector: + # Don't run on forks. + if: github.repository == 'rectorphp/rector-src' + runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 10 steps: # sometime, when 2 or more consecutive PRs merged, the checkout rectorphp/rector-src is overlapped # and reverting other commit change # this should not happen on create a tag, so wait first - - name: "Wait 3 minutes before checkout rectorphp/rector-src on create a tag" + name: "Wait before checkout rectorphp/rector-src on create a tag" if: "startsWith(github.ref, 'refs/tags/')" - run: sleep 180 + run: sleep 20 - - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.3 coverage: none # fixes https://github.com/rectorphp/rector/pull/4559/checks?check_run_id=1359814403, see https://github.com/shivammathur/setup-php#composer-github-oauth env: COMPOSER_TOKEN: ${{ secrets.ACCESS_TOKEN }} - # install only prod dependencies - do not use ramsey, it uses cache including "dev", we want to avoid it here + # install do not use composer/ramsey github action package, it uses cache including "dev", we want to avoid it here + # this run is needed to install patches, the --no-dev skips them + - run: composer install --ansi + + # to remove dev dependencies - run: composer install --no-dev --ansi - # early downgrade individual functions and files of symfony Attribute classes - - run: bin/rector process src/functions -c build/config/config-downgrade.php --ansi + # early downgrade individual functions + - run: bin/rector process src/functions/node_helper.php -c build/config/config-downgrade.php --ansi # 1. copy files to $NESTED_DIRECTORY directory Exclude the scoped/nested directories to prevent rsync from copying in a loop - run: rsync --exclude rector-build -av * rector-build --quiet - - run: rm -rf rector-build/packages-tests rector-build/rules-tests rector-build/tests + + - run: rm -rf rector-build/rules-tests rector-build/templates rector-build/tests rector-build/scripts/validate-phpstan-version.php rector-build/vendor/tracy/tracy/examples rector-build/vendor/symfony/console/Tester rector-build/vendor/symfony/console/Event rector-build/vendor/symfony/console/EventListener rector-build/vendor/tracy/tracy/examples rector-build/vendor/tracy/tracy/src/Bridges rector-build/vendor/tracy/tracy/src/Tracy/Bar rector-build/vendor/tracy/tracy/src/Tracy/Session rector-build/vendor/symfony/service-contracts/Test # 2. downgrade rector - - run: sh build/downgrade-rector.sh rector-build + - run: php -d memory_limit=-1 bin/rector process rector-build/bin rector-build/config rector-build/src rector-build/rules rector-build/vendor --config build/config/config-downgrade.php --ansi --no-diffs # 3. prefix classes - run: sh build/build-rector-scoped.sh rector-build rector-prefixed-downgraded - # 4. lint the code for PHP 7.1 - this must happen here, as setup-php allows only one PHP version switch: https://github.com/shivammathur/setup-php/issues/434 + # 4. lint the code for PHP 7.4 - this must happen here, as setup-php allows only one PHP version switch: https://github.com/shivammathur/setup-php/issues/434 - uses: shivammathur/setup-php@v2 with: - php-version: 7.1 + php-version: 7.4 coverage: none - - run: composer create-project php-parallel-lint/php-parallel-lint php-parallel-lint --ansi - - run: php-parallel-lint/parallel-lint rector-prefixed-downgraded --exclude rector-prefixed-downgraded/stubs --exclude rector-prefixed-downgraded/vendor/symfony/error-handler/Resources --exclude rector-prefixed-downgraded/vendor/symfony/http-kernel/Resources --exclude rector-prefixed-downgraded/vendor/rector/rector-nette/tests --exclude rector-prefixed-downgraded/vendor/symfony/polyfill-mbstring/bootstrap80.php --exclude rector-prefixed-downgraded/vendor/tracy/tracy/examples --exclude rector-prefixed-downgraded/vendor/ssch/typo3-rector/templates/maker + - run: composer global require php-parallel-lint/php-parallel-lint --ansi + - run: /home/runner/.composer/vendor/bin/parallel-lint rector-prefixed-downgraded --exclude rector-prefixed-downgraded/stubs --exclude rector-prefixed-downgraded/vendor/tracy/tracy/examples --exclude rector-prefixed-downgraded/vendor/rector/rector-generator/templates --exclude rector-prefixed-downgraded/vendor/symfony/console/Debug/CliRequest.php # 5. copy repository meta files - run: | @@ -76,17 +84,19 @@ jobs: # 6. clone remote repository, so we can push it - - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" with: repository: rectorphp/rector path: remote-repository token: ${{ secrets.ACCESS_TOKEN }} # remove remote files, to avoid piling up dead code in remote repository - - run: rm -rf remote-repository/.github remote-repository/config remote-repository/src remote-repository/rules remote-repository/packages remote-repository/vendor + - run: rm -rf remote-repository/.github remote-repository/e2e remote-repository/docs remote-repository/config remote-repository/src remote-repository/rules remote-repository/packages remote-repository/vendor remote-repository/stubs-rector - run: cp -a rector-prefixed-downgraded/. remote-repository + - run: rm -rf remote-repository/scripts/add-phpstan-self-replace.php remote-repository/scripts/test-fixture-stats.php remote-repository/scripts/check-before-after-same-fixtures.php remote-repository/scripts/no-php-file-in-fixtures.php + # 7. setup git - working-directory: remote-repository @@ -98,16 +108,21 @@ jobs: - name: "Get Git log" id: git-log - run: echo ::set-output name=log::$(git log ${{ github.event.before }}..${{ github.event.after }} --reverse --pretty='%H %s' | sed -e 's/^/https:\/\/github.com\/rectorphp\/rector-src\/commit\//') + run: | + echo "log<> $GITHUB_OUTPUT + echo "$(git log ${{ github.event.before }}..${{ github.event.after }} --reverse --pretty='https://github.com/rectorphp/rector-src/commit/%H %s')" >> $GITHUB_OUTPUT + echo 'EOF' >> $GITHUB_OUTPUT # 8.A publish it to remote repository without tag - name: "Commit Prefixed - main" working-directory: remote-repository if: "!startsWith(github.ref, 'refs/tags/')" + env: + INPUT_LOG: ${{ steps.git-log.outputs.log }} run: | git add --all - git commit -m "Updated Rector to commit ${{ github.event.after }}" -m "${{ steps.git-log.outputs.log }}" + git commit -m "Updated Rector to commit ${{ github.event.after }}" -m "$INPUT_LOG" git push --quiet origin main # 8.B publish it to remote repository with tag @@ -115,9 +130,11 @@ jobs: name: "Commit Prefixed - tag" working-directory: remote-repository if: "startsWith(github.ref, 'refs/tags/')" + env: + INPUT_LOG: ${{ steps.git-log.outputs.log }} run: | git add --all - git commit -m "Rector ${GITHUB_REF#refs/tags/}" -m "${{ steps.git-log.outputs.log }}" + git commit -m "Rector ${GITHUB_REF#refs/tags/}" -m "$INPUT_LOG" git push --quiet origin main git tag ${GITHUB_REF#refs/tags/} -m "${GITHUB_REF#refs/tags/}" git push --quiet origin ${GITHUB_REF#refs/tags/} diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index 82a5520882f..bc4bb163f37 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -18,43 +18,80 @@ jobs: run: composer validate --ansi - - name: 'Validate Max File Length' - run: vendor/bin/easy-ci validate-file-length packages rules src tests + name: 'Run with Space in Directory' + run: bin/rector process tests-paths/path/with\ space/SomeFile.php --clear-cache + + - + name: 'Preload php-parser Order' + run: php preload.php - name: 'PHPStan' - run: vendor/bin/phpstan analyse --ansi --error-format symplify + run: vendor/bin/phpstan analyse --ansi + + - + name: 'Avoid duplicate short class names' + run: php scripts/unique-rector-short-class-name.php + + - + name: 'Help and Version' + run: + bin/rector --help + bin/rector --version + + - + name: 'Commented Code' + run: vendor/bin/swiss-knife check-commented-code src rules tests rules-tests --line-limit 5 --ansi + + - + name: 'Active Classes' + run: | + vendor/bin/class-leak check bin config src rules utils --skip-suffix "Rector" --skip-type="Rector\\Utils\\Compiler\\Unprefixer" --skip-type="Rector\\NodeCollector\\BinaryOpConditionsCollector" --skip-type="Rector\\Set\\Contract\\SetListInterface" - - name: 'PHPStan for config' - run: composer phpstan-config + name: 'Compatible PHPStan versions' + run: php scripts/validate-phpstan-version.php - - name: Commented Code - run: vendor/bin/easy-ci check-commented-code src packages rules tests packages-tests rules-tests --line-limit 5 --ansi + name: 'Finalize classes' + run: vendor/bin/swiss-knife finalize-classes src tests rules --dry-run --skip-file="src/PhpParser/Node/FileNode.php" + - + name: 'Check before/after test fixture on no-changes' + run: php scripts/check-before-after-same-fixtures.php + + - + name: 'Check fixture classes are different to nodes' + run: php scripts/avoid-short-node-names-in-fixtures.php + + - + name: 'Check no "*.php" files in rules Fixture directory' + run: php scripts/no-php-file-in-fixtures.php + + - + name: 'Detect composer dependency issues' + run: vendor/bin/composer-dependency-analyser - # see https://github.com/rectorphp/rector-generator - - name: 'Rector Generate From Recipe' + name: "PHP Linter" run: | - bin/rector init-recipe --ansi - bin/rector generate --ansi + composer require php-parallel-lint/php-parallel-lint --ansi + vendor/bin/parallel-lint src bin/rector config tests rules --colors name: ${{ matrix.actions.name }} runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 10 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 # see https://github.com/shivammathur/setup-php - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.3 coverage: none - - uses: "ramsey/composer-install@v1" + - uses: "ramsey/composer-install@v4" - run: ${{ matrix.actions.run }} diff --git a/.github/workflows/code_analysis_no_dev.yaml b/.github/workflows/code_analysis_no_dev.yaml index a398def9658..19bb5787803 100644 --- a/.github/workflows/code_analysis_no_dev.yaml +++ b/.github/workflows/code_analysis_no_dev.yaml @@ -8,34 +8,19 @@ env: COMPOSER_ROOT_VERSION: "dev-main" jobs: - matrix: - strategy: - fail-fast: false - matrix: - actions: - - - name: 'Rector List' - run: bin/rector list - - - - name: 'Show command' - run: bin/rector show --ansi - - name: ${{ matrix.actions.name }} + code_analysis_no_dev: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 10 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 # see https://github.com/shivammathur/setup-php - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.3 coverage: none - # must be removed, as local config is missing dev dependencies - - run: rm phpstan-for-rector.neon - - - run: composer install --no-progress --ansi --no-dev - - - run: ${{ matrix.actions.run }} + - run: | + composer install --ansi + composer install --no-progress --ansi --no-dev + - run: bin/rector list --ansi diff --git a/.github/workflows/compat_test.yaml b/.github/workflows/compat_test.yaml new file mode 100644 index 00000000000..d44f4f85736 --- /dev/null +++ b/.github/workflows/compat_test.yaml @@ -0,0 +1,29 @@ +# see https://github.com/rectorphp/rector/issues/9416 +name: PHPUnit and PHPStan Compat Test + +on: + push: + branches: + - main + pull_request: null + +jobs: + compat_test: + runs-on: ubuntu-latest + + steps: + - + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + coverage: none + + - run: composer create-project "rector/rector-compat-tests:dev-main" . + + - run: vendor/bin/phpunit tests/PHPStan + + - run: vendor/bin/phpunit tests/Rector + + - run: vendor/bin/phpunit tests + + - run: vendor/bin/phpunit diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 00000000000..be970e32f7d --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,77 @@ +# This workflow runs system tests: Use the Rector application from the source +# checkout to process "fixture" projects in e2e/ directory +# to see if those can be processed successfully +name: End to End tests + +on: + pull_request: + branches: + - main + push: + branches: + - main + +env: + # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 + COMPOSER_ROOT_VERSION: "dev-main" + +jobs: + end_to_end: + runs-on: ubuntu-latest + timeout-minutes: 3 + strategy: + fail-fast: false + matrix: + php_version: ['8.3'] + directory: + - 'e2e/applied-auto-import' + - 'e2e/applied-polyfill-php80' + - 'e2e/applied-rule-change-docblock' + - 'e2e/applied-rule-removed-node' + - 'e2e/applied-rule-return-array-nodes' + - 'e2e/config-dist-fallback' + - 'e2e/config-file-priority' + - 'e2e/different-path-over-skip-config' + - 'e2e/invalid-paths' + - 'e2e/no-parallel-reflection-resolver' + - 'e2e/only-option' + - 'e2e/only-option-quote-double-equalnone' + - 'e2e/only-option-quote-single' + - 'e2e/only-option-quote-single-bsdouble' + - 'e2e/only-option-quote-single-equalnone' + - 'e2e/parallel-custom-config' + - 'e2e/parallel-reflection-resolver' + - 'e2e/parallel with space' + - 'e2e/print-new-node' + + name: End to end test - ${{ matrix.directory }} + + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + coverage: none + + # run in root rector-src + - run: composer install --ansi + + # run in e2e subdir + - + run: composer install --ansi + working-directory: ${{ matrix.directory }} + + # run e2e test + - run: php ../e2eTestRunner.php --config custom/config/rector.php + working-directory: ${{ matrix.directory }} + if: ${{ matrix.directory == 'e2e/parallel-custom-config' }} + + # this tests "-c", that was not working on parallel before, see https://github.com/rectorphp/rector-src/pull/1620 + - run: php ../e2eTestRunner.php -c custom/config/rector.php + working-directory: ${{ matrix.directory }} + if: ${{ matrix.directory == 'e2e/parallel-custom-config' }} + + - run: php ../e2eTestRunner.php + working-directory: ${{ matrix.directory }} + if: ${{ matrix.directory != 'e2e/parallel-custom-config' }} diff --git a/.github/workflows/e2e_command_with_option.yaml b/.github/workflows/e2e_command_with_option.yaml new file mode 100644 index 00000000000..e0edf1fcb7c --- /dev/null +++ b/.github/workflows/e2e_command_with_option.yaml @@ -0,0 +1,65 @@ +name: End to End tests command with option + +on: + pull_request: + branches: + - main + push: + branches: + - main +env: + # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 + COMPOSER_ROOT_VERSION: "dev-main" + +jobs: + code_analysis: + strategy: + fail-fast: false + + name: End to End tests command with option + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + # see https://github.com/shivammathur/setup-php + - + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + coverage: none + + - uses: "ramsey/composer-install@v4" + + # test various commands with options + - working-directory: e2e/command-with-option + run: | + # other command + ../../bin/rector help + php ../../bin/rector help + + # with explicit "process" command + ../../bin/rector process --output-format json --dry-run + ../../bin/rector process --output-format=json --dry-run + php ../../bin/rector process --output-format json --dry-run + php ../../bin/rector process --output-format=json --dry-run + + ../../bin/rector process some_file.php --output-format json + ../../bin/rector process some_file.php --output-format=json + php ../../bin/rector process some_file.php --output-format json + php ../../bin/rector process some_file.php --output-format=json + + php ../../bin/rector p some_file.php --output-format=json + + # with implicit process command + ../../bin/rector --output-format json --dry-run + ../../bin/rector --output-format=json --dry-run + php ../../bin/rector --output-format json --dry-run + php ../../bin/rector --output-format=json --dry-run + + ../../bin/rector some_file.php --output-format json + ../../bin/rector some_file.php --output-format=json + php ../../bin/rector some_file.php --output-format json + php ../../bin/rector some_file.php --output-format=json + diff --git a/.github/workflows/e2e_with_cache.yaml b/.github/workflows/e2e_with_cache.yaml new file mode 100644 index 00000000000..97b6062b6a4 --- /dev/null +++ b/.github/workflows/e2e_with_cache.yaml @@ -0,0 +1,76 @@ +# This workflow runs system tests: Use the Rector application from the source +# checkout to process "fixture" projects in e2e/ directory +# to see if those can be processed successfully +name: End to End tests with cache + +on: + pull_request: + branches: + - main + push: + branches: + - main + +env: + # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 + COMPOSER_ROOT_VERSION: "dev-main" + +jobs: + end_to_end: + runs-on: ubuntu-latest + timeout-minutes: 3 + strategy: + fail-fast: false + matrix: + php_version: ['8.3'] + directory: + - 'e2e/applied-rule-removed-node-with-cache' + - 'e2e/timeout-file-not-cached' + + name: End to end test - ${{ matrix.directory }} + + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + coverage: none + + # run in root rector-src + - run: composer install --ansi + + # run in e2e subdir + - + run: composer install --ansi + working-directory: ${{ matrix.directory }} + + # run e2e test + - run: php ../e2eTestRunner.php + working-directory: ${{ matrix.directory }} + + # this tests that a 2nd run with cache and "--dry-run" gives same results, see https://github.com/rectorphp/rector-src/pull/3614#issuecomment-1507742338 + - run: php ../e2eTestRunnerWithCache.php + working-directory: ${{ matrix.directory }} + + cache_meta_extension: + runs-on: ubuntu-latest + timeout-minutes: 3 + + name: End to end test - e2e/cache-meta-extension + + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + coverage: none + + - run: composer install --ansi + + - run: composer install --ansi + working-directory: e2e/cache-meta-extension + + - run: php e2eTestRunnerCacheInvalidation.php + working-directory: e2e/cache-meta-extension diff --git a/.github/workflows/e2e_with_no_diffs.yaml b/.github/workflows/e2e_with_no_diffs.yaml new file mode 100644 index 00000000000..d44364b203c --- /dev/null +++ b/.github/workflows/e2e_with_no_diffs.yaml @@ -0,0 +1,56 @@ +# This workflow runs system tests: Use the Rector application from the source +# checkout to process "fixture" projects in e2e/ directory +# to see if those can be processed successfully +name: End to End tests with no diffs + +on: + pull_request: + branches: + - main + push: + branches: + - main + +env: + # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 + COMPOSER_ROOT_VERSION: "dev-main" + +jobs: + end_to_end: + runs-on: ubuntu-latest + timeout-minutes: 3 + strategy: + fail-fast: false + matrix: + php_version: ['8.3'] + directory: + - 'e2e/applied-rule-removed-node-no-diffs' + - 'e2e/applied-no-diffs-format-json' + + name: End to end test - ${{ matrix.directory }} + + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + coverage: none + + # run in root rector-src + - run: composer install --ansi + + # run in e2e subdir + - + run: composer install --ansi + working-directory: ${{ matrix.directory }} + + # run e2e test + - run: php ../e2eTestRunner.php --no-diffs + working-directory: ${{ matrix.directory }} + if: ${{ matrix.directory == 'e2e/applied-rule-removed-node-no-diffs' }} + + # run e2e test + - run: php ../e2eTestRunner.php --no-diffs --output-format=json + working-directory: ${{ matrix.directory }} + if: ${{ matrix.directory == 'e2e/applied-no-diffs-format-json' }} diff --git a/.github/workflows/lock.yaml b/.github/workflows/lock.yaml new file mode 100644 index 00000000000..e66cb879fa3 --- /dev/null +++ b/.github/workflows/lock.yaml @@ -0,0 +1,23 @@ +name: "Lock closed PRs" + +on: + schedule: + - cron: "0 3 * * 0" # every Sunday at 03:00 UTC + workflow_dispatch: + +jobs: + lock: + runs-on: ubuntu-latest + + steps: + - uses: dessant/lock-threads@v5 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + pr-inactive-days: 150 + + pr-comment: > + This pull request has been automatically locked because it has been closed for 150 days. + Please open a new PR if you want to continue the work. + + exclude-any-pr-labels: "keep-open,no-lock" diff --git a/.github/workflows/packages_tests.yaml b/.github/workflows/packages_tests.yaml index ad556db1e66..ea071e6befe 100644 --- a/.github/workflows/packages_tests.yaml +++ b/.github/workflows/packages_tests.yaml @@ -15,26 +15,29 @@ env: jobs: packages_tests: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 3 strategy: fail-fast: false matrix: repository_name: - # must be repository name, e.g. https://github.com/rectorphp/rector-nette + # must be repository name, e.g. https://github.com/rectorphp/rector-symfony - rectorphp/rector-symfony - rectorphp/rector-phpunit - rectorphp/rector-doctrine - - rectorphp/rector-laravel - - rectorphp/rector-nette - - rectorphp/rector-cakephp - - rectorphp/rector-phpoffice - - sabbelasichon/typo3-rector + - rectorphp/rector-downgrade-php + actions: + - + name: 'Unit Tests' + run: vendor/bin/phpunit + - + name: 'PHPStan' + run: vendor/bin/phpstan steps: # see https://github.com/actions/checkout#usage - - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" with: repository: ${{ matrix.repository_name }} ref: "main" @@ -42,16 +45,16 @@ jobs: - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.3 coverage: none - run: composer config minimum-stability dev # test with current commit in a pull-request - - run: composer require rector/rector-src dev-main#${{github.event.pull_request.head.sha}} --no-install + run: composer require rector/rector-src dev-main#${{github.event.pull_request.head.sha}} --no-update if: ${{ github.event_name == 'pull_request' }} - run: composer install --ansi - - run: vendor/bin/phpunit + - run: ${{ matrix.actions.run }} diff --git a/.github/workflows/php_linter.yaml b/.github/workflows/php_linter.yaml deleted file mode 100644 index 51b2fae809d..00000000000 --- a/.github/workflows/php_linter.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: PHP Linter - -on: - pull_request: null - -env: - # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 - COMPOSER_ROOT_VERSION: "dev-main" - -jobs: - php_linter: - runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - uses: actions/checkout@v2 - - - - uses: shivammathur/setup-php@v2 - with: - php-version: 8.0 - coverage: none - - - run: composer create-project php-parallel-lint/php-parallel-lint php-parallel-lint - - - run: php-parallel-lint/parallel-lint src bin/rector config tests packages rules --colors --exclude rules/psr4/tests/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Source --exclude rules/autodiscovery/tests/Rector/FileNode/MoveInterfacesToContractNamespaceDirectoryRector/Expected --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/Source/ExpectedSomeClassCopyEvent.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceMagicPropertyEventWithEventClassRector/Source --exclude rules/type-declaration/tests/Rector/ClassMethod/ParamTypeFromStrictTypedPropertyRector/Source diff --git a/.github/workflows/phpstan_printer_test.yaml b/.github/workflows/phpstan_printer_test.yaml new file mode 100644 index 00000000000..49e5e16d8fb --- /dev/null +++ b/.github/workflows/phpstan_printer_test.yaml @@ -0,0 +1,41 @@ +name: PHPStan Printer Test + +on: + pull_request: null + push: + branches: + - main + + + +env: + # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 + COMPOSER_ROOT_VERSION: "dev-main" + +jobs: + tests: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + php-versions: ['8.3'] + + runs-on: ${{ matrix.os }} + timeout-minutes: 3 + + name: PHP ${{ matrix.php-versions }} tests (${{ matrix.os }}) + steps: + - uses: actions/checkout@v4 + + - + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + coverage: none + # to display warning when assert() is called, eg: on direct getArgs() on CallLike + # and check against first class callable strlen(...) + ini-values: zend.assertions=1 + + - uses: "ramsey/composer-install@v4" + + - run: vendor/bin/phpunit tests/PhpParser/Printer/PHPStanPrinterTest.php --colors diff --git a/.github/workflows/rector.yaml b/.github/workflows/rector.yaml index 6418bca0450..743cb3ebc33 100644 --- a/.github/workflows/rector.yaml +++ b/.github/workflows/rector.yaml @@ -1,77 +1,42 @@ -#### -# Due to some Github Actions limitations, we are running realtime fixes (commits) only for self-owned-pr -# -# Current limitations: -# - Secrets (ACCESS_TOKEN) are not available in PRs from forks -# - Github Token has Read-only access (can not commit), Personal Access Token must be used instead -# - Github Token does not trigger workflows after push -# -# So we basically have chicken-egg problem here -# -# https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#permissions-for-the-github_token -#### name: Rector on: pull_request: null -env: - # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 - COMPOSER_ROOT_VERSION: "dev-main" - jobs: rector: strategy: fail-fast: false - matrix: - paths: - - src tests rules-tests packages packages-tests - - rules - - config utils scoper.php runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 8 + if: github.event.pull_request.head.repo.full_name == 'rectorphp/rector-src' steps: - # workaround for missing secret in fork PRs - see https://github.com/actions/checkout/issues/298 - # see https://github.com/rectorphp/rector/commit/d395e1c28b8e6a56711dcc2e10490a82965850e4 - - if: github.event.pull_request.head.repo.full_name == github.repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: # Must be used to trigger workflow after push token: ${{ secrets.ACCESS_TOKEN }} - # in forks, the token is not available - so we cannot us eit - - - if: github.event.pull_request.head.repo.full_name != github.repository - uses: actions/checkout@v2 + - run: echo "run on ${{ github.event.pull_request.head.repo.full_name }}" - uses: shivammathur/setup-php@v2 with: - # PHP 7.3 is required, so Rector's code is PHP 7.3 compatible even after refactoring - php-version: 8.0 + # PHP 8.3 is required, so Rector's code is PHP 8.3 compatible even after refactoring + php-version: 8.3 coverage: none - run: composer install --no-progress --ansi ## First run Rector - here can't be --dry-run !!! it would stop the job with it and not commit anything in the future - - run: bin/rector process ${{ matrix.paths }} --ansi --no-progress-bar - - - run: vendor/bin/ecs check --match-git-diff --fix --ansi + - run: bin/rector process --ansi - # see https://github.com/EndBug/add-and-commit - # commit only to core contributors who have repository access - if: github.event.pull_request.head.repo.full_name == github.repository - uses: EndBug/add-and-commit@v7.2.1 + uses: stefanzweifel/git-auto-commit-action@v5 with: - # The arguments for the `git add` command (see the paragraph below for more info) - add: . - message: "[ci-review] Rector Rectify" - author_name: "GitHub Action" - author_email: "action@github.com" - env: - # to get push access - GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} + commit_message: '[ci-review] Rector Rectify' + commit_author: 'GitHub Action ' + commit_user_email: 'action@github.com' diff --git a/.github/workflows/rector_laravel_rector_dev.yaml b/.github/workflows/rector_laravel_rector_dev.yaml new file mode 100644 index 00000000000..ad80c419076 --- /dev/null +++ b/.github/workflows/rector_laravel_rector_dev.yaml @@ -0,0 +1,30 @@ +name: Rector Laravel with dev-main + +on: + push: + branches: + - main + pull_request: null + +env: + # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 + COMPOSER_ROOT_VERSION: "dev-main" + +jobs: + rector_laravel_rector_dev: + runs-on: ubuntu-latest + + steps: + - + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + coverage: none + + # fixes https://github.com/rectorphp/rector/pull/4559/checks?check_run_id=1359814403, see https://github.com/shivammathur/setup-php#composer-github-oauth + env: + COMPOSER_TOKEN: ${{ secrets.ACCESS_TOKEN }} + + - run: git clone https://github.com/driftingly/rector-laravel.git + - run: composer require rector/rector:dev-main --working-dir rector-laravel + - run: cd rector-laravel && vendor/bin/phpunit diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 00000000000..9c7e1704401 --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,18 @@ +name: "Close stale issues and PRs" + +on: + schedule: + # runs daily at midnight + - cron: '0 0 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v10 + with: + days-before-stale: 60 + days-before-close: 90 + stale-issue-message: 'This issue has been automatically marked as stale due to inactivity for past 2 months. It will be closed in next 30 days if no further activity occurs.' + close-issue-message: 'Closing this issue as inactive for 30 days after marking stale.' + operations-per-run: 10 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f8c3d6a7011..0b189cfa00b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -2,6 +2,11 @@ name: Tests on: pull_request: null + push: + branches: + - main + + env: # see https://github.com/composer/composer/issues/9368#issuecomment-718112361 @@ -9,34 +14,28 @@ env: jobs: tests: - runs-on: ubuntu-latest - timeout-minutes: 30 - strategy: fail-fast: false matrix: - php: ['8.0'] - path: - - tests - - rules-tests - - packages-tests + os: [ubuntu-latest, windows-latest] + php-versions: ['8.3', '8.4', '8.5'] - name: PHP ${{ matrix.php }} tests for ${{ matrix.path }} + runs-on: ${{ matrix.os }} + timeout-minutes: 4 + + name: PHP ${{ matrix.php-versions }} tests (${{ matrix.os }}) steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php }} + php-version: ${{ matrix.php-versions }} coverage: none + # to display warning when assert() is called, eg: on direct getArgs() on CallLike + # and check against first class callable strlen(...) + ini-values: zend.assertions=1 - - uses: "ramsey/composer-install@v1" - - # with --runner=WrapperRunner, it is faster, only ideal with 4 processes (p4) - # @see https://github.com/rectorphp/rector-src/pull/551#issuecomment-889990905 - - run: vendor/bin/paratest -p4 --runner=WrapperRunner ${{ matrix.path }} - if: ${{ matrix.path == 'rules-tests' }} + - uses: "ramsey/composer-install@v4" - - run: vendor/bin/phpunit ${{ matrix.path }} - if: ${{ matrix.path != 'rules-tests' }} + - run: vendor/bin/phpunit --colors diff --git a/.github/workflows/typos.yaml b/.github/workflows/typos.yaml new file mode 100644 index 00000000000..ea339ba5465 --- /dev/null +++ b/.github/workflows/typos.yaml @@ -0,0 +1,24 @@ +# see https://github.com/crate-ci/typos +name: "Typos" + +on: + pull_request: + push: + branches: + - "main" + +jobs: + typos: + name: "Check for typos" + runs-on: "ubuntu-latest" + + steps: + - uses: actions/checkout@v4 + + + + - name: "Check for typos" + uses: "crate-ci/typos@v1.44.0" + with: + config: .github/typos.toml + files: "README.md src config rules tests rules-tests templates" diff --git a/.github/workflows/weekly_pull_requests.yaml b/.github/workflows/weekly_pull_requests.yaml index 324f7aa1baf..01f59d89fb2 100644 --- a/.github/workflows/weekly_pull_requests.yaml +++ b/.github/workflows/weekly_pull_requests.yaml @@ -11,15 +11,13 @@ env: jobs: weekly_pull_requests: + # Don't run on forks. + if: github.repository == 'rectorphp/rector-src' + strategy: fail-fast: false matrix: actions: - - - name: "Re-Generate Nodes/Rectors Documentation" - run: "composer docs" - branch: 'automated-regenerated-nodes-rectors-documentation' - - name: 'Apply Coding Standard' run: "composer fix-cs" @@ -32,11 +30,10 @@ jobs: name: ${{ matrix.actions.name }} runs-on: ubuntu-latest - timeout-minutes: 30 steps: - - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: token: ${{ secrets.ACCESS_TOKEN }} @@ -44,17 +41,17 @@ jobs: - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.3 coverage: none - - uses: "ramsey/composer-install@v1" + - uses: "ramsey/composer-install@v4" - run: ${{ matrix.actions.run }} # see https://github.com/peter-evans/create-pull-request - name: Create pull-request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v6 id: cpr with: token: ${{ secrets.ACCESS_TOKEN }} @@ -67,7 +64,7 @@ jobs: - name: Enable Pull Request Automerge if: steps.cpr.outputs.pull-request-operation == 'created' - uses: peter-evans/enable-pull-request-automerge@v1 + uses: peter-evans/enable-pull-request-automerge@v3 with: token: ${{ secrets.ACCESS_TOKEN }} pull-request-number: ${{ steps.cpr.outputs.pull-request-number }} diff --git a/.gitignore b/.gitignore index 4cd6c70598f..b0ad8f0b6fb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,13 +4,12 @@ composer.lock # PHPStorm meta files .idea/ -.phpunit.result.cache - -# often customized locally - example on Github is just fine -rector-recipe.php +# VS Code files +.vscode -# testing -abz +.phpunit.result.cache +# since PHPUnit 10 +.phpunit.cache # scoped & downgraded version php-scoper.phar @@ -18,3 +17,8 @@ box.phar php-parallel-lint tmp + +# Allow overrides for Docker configuration (custom volumes, env variables etc) +docker-compose.override.yml + +/temp_* diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index 5f4982ad3f4..16963957bf1 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -47,50 +47,24 @@ \PhpParser\Node::getAttribute(), 0, \Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, - \Rector\NodeTypeResolver\Node\AttributeKey::CLASS_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::CLASS_NAME, - \Rector\NodeTypeResolver\Node\AttributeKey::METHOD_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::NEXT_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::PREVIOUS_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::CURRENT_STATEMENT, - \Rector\NodeTypeResolver\Node\AttributeKey::PREVIOUS_STATEMENT, - \Rector\NodeTypeResolver\Node\AttributeKey::USE_NODES, - \Rector\NodeTypeResolver\Node\AttributeKey::START_TOKEN_POSITION, \Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE, \Rector\NodeTypeResolver\Node\AttributeKey::PHP_DOC_INFO, \Rector\NodeTypeResolver\Node\AttributeKey::KIND, \Rector\NodeTypeResolver\Node\AttributeKey::IS_REGULAR_PATTERN, \Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NAME, \Rector\NodeTypeResolver\Node\AttributeKey::COMMENTS, - \Rector\NodeTypeResolver\Node\AttributeKey::VIRTUAL_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::PARAMETER_POSITION, - \Rector\NodeTypeResolver\Node\AttributeKey::ARGUMENT_POSITION, + \Rector\NodeTypeResolver\Node\AttributeKey::RAW_VALUE, ); expectedArguments( \PhpParser\Node::setAttribute(), 0, \Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, - \Rector\NodeTypeResolver\Node\AttributeKey::CLASS_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::CLASS_NAME, - \Rector\NodeTypeResolver\Node\AttributeKey::METHOD_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::NEXT_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::PREVIOUS_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::CURRENT_STATEMENT, - \Rector\NodeTypeResolver\Node\AttributeKey::PREVIOUS_STATEMENT, - \Rector\NodeTypeResolver\Node\AttributeKey::USE_NODES, - \Rector\NodeTypeResolver\Node\AttributeKey::START_TOKEN_POSITION, \Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE, \Rector\NodeTypeResolver\Node\AttributeKey::PHP_DOC_INFO, \Rector\NodeTypeResolver\Node\AttributeKey::KIND, \Rector\NodeTypeResolver\Node\AttributeKey::IS_REGULAR_PATTERN, \Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NAME, \Rector\NodeTypeResolver\Node\AttributeKey::COMMENTS, - \Rector\NodeTypeResolver\Node\AttributeKey::VIRTUAL_NODE, - \Rector\NodeTypeResolver\Node\AttributeKey::PARAMETER_POSITION, - \Rector\NodeTypeResolver\Node\AttributeKey::ARGUMENT_POSITION, + \Rector\NodeTypeResolver\Node\AttributeKey::RAW_VALUE, ); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 596ea9dc89f..8f336e9e58b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,36 @@ -## How to Contribute +# How to Contribute -Contributions here are more than welcomed! You can contribute to [rector-src](https://github.com/rectorphp/rector-src) repository. +Contributions here are more than welcomed! You can contribute to [rector-src](https://github.com/rectorphp/rector-src) repository or one of [extension packages](https://github.com/rectorphp/). -There 3 rules will highly increase changes to get your PR merged: +## Preparing Local Environment + +1. Fork the [rector/rector-src](https://github.com/rectorphp/rector-src) repository and clone it + +```bash +git clone git@github.com:/rector-src.git +cd rector-src +``` + +2. We use PHP 8.3 and composer + +Install dependencies and verify your local environment: + +```bash +composer update +composer check-platform-reqs +``` + +*Note: using Docker for contributing is strongly discouraged, as it requires [extra knowledge of composer internals](https://github.com/composer/composer/issues/9368#issuecomment-718112361).* + +Then you can start working with the code :+1: + +
+ +Do you want to **contribute a failing test**? [This tutorial will show you how](https://getrector.com/documentation/reporting-issue-with-rector) + +## Preparing Pull Request + +3 steps will make your pull-request easy to merge: - **1 feature per pull-request** - **new features need tests** @@ -18,4 +46,32 @@ There 3 rules will highly increase changes to get your PR merged: composer fix-cs ``` -We would be happy to accept PRs that follow these guidelines. \ No newline at end of file +We would be happy to accept PRs that follow these guidelines. + +### Using Docker + +A `docker-compose.yml` file is provided to make it easier to run the CI checks locally. +To use it, you need to have Docker installed on your machine, then you can build the image and execute the +above commands in a docker container: + +```bash +# Build the docker image +docker compose build --pull + +# Install dependencies +docker compose run --rm php composer install + +# Run the entire CI suite +docker compose run --rm php composer complete-check + +# Fix the coding standards +docker compose run --rm php composer fix-cs +``` + +## TroubleShooting +If you are on macOS, and got hang on applying patch, you may need to install gpatch, you can install with: + +`brew install gpatch` + +## Repository layout +Documentation goes into `build/target-repository/docs`. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000000..446f4d3c9c4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM php:8.3-cli-alpine + +WORKDIR /etc/rector + +# required for composer patches +RUN apk add --no-cache patch git + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +RUN mkdir -p /etc/rector +RUN git config --global --add safe.directory /etc/rector diff --git a/LICENSE b/LICENSE index a89e419fd50..bdd7b9aabd5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ The MIT License --------------- -Copyright (c) 2017-present Tomáš Votruba (https://tomasvotruba.cz) +Copyright (c) 2017-present Tomas Votruba (https://tomasvotruba.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/README.md b/README.md index 30485c1066c..49960ee5ef9 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,50 @@ Head to [`rectorphp/rector`](http://github.com/rectorphp/rector) for documentati ## Building `rectorphp/rector` -Code of this repository requires PHP 8. For `rector/rector` package user the build downgrades code to PHP 7.1 and higher. +Code of this repository requires PHP 8.3. For `rector/rector` package the builder downgrades code to PHP 7.4+. + +
## How to Contribute Please read [contributing guideline](/CONTRIBUTING.md) for how to contribute to rector. +
+ +## Debug Tests + +Do you need to measure speed of particular test? Or just check which test fixture is failing? Make use of pretty print: + +```bash + vendor/bin/phpunit -d --enable-pretty-print +``` + +
+ ## Code of Conduct This project adheres to a [Contributor Code of Conduct](/CODE_OF_CONDUCT.md) By participating in this project and its community, you are expected to uphold this code. + +
+ +## Rector Packages CI Status + +**Symfony** - https://github.com/rectorphp/rector-symfony + +* ![](https://github.com/rectorphp/rector-symfony/actions/workflows/tests.yaml/badge.svg) +![](https://github.com/rectorphp/rector-symfony/actions/workflows/code_analysis.yaml/badge.svg) + +**PHPUnit** - https://github.com/rectorphp/rector-phpunit + +* ![](https://github.com/rectorphp/rector-phpunit/actions/workflows/tests.yaml/badge.svg) +![](https://github.com/rectorphp/rector-phpunit/actions/workflows/code_analysis.yaml/badge.svg) + +**Doctrine** - https://github.com/rectorphp/rector-doctrine + +* ![](https://github.com/rectorphp/rector-doctrine/actions/workflows/tests.yaml/badge.svg) +![](https://github.com/rectorphp/rector-doctrine/actions/workflows/code_analysis.yaml/badge.svg) + +**Downgrade PHP** - https://github.com/rectorphp/rector-downgrade-php + +* ![](https://github.com/rectorphp/rector-downgrade-php/actions/workflows/tests.yaml/badge.svg) +![](https://github.com/rectorphp/rector-downgrade-php/actions/workflows/code_analysis.yaml/badge.svg) diff --git a/UPGRADE.md b/UPGRADE.md deleted file mode 100644 index c55b092df4f..00000000000 --- a/UPGRADE.md +++ /dev/null @@ -1,127 +0,0 @@ -# How to Upgrade from Rector 0.9 to 0.10 (2021-03) - -Use prepare Rector set to upgrade your code: - -```bash -vendor/bin/rector process src --config vendor/rector/rector/upgrade/rector_010.php -``` - -Some changes have to be handled manually: - -## In Symfony project, clear `config/bundles.php` - -- drop `PhpConfigPrinterBundle` class - -## Removed Attributes - -- `getAttribute(AttributeKey::PARENT_CLASS_NAME)` → use `$scope->getClassReflection()` instead -- `getAttribute(AttributeKey::NAMESPACE_NAME)` → use `$scope->getNamespace()` instead -- `getAttribute(AttributeKey::NAMESPACE_NODE)` → use `$scope->getNamespace()` instead - -
- -# How to Upgrade From Rector 0.8 to 0.9 (2020-12) - -## In Symfony project, clear `config/bundles.php` - -- drop `ComposerJsonManipulatorBundle` class -- drop `ConsoleColorDiffBundle` class - -## Set Consolidation - -Sets with ambiguous naming were removed and rules moved to proper-named sets: - -```diff - use Rector\Core\Configuration\Option; - use Rector\Set\ValueObject\SetList; - use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - - return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::SETS, [ -- SetList::SOLID, -+ SetList::CODING_STYLE, -- SetList::PHPSTAN, -+ SetList::PRIVATIZATION, - ]); - }; -``` - -## Single `SKIP` option `rector.php` - -Since Rector 0.9 we switched from internal skipping to [`symplify/skipper` package](https://tomasvotruba.com/blog/2020/12/10/new-in-symplify-9-skipper-skipping-files-and-rules-made-simple/). Now there is only one `Option::SKIP` parameter to handle both paths and classes. - -Replace deprecated `Option::EXCLUDE_RECTORS` parameters with `Option::SKIP`: - -```diff - use Rector\Core\Configuration\Option; - use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - - return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - -- $parameters->set(Option::EXCLUDE_RECTORS, [ -+ $parameters->set(Option::SKIP, [ - SomeRector::class, - ]); - }; -``` - -Replace deprecated `Option::EXCLUDE_PATHS` parameters with `Option::SKIP`: - -```diff - use Rector\Core\Configuration\Option; - use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - - return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - -- $parameters->set(Option::EXCLUDE_PATHS, [ -+ $parameters->set(Option::SKIP, [ - __DIR__ . '/SomePath, - ]); - }; -``` - -Be sure to have **exactly 1** `Option::SKIP` in the end, as the Symfony parameters are not merged, but overridden: - -```diff - use Rector\Core\Configuration\Option; - use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - - return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::SKIP, [ - SomeRector::class, -- ]); -- -- $parameters->set(Option::SKIP, [ - __DIR__ . '/SomePath, - ]); - }; -``` - -## From CLI `--set`/`--level` to config - -Rector now works more and more with set stacking. The number of sets is growing and IDE autocomplete helps to work with them effectively. If you use these options in CLI, move them to `rector.php` config like this: - -```diff --vendor/bin/rector process src --set php80 -+vendor/bin/rector process src -``` - -```diff - use Rector\Core\Configuration\Option; -+use Rector\Set\ValueObject\SetList; - use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - - return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::SETS, [ -+ SetList::PHP_80, - ]); - }; -``` diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 00000000000..f2089bac57d --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,213 @@ +# Upgrading from Rector 2.2.14 to 2.3 + +* `FileWithoutNamespace` is deprecated, and replaced by `FileNode` that represents both namespaced and non-namespaced files and allow changes inside +* `beforeTraverse()` is now marked as `@final`, use `getNodeTypes()` with `FileNode::class` instead + +**Before** + +```php +use Rector\PhpParser\Node\FileWithoutNamespace; +use Rector\Rector\AbstractRector; + +final class SomeRector extends AbstractRector +{ + public function getNodeTypes(): array + { + return [FileWithoutNamespace::class]; + } + + public function beforeTraverse(array $nodes): array + { + // some node hacking + } + + /** + * @param FileWithoutNamespace $node + */ + public function refactor(Node $node): ?Node + { + // ... + } + +} +``` + +**After** + +```php +use Rector\PhpParser\Node\FileNode; +use Rector\Rector\AbstractRector; + +final class SomeRector extends AbstractRector +{ + public function getNodeTypes(): array + { + return [FileNode::class]; + } + + /** + * @param FileNode $node + */ + public function refactor(Node $node): ?Node + { + foreach ($node->stmts as $stmt) { + // check if has declare_strict already? + // ... + + // create it + $declareStrictTypes = $this->createDeclareStrictTypesNode(); + + // add it + $node->stmts = array_merge([$declareStrictTypes], $node->stmts); + } + + return $node; + } + +} +``` + +
+ +The `FileNode` handles both namespaced and non-namespaced files. To handle the first stmts inside the file, you hook into 2 nodes: + +```php +use Rector\PhpParser\Node\FileNode; +use Rector\Rector\AbstractRector; +use PhpParser\Node\Stmt\Namespace_; + +final class SomeRector extends AbstractRector +{ + public function getNodeTypes(): array + { + return [FileNode::class, Namespace_::class]; + } + + /** + * @param FileNode|Namespace_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node instanceof FileNode && $node->isNamespaced()) { + // handled in the Namespace_ node + return null; + } + + foreach ($node->stmts as $stmt) { + // modify stmts in desired way here + } + + return $node; + } + +} +``` + +
+ +# Upgrading from Rector 1.x to 2.0 + +## PHP version requirements + +Rector now uses PHP 7.4 or newer to run. + +
+ +## Rector now uses PHP-Parser 5 + +See [upgrading guide](https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md) for PHP-Parser. + +
+ +## Rector now uses PHPStan 2 + +See [upgrading guide](https://github.com/phpstan/phpstan-src/blob/2.0.x/UPGRADING.md) for PHPStan. + +
+ +## Upgrade for custom Rules writers + +### 1. `AbstractScopeAwareRector` is removed, use `AbstractRector` instead + +The `Rector\Rector\AbstractScopeAwareRector` was too granular to fetch single helper object. It made creating new custom rules ambiguous, one layer more complex and confusing. This class has been removed in favor of standard `AbstractRector`. The `Scope` object can be fetched via `ScopeFetcher`. + +**Before** + +```php +use Rector\Rector\AbstractScopeAwareRector; + +final class SimpleRector extends AbstractScopeAwareRector +{ + public function refactorWithScope(Node $node, Scope $scope): ?Node + { + // ... + } +} +``` + +**After** + +```php +use Rector\Rector\AbstractRector; +use Rector\PHPStan\ScopeFetcher; + +final class SimpleRector extends AbstractRector +{ + public function refactor(Node $node): ?Node + { + if (...) { + // this allow to fetch scope only when needed + $scope = ScopeFetcher::fetch($node); + } + + // ... + } +} +``` + + +### 2. `AbstractRector` get focused on code, the `getRuleDefinition()` is no longer required + +Core rules need documentation, so people can read their feature and [search through](https://getrector.com/find-rule) them. Yet for writing custom rules and local rules, its not necessary. People often filled it empty, just to make Rector happy. + +This is no longer needed. Now The `getRuleDefinition()` method has been removed: + +```diff + use Rector\Rector\AbstractRector; +-use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +-use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; + + final class SimpleRector extends AbstractRector + { +- public function getRuleDefinition(): RuleDefinition +- { +- return new RuleDefinition('// @todo fill the description', [ +- new CodeSample( +- <<<'CODE_SAMPLE' +-// @todo fill code before +-CODE_SAMPLE +- , +- <<<'CODE_SAMPLE' +-// @todo fill code after +-CODE_SAMPLE +- ), +- ]); +- } + + // valuable code here + } +``` + +If you need description yourself to understand rule after many months, use the common place for documentation - docblock above class. + + +### 3. `SetListInterface` was removed + +The deprecated `SetListInterface` was removed, if you created your own list just remove the Interface from it: + +```diff +-use Rector\Set\Contract\SetListInterface; + +-final class YourSetList implements SetListInterface ++final class YourSetList +``` diff --git a/bin/rector.php b/bin/rector.php index 85dfaf75d62..5860ed1b7d0 100755 --- a/bin/rector.php +++ b/bin/rector.php @@ -3,16 +3,16 @@ declare(strict_types=1); use Nette\Utils\Json; +use Rector\Bootstrap\RectorConfigsResolver; use Rector\ChangesReporting\Output\JsonOutputFormatter; -use Rector\Core\Bootstrap\RectorConfigsResolver; -use Rector\Core\Configuration\Option; -use Rector\Core\Console\ConsoleApplication; -use Rector\Core\Console\Style\SymfonyStyleFactory; -use Rector\Core\DependencyInjection\RectorContainerFactory; -use Rector\Core\HttpKernel\RectorKernel; +use Rector\Configuration\Option; +use Rector\Console\Style\SymfonyStyleFactory; +use Rector\DependencyInjection\LazyContainerFactory; +use Rector\DependencyInjection\RectorContainerFactory; +use Rector\Util\Reflection\PrivatesAccessor; +use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArgvInput; -use Symplify\PackageBuilder\Reflection\PrivatesCaller; // @ intentionally: continue anyway @ini_set('memory_limit', '-1'); @@ -24,73 +24,21 @@ define('__RECTOR_RUNNING__', true); - // Require Composer autoload.php $autoloadIncluder = new AutoloadIncluder(); $autoloadIncluder->includeDependencyOrRepositoryVendorAutoloadIfExists(); - -// load extracted PHPStan with its own preload.php -$extractedPhpstanAutoload = __DIR__ . '/../vendor/phpstan/phpstan-extracted/vendor/autoload.php'; -if (file_exists($extractedPhpstanAutoload)) { - require_once $extractedPhpstanAutoload; -} elseif (should_include_preload()) { - require_once __DIR__ . '/../preload.php'; -} - -require_once __DIR__ . '/../src/constants.php'; - -// pre-set for PHP 5.6/7.0 downgraded version -$autoloadIncluder->loadIfExistsAndNotLoadedYet( - __DIR__ . '/../vendor/phpstan/phpstan-extracted/vendor/phpstan-autoload.php' -); - -$autoloadIncluder->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/scoper-autoload.php'); - -$autoloadIncluder->autoloadProjectAutoloaderFile(); -$autoloadIncluder->autoloadFromCommandLine(); - -$rectorConfigsResolver = new RectorConfigsResolver(); - -try { - $bootstrapConfigs = $rectorConfigsResolver->provide(); - $rectorContainerFactory = new RectorContainerFactory(); - $container = $rectorContainerFactory->createFromBootstrapConfigs($bootstrapConfigs); -} catch (Throwable $throwable) { - // for json output - $argvInput = new ArgvInput(); - $outputFormat = $argvInput->getParameterOption('--' . Option::OUTPUT_FORMAT); - - // report fatal error in json format - if ($outputFormat === JsonOutputFormatter::NAME) { - echo Json::encode([ - 'fatal_errors' => [$throwable->getMessage()], - ]); - } else { - // report fatal errors in console format - $symfonyStyleFactory = new SymfonyStyleFactory(new PrivatesCaller()); - $symfonyStyle = $symfonyStyleFactory->create(); - $symfonyStyle->error($throwable->getMessage()); - } - - exit(Command::FAILURE); -} - -/** @var ConsoleApplication $application */ -$application = $container->get(ConsoleApplication::class); -exit($application->run()); - final class AutoloadIncluder { /** * @var string[] */ - private $alreadyLoadedAutoloadFiles = []; + private array $alreadyLoadedAutoloadFiles = []; public function includeDependencyOrRepositoryVendorAutoloadIfExists(): void { // Rector's vendor is already loaded - if (class_exists(RectorKernel::class)) { + if (class_exists(LazyContainerFactory::class)) { return; } @@ -107,12 +55,34 @@ public function autoloadProjectAutoloaderFile(): void $this->loadIfExistsAndNotLoadedYet(__DIR__ . '/../../../autoload.php'); } + /** + * In case Rector is installed as global dependency + */ + public function autoloadRectorInstalledAsGlobalDependency(): void + { + if (dirname(__DIR__) === dirname(getcwd(), 2)) { + return; + } + + if (is_dir('vendor/rector/rector')) { + return; + } + + $this->loadIfExistsAndNotLoadedYet('vendor/autoload.php'); + } + public function autoloadFromCommandLine(): void { $cliArgs = $_SERVER['argv']; - $autoloadOptionPosition = array_search('-a', $cliArgs, true) ?: array_search('--autoload-file', $cliArgs, true); - if (! $autoloadOptionPosition) { + $aOptionPosition = array_search('-a', $cliArgs, true); + $autoloadFileOptionPosition = array_search('--autoload-file', $cliArgs, true); + + if (is_int($aOptionPosition)) { + $autoloadOptionPosition = $aOptionPosition; + } elseif (is_int($autoloadFileOptionPosition)) { + $autoloadOptionPosition = $autoloadFileOptionPosition; + } else { return; } @@ -127,7 +97,6 @@ public function autoloadFromCommandLine(): void public function loadIfExistsAndNotLoadedYet(string $filePath): void { - // the scoper-autoload.php is exists in phpstan-extracted/vendor/scoper-autoload.php, move the check in : if (! file_exists($filePath)) { return; } @@ -136,24 +105,54 @@ public function loadIfExistsAndNotLoadedYet(string $filePath): void return; } - $this->alreadyLoadedAutoloadFiles[] = realpath($filePath); + /** @var non-empty-string $realPath always string after file_exists() check */ + $realPath = realpath($filePath); + $this->alreadyLoadedAutoloadFiles[] = $realPath; require_once $filePath; } } +if (file_exists(__DIR__ . '/../preload.php') && is_dir(__DIR__ . '/../vendor')) { + require_once __DIR__ . '/../preload.php'; +} + +// require rector-src on split packages +if (file_exists(__DIR__ . '/../preload-split-package.php') && is_dir(__DIR__ . '/../../../../vendor')) { + require_once __DIR__ . '/../preload-split-package.php'; +} -// load local php-parser only in prefixed version or development repository -function should_include_preload(): bool -{ - if (file_exists(__DIR__ . '/../vendor/scoper-autoload.php')) { - return true; - } +$autoloadIncluder->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/scoper-autoload.php'); +$autoloadIncluder->autoloadProjectAutoloaderFile(); +$autoloadIncluder->autoloadRectorInstalledAsGlobalDependency(); +$autoloadIncluder->autoloadFromCommandLine(); + +$rectorConfigsResolver = new RectorConfigsResolver(); - if (! file_exists(getcwd() . '/composer.json')) { - return false; +try { + $bootstrapConfigs = $rectorConfigsResolver->provide(); + $rectorContainerFactory = new RectorContainerFactory(); + $container = $rectorContainerFactory->createFromBootstrapConfigs($bootstrapConfigs); +} catch (Throwable $throwable) { + // for json output + $argvInput = new ArgvInput(); + $outputFormat = $argvInput->getParameterOption('--' . Option::OUTPUT_FORMAT); + + // report fatal error in json format + if ($outputFormat === JsonOutputFormatter::NAME) { + echo Json::encode([ + 'fatal_errors' => [$throwable->getMessage()], + ]); + } else { + // report fatal errors in console format + $symfonyStyleFactory = new SymfonyStyleFactory(new PrivatesAccessor()); + $symfonyStyle = $symfonyStyleFactory->create(); + $symfonyStyle->error(str_replace("\r\n", "\n", $throwable->getMessage())); } - $composerJsonFileContent = file_get_contents(getcwd() . '/composer.json'); - return strpos($composerJsonFileContent, '"name": "rector/rector"') !== false; + exit(Command::FAILURE); } + +/** @var Application $application */ +$application = $container->get(Application::class); +exit($application->run()); diff --git a/build/build-preload.php b/build/build-preload.php index 521ad73e633..c7c62f96787 100755 --- a/build/build-preload.php +++ b/build/build-preload.php @@ -5,50 +5,377 @@ declare(strict_types=1); use Nette\Utils\Strings; +use Rector\Console\Style\SymfonyStyleFactory; +use Rector\Util\Reflection\PrivatesAccessor; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Finder\Finder; -require __DIR__ . '/../vendor/autoload.php'; +$possiblePaths = [ + // rector-src + __DIR__ . '/../vendor/autoload.php', + // rector package dependency + __DIR__ . '/../../../../vendor/autoload.php', +]; + +foreach ($possiblePaths as $possiblePath) { + if (! file_exists($possiblePath)) { + continue; + } + + require $possiblePath; + break; +} $buildDirectory = $argv[1]; -buildPreloadScript($buildDirectory); +$symfonyStyleFactory = new SymfonyStyleFactory(new PrivatesAccessor()); +$symfonyStyle = $symfonyStyleFactory->create(); -function buildPreloadScript(string $buildDirectory): void -{ - $vendorDir = $buildDirectory . '/vendor'; - if (!is_dir($vendorDir . '/nikic/php-parser/lib/PhpParser')) { - return; - } +if (! is_string($buildDirectory)) { + $errorMessage = 'Provide build directory path as an argument, e.g. "php build-preload.php rector-build-directory"'; + $symfonyStyle->error($errorMessage); + exit(Command::FAILURE); +} + +$preloadBuilder = new PreloadBuilder(); +$preloadBuilder->buildPreloadScript($buildDirectory, $buildDirectory . '/preload.php'); +$preloadBuilder->buildPreloadScriptForSplitPackage($buildDirectory, $buildDirectory . '/preload-split-package.php'); - $preloadFileContent = <<<'php' +final class PreloadBuilder +{ + private const string PRELOAD_FILE_TEMPLATE = <<<'CODE_SAMPLE' buildPreloadScriptPhpParser($buildDirectory, $preloadFile); + $this->buildPreloadScriptPhpDocParser($buildDirectory, $preloadFile); + } + + public function buildPreloadScriptForSplitPackage(string $buildDirectory, string $preloadFile): void + { + $this->buildPreloadScriptForSplitPhpParser($buildDirectory, $preloadFile); + $this->buildPreloadScriptForSplitPhpDocParser($buildDirectory, $preloadFile); + } + + private function buildPreloadScriptForSplitPhpParser(string $buildDirectory, string $preloadFile): void + { + $vendorDir = $buildDirectory . '/vendor'; + if (! is_dir($vendorDir . '/nikic/php-parser/lib/PhpParser')) { + return; + } + + // 1. find php-parser file infos + $fileInfos = $this->findPhpParserFilesAndSortThem($vendorDir); -php; + // 2. create preload.php from provided files + $preloadFileContent = $this->createPreloadFileContentForSplitPackage($fileInfos); - $finder = (new Finder()) - ->files() - ->name('*.php') - ->in($vendorDir . '/nikic/php-parser/lib/PhpParser') - ->notPath('#\/tests\/#') - ->notPath('#\/config\/#') - ->notPath('#\/set\/#') - ->in($vendorDir . '/symplify/symfony-php-config'); + file_put_contents($preloadFile, $preloadFileContent); + } + + private function buildPreloadScriptForSplitPhpDocParser(string $buildDirectory, string $preloadFile): void + { + $vendorDir = $buildDirectory . '/vendor'; + if (! is_dir($vendorDir . '/phpstan/phpdoc-parser')) { + return; + } - $fileInfos = iterator_to_array($finder->getIterator()); - $fileInfos[] = new SplFileInfo(__DIR__ . '/../vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php'); + // 1. find phpdoc-parser file infos + $fileInfos = $this->findPhpDocParserFilesAndSortThem($vendorDir); + + // 2. create preload-split-package.php from provided files + $preloadFileContent = $this->createPreloadFileContentForSplitPackage($fileInfos, true); + + file_put_contents($preloadFile, $preloadFileContent, FILE_APPEND); + } - foreach ($fileInfos as $fileInfo) { - $realPath = $fileInfo->getRealPath(); - if ($realPath === false) { - continue; + private function buildPreloadScriptPhpDocParser(string $buildDirectory, string $preloadFile): void + { + $vendorDir = $buildDirectory . '/vendor'; + if (! is_dir($vendorDir . '/phpstan/phpdoc-parser')) { + return; + } + + // 1. find phpdoc-parser file infos + $fileInfos = $this->findPhpDocParserFilesAndSortThem($vendorDir); + + // 2. create preload.php from provided files + $preloadFileContent = $this->createPreloadFileContent($fileInfos, true); + + file_put_contents($preloadFile, $preloadFileContent, FILE_APPEND); + } + + private function buildPreloadScriptPhpParser(string $buildDirectory, string $preloadFile): void + { + $vendorDir = $buildDirectory . '/vendor'; + if (! is_dir($vendorDir . '/nikic/php-parser/lib/PhpParser')) { + return; + } + + // 1. find php-parser file infos + $fileInfos = $this->findPhpParserFilesAndSortThem($vendorDir); + + // 3. create preload.php from provided files + $preloadFileContent = $this->createPreloadFileContent($fileInfos); + + file_put_contents($preloadFile, $preloadFileContent); + } + + /** + * @return SplFileInfo[] + */ + private function findPhpParserFiles(string $vendorDir): array + { + $finder = (new Finder()) + ->files() + ->name('*.php') + ->in($vendorDir . '/nikic/php-parser/lib/PhpParser') + ->notPath('#\/tests\/#') + ->notPath('#\/config\/#') + ->notPath('#\/set\/#') + ->sortByName(); + + return iterator_to_array($finder->getIterator()); + } + + /** + * @return SplFileInfo[] + */ + private function findPhpDocParserFiles(string $vendorDir): array + { + $finder = (new Finder()) + ->files() + ->name('*.php') + ->in($vendorDir . '/phpstan/phpdoc-parser') + ->sortByName(); + + return iterator_to_array($finder->getIterator()); + } + + /** + * @param SplFileInfo[] $fileInfos + */ + private function createPreloadFileContent(array $fileInfos, bool $append = false): string + { + $preloadFileContent = $append ? '' : self::PRELOAD_FILE_TEMPLATE . PHP_EOL . PHP_EOL; + + foreach ($fileInfos as $fileInfo) { + $realPath = $fileInfo->getRealPath(); + if ($realPath === false) { + continue; + } + + $preloadFileContent .= $this->createRequireOnceFilePathLine($realPath); + } + + return $preloadFileContent; + } + + /** + * @param SplFileInfo[] $fileInfos + */ + private function createPreloadFileContentForSplitPackage(array $fileInfos, bool $append = false): string + { + $preloadFileContent = $append ? '' : self::PRELOAD_FILE_TEMPLATE . PHP_EOL . PHP_EOL; + + foreach ($fileInfos as $fileInfo) { + $realPath = $fileInfo->getRealPath(); + if ($realPath === false) { + continue; + } + + $preloadFileContent .= $this->createRequireOnceFilePathLineForSplitPackage($realPath); + } + + return $preloadFileContent; + } + + private function createRequireOnceFilePathLineForSplitPackage(string $realPath): string + { + if (! str_contains($realPath, 'vendor')) { + $filePath = '/src/' . Strings::after($realPath, '/src/'); + return "require_once __DIR__ . '" . $filePath . "';" . PHP_EOL; + } + + $filePath = '/../../../vendor/' . Strings::after($realPath, 'vendor/'); + return "require_once __DIR__ . '" . $filePath . "';" . PHP_EOL; + } + + private function createRequireOnceFilePathLine(string $realPath): string + { + if (! str_contains($realPath, 'vendor')) { + $filePath = '/src/' . Strings::after($realPath, '/src/'); + return "require_once __DIR__ . '" . $filePath . "';" . PHP_EOL; } $filePath = '/vendor/' . Strings::after($realPath, 'vendor/'); - $preloadFileContent .= "require_once __DIR__ . '" . $filePath . "';" . PHP_EOL; + return "require_once __DIR__ . '" . $filePath . "';" . PHP_EOL; } - file_put_contents($buildDirectory . '/preload.php', $preloadFileContent); + private function matchFilePriorityPosition(SplFileInfo $splFileInfo): int + { + // to make <=> operator work + $highPriorityFiles = array_reverse(self::HIGH_PRIORITY_FILES); + + $fileRealPath = $splFileInfo->getRealPath(); + + // file not found, e.g. in rector-src dev dependency + if ($fileRealPath === false) { + return 0; + } + + foreach ($highPriorityFiles as $position => $highPriorityFile) { + if (str_ends_with($fileRealPath, '/' . $highPriorityFile)) { + return $position; + } + } + + return self::PRIORITY_LESS_FILE_POSITION; + } + + /** + * @return SplFileInfo[] + */ + private function findPhpDocParserFilesAndSortThem(string $vendorDir): array + { + // 1. find php-parser file infos + $fileInfos = $this->findPhpDocParserFiles($vendorDir); + + // 2. put first-class usages first + return $this->sortFileInfos($fileInfos); + } + + /** + * @return SplFileInfo[] + */ + private function findPhpParserFilesAndSortThem(string $vendorDir): array + { + // 1. find php-parser file infos + $fileInfos = $this->findPhpParserFiles($vendorDir); + + // 2. put first-class usages first + $fileInfos = $this->sortFileInfos($fileInfos); + + foreach ($fileInfos as $key => $fileInfo) { + foreach (self::IN_USE_CLASS_FILES as $inUseClassFile) { + if (str_ends_with($fileInfo->getPathname(), $inUseClassFile)) { + unset($fileInfos[$key]); + continue 2; + } + } + } + + $fileInfos = array_values($fileInfos); + + $stmtsAwareInterface = new SplFileInfo(__DIR__ . '/../src/Contract/PhpParser/Node/StmtsAwareInterface.php'); + array_splice($fileInfos, 1, 0, [$stmtsAwareInterface]); + + return $fileInfos; + } + + /** + * @param SplFileInfo[] $fileInfos + * @return SplFileInfo[] + */ + private function sortFileInfos(array $fileInfos): array + { + usort($fileInfos, function (SplFileInfo $firstFileInfo, SplFileInfo $secondFileInfo): int { + $firstFilePosition = $this->matchFilePriorityPosition($firstFileInfo); + $secondFilePosition = $this->matchFilePriorityPosition($secondFileInfo); + + return $secondFilePosition <=> $firstFilePosition; + }); + + return $fileInfos; + } } diff --git a/build/build-rector-scoped.sh b/build/build-rector-scoped.sh index b63bf73eb77..198919e9f8b 100644 --- a/build/build-rector-scoped.sh +++ b/build/build-rector-scoped.sh @@ -5,11 +5,8 @@ export TERM=xterm-color # show errors set -e - -# script fails if trying to access to an undefined variable set -u - # functions note() { @@ -29,18 +26,23 @@ RESULT_DIRECTORY=$2 note "Starts" -# this will remove dependency on dev packages that are imported in phpstan.neon -rm -f "$BUILD_DIRECTORY/phpstan-for-rector.neon" - # 2. scope it -note "Running scoper to $RESULT_DIRECTORY" -wget https://github.com/humbug/php-scoper/releases/download/0.14.0/php-scoper.phar -N --no-verbose +note "Downloading php-scoper.phar" +wget https://github.com/humbug/php-scoper/releases/download/0.18.17/php-scoper.phar -N --no-verbose -# Work around possible PHP memory limits -php -d memory_limit=-1 php-scoper.phar add-prefix bin config src packages rules vendor composer.json --output-dir "../$RESULT_DIRECTORY" --config scoper.php --force --ansi --working-dir "$BUILD_DIRECTORY" +php "$BUILD_DIRECTORY/scripts/add-phpstan-self-replace.php" +note "Remove PHPStan to avoid duplicating it" + +composer remove phpstan/phpstan -W --update-no-dev --working-dir "$BUILD_DIRECTORY" + +note "PHPStan now removed, safe to start php-scoper from here" + +# Work around possible PHP memory limits +note "Running php-scoper on /bin, /config, /src, /rules and /vendor" +php -d memory_limit=-1 php-scoper.phar add-prefix bin config src rules vendor composer.json UPGRADING.md --output-dir "../$RESULT_DIRECTORY" --config scoper.php --force --ansi --working-dir "$BUILD_DIRECTORY"; -# note "Dumping Composer Autoload" +note "Dumping prefixed Composer Autoload" composer dump-autoload --working-dir "$RESULT_DIRECTORY" --ansi --classmap-authoritative --no-dev rm -rf "$BUILD_DIRECTORY" diff --git a/build/config/config-downgrade.php b/build/config/config-downgrade.php index 9ba3f22d9f4..d82be6a89d2 100644 --- a/build/config/config-downgrade.php +++ b/build/config/config-downgrade.php @@ -2,76 +2,14 @@ declare(strict_types=1); -use PhpParser\NodeVisitor; -use PhpParser\NodeVisitorAbstract; -use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator; -use PHPStan\PhpDocParser\Ast\Node; -use Rector\Core\Configuration\Option; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Contract\Rector\RectorInterface; -use Rector\Core\Stubs\PHPStanStubLoader; -use Rector\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector; -use Rector\NodeNameResolver\Contract\NodeNameResolverInterface; -use Rector\Set\ValueObject\DowngradeSetList; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\StyleInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SimplePhpDocParser\Contract\PhpDocNodeVisitorInterface; +use Rector\Config\RectorConfig; -$phpStanStubLoader = new PHPStanStubLoader(); -$phpStanStubLoader->loadStubs(); +require_once __DIR__ . '/../target-repository/stubs-rector/PHPUnit/Framework/TestCase.php'; -require_once __DIR__ . '/../../stubs-rector/PHPUnit/Framework/TestCase.php'; -require_once __DIR__ . '/../../stubs/Composer/EventDispatcher/EventSubscriberInterface.php'; -require_once __DIR__ . '/../../stubs/Composer/Plugin/PluginInterface.php'; -require_once __DIR__ . '/../../stubs/Nette/DI/CompilerExtension.php'; - -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::SKIP, DowngradeRectorConfig::DEPENDENCY_EXCLUDE_PATHS); - $parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, __DIR__ . '/phpstan-for-downgrade.neon'); - - $containerConfigurator->import(DowngradeSetList::PHP_80); - $containerConfigurator->import(DowngradeSetList::PHP_74); - $containerConfigurator->import(DowngradeSetList::PHP_73); - $containerConfigurator->import(DowngradeSetList::PHP_72); - - $services = $containerConfigurator->services(); - $services->set(DowngradeParameterTypeWideningRector::class) - ->call('configure', [[ - DowngradeParameterTypeWideningRector::SAFE_TYPES => [ - RectorInterface::class, - NodeVisitorAbstract::class, - NodeVisitor::class, - ConfigurableRectorInterface::class, - OutputInterface::class, - StyleInterface::class, - PhpDocNodeVisitorInterface::class, - Node::class, - NodeNameResolverInterface::class, - // phpstan - SourceLocator::class, - \PHPStan\PhpDocParser\Ast\Node::class, - \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode::class, - \PHPStan\PhpDocParser\Ast\NodeAttributes::class, - \PhpParser\Parser::class, - \Rector\Naming\Contract\RenameParamValueObjectInterface::class, - \Symplify\RuleDocGenerator\Contract\RuleCodeSamplePrinterInterface::class, - \Symplify\RuleDocGenerator\Contract\Category\CategoryInfererInterface::class, - \PhpParser\PrettyPrinterAbstract::class, - \Helmich\TypoScriptParser\Parser\Traverser\Visitor::class, - ], - DowngradeParameterTypeWideningRector::SAFE_TYPES_TO_METHODS => [ - ContainerInterface::class => [ - 'setParameter', - 'getParameter', - 'hasParameter', - ], - ], - ]]); -}; +return RectorConfig::configure() + ->withSkip(DowngradeRectorConfig::DEPENDENCY_EXCLUDE_PATHS) + ->withPHPStanConfigs([__DIR__ . '/phpstan-for-downgrade.neon']) + ->withDowngradeSets(php74: true); /** * Configuration consts for the different rector.php config files @@ -86,28 +24,11 @@ final class DowngradeRectorConfig // symfony test are parts of package '*/Test/*', - // only for dev - 'packages/Testing/PhpConfigPrinter/*', - // Individual classes that can be excluded because // they are not used by Rector, and they use classes // loaded with "require-dev" so it'd throw an error - - // use relative paths, so files are excluded on nested directory too - 'vendor/symfony/http-kernel/HttpKernelBrowser.php', - 'vendor/symfony/http-foundation/Session/*', - 'vendor/symfony/string/Slugger/AsciiSlugger.php', - 'vendor/symfony/cache/*', - 'nette/caching/src/Bridges/*', - - // This class has an issue for PHP 7.1: - // https://github.com/rectorphp/rector/issues/4816#issuecomment-743209526 - // It doesn't happen often, and Rector doesn't use it, so then - // we simply skip downgrading this class - 'vendor/symfony/dependency-injection/ExpressionLanguage.php', - 'vendor/symfony/dependency-injection/ExpressionLanguageProvider.php', - 'vendor/symfony/var-dumper/Caster/*', - // depends on PHPUnit, that is only in dev deps - 'vendor/myclabs/php-enum/src/PHPUnit/Comparator.php', + // only for composer patches on composer install - not needed in final package + 'vendor/cweagans/*', + 'vendor/rector/rector-generator/templates', ]; } diff --git a/build/config/phpstan-for-downgrade.neon b/build/config/phpstan-for-downgrade.neon index b3c3d93f88f..963c89ec1d3 100644 --- a/build/config/phpstan-for-downgrade.neon +++ b/build/config/phpstan-for-downgrade.neon @@ -3,9 +3,5 @@ parameters: inferPrivatePropertyTypeFromConstructor: true scanDirectories: - # this is needed for symfony/dependendency-injection as it has hidden dependency on symfony/expression-language that we don't use here + # this is needed for symfony/dependency-injection as it has hidden dependency on symfony/expression-language that we don't use here - ../../stubs - - # see https://github.com/rectorphp/rector/issues/3490#issue-634342324 - featureToggles: - disableRuntimeReflectionProvider: true diff --git a/build/downgrade-rector.sh b/build/downgrade-rector.sh deleted file mode 100644 index b4a279e89b6..00000000000 --- a/build/downgrade-rector.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -# see https://stackoverflow.com/questions/66644233/how-to-propagate-colors-from-bash-script-to-github-action?noredirect=1#comment117811853_66644233 -export TERM=xterm-color - -# show errors -set -e - -# script fails if trying to access to an undefined variable -set -u - - -# configure - 1st argument, use like -# sh build/downgrade-rector.sh -BUILD_DIRECTORY=$1 - -#--------------------------------------------- - -# 1. downgrade it -echo "[NOTE] Running downgrade in '$BUILD_DIRECTORY' directory\n"; - -# 2. debug downgrade paths -bin/rector downgrade-paths --config build/config/config-downgrade.php --working-dir $BUILD_DIRECTORY --ansi - -# 3. provide directories to downgrade; includes the rector dirs -directories=$(php bin/rector downgrade-paths --config build/config/config-downgrade.php --working-dir $BUILD_DIRECTORY --ansi) -# experimental - -# split array see https://stackoverflow.com/a/1407098/1348344 -export IFS=";" - -# 4. downgrade the directories -for directory in $directories; do - echo "[NOTE] Downgrading '$directory' directory\n" - - # --working-dir is needed, so "SKIP" parameter is applied in absolute path of nested directory - php -d memory_limit=-1 bin/rector process $directory --config build/config/config-downgrade.php --working-dir $BUILD_DIRECTORY --ansi -done - - -# CONFIRMED: give time to print all the files, before the next process takes over newly printed content -# avoids bugs like these half of files done, next half waiting https://github.com/rectorphp/rector-src/runs/2565478682 -sleep 35 diff --git a/build/target-repository/.gitattributes b/build/target-repository/.gitattributes index e5542d52d2f..63ea11b4663 100644 --- a/build/target-repository/.gitattributes +++ b/build/target-repository/.gitattributes @@ -17,7 +17,7 @@ CODE_OF_CONDUCT.md export-ignore # exclude only root dirs, otherwise nette/utils is removed - see https://github.com/TomasVotruba/tomasvotruba.com/pull/1197/checks?check_run_id=2577329283 /stubs export-ignore -docs/images export-ignore +docs/ export-ignore .github export-ignore /e2e export-ignore diff --git a/build/target-repository/.github/ISSUE_TEMPLATE/1_Bug_report.md b/build/target-repository/.github/ISSUE_TEMPLATE/1_Bug_report.md index c6624245929..5d619481cb7 100644 --- a/build/target-repository/.github/ISSUE_TEMPLATE/1_Bug_report.md +++ b/build/target-repository/.github/ISSUE_TEMPLATE/1_Bug_report.md @@ -8,15 +8,15 @@ about: Report errors and problems -| Subject | Details | -| :------------- | :---------------------------------------------------------------| -| Rector version | e.g. v0.11.5 (invoke `vendor/bin/rector --version`) | +| Subject | Details | +| :------------- | :------------------------------------------------- | +| Rector version | e.g. v1.0.0 (invoke `vendor/bin/rector --version`) | ## Minimal PHP Code Causing Issue - + ## Expected Behaviour diff --git a/build/target-repository/.github/workflows/along_other_packages.yaml b/build/target-repository/.github/workflows/along_other_packages.yaml index 596b2affbf6..178bdb987ad 100644 --- a/build/target-repository/.github/workflows/along_other_packages.yaml +++ b/build/target-repository/.github/workflows/along_other_packages.yaml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php_version: ['7.1', '7.2', '7.3', '7.4', '8.0'] + php_version: ['7.4', '8.0', '8.1'] commands: - name: 'Composer Dependency' @@ -26,12 +26,12 @@ jobs: - name: 'Along PHPStan' - install: composer require phpstan/phpstan:^0.12.96 --dev --ansi + install: composer require phpstan/phpstan:^2.0 --dev --ansi name: "PHP ${{ matrix.php_version }}" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: @@ -39,7 +39,7 @@ jobs: coverage: none # wait for deploy to packagist - - run: sleep 70 + - run: sleep 40 - run: | mkdir standalone @@ -47,6 +47,9 @@ jobs: - run: | # run + yes | composer init --name some/prj + composer config minimum-stability dev + composer config prefer-stable true composer require rector/rector:dev-main --dev --ansi ${{ matrix.commands.install }} working-directory: standalone diff --git a/build/target-repository/.github/workflows/bare_run.yaml b/build/target-repository/.github/workflows/bare_run.yaml deleted file mode 100644 index 013e982c267..00000000000 --- a/build/target-repository/.github/workflows/bare_run.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: Bare Run - -on: - pull_request: null - push: - branches: - - main - -jobs: - bare_run: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - php_version: ['7.1', '7.2', '7.3', '7.4', '8.0'] - - steps: - - uses: actions/checkout@v2 - - - - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php_version }} - coverage: none - - - run: php bin/rector --version --ansi - - - run: php bin/rector list --ansi diff --git a/build/target-repository/.github/workflows/e2e.yaml b/build/target-repository/.github/workflows/e2e.yaml index b0cd9010e7a..c09aa86b2f8 100644 --- a/build/target-repository/.github/workflows/e2e.yaml +++ b/build/target-repository/.github/workflows/e2e.yaml @@ -4,7 +4,6 @@ name: End to End tests on: - pull_request: null push: branches: - main @@ -15,18 +14,17 @@ jobs: strategy: fail-fast: false matrix: - php_version: ['7.1', '7.2', '7.3', '7.4', '8.0'] + php_version: ['7.4', '8.0', '8.1'] directory: - - 'e2e/attributes' + - 'e2e/define-constant' - 'e2e/dont-execute-code' - - 'e2e/finalize' - 'e2e/parse-php7-code' - 'e2e/parse-php8-code' - name: End to end test - ${{ matrix.directory }} + name: Tests on PHP ${{ matrix.php_version }} - ${{ matrix.directory }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: @@ -34,9 +32,9 @@ jobs: coverage: none - - run: composer install --ansi + run: composer require rector/rector:dev-main --dev working-directory: ${{ matrix.directory }} - - run: ../../bin/rector process --dry-run --ansi + run: vendor/bin/rector process --dry-run --ansi working-directory: ${{ matrix.directory }} diff --git a/build/target-repository/.github/workflows/e2e_diff.yaml b/build/target-repository/.github/workflows/e2e_diff.yaml new file mode 100644 index 00000000000..2bf284f0be9 --- /dev/null +++ b/build/target-repository/.github/workflows/e2e_diff.yaml @@ -0,0 +1,37 @@ +# This workflow runs system tests: Use the Rector application from the source +# checkout to process "fixture" projects in tests/system-tests +# to see if those can be processed successfully +name: End to End tests that expect diff + +on: + push: + branches: + - main + +jobs: + end_to_end_with_diff: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php_version: ['7.4', '8.0', '8.1'] + directory: + - 'e2e/attributes' + + name: Tests with diffs on PHP ${{ matrix.php_version }} - ${{ matrix.directory }} + + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + coverage: none + + - + run: composer require rector/rector:dev-main --dev + working-directory: ${{ matrix.directory }} + + - + run: vendor/bin/rector process --ansi + working-directory: ${{ matrix.directory }} diff --git a/build/target-repository/.github/workflows/e2e_global.yaml b/build/target-repository/.github/workflows/e2e_global.yaml new file mode 100644 index 00000000000..8e7c55f9471 --- /dev/null +++ b/build/target-repository/.github/workflows/e2e_global.yaml @@ -0,0 +1,39 @@ +name: End to End global tests + +on: + push: + branches: + - main + +jobs: + end_to_end: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php_version: ['7.4', '8.0', '8.1'] + directory: + - 'e2e/global-install' + + name: End to end test - ${{ matrix.directory }} + + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + coverage: none + + # wait for deploy to packagist + - run: sleep 40 + + - + run: | + composer global require --dev rector/rector:dev-main + composer install --ansi + working-directory: ${{ matrix.directory }} + + - + run: /home/runner/.composer/vendor/bin/rector process --ansi --clear-cache + working-directory: ${{ matrix.directory }} diff --git a/build/target-repository/.github/workflows/e2e_php74.yaml b/build/target-repository/.github/workflows/e2e_php74.yaml new file mode 100644 index 00000000000..3cfb424693b --- /dev/null +++ b/build/target-repository/.github/workflows/e2e_php74.yaml @@ -0,0 +1,31 @@ +# This workflow runs system tests: Use the Rector application from the source +# checkout to process "fixture" projects in tests/system-tests +# to see if those can be processed successfully +name: End to End tests on PHP 7.4 + +on: + push: + branches: + - main + +jobs: + end_to_end_on_php74: + runs-on: ubuntu-latest + + name: End to end test - PHP 7.4 and Match class name + + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: "7.4" + coverage: none + + - + run: composer require rector/rector:dev-main --dev + working-directory: e2e/parse-match-class-on-php74 + + - + run: vendor/bin/rector process --ansi + working-directory: e2e/parse-match-class-on-php74 diff --git a/build/target-repository/.github/workflows/lock.yaml b/build/target-repository/.github/workflows/lock.yaml new file mode 100644 index 00000000000..00da1a3e759 --- /dev/null +++ b/build/target-repository/.github/workflows/lock.yaml @@ -0,0 +1,23 @@ +name: "Lock closed issues" + +on: + schedule: + - cron: "0 3 * * 0" # every Sunday at 03:00 UTC + workflow_dispatch: + +jobs: + lock: + runs-on: ubuntu-latest + + steps: + - uses: dessant/lock-threads@v5 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + issue-inactive-days: 150 + + issue-comment: > + This issue has been automatically locked because it has been closed for 150 days. + Please open a new issue if you have a similar problem. + + exclude-any-issue-labels: "keep-open,no-lock" diff --git a/build/target-repository/.github/workflows/stale.yaml.yml b/build/target-repository/.github/workflows/stale.yaml.yml new file mode 100644 index 00000000000..87694c7bd5d --- /dev/null +++ b/build/target-repository/.github/workflows/stale.yaml.yml @@ -0,0 +1,17 @@ +name: "Close stale issues and PRs" + +on: + schedule: + # runs daily at midnight + - cron: '0 0 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v10 + with: + days-before-stale: 60 + days-before-close: 90 + stale-issue-message: 'This issue has been automatically marked as stale due to inactivity. It will be closed in next 30 days if no further activity occurs.' + close-issue-message: 'Closing this issue due to prolonged inactivity.' diff --git a/build/target-repository/.github/workflows/standalone_rule_test.yaml b/build/target-repository/.github/workflows/standalone_rule_test.yaml index 39107a91887..edcefd315d0 100644 --- a/build/target-repository/.github/workflows/standalone_rule_test.yaml +++ b/build/target-repository/.github/workflows/standalone_rule_test.yaml @@ -1,7 +1,6 @@ name: Standalone Rule Test on: - pull_request: null push: branches: - main @@ -9,29 +8,30 @@ on: jobs: standalone_rule_test: runs-on: ubuntu-latest - strategy: fail-fast: false matrix: - actions: - - - name: 'Rector Prefixed' - run: composer require rector/rector:dev-main --dev + php_version: ['7.4'] + directory: + - 'e2e/rector-prefixed-rule-test' + + name: End to end test - ${{ matrix.directory }} steps: - # see https://github.com/rectorphp/rector-prefixed-rule-test - - - uses: actions/checkout@v2 - with: - repository: rectorphp/rector-prefixed-rule-test + - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: '7.3' + php-version: ${{ matrix.php_version }} coverage: none - - uses: "ramsey/composer-install@v1" + # wait for deploy to packagist + - run: sleep 40 - - run: ${{ matrix.actions.run }} + - + run: composer install --ansi + working-directory: ${{ matrix.directory }} - - run: vendor/bin/phpunit + - + run: vendor/bin/phpunit + working-directory: ${{ matrix.directory }} diff --git a/build/target-repository/README.md b/build/target-repository/README.md index 9644512af92..42bd21a8827 100644 --- a/build/target-repository/README.md +++ b/build/target-repository/README.md @@ -4,52 +4,21 @@
-Rector instantly upgrades and refactors the PHP code of your application. It can help you 2 major areas: +Rector instantly upgrades and refactors the PHP code of your application. It can help you in 2 major areas: ### 1. Instant Upgrades -Rector now supports upgrades from PHP 5.3 to 8.0 and major open-source projects like [Symfony](https://github.com/rectorphp/rector-symfony), [PHPUnit](https://github.com/rectorphp/rector-phpunit), [Nette](https://github.com/rectorphp/rector-nette), [Laravel](https://github.com/rectorphp/rector-laravel), [CakePHP](https://github.com/rectorphp/rector-cakephp), [Doctrine](https://github.com/rectorphp/rector-doctrine) and [Typo3](https://github.com/sabbelasichon/typo3-rector) out of the box. Do you want to **be constantly on the latest PHP/framework version without effort**? +Rector now supports upgrades from PHP 5.3 to 8.5 and major open-source projects like [Symfony](https://github.com/rectorphp/rector-symfony), [PHPUnit](https://github.com/rectorphp/rector-phpunit), and [Doctrine](https://github.com/rectorphp/rector-doctrine). Do you want to **be constantly on the latest PHP and Framework without effort**? Use Rector to handle **instant upgrades** for you. ### 2. Automated Refactoring -Do you have code quality you need, but struggle to keep it with new developers in your team? Do you want see smart code-reviews even when every senior developers sleeps? +Do you have code quality you need, but struggle to keep it with new developers in your team? Do you want to see smart code-reviews even when every senior developers sleeps? Add Rector to your CI and let it **continuously refactor your code** and keep the code quality high. -
- -## Read a First Book About Rector - -Are you curious, how Rector works internally, how to create your own rules and test them and why Rector was born? In May 2021 we've released the very first book: *Rector - The Power of Automated Refactoring*. - - - - - -By [buying a book](https://leanpub.com/rector-the-power-of-automated-refactoring) you directly support maintainers who are working on Rector. - -
- -## Documentation - -- [Explore 450+ Rector Rules](/docs/rector_rules_overview.md) -- [Auto Import Names](/docs/auto_import_names.md) -- [How to Ignore Rule or Paths](/docs/how_to_ignore_rule_or_paths.md) -- [Static Reflection and Autoload](/docs/static_reflection_and_autoload.md) -- [How to Configure Rule](/docs/how_to_configure_rules.md) -- [Beyond PHP - Entering the realm of FileProcessors](/docs/beyond_php_file_processors.md) - -### For Rule Developers and Contributors - -- [How Does Rector Work?](/docs/how_it_works.md) -- [PHP Parser Nodes](https://github.com/rectorphp/php-parser-nodes-docs/) -- [How to Work with Doc Block and Comments](/docs/how_to_work_with_doc_block_and_comments.md) -- [How to Generate New Rector Rule](/docs/create_own_rule.md) -- [How to add Test for Rector Rule](/docs/how_to_add_test_for_rector_rule.md) -- [How to create a custom FileProcessor](/docs/create_custom_fileprocessor.md) -
+Read our [blogpost](https://getrector.com/blog/new-setup-ci-command-to-let-rector-work-for-you) to see how to set up automated refactoring. ## Install @@ -57,11 +26,9 @@ By [buying a book](https://leanpub.com/rector-the-power-of-automated-refactoring composer require rector/rector --dev ``` -
- ## Running Rector -There a 2 main ways to use Rector: +There are 2 main ways to use Rector: - a *single rule*, to have the change under control - or group of rules called *sets* @@ -69,81 +36,88 @@ There a 2 main ways to use Rector: To use them, create a `rector.php` in your root directory: ```bash -vendor/bin/rector init +vendor/bin/rector ``` And modify it: ```php -// rector.php -use Rector\Php74\Rector\Property\TypedPropertyRector; -use Rector\Set\ValueObject\SetList; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - // here we can define, what sets of rules will be applied - // tip: use "SetList" class to autocomplete sets - $containerConfigurator->import(SetList::CODE_QUALITY); +use Rector\Config\RectorConfig; +use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromStrictConstructorRector; +return RectorConfig::configure() // register single rule - $services = $containerConfigurator->services(); - $services->set(TypedPropertyRector::class); -}; + ->withRules([ + TypedPropertyFromStrictConstructorRector::class + ]) + // here we can define, what prepared sets of rules will be applied + ->withPreparedSets( + deadCode: true, + codeQuality: true + ); ``` Then dry run Rector: ```bash -vendor/bin/rector process src --dry-run +vendor/bin/rector src --dry-run ``` Rector will show you diff of files that it *would* change. To *make* the changes, drop `--dry-run`: ```bash -vendor/bin/rector process src +vendor/bin/rector src ``` -*Note: `rector.php` is loaded by default. For different location, use `--config` option.* +## Documentation -*Note: Rector will only update legacy code to utilize new features which are supported by the PHP version defined in your `composer.json` file. For instance, if require.php is `>=7.2.5`, Rector will not make changes which are only available for PHP versions after 7.2.5.* +* Find [full documentation here](https://getrector.com/documentation/). +* [Explore Rector Rules](https://getrector.com/find-rule)
-## Configuration +## Learn Faster with a Book -```php -// rector.php -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersion; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +Are you curious, how Rector works internally, how to create your own rules and test them and why Rector was born? +Read [Rector - The Power of Automated Refactoring](https://leanpub.com/rector-the-power-of-automated-refactoring) that will take you step by step through the Rector setup and how to create your own rules. -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); +
- // paths to refactor; solid alternative to CLI arguments - $parameters->set(Option::PATHS, [__DIR__ . '/src', __DIR__ . '/tests']); +## Empowered by Community :heart: - // is your PHP version different from the one your refactor to? [default: your PHP version], uses PHP_VERSION_ID format - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_72); +The Rector community is powerful thanks to active maintainers who take care of Rector sets for particular projects. - // Path to phpstan with extensions, that PHPSTan in Rector uses to determine types - $parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, getcwd() . '/phpstan-for-config.neon'); -}; -``` +Among there projects belong: + +* [palantirnet/drupal-rector](https://github.com/palantirnet/drupal-rector) +* [craftcms/rector](https://github.com/craftcms/rector) +* [FriendsOfShopware/shopware-rector](https://github.com/FriendsOfShopware/shopware-rector) +* [sabbelasichon/typo3-rector](https://github.com/sabbelasichon/typo3-rector) +* [sulu/sulu-rector](https://github.com/sulu/sulu-rector) +* [efabrica-team/rector-nette](https://github.com/efabrica-team/rector-nette) +* [Sylius/SyliusRector](https://github.com/Sylius/SyliusRector) +* [CoditoNet/rector-money](https://github.com/CoditoNet/rector-money) +* [laminas/laminas-servicemanager-migration](https://github.com/laminas/laminas-servicemanager-migration) +* [cakephp/upgrade](https://github.com/cakephp/upgrade) +* [driftingly/rector-laravel](https://github.com/driftingly/rector-laravel) +* [contao/contao-rector](https://github.com/contao/contao-rector) +* [php-static-analysis/rector-rule](https://github.com/php-static-analysis/rector-rule) +* [ibexa/rector](https://github.com/ibexa/rector) +* [guanguans/rector-rules](https://github.com/guanguans/rector-rules)
-## Support +## Hire us to get Job Done :muscle: -Rector is a tool that [we develop](https://getrector.org/) and share for free, so anyone can automate their refactoring. But not everyone has dozens of hours to understand complexity of abstract-syntax-tree in their own time. **That's why we provide commercial support - to save your time**. +Rector is a tool that [we develop](https://getrector.com/) and share for free, so anyone can automate their refactoring. But not everyone has dozens of hours to understand complexity of abstract-syntax-tree in their own time. **That's why we provide commercial support - to save your time**. -Would you like to apply Rector on your code base but don't have time for the struggle with your project? [Hire us](https://getrector.org/contact) to get there faster. +Would you like to apply Rector on your code base but don't have time for the struggle with your project? [Hire us](https://getrector.com/contact) to get there faster.
## How to Contribute -See [the contribution guide](/CONTRIBUTING.md). +See [the contribution guide](/CONTRIBUTING.md) or go to development repository [rector/rector-src](https://github.com/rectorphp/rector-src).
@@ -152,7 +126,7 @@ See [the contribution guide](/CONTRIBUTING.md). You can use `--debug` option, that will print nested exceptions output: ```bash -vendor/bin/rector process src/Controller --dry-run --debug +vendor/bin/rector src/Controller --dry-run --debug ``` Or with Xdebug: @@ -161,31 +135,33 @@ Or with Xdebug: 2. Add `--xdebug` option when running Rector ```bash -vendor/bin/rector process src/Controller --dry-run --xdebug +vendor/bin/rector src/Controller --dry-run --xdebug ``` -To assist with simple debugging Rector provides a 2 helpers to pretty-print AST-nodes: +To assist with simple debugging Rector provides 2 helpers to pretty-print AST-nodes: ```php use PhpParser\Node\Scalar\String_; - $node = new String_('hello world!'); // prints node to string, as PHP code displays it print_node($node); - -// dump nested node object with nested properties -dump_node($node); -// 2nd argument is how deep the nesting is - this makes sure the dump is short and useful -dump_node($node, 1); ```
## Known Drawbacks +* Rector uses [nikic/php-parser](https://github.com/nikic/PHP-Parser/), built on technology called an *abstract syntax tree* (AST). An AST doesn't know about spaces and when written to a file it produces poorly formatted code in both PHP and docblock annotations. + +* Rector in parallel mode will work most of the times for most OS. On Windows, you may encounter issues unresolvable despite of following the [Troubleshooting Parallel](https://getrector.com/documentation/troubleshooting-parallel) guide. In such case, check if you are using Powershell 7 (pwsh). Change your terminal to command prompt (cmd) or bash for Windows. + ### How to Apply Coding Standards? -Rector uses [nikic/php-parser](https://github.com/nikic/PHP-Parser/), built on technology called an *abstract syntax tree* (AST). An AST doesn't know about spaces and when written to a file it produces poorly formatted code in both PHP and docblock annotations. **That's why your project needs to have a coding standard tool** and a set of formatting rules, so it can make Rector's output code nice and shiny again. +**Your project needs to have a coding standard tool** and a set of formatting rules, so it can make Rector's output code nice and shiny again. We're using [ECS](https://github.com/symplify/easy-coding-standard) with [this setup](https://github.com/rectorphp/rector-src/blob/main/ecs.php). + +### May cause unexpected output on File with mixed PHP+HTML content + +When you apply changes to files with PHP + HTML content, you may need to manually verify the changed file after apply the changes. diff --git a/build/target-repository/bootstrap.php b/build/target-repository/bootstrap.php index a91d01fd102..564f5ffae4b 100644 --- a/build/target-repository/bootstrap.php +++ b/build/target-repository/bootstrap.php @@ -1,9 +1,35 @@ = 12 +) { + require_once __DIR__ . '/preload.php'; +} +// inspired by https://github.com/phpstan/phpstan/blob/master/bootstrap.php spl_autoload_register(function (string $class): void { static $composerAutoloader; @@ -18,21 +44,10 @@ // prefixed version autoload $composerAutoloader = require __DIR__ . '/vendor/autoload.php'; } - $composerAutoloader->loadClass($class); - } - - // aliased by php-scoper, that's why its missing - if ($class === 'Symplify\SmartFileSystem\SmartFileInfo') { - $filePath = __DIR__ . '/vendor/symplify/smart-file-system/src/SmartFileInfo.php'; - if (file_exists($filePath)) { - require $filePath; - } - } - if ($class === 'Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator') { - // avoid duplicated autoload bug in Rector demo runner - if (class_exists('Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator', false)) { - return; + // some weird collision with PHPStan custom rule tests + if (! is_int($composerAutoloader)) { + $composerAutoloader->loadClass($class); } } }); diff --git a/build/target-repository/composer.json b/build/target-repository/composer.json index dc3467fbf3b..68290e50bba 100644 --- a/build/target-repository/composer.json +++ b/build/target-repository/composer.json @@ -1,32 +1,30 @@ { "name": "rector/rector", - "description": "Prefixed and PHP 7.1 downgraded version of rector/rector", + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", "license": "MIT", + "keywords": ["dev", "refactoring", "automation", "migration"], "bin": [ "bin/rector" ], "require": { - "php": "^7.1|^8.0", - "phpstan/phpstan": "0.12.96" + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.41" }, "autoload": { "files": [ "bootstrap.php" ] }, - "extra": { - "branch-alias": { - "dev-main": "0.11-dev" - } - }, "conflict": { - "phpstan/phpdoc-parser": "<=0.5.3", - "phpstan/phpstan": "<=0.12.82", - "rector/rector-prefixed": "*", "rector/rector-phpunit": "*", "rector/rector-symfony": "*", "rector/rector-doctrine": "*", - "rector/rector-nette": "*", - "rector/rector-cakephp": "*" + "rector/rector-downgrade-php": "*" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" } } diff --git a/build/target-repository/docs/auto_import_names.md b/build/target-repository/docs/auto_import_names.md deleted file mode 100644 index 94671793349..00000000000 --- a/build/target-repository/docs/auto_import_names.md +++ /dev/null @@ -1,70 +0,0 @@ -# Auto Import Names - -Rector works with all class names as fully qualified by default, so it know the exact types. In most coding standard, that's not desired behavior, because short version with `use` statement is preferred: - -```diff --$object = new \App\Some\Namespace\SomeClass(); -+use App\Some\Namespace\SomeClass; -+$object = new SomeClass(); -``` - - -To import FQN like these, configure `rector.php` with: - -```php -$parameters->set(Option::AUTO_IMPORT_NAMES, true); -``` - -
- -If you enable this feature, the class names in docblocks are imported as well: - -```diff -+use App\Some\Namespace\SomeClass; --/** @var \App\Some\Namespace\SomeClass $someClass */ -+/** @var SomeClass $someClass */ - $someClass = ...; -``` - -Do you want to skip them? - -```php -$parameters->set(Option::IMPORT_DOC_BLOCKS, false); -``` - -
- -Single short classes are imported too: - -```diff -+use DateTime; --$someClass = \DateTime(); -+$someClass = DateTime(); -``` - -Do you want to keep those? - -```php -$parameters->set(Option::IMPORT_SHORT_CLASSES, false); -``` - -## How to Remove Unused Imports? - -To remove imports, use [ECS](github.com/symplify/easy-coding-standard) with [`NoUnusedImportsFixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.18/doc/rules/import/no_unused_imports.rst) rule: - -```php -// ecs.php -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(NoUnusedImportsFixer::class); -}; -``` - -Run it: - -```bash -vendor/bin/ecs check src --fix -``` diff --git a/build/target-repository/docs/beyond_php_file_processors.md b/build/target-repository/docs/beyond_php_file_processors.md deleted file mode 100644 index 6c1638a347c..00000000000 --- a/build/target-repository/docs/beyond_php_file_processors.md +++ /dev/null @@ -1,101 +0,0 @@ -# Beyond PHP - FileProcessors - -You think Rector is all about PHP? You might be wrong. -Sure, the vast majority of the rules included in Rector is for PHP-Code. That´s true. - -But since version 0.11.x Rector introduced the concept of so called FileProcessors. -When you are running Rector with the process command all collected files from your configured paths -are iterated through all registered FileProcessors. - -Each FileProcessor must implement the [FileProcessorInterface](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Processor/FileProcessorInterface.php) and must decide if it is able to handle a given file by -the [supports](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Processor/FileProcessorInterface.php#L11) method or not. - -Rector itself already ships with three FileProcessors. Whereas the most important one, you guessed it, is the PhpFileProcessor. - -But another nice one is the ComposerFileProcessor. The ComposerFileProcessor lets you manipulate composer.json files in your project. -Let´s say you want to define a custom configuration where you want to update the version constraint of some packages. -All you have to do is using the ChangePackageVersionComposerRector: - -```php -services(); - $services->set(ChangePackageVersionComposerRector::class) - ->call('configure', [[ - // we use constant for keys to save you from typos - ChangePackageVersionComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('symfony/yaml', '^5.0'), - ]), - ]]); -}; -``` - -There are some more rules related to manipulate your composer.json files. Let´s see them in action: - -```php -services(); - - // Add a package to the require section of your composer.json - $services->set(AddPackageToRequireComposerRector::class) - ->call('configure', [[ - // we use constant for keys to save you from typos - AddPackageToRequireComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('symfony/yaml', '^5.0'), - ]), - ]]); - - // Add a package to the require dev section of your composer.json - $services->set(AddPackageToRequireDevComposerRector::class) - ->call('configure', [[ - // we use constant for keys to save you from typos - AddPackageToRequireDevComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('phpunit/phpunit', '^9.0'), - ]), - ]]); - - // Remove a package from composer.json - $services->set(RemovePackageComposerRector::class) - ->call('configure', [[ - // we use constant for keys to save you from typos - RemovePackageComposerRector::PACKAGE_NAMES => ['symfony/console'] - ]]); - - // Replace a package in the composer.json - $services->set(ReplacePackageAndVersionComposerRector::class) - ->call('configure', [[ - // we use constant for keys to save you from typos - ReplacePackageAndVersionComposerRector::REPLACE_PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new ReplacePackageAndVersion('vendor1/package2', 'vendor2/package1', '^3.0'), - ]), - ]]); -}; -``` - -Behind every FileProcessor are one or multiple rules which are in turn implementing a dedicated Interface extending the [RectorInterface](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Rector/RectorInterface.php). -In the case of the ComposerFileProcessor all rules are implementing the [ComposerRectorInterface](https://github.com/rectorphp/rector-src/blob/main/rules/Composer/Contract/Rector/ComposerRectorInterface.php) - -Are you eager to create your own one? Dive in and have a look at [How to create a custom FileProcessor](how_to_create_custom_fileprocessor.md) - - diff --git a/build/target-repository/docs/create_own_rule.md b/build/target-repository/docs/create_own_rule.md deleted file mode 100644 index 63a7655372d..00000000000 --- a/build/target-repository/docs/create_own_rule.md +++ /dev/null @@ -1,183 +0,0 @@ -# 3 Steps to Create Your Own Rector - -First, make sure it's not covered by [any existing Rectors](/docs/rector_rules_overview.md). -Let's say we want to **change method calls from `set*` to `change*`**. - -```diff - $user = new User(); --$user->setPassword('123456'); -+$user->changePassword('123456'); -``` - -## 1. Create a New Rector and Implement Methods - -Create a class that extends [`Rector\Core\Rector\AbstractRector`](/src/Rector/AbstractRector.php). It will inherit useful methods e.g. to check node type and name. See the source (or type `$this->` in an IDE) for a list of available methods. - -```php -declare(strict_types=1); - -namespace Utils\Rector\Rector; - -use Nette\Utils\Strings; -use PhpParser\Node; -use PhpParser\Node\Identifier; -use PhpParser\Node\Expr\MethodCall; -use Rector\Core\Rector\AbstractRector; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; -use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; - -final class MyFirstRector extends AbstractRector -{ - /** - * @return array> - */ - public function getNodeTypes(): array - { - // what node types are we looking for? - // pick any node from https://github.com/rectorphp/php-parser-nodes-docs/ - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - we can add "MethodCall" type here, because - * only this node is in "getNodeTypes()" - */ - public function refactor(Node $node): ?Node - { - // we only care about "set*" method names - if (! $this->isName($node->name, 'set*')) { - // return null to skip it - return null; - } - - $methodCallName = $this->getName($node->name); - $newMethodCallName = Strings::replace($methodCallName, '#^set#', 'change'); - - $node->name = new Identifier($newMethodCallName); - - // return $node if you modified it - return $node; - } - - /** - * This method helps other to understand the rule and to generate documentation. - */ - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change method calls from set* to change*.', [ - new CodeSample( - // code before - '$user->setPassword("123456");', - // code after - '$user->changePassword("123456");' - ), - ] - ); - } -} -``` - - -## File Structure - -This is how the file structure for custom rule in your own project will look like: - -```bash -/src/ - /YourCode.php -/utils - /rector - /src - /Rector - MyFirstRector.php -rector.php -composer.json -``` - -Writing test saves you lot of time in future debugging. Here is a file structure with tests: - -```bash -/src/ - /YourCode.php -/utils - /rector - /src - /Rector - MyFirstRector.php - /tests - /Rector - /MyFirstRector - /Fixture - test_fixture.php.inc - /config - config.php - MyFirstRectorTest.php -rector.php -composer.json -``` - -## Update `composer.json` - -We also need to load Rector rules in `composer.json`: - -```json -{ - "autoload": { - "psr-4": { - "App\\": "src" - } - }, - "autoload-dev": { - "psr-4": { - "Utils\\Rector\\": "utils/rector/src", - "Utils\\Rector\\Tests\\": "utils/rector/tests" - } - } -} -``` - -After adding this to `composer.json`, be sure to reload Composer's class map: - -```bash -composer dump-autoload -``` - -## 2. Register It - -```php -services(); - $services->set(MyFirstRector::class); -}; -``` - -## 3. Let Rector Refactor Your Code - -The `rector.php` configuration is loaded by default, so we can skip it. - -```bash -# see the diff first -vendor/bin/rector process src --dry-run - -# if it's ok, apply -vendor/bin/rector process src -``` - -That's it! - -
- -## Generating a Rector Rule - -Do you want to save time with making rules and tests? - -Use [the `generate` command](https://github.com/rectorphp/rector-generator). diff --git a/build/target-repository/docs/how_it_works.md b/build/target-repository/docs/how_it_works.md deleted file mode 100644 index 02702dfc4ce..00000000000 --- a/build/target-repository/docs/how_it_works.md +++ /dev/null @@ -1,106 +0,0 @@ -# How Does Rector Work? - -(Inspired by [*How it works* in BetterReflection](https://github.com/Roave/BetterReflection/blob/master/docs/how-it-works.md)) - -## 1. Finds all files and Load Configured Rectors - -- The application finds files in the source code you provide and registered Rectors - from `--config` or local `rector.php` -- Then it iterates all found files and applies relevant Rectors to them. -- A *Rector* in this context is 1 single class that modifies 1 thing, e.g. changes the class name - -## 2. Parse and Reconstruct 1 File - -The iteration of files, nodes and Rectors respects this lifecycle: - -```php - nodes - /** @var Parser $phpParser */ - $nodes = $phpParser->parse(file_get_contents($fileInfo->getRealPath())); - - // nodes => 1 node - foreach ($nodes as $node) { // rather traverse all of them - /** @var PhpRectorInterface[] $rectors */ - foreach ($rectors as $rector) { - foreach ($rector->getNodeTypes() as $nodeType) { - if (is_a($node, $nodeType, true)) { - $rector->refactor($node); - } - } - } - } -} -``` - -### 2.1 Prepare Phase - -- Files are parsed by [`nikic/php-parser`](https://github.com/nikic/PHP-Parser), 4.0 that supports writing modified tree back to a file -- Then nodes (array of objects by parser) are traversed by `StandaloneTraverseNodeTraverser` to prepare their metadata, e.g. the class name, the method node the node is in, the namespace name etc. added by `$node->setAttribute(Attribute::CLASS_NODE, 'value')`. - -### 2.2 Rectify Phase - -- When all nodes are ready, the application iterates on all active Rectors -- Each node is compared with `$rector->getNodeTypes()` method to see if this Rector should do some work on it, e.g. is this class name called `OldClassName`? -- If it doesn't match, it goes to next node. -- If it matches, the `$rector->reconstruct($node)` method is called -- Active Rector change everything they have to and return changed nodes - -### 2.2.1 Order of Rectors - -- Nodes to run rectors are iterated in the node traversal order. - -E.g. rectors for `Class_` node always run before rectors for `ClassMethod` in one class. - -- Rectors are run by the natural order in the configuration, meaning the first -in the configuration will be run first. - -E.g. in this case, first the `@expectedException` annotation will be changed to a method, - then the `setExpectedException` method will be changed to `expectedException`. - -```php -services(); - $services->set(Rector\PHPUnit\Rector\ClassMethod\ExceptionAnnotationRector::class); - $services->set(Rector\Renaming\Rector\MethodCall\RenameMethodRector::class) - ->arg('$oldToNewMethodsByClass', [ - PHPUnit\Framework\TestClass::class => [ - 'setExpectedException' => 'expectedException', - 'setExpectedExceptionRegExp' => 'expectedException', - ], - ]); -}; -``` - -### 2.3 Save File/Diff Phase - -- When work on all nodes of 1 file is done, the file will be saved if it has some changes -- Or if the `--dry-run` option is on, it will store the *git-like* diff thanks to [GeckoPackages/GeckoDiffOutputBuilder](https://github.com/GeckoPackages/GeckoDiffOutputBuilder) -- Then Rector will go to the next file - -## 3 Reporting - -- After this, Rector displays the list of changed files -- Or with `--dry-run` option the diff of these files - -### Similar Projects - -- [ClangMR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/41342.pdf) for C++ by Google (closed source) - almost idential workflow, developed independently though -- [hhast](https://github.com/hhvm/hhast) - HHVM AST + format preserving + mirations -- [facebook/jscodeshift](https://github.com/facebook/jscodeshift) for Javascript -- [silverstripe/silverstripe-upgrader](https://github.com/silverstripe/silverstripe-upgrader) for PHP CMS, Silverstripe -- [dereuromark/upgrade](https://github.com/dereuromark/upgrade) for PHP Framework, CakePHP diff --git a/build/target-repository/docs/how_to_add_test_for_rector_rule.md b/build/target-repository/docs/how_to_add_test_for_rector_rule.md deleted file mode 100644 index af80ac9b98f..00000000000 --- a/build/target-repository/docs/how_to_add_test_for_rector_rule.md +++ /dev/null @@ -1,166 +0,0 @@ -# How to Add Test for Rector Rule - -## 1. Create an environment for testing - -When using Rector to update your own code, you will typically be using release repository installed by composer, however, when adding tests, you will need to use the development repository as shown: - -- Fork https://github.com/rectorphp/rector-src -- Clone it locally -- Install dependencies by executing `composer install` -- Tests your installation by executing `composer fix-cs` and `composer phpstan` -- Create a new branch for your test -- Add your test as described below. Note that the rector binary is located at `bin/rector` instead of the typical `vendor/bin/rector` -- Push the branch and create a new pull request to https://github.com/rectorphp/rector-src - -Alternatively, the above may be performed on the CLI - -```bash -# Authenticate -gh auth login - -# Fork and clone the repository -gh repo fork https://github.com/rectorphp/rector-src - -# Install dependencies -cd rector-src -composer install - -# Test installation -composer fix-cs -composer phpstan - -# Create and checkout a branch -git checkout -b your_branch_name - -# Create test as described below - -# Push the branch -git push -u origin your_branch_name - -# create new pull request to https://github.com/rectorphp/rector-src -gh pr create --title "Your title text" --body "Your body text" -``` - -## 2. Detect the Rector Rule - -Run Rector only on 1 directory, or better 1 file. - -```bash -bin/rector process /some-file.php -``` - -See "Applied rules" under the diff: - -![Applied Rules](/docs/images/docs_applied_rules.png) - -Our rule in this example is: `Rector\Privatization\Rector\Class_\FinalizeClassesWithoutChildrenRector` - -This rule's job is to add `final` to every class that has no children and is not a Doctrine entity = everywhere it can without breaking our code. - -## 3. Detect the Minimal File - -Usually, the Rector diff output is long and contains many other errors related to other rules. It's a mess; we can't use that for a test fixture. We need to find **1 responsible line**. - -The best way is to copy the file to local code, e.g. `app/SomeFile.php` a put only the broken line there. - -In our case, all we need is: - -```php -class StaticEasyPrefixer -{ -} -``` - -Then rerun Rector to confirm: - -```bash -bin/rector process app/SomeFile.php -``` - -Do we have the same diff? Great! - -## 4. Find the Rector Test Case - -Now we need to find the test case. The test case name is rule + `Test` suffix. - -`FinalizeClassesWithoutChildrenRector` - -↓ - -`FinalizeClassesWithoutChildrenRectorTest` (test class) - -↓ - -`FinalizeClassesWithoutChildrenRectorTest.php` (test file) - -Right here: - -![Rule Test Case](/docs/images/docs_rule_test_case.png) - -## 5. Add Change or No-Change Test Fixture File - -Next to the test case, there is `/Fixture` directory. It contains many test fixture files that verified the Rector rule work correctly in all possible cases. - -Do you see *test fixture file* first time? It's a file with real-life PHP code that test 1 specific case that rule should cover or avoid. E.g., one test fixture file can contain a Doctrine entity that cannot be final and should be skipped by this rule. By convention, the first fixture file has the name `fixture.php.inc`. - -In the `/Fixture` directory, we create our test fixture file, e.g., `add_final.php.inc`. The `.php.inc` is there on purpose, so the file is hidden from coding standard tools and static analysis. - -There are 2 fixture formats. - -### 1. The Code Should Change - -```bash - ------ - -``` - -### 2. The Code Should Be Skipped - -```bash - -``` - -
- -In this particular case, the code should change - `final` should be added so that the test fixture would look like this: - -```php - ------ - -``` - -- The closing `?>` is there for slightly better PHPStorm. -- The PSR-4 namespace is there to make each class unique because the test classes are loaded to an analysis by reflection and must be unique -- The file name conventions => class is `add_final.php.inc` => `AddFinal` class - -Run PHPUnit with the test file to confirm: - -```bash -vendor/bin/phpunit rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/FinalizeClassesWithoutChildrenRectorTest.php -``` - -To run only the single test fixture, add `--filter test#X`, where X is the fixture's order number. - -```bash -vendor/bin/phpunit rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/FinalizeClassesWithoutChildrenRectorTest.php --filter test#4 -``` - -If PHPUnit fails, you've successfully added a test case! :) diff --git a/build/target-repository/docs/how_to_configure_rules.md b/build/target-repository/docs/how_to_configure_rules.md deleted file mode 100644 index 0bc338e03e3..00000000000 --- a/build/target-repository/docs/how_to_configure_rules.md +++ /dev/null @@ -1,27 +0,0 @@ -# How To Configure Rules - -Rector rules that implement `Rector\Core\Contract\Rector\ConfigurableRectorInterface` can be configured. - -Typical example is `Rector\Renaming\Rector\Name\RenameClassRector`: - -```php -services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - // we use constant for keys to save you from typos - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'App\SomeOldClass' => 'App\SomeNewClass', - ], - ]]); -}; -``` diff --git a/build/target-repository/docs/how_to_create_custom_fileprocessor.md b/build/target-repository/docs/how_to_create_custom_fileprocessor.md deleted file mode 100644 index d7b291c0c5c..00000000000 --- a/build/target-repository/docs/how_to_create_custom_fileprocessor.md +++ /dev/null @@ -1,206 +0,0 @@ -# Create your own custom FileProcessor - -This section is all about creating your custom specific FileProcessor. -If you don´t know the concept of FileProcessors in the context of Rector, have a look at [Beyond PHP - Entering the realm of FileProcessors](beyond_php_file_processors.md) - -Most of the examples starting with a rather contrived example, let´s do it the same. - -Imagine you would like to replace the sentence "Make america great again" to "Make the whole world a better place to be" in every file named bold_statement.txt. - -In order to do so, we create the BoldStatementFileProcessor like that: - -```php -getSmartFileInfo(); - return 'bold_statement.txt' === $smartFileInfo->getBasename(); - } - - /** - * @param File[] $files - */ - public function process(array $files): void - { - foreach ($files as $file) { - $this->processFile($file); - } - } - - private function processFile(File $file): void - { - $oldContent = $file->getFileContent(); - - if(false === strpos($oldContent, self::OLD_STATEMENT)) { - return; - } - - $newFileContent = str_replace(self::OLD_STATEMENT, 'Make the whole world a better place to be', $oldContent); - $file->changeFileContent($newFileContent); - } - - public function getSupportedFileExtensions(): array - { - return ['txt']; - } -} - -``` - -Now register your FileProcessor in your configuration (actually in the container): - -```php -services(); - $services->set(BoldStatementFileProcessor::class); -}; -``` - -Run rector again and see what happens. Yes, we made the world better. - -The astute reader has noticed, that the BoldStatementFileProcessor is not really reusable and easily extendable. -So it would be much better to separate the processing from the actual rule(s). -This is also the best practice in all Rector internal FileProcessors. So, let´s just do that. - -Create a new dedicated Interface for our rules used by the BoldStatementFileProcessor. Just call it BoldStatementRectorInterface. - -```php -boldStatementRectors = $boldStatementRectors; - } - - public function supports(File $file): bool - { - $smartFileInfo = $file->getSmartFileInfo(); - return 'bold_statement.txt' === $smartFileInfo->getBasename(); - } - - /** - * @param File[] $files - */ - public function process(array $files): void - { - foreach ($files as $file) { - $this->processFile($file); - } - } - - private function processFile(File $file): void - { - foreach ($this->boldStatementRectors as $boldStatementRector) { - $changeFileContent = $boldStatementRector->transform($file->getFileContent()); - $file->changeFileContent($changeFileContent); - } - } - - public function getSupportedFileExtensions(): array - { - return ['txt']; - } -} - -``` - -Notice the annotation BoldStatementRectorInterface[]. This is important to inject all active classes implementing the BoldStatementRectorInterface into the BoldStatementFileProcessor. -Yes, we said active. So last but not least we must register our new rule in the container, so it is applied: - -```php -services(); - $services->set(BoldStatementFileProcessor::class); - $services->set(BoldStatementMakeAmericaGreatAgainRector::class); -}; -``` - -Run rector again and yes, we made the world a better place again. - -Puh. This was a long ride. But we are done and have our new shiny BoldStatementFileProcessor in place. -Now, it´s up to you, to create something useful. But always keep in mind: Try to make the world a better place to be. diff --git a/build/target-repository/docs/how_to_ignore_rule_or_paths.md b/build/target-repository/docs/how_to_ignore_rule_or_paths.md deleted file mode 100644 index b682083f3da..00000000000 --- a/build/target-repository/docs/how_to_ignore_rule_or_paths.md +++ /dev/null @@ -1,69 +0,0 @@ -# How To Ignore Rule or Paths - -## Preferred Way: Config - -```php -parameters(); - - // is there a file you need to skip? - $parameters->set(Option::SKIP, [ - // single file - __DIR__ . '/src/ComplicatedFile.php', - // or directory - __DIR__ . '/src', - // or fnmatch - __DIR__ . '/src/*/Tests/*', - - // is there single rule you don't like from a set you use? - SimplifyIfReturnBoolRector::class, - - // or just skip rule in specific directory - SimplifyIfReturnBoolRector::class => [ - // single file - __DIR__ . '/src/ComplicatedFile.php', - // or directory - __DIR__ . '/src', - // or fnmatch - __DIR__ . '/src/*/Tests/*', - ], - ]); -}; -``` - -## In a File - -For in-file exclusion, use `@noRector \Rector\SomeClass\NameRector` annotation: - -```php -import(SetList::MY_FRAMEWORK_20); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PATHS, [__DIR__ . '/src']); -}; -``` diff --git a/build/target-repository/docs/how_to_use_prefixed_service_in_custom_rule.md b/build/target-repository/docs/how_to_use_prefixed_service_in_custom_rule.md deleted file mode 100644 index f3ffd7c8601..00000000000 --- a/build/target-repository/docs/how_to_use_prefixed_service_in_custom_rule.md +++ /dev/null @@ -1,41 +0,0 @@ -# How to Use Prefixed Service In Custom Rule - -Since `rector/rector` version 0.11, it is not possible to use service that previously not prefixed in previous version, for example, you have the following custom rector rule with `Symplify\PackageBuilder\Strings\StringFormatConverter` dependency: - -```php -use Symplify\PackageBuilder\Strings\StringFormatConverter; - -final class UnderscoreToCamelCaseVariableNameRector extends AbstractRector -{ - public function __construct(StringFormatConverter $stringFormatter) - { - // ... - } -} -``` - -For above example, the `Symplify\PackageBuilder\Strings\StringFormatConverter` is no longer exists, you can consume via require --dev it: - -```bash -composer require --dev symplify/package-builder -``` - -After that, you need to register the `symplify/package-builder`'s src to service in `rector.php`: - -```php -services(); - $services->set(StringFormatConverter::class); - - // ... -}; -``` - -Now, the `Symplify\PackageBuilder\Strings\StringFormatConverter` service will be detected again. \ No newline at end of file diff --git a/build/target-repository/docs/how_to_work_with_doc_block_and_comments.md b/build/target-repository/docs/how_to_work_with_doc_block_and_comments.md deleted file mode 100644 index b44298a733a..00000000000 --- a/build/target-repository/docs/how_to_work_with_doc_block_and_comments.md +++ /dev/null @@ -1,100 +0,0 @@ -# How To Work with Doc Block and Comments - -Let's say we have a doc block: - -```php -/** - * @return int - */ -public function run() -{ - return 1000; -} -``` - -## How to get a Return Type? - -To get e.g. return type, use `PhpDocInfo` value object with useful methods: - -```php -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; - -/** @var PhpDocInfoFactory $phpDocInfoFactory */ -$phpDocInfo = $phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - -// then use any method you like -$returnType = $phpDocInfo->getReturnType(); -// instance of "\PHPStan\Type\IntegerType" -var_dump($returnType); -``` - -## How to Remove node? - -```php -use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; - -/** @var PhpDocInfo $phpDocInfo */ -$phpDocInfo->removeByType(ReturnTagValueNode::class); -``` - -## How create PhpDocInfo for a new node? - -In case you build a new node and want to work with its doc block, you need to create it first: - -```php -// the "PhpDocInfoFactory" service is already available in children of "AbstractRector" -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; - -/** @var PhpDocInfoFactory $phpDocInfoFactory */ -$phpDocInfo = $phpDocInfoFactory->createFromNodeOrEmpty($node); -``` - -## How to get Param with Names and Types? - -```php -use PHPStan\Type\Type; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; - -/** @var PhpDocInfo $phpDocInfo */ -$paramTypes = $phpDocInfo->getParamTypesByName(); - -/** @var array $paramTypes */ -var_dump($paramTypes); -``` - -## How to Get Class Annotation? - -Doctrine class annotations are annotations based on [`doctrine/annotations`](https://github.com/doctrine/annotations/) package. They are classes that have `@annotation`. Most common are Doctrine entity, column, one to many etc., but also Symfony route or Symfony validation annotations. - -Let's look how to work one for Doctrine entity: - -```php -use Doctrine\ORM\Mapping as ORM; - -/** - * @ORM\Entity - */ -class UserEntity -{ -} -``` - -```php -use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; - -/** @var PhpDocInfo $phpDocInfo */ -$entityTagValueNode = $phpDocInfo->findOneByAnnotationClass('Doctrine\ORM\Mapping\Entity'); -if (! $entityTagValueNode instanceof DoctrineAnnotationTagValueNode) { - return null; -} - -$annotationClass = $entityTagValueNode->identifierTypeNode; -var_dump($annotationClass); // \PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode("Doctrine\ORM\Mapping\Entity") - -$values = $entityTagValueNode->getValues(); -var_dump($values); // [] -``` diff --git a/build/target-repository/docs/images/cakephp.png b/build/target-repository/docs/images/cakephp.png deleted file mode 100644 index 70a29431147..00000000000 Binary files a/build/target-repository/docs/images/cakephp.png and /dev/null differ diff --git a/build/target-repository/docs/images/docs_applied_rules.png b/build/target-repository/docs/images/docs_applied_rules.png deleted file mode 100644 index d7dfaf0821c..00000000000 Binary files a/build/target-repository/docs/images/docs_applied_rules.png and /dev/null differ diff --git a/build/target-repository/docs/images/docs_rule_test_case.png b/build/target-repository/docs/images/docs_rule_test_case.png deleted file mode 100644 index f6d806584be..00000000000 Binary files a/build/target-repository/docs/images/docs_rule_test_case.png and /dev/null differ diff --git a/build/target-repository/docs/images/drupal.png b/build/target-repository/docs/images/drupal.png deleted file mode 100644 index 71c69821970..00000000000 Binary files a/build/target-repository/docs/images/drupal.png and /dev/null differ diff --git a/build/target-repository/docs/images/php.png b/build/target-repository/docs/images/php.png deleted file mode 100644 index 31c81faff30..00000000000 Binary files a/build/target-repository/docs/images/php.png and /dev/null differ diff --git a/build/target-repository/docs/images/phpunit.png b/build/target-repository/docs/images/phpunit.png deleted file mode 100644 index a9bf56c1bed..00000000000 Binary files a/build/target-repository/docs/images/phpunit.png and /dev/null differ diff --git a/build/target-repository/docs/images/space.png b/build/target-repository/docs/images/space.png deleted file mode 100644 index c36f09e0798..00000000000 Binary files a/build/target-repository/docs/images/space.png and /dev/null differ diff --git a/build/target-repository/docs/images/symfony.png b/build/target-repository/docs/images/symfony.png deleted file mode 100644 index b980e3c1c64..00000000000 Binary files a/build/target-repository/docs/images/symfony.png and /dev/null differ diff --git a/build/target-repository/docs/images/typo3.png b/build/target-repository/docs/images/typo3.png deleted file mode 100644 index 105e1060e69..00000000000 Binary files a/build/target-repository/docs/images/typo3.png and /dev/null differ diff --git a/build/target-repository/docs/init_command.md b/build/target-repository/docs/init_command.md deleted file mode 100644 index f0fda33ff28..00000000000 --- a/build/target-repository/docs/init_command.md +++ /dev/null @@ -1,97 +0,0 @@ -# How to generate a configuration file - -To start quickly you can run the init command - -```bash -vendor/bin/rector init -``` - -This will create a `rector.php` if it doesn't already exist in your root directory with some sensitive defaults to start with. - -```php -// rector.php -use Rector\Core\Configuration\Option; -use Rector\Php74\Rector\Property\TypedPropertyRector; -use Rector\Set\ValueObject\SetList; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - // here we can define, what sets of rules will be applied - $containerConfigurator->import(SetList::CODE_QUALITY); - - // register single rule - $services = $containerConfigurator->services(); - $services->set(TypedPropertyRector::class); -}; -``` - -The init command takes an option called `--template-type`. -If some other Rector extension like [rector-nette](https://github.com/rectorphp/rector-nette) or [typo3-rector](https://github.com/sabbelasichon/typo3-rector) provides such a custom template type you can specify it here: - -```bash -vendor/bin/rector init --template-type=typo3 -``` - -The rector.php file for TYPO3 contains useful framework specific defaults to start from: - -```php -use Ssch\TYPO3Rector\Set\Typo3SetList; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Rector\PostRector\Rector\NameImportingPostRector; -use Rector\Core\Configuration\Option; - -return static function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import(Typo3SetList::TYPO3_76); - $containerConfigurator->import(Typo3SetList::TYPO3_87); - $containerConfigurator->import(Typo3SetList::TYPO3_95); - $containerConfigurator->import(Typo3SetList::TYPO3_104); - $containerConfigurator->import(Typo3SetList::TYPO3_11); - - // get parameters - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::SKIP, [ - NameImportingPostRector::class => [ - 'ClassAliasMap.php', - 'ext_localconf.php', - 'ext_emconf.php', - 'ext_tables.php', - __DIR__ . '/**/Configuration/TCA/*', - __DIR__ . '/**/Configuration/RequestMiddlewares.php', - __DIR__ . '/**/Configuration/Commands.php', - __DIR__ . '/**/Configuration/AjaxRoutes.php', - __DIR__ . '/**/Configuration/Extbase/Persistence/Classes.php', - ], - ]); -}; -``` - -If you just want to use the default template provided by Rector you can omit the --template-type option. - -# How to add a template type as a developer -In order to provide a new template type as a developer you should create a custom template class implementing the TemplateResolverInterface: - -```php -use Rector\Core\Contract\Template\TemplateResolverInterface; - -final class MyCustomTemplate implements TemplateResolverInterface -{ - /** - * @var string - */ - private const TYPE = 'custom'; - - public function provide(): string - { - return __DIR__ . '/path/to/custom/template.php.dist'; - } - public function supports(string $type): bool - { - return $type === self::TYPE; - } - - public function __toString(): string - { - return self::TYPE; - } -} -``` diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md deleted file mode 100644 index 9a020a7994c..00000000000 --- a/build/target-repository/docs/rector_rules_overview.md +++ /dev/null @@ -1,11985 +0,0 @@ -# 477 Rules Overview - -
- -## Categories - -- [Arguments](#arguments) (4) - -- [Autodiscovery](#autodiscovery) (4) - -- [Carbon](#carbon) (2) - -- [CodeQuality](#codequality) (68) - -- [CodingStyle](#codingstyle) (38) - -- [Composer](#composer) (6) - -- [DeadCode](#deadcode) (49) - -- [Defluent](#defluent) (9) - -- [DependencyInjection](#dependencyinjection) (3) - -- [DowngradePhp53](#downgradephp53) (1) - -- [DowngradePhp70](#downgradephp70) (11) - -- [DowngradePhp71](#downgradephp71) (9) - -- [DowngradePhp72](#downgradephp72) (4) - -- [DowngradePhp73](#downgradephp73) (6) - -- [DowngradePhp74](#downgradephp74) (10) - -- [DowngradePhp80](#downgradephp80) (18) - -- [DowngradePhp81](#downgradephp81) (1) - -- [EarlyReturn](#earlyreturn) (11) - -- [LeagueEvent](#leagueevent) (1) - -- [MockeryToProphecy](#mockerytoprophecy) (2) - -- [MysqlToMysqli](#mysqltomysqli) (4) - -- [Naming](#naming) (6) - -- [Order](#order) (1) - -- [PSR4](#psr4) (2) - -- [Php52](#php52) (2) - -- [Php53](#php53) (4) - -- [Php54](#php54) (2) - -- [Php55](#php55) (3) - -- [Php56](#php56) (2) - -- [Php70](#php70) (19) - -- [Php71](#php71) (9) - -- [Php72](#php72) (10) - -- [Php73](#php73) (9) - -- [Php74](#php74) (14) - -- [Php80](#php80) (17) - -- [Php81](#php81) (5) - -- [PhpSpecToPHPUnit](#phpspectophpunit) (7) - -- [PostRector](#postrector) (7) - -- [Privatization](#privatization) (10) - -- [Removing](#removing) (6) - -- [RemovingStatic](#removingstatic) (8) - -- [Renaming](#renaming) (11) - -- [Restoration](#restoration) (6) - -- [Transform](#transform) (34) - -- [TypeDeclaration](#typedeclaration) (20) - -- [Visibility](#visibility) (2) - -
- -## Arguments - -### ArgumentAdderRector - -This Rector adds new default arguments in calls of defined methods and class types. - -:wrench: **configure it!** - -- class: [`Rector\Arguments\Rector\ClassMethod\ArgumentAdderRector`](../rules/Arguments/Rector/ClassMethod/ArgumentAdderRector.php) - -```php -use Rector\Arguments\Rector\ClassMethod\ArgumentAdderRector; -use Rector\Arguments\ValueObject\ArgumentAdder; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ArgumentAdderRector::class) - ->call('configure', [[ - ArgumentAdderRector::ADDED_ARGUMENTS => ValueObjectInliner::inline([ - new ArgumentAdder('SomeExampleClass', 'someMethod', 0, 'someArgument', true, 'SomeType', null), - ]), - ]]); -}; -``` - -↓ - -```diff - $someObject = new SomeExampleClass; --$someObject->someMethod(); -+$someObject->someMethod(true); -``` - -
- -```php -use Rector\Arguments\Rector\ClassMethod\ArgumentAdderRector; -use Rector\Arguments\ValueObject\ArgumentAdder; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ArgumentAdderRector::class) - ->call('configure', [[ - ArgumentAdderRector::ADDED_ARGUMENTS => ValueObjectInliner::inline([ - new ArgumentAdder('SomeExampleClass', 'someMethod', 0, 'someArgument', true, 'SomeType', null), - ]), - ]]); -}; -``` - -↓ - -```diff - class MyCustomClass extends SomeExampleClass - { -- public function someMethod() -+ public function someMethod($value = true) - { - } - } -``` - -
- -### FunctionArgumentDefaultValueReplacerRector - -Streamline the operator arguments of version_compare function - -:wrench: **configure it!** - -- class: [`Rector\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector`](../rules/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector.php) - -```php -use Rector\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector; -use Rector\Arguments\ValueObject\ReplaceFuncCallArgumentDefaultValue; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(FunctionArgumentDefaultValueReplacerRector::class) - ->call('configure', [[ - FunctionArgumentDefaultValueReplacerRector::REPLACED_ARGUMENTS => ValueObjectInliner::inline([ - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'), - ]), - ]]); -}; -``` - -↓ - -```diff --version_compare(PHP_VERSION, '5.6', 'gte'); -+version_compare(PHP_VERSION, '5.6', 'ge'); -``` - -
- -### ReplaceArgumentDefaultValueRector - -Replaces defined map of arguments in defined methods and their calls. - -:wrench: **configure it!** - -- class: [`Rector\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector`](../rules/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector.php) - -```php -use Rector\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector; -use Rector\Arguments\ValueObject\ReplaceArgumentDefaultValue; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ReplaceArgumentDefaultValueRector::class) - ->call('configure', [[ - ReplaceArgumentDefaultValueRector::REPLACED_ARGUMENTS => ValueObjectInliner::inline([ - new ReplaceArgumentDefaultValue('SomeClass', 'someMethod', 0, 'SomeClass::OLD_CONSTANT', false), - ]), - ]]); -}; -``` - -↓ - -```diff - $someObject = new SomeClass; --$someObject->someMethod(SomeClass::OLD_CONSTANT); -+$someObject->someMethod(false); -``` - -
- -### SwapFuncCallArgumentsRector - -Swap arguments in function calls - -:wrench: **configure it!** - -- class: [`Rector\Arguments\Rector\FuncCall\SwapFuncCallArgumentsRector`](../rules/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector.php) - -```php -use Rector\Arguments\Rector\FuncCall\SwapFuncCallArgumentsRector; -use Rector\Arguments\ValueObject\SwapFuncCallArguments; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(SwapFuncCallArgumentsRector::class) - ->call('configure', [[ - SwapFuncCallArgumentsRector::FUNCTION_ARGUMENT_SWAPS => ValueObjectInliner::inline([ - new SwapFuncCallArguments('some_function', [1, 0]), ] - ), - ]]); -}; -``` - -↓ - -```diff - final class SomeClass - { - public function run($one, $two) - { -- return some_function($one, $two); -+ return some_function($two, $one); - } - } -``` - -
- -## Autodiscovery - -### MoveEntitiesToEntityDirectoryRector - -Move entities to Entity namespace - -- class: [`Rector\Autodiscovery\Rector\Class_\MoveEntitiesToEntityDirectoryRector`](../rules/Autodiscovery/Rector/Class_/MoveEntitiesToEntityDirectoryRector.php) - -```diff --// file: app/Controller/Product.php -+// file: app/Entity/Product.php - --namespace App\Controller; -+namespace App\Entity; - - use Doctrine\ORM\Mapping as ORM; - - /** - * @ORM\Entity - */ - class Product - { - } -``` - -
- -### MoveInterfacesToContractNamespaceDirectoryRector - -Move interface to "Contract" namespace - -- class: [`Rector\Autodiscovery\Rector\Interface_\MoveInterfacesToContractNamespaceDirectoryRector`](../rules/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector.php) - -```diff --// file: app/Exception/Rule.php -+// file: app/Contract/Rule.php - --namespace App\Exception; -+namespace App\Contract; - - interface Rule - { - } -``` - -
- -### MoveServicesBySuffixToDirectoryRector - -Move classes by their suffix to their own group/directory - -:wrench: **configure it!** - -- class: [`Rector\Autodiscovery\Rector\Class_\MoveServicesBySuffixToDirectoryRector`](../rules/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector.php) - -```php -use Rector\Autodiscovery\Rector\Class_\MoveServicesBySuffixToDirectoryRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(MoveServicesBySuffixToDirectoryRector::class) - ->call('configure', [[ - MoveServicesBySuffixToDirectoryRector::GROUP_NAMES_BY_SUFFIX => ['Repository'], - ]]); -}; -``` - -↓ - -```diff --// file: app/Entity/ProductRepository.php -+// file: app/Repository/ProductRepository.php - --namespace App\Entity; -+namespace App\Repository; - - class ProductRepository - { - } -``` - -
- -### MoveValueObjectsToValueObjectDirectoryRector - -Move value object to ValueObject namespace/directory - -:wrench: **configure it!** - -- class: [`Rector\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector`](../rules/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector.php) - -```php -use Rector\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(MoveValueObjectsToValueObjectDirectoryRector::class) - ->call('configure', [[ - MoveValueObjectsToValueObjectDirectoryRector::TYPES => ['ValueObjectInterfaceClassName'], - MoveValueObjectsToValueObjectDirectoryRector::SUFFIXES => ['Search'], - MoveValueObjectsToValueObjectDirectoryRector::ENABLE_VALUE_OBJECT_GUESSING => true, - ]]); -}; -``` - -↓ - -```diff --// app/Exception/Name.php -+// app/ValueObject/Name.php - class Name - { - private $name; - - public function __construct(string $name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } - } -``` - -
- -## Carbon - -### ChangeCarbonSingularMethodCallToPluralRector - -Change setter methods with args to their plural names on `Carbon\Carbon` - -- class: [`Rector\Carbon\Rector\MethodCall\ChangeCarbonSingularMethodCallToPluralRector`](../rules/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector.php) - -```diff - use Carbon\Carbon; - - final class SomeClass - { - public function run(Carbon $carbon, $value): void - { -- $carbon->addMinute($value); -+ $carbon->addMinutes($value); - } - } -``` - -
- -### ChangeDiffForHumansArgsRector - -Change methods arguments of `diffForHumans()` on `Carbon\Carbon` - -- class: [`Rector\Carbon\Rector\MethodCall\ChangeDiffForHumansArgsRector`](../rules/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector.php) - -```diff - use Carbon\Carbon; - - final class SomeClass - { - public function run(Carbon $carbon): void - { -- $carbon->diffForHumans(null, true); -+ $carbon->diffForHumans(null, \Carbon\CarbonInterface::DIFF_ABSOLUTE); - -- $carbon->diffForHumans(null, false); -+ $carbon->diffForHumans(null, \Carbon\CarbonInterface::DIFF_RELATIVE_AUTO); - } - } -``` - -
- -## CodeQuality - -### AbsolutizeRequireAndIncludePathRector - -include/require to absolute path. This Rector might introduce backwards incompatible code, when the include/require beeing changed depends on the current working directory. - -- class: [`Rector\CodeQuality\Rector\Include_\AbsolutizeRequireAndIncludePathRector`](../rules/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector.php) - -```diff - class SomeClass - { - public function run() - { -- require 'autoload.php'; -+ require __DIR__ . '/autoload.php'; - - require $variable; - } - } -``` - -
- -### AddPregQuoteDelimiterRector - -Add preg_quote delimiter when missing - -- class: [`Rector\CodeQuality\Rector\FuncCall\AddPregQuoteDelimiterRector`](../rules/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector.php) - -```diff --'#' . preg_quote('name') . '#'; -+'#' . preg_quote('name', '#') . '#'; -``` - -
- -### AndAssignsToSeparateLinesRector - -Split 2 assigns ands to separate line - -- class: [`Rector\CodeQuality\Rector\LogicalAnd\AndAssignsToSeparateLinesRector`](../rules/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector.php) - -```diff - class SomeClass - { - public function run() - { - $tokens = []; -- $token = 4 and $tokens[] = $token; -+ $token = 4; -+ $tokens[] = $token; - } - } -``` - -
- -### ArrayKeyExistsTernaryThenValueToCoalescingRector - -Change `array_key_exists()` ternary to coalesing - -- class: [`Rector\CodeQuality\Rector\Ternary\ArrayKeyExistsTernaryThenValueToCoalescingRector`](../rules/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector.php) - -```diff - class SomeClass - { - public function run($values, $keyToMatch) - { -- $result = array_key_exists($keyToMatch, $values) ? $values[$keyToMatch] : null; -+ $result = $values[$keyToMatch] ?? null; - } - } -``` - -
- -### ArrayKeysAndInArrayToArrayKeyExistsRector - -Replace `array_keys()` and `in_array()` to `array_key_exists()` - -- class: [`Rector\CodeQuality\Rector\FuncCall\ArrayKeysAndInArrayToArrayKeyExistsRector`](../rules/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector.php) - -```diff - class SomeClass - { - public function run($packageName, $values) - { -- $keys = array_keys($values); -- return in_array($packageName, $keys, true); -+ return array_key_exists($packageName, $values); - } - } -``` - -
- -### ArrayMergeOfNonArraysToSimpleArrayRector - -Change array_merge of non arrays to array directly - -- class: [`Rector\CodeQuality\Rector\FuncCall\ArrayMergeOfNonArraysToSimpleArrayRector`](../rules/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector.php) - -```diff - class SomeClass - { - public function go() - { - $value = 5; - $value2 = 10; - -- return array_merge([$value], [$value2]); -+ return [$value, $value2]; - } - } -``` - -
- -### ArrayThisCallToThisMethodCallRector - -Change `[$this, someMethod]` without any args to `$this->someMethod()` - -- class: [`Rector\CodeQuality\Rector\Array_\ArrayThisCallToThisMethodCallRector`](../rules/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector.php) - -```diff - class SomeClass - { - public function run() - { -- $values = [$this, 'giveMeMore']; -+ $values = $this->giveMeMore(); - } - - public function giveMeMore() - { - return 'more'; - } - } -``` - -
- -### BooleanNotIdenticalToNotIdenticalRector - -Negated identical boolean compare to not identical compare (does not apply to non-bool values) - -- class: [`Rector\CodeQuality\Rector\Identical\BooleanNotIdenticalToNotIdenticalRector`](../rules/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector.php) - -```diff - class SomeClass - { - public function run() - { - $a = true; - $b = false; - -- var_dump(! $a === $b); // true -- var_dump(! ($a === $b)); // true -+ var_dump($a !== $b); // true -+ var_dump($a !== $b); // true - var_dump($a !== $b); // true - } - } -``` - -
- -### CallUserFuncWithArrowFunctionToInlineRector - -Refactor `call_user_func()` with arrow function to direct call - -- class: [`Rector\CodeQuality\Rector\FuncCall\CallUserFuncWithArrowFunctionToInlineRector`](../rules/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector.php) - -```diff - final class SomeClass - { - public function run() - { -- $result = \call_user_func(fn () => 100); -+ $result = 100; - } - } -``` - -
- -### CallableThisArrayToAnonymousFunctionRector - -Convert [$this, "method"] to proper anonymous function - -- class: [`Rector\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector`](../rules/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector.php) - -```diff - class SomeClass - { - public function run() - { - $values = [1, 5, 3]; -- usort($values, [$this, 'compareSize']); -+ usort($values, function ($first, $second) { -+ return $this->compareSize($first, $second); -+ }); - - return $values; - } - - private function compareSize($first, $second) - { - return $first <=> $second; - } - } -``` - -
- -### ChangeArrayPushToArrayAssignRector - -Change `array_push()` to direct variable assign - -- class: [`Rector\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector`](../rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php) - -```diff - class SomeClass - { - public function run() - { - $items = []; -- array_push($items, $item); -+ $items[] = $item; - } - } -``` - -
- -### CombineIfRector - -Merges nested if statements - -- class: [`Rector\CodeQuality\Rector\If_\CombineIfRector`](../rules/CodeQuality/Rector/If_/CombineIfRector.php) - -```diff - class SomeClass - { - public function run() - { -- if ($cond1) { -- if ($cond2) { -- return 'foo'; -- } -+ if ($cond1 && $cond2) { -+ return 'foo'; - } - } - } -``` - -
- -### CombinedAssignRector - -Simplify `$value` = `$value` + 5; assignments to shorter ones - -- class: [`Rector\CodeQuality\Rector\Assign\CombinedAssignRector`](../rules/CodeQuality/Rector/Assign/CombinedAssignRector.php) - -```diff --$value = $value + 5; -+$value += 5; -``` - -
- -### CommonNotEqualRector - -Use common != instead of less known <> with same meaning - -- class: [`Rector\CodeQuality\Rector\NotEqual\CommonNotEqualRector`](../rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php) - -```diff - final class SomeClass - { - public function run($one, $two) - { -- return $one <> $two; -+ return $one != $two; - } - } -``` - -
- -### CompactToVariablesRector - -Change `compact()` call to own array - -- class: [`Rector\CodeQuality\Rector\FuncCall\CompactToVariablesRector`](../rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php) - -```diff - class SomeClass - { - public function run() - { - $checkout = 'one'; - $form = 'two'; - -- return compact('checkout', 'form'); -+ return ['checkout' => $checkout, 'form' => $form]; - } - } -``` - -
- -### CompleteDynamicPropertiesRector - -Add missing dynamic properties - -- class: [`Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector`](../rules/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector.php) - -```diff - class SomeClass - { -+ /** -+ * @var int -+ */ -+ public $value; -+ - public function set() - { - $this->value = 5; - } - } -``` - -
- -### ConsecutiveNullCompareReturnsToNullCoalesceQueueRector - -Change multiple null compares to ?? queue - -- class: [`Rector\CodeQuality\Rector\If_\ConsecutiveNullCompareReturnsToNullCoalesceQueueRector`](../rules/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector.php) - -```diff - class SomeClass - { - public function run() - { -- if (null !== $this->orderItem) { -- return $this->orderItem; -- } -- -- if (null !== $this->orderItemUnit) { -- return $this->orderItemUnit; -- } -- -- return null; -+ return $this->orderItem ?? $this->orderItemUnit; - } - } -``` - -
- -### DateTimeToDateTimeInterfaceRector - -Changes DateTime type-hint to DateTimeInterface - -- class: [`Rector\CodeQuality\Rector\ClassMethod\DateTimeToDateTimeInterfaceRector`](../rules/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector.php) - -```diff - class SomeClass { -- public function methodWithDateTime(\DateTime $dateTime) -+ /** -+ * @param \DateTime|\DateTimeImmutable $dateTime -+ */ -+ public function methodWithDateTime(\DateTimeInterface $dateTime) - { - return true; - } - } -``` - -
- -### ExplicitBoolCompareRector - -Make if conditions more explicit - -- class: [`Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector`](../rules/CodeQuality/Rector/If_/ExplicitBoolCompareRector.php) - -```diff - final class SomeController - { - public function run($items) - { -- if (!count($items)) { -+ if (count($items) === 0) { - return 'no items'; - } - } - } -``` - -
- -### FixClassCaseSensitivityNameRector - -Change miss-typed case sensitivity name to correct one - -- class: [`Rector\CodeQuality\Rector\Name\FixClassCaseSensitivityNameRector`](../rules/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector.php) - -```diff - final class SomeClass - { - public function run() - { -- $anotherClass = new anotherclass; -+ $anotherClass = new AnotherClass; - } - } - - final class AnotherClass - { - } -``` - -
- -### FlipTypeControlToUseExclusiveTypeRector - -Flip type control to use exclusive type - -- class: [`Rector\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector`](../rules/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector.php) - -```diff - class SomeClass - { - public function __construct(array $values) - { -- /** @var PhpDocInfo|null $phpDocInfo */ - $phpDocInfo = $functionLike->getAttribute(AttributeKey::PHP_DOC_INFO); -- if ($phpDocInfo === null) { -+ if (! $phpDocInfo instanceof PhpDocInfo) { - return; - } - } - } -``` - -
- -### ForRepeatedCountToOwnVariableRector - -Change `count()` in for function to own variable - -- class: [`Rector\CodeQuality\Rector\For_\ForRepeatedCountToOwnVariableRector`](../rules/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector.php) - -```diff - class SomeClass - { - public function run($items) - { -- for ($i = 5; $i <= count($items); $i++) { -+ $itemsCount = count($items); -+ for ($i = 5; $i <= $itemsCount; $i++) { - echo $items[$i]; - } - } - } -``` - -
- -### ForToForeachRector - -Change `for()` to `foreach()` where useful - -- class: [`Rector\CodeQuality\Rector\For_\ForToForeachRector`](../rules/CodeQuality/Rector/For_/ForToForeachRector.php) - -```diff - class SomeClass - { - public function run($tokens) - { -- for ($i = 0, $c = count($tokens); $i < $c; ++$i) { -- if ($tokens[$i][0] === T_STRING && $tokens[$i][1] === 'fn') { -+ foreach ($tokens as $i => $token) { -+ if ($token[0] === T_STRING && $token[1] === 'fn') { - $tokens[$i][0] = self::T_FN; - } - } - } - } -``` - -
- -### ForeachItemsAssignToEmptyArrayToAssignRector - -Change `foreach()` items assign to empty array to direct assign - -- class: [`Rector\CodeQuality\Rector\Foreach_\ForeachItemsAssignToEmptyArrayToAssignRector`](../rules/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector.php) - -```diff - class SomeClass - { - public function run($items) - { - $collectedItems = []; - -- foreach ($items as $item) { -- $collectedItems[] = $item; -- } -+ $collectedItems = $items; - } - } -``` - -
- -### ForeachToInArrayRector - -Simplify `foreach` loops into `in_array` when possible - -- class: [`Rector\CodeQuality\Rector\Foreach_\ForeachToInArrayRector`](../rules/CodeQuality/Rector/Foreach_/ForeachToInArrayRector.php) - -```diff --foreach ($items as $item) { -- if ($item === 'something') { -- return true; -- } --} -- --return false; -+return in_array('something', $items, true); -``` - -
- -### GetClassToInstanceOfRector - -Changes comparison with get_class to instanceof - -- class: [`Rector\CodeQuality\Rector\Identical\GetClassToInstanceOfRector`](../rules/CodeQuality/Rector/Identical/GetClassToInstanceOfRector.php) - -```diff --if (EventsListener::class === get_class($event->job)) { } -+if ($event->job instanceof EventsListener) { } -``` - -
- -### InArrayAndArrayKeysToArrayKeyExistsRector - -Simplify `in_array` and `array_keys` functions combination into `array_key_exists` when `array_keys` has one argument only - -- class: [`Rector\CodeQuality\Rector\FuncCall\InArrayAndArrayKeysToArrayKeyExistsRector`](../rules/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector.php) - -```diff --in_array("key", array_keys($array), true); -+array_key_exists("key", $array); -``` - -
- -### InlineIfToExplicitIfRector - -Change inline if to explicit if - -- class: [`Rector\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector`](../rules/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector.php) - -```diff - class SomeClass - { - public function run() - { - $userId = null; - -- is_null($userId) && $userId = 5; -+ if (is_null($userId)) { -+ $userId = 5; -+ } - } - } -``` - -
- -### IntvalToTypeCastRector - -Change `intval()` to faster and readable (int) `$value` - -- class: [`Rector\CodeQuality\Rector\FuncCall\IntvalToTypeCastRector`](../rules/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector.php) - -```diff - class SomeClass - { - public function run($value) - { -- return intval($value); -+ return (int) $value; - } - } -``` - -
- -### IsAWithStringWithThirdArgumentRector - -Complete missing 3rd argument in case `is_a()` function in case of strings - -- class: [`Rector\CodeQuality\Rector\FuncCall\IsAWithStringWithThirdArgumentRector`](../rules/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector.php) - -```diff - class SomeClass - { - public function __construct(string $value) - { -- return is_a($value, 'stdClass'); -+ return is_a($value, 'stdClass', true); - } - } -``` - -
- -### IssetOnPropertyObjectToPropertyExistsRector - -Change isset on property object to `property_exists()` and not null check - -- class: [`Rector\CodeQuality\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector`](../rules/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector.php) - -```diff - class SomeClass - { - private $x; - - public function run(): void - { -- isset($this->x); -+ property_exists($this, 'x') && $this->x !== null; - } - } -``` - -
- -### JoinStringConcatRector - -Joins concat of 2 strings, unless the length is too long - -- class: [`Rector\CodeQuality\Rector\Concat\JoinStringConcatRector`](../rules/CodeQuality/Rector/Concat/JoinStringConcatRector.php) - -```diff - class SomeClass - { - public function run() - { -- $name = 'Hi' . ' Tom'; -+ $name = 'Hi Tom'; - } - } -``` - -
- -### LogicalToBooleanRector - -Change OR, AND to ||, && with more common understanding - -- class: [`Rector\CodeQuality\Rector\LogicalAnd\LogicalToBooleanRector`](../rules/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector.php) - -```diff --if ($f = false or true) { -+if (($f = false) || true) { - return $f; - } -``` - -
- -### NarrowUnionTypeDocRector - -Changes docblock by narrowing type - -- class: [`Rector\CodeQuality\Rector\ClassMethod\NarrowUnionTypeDocRector`](../rules/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector.php) - -```diff - class SomeClass { - /** -- * @param object|DateTime $message -+ * @param DateTime $message - */ - public function getMessage(object $message) - { - } - } -``` - -
- -### NewStaticToNewSelfRector - -Change unsafe new `static()` to new `self()` - -- class: [`Rector\CodeQuality\Rector\New_\NewStaticToNewSelfRector`](../rules/CodeQuality/Rector/New_/NewStaticToNewSelfRector.php) - -```diff - class SomeClass - { - public function build() - { -- return new static(); -+ return new self(); - } - } -``` - -
- -### RemoveAlwaysTrueConditionSetInConstructorRector - -If conditions is always true, perform the content right away - -- class: [`Rector\CodeQuality\Rector\FunctionLike\RemoveAlwaysTrueConditionSetInConstructorRector`](../rules/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector.php) - -```diff - final class SomeClass - { - private $value; - - public function __construct(stdClass $value) - { - $this->value = $value; - } - - public function go() - { -- if ($this->value) { -- return 'yes'; -- } -+ return 'yes'; - } - } -``` - -
- -### RemoveSoleValueSprintfRector - -Remove `sprintf()` wrapper if not needed - -- class: [`Rector\CodeQuality\Rector\FuncCall\RemoveSoleValueSprintfRector`](../rules/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector.php) - -```diff - class SomeClass - { - public function run() - { -- $value = sprintf('%s', 'hi'); -+ $value = 'hi'; - - $welcome = 'hello'; -- $value = sprintf('%s', $welcome); -+ $value = $welcome; - } - } -``` - -
- -### SetTypeToCastRector - -Changes `settype()` to (type) where possible - -- class: [`Rector\CodeQuality\Rector\FuncCall\SetTypeToCastRector`](../rules/CodeQuality/Rector/FuncCall/SetTypeToCastRector.php) - -```diff - class SomeClass - { - public function run($foo) - { -- settype($foo, 'string'); -+ $foo = (string) $foo; - -- return settype($foo, 'integer'); -+ return (int) $foo; - } - } -``` - -
- -### ShortenElseIfRector - -Shortens else/if to elseif - -- class: [`Rector\CodeQuality\Rector\If_\ShortenElseIfRector`](../rules/CodeQuality/Rector/If_/ShortenElseIfRector.php) - -```diff - class SomeClass - { - public function run() - { - if ($cond1) { - return $action1; -- } else { -- if ($cond2) { -- return $action2; -- } -+ } elseif ($cond2) { -+ return $action2; - } - } - } -``` - -
- -### SimplifyArraySearchRector - -Simplify array_search to in_array - -- class: [`Rector\CodeQuality\Rector\Identical\SimplifyArraySearchRector`](../rules/CodeQuality/Rector/Identical/SimplifyArraySearchRector.php) - -```diff --array_search("searching", $array) !== false; -+in_array("searching", $array); -``` - -
- -```diff --array_search("searching", $array, true) !== false; -+in_array("searching", $array, true); -``` - -
- -### SimplifyBoolIdenticalTrueRector - -Symplify bool value compare to true or false - -- class: [`Rector\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector`](../rules/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector.php) - -```diff - class SomeClass - { - public function run(bool $value, string $items) - { -- $match = in_array($value, $items, TRUE) === TRUE; -- $match = in_array($value, $items, TRUE) !== FALSE; -+ $match = in_array($value, $items, TRUE); -+ $match = in_array($value, $items, TRUE); - } - } -``` - -
- -### SimplifyConditionsRector - -Simplify conditions - -- class: [`Rector\CodeQuality\Rector\Identical\SimplifyConditionsRector`](../rules/CodeQuality/Rector/Identical/SimplifyConditionsRector.php) - -```diff --if (! ($foo !== 'bar')) {... -+if ($foo === 'bar') {... -``` - -
- -### SimplifyDeMorganBinaryRector - -Simplify negated conditions with de Morgan theorem - -- class: [`Rector\CodeQuality\Rector\BooleanNot\SimplifyDeMorganBinaryRector`](../rules/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector.php) - -```diff - $a = 5; - $b = 10; --$result = !($a > 20 || $b <= 50); -+$result = $a <= 20 && $b > 50; -``` - -
- -### SimplifyDuplicatedTernaryRector - -Remove ternary that duplicated return value of true : false - -- class: [`Rector\CodeQuality\Rector\Ternary\SimplifyDuplicatedTernaryRector`](../rules/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector.php) - -```diff - class SomeClass - { - public function run(bool $value, string $name) - { -- $isTrue = $value ? true : false; -+ $isTrue = $value; - $isName = $name ? true : false; - } - } -``` - -
- -### SimplifyEmptyArrayCheckRector - -Simplify `is_array` and `empty` functions combination into a simple identical check for an empty array - -- class: [`Rector\CodeQuality\Rector\BooleanAnd\SimplifyEmptyArrayCheckRector`](../rules/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector.php) - -```diff --is_array($values) && empty($values) -+$values === [] -``` - -
- -### SimplifyForeachToArrayFilterRector - -Simplify foreach with function filtering to array filter - -- class: [`Rector\CodeQuality\Rector\Foreach_\SimplifyForeachToArrayFilterRector`](../rules/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector.php) - -```diff --$directories = []; - $possibleDirectories = []; --foreach ($possibleDirectories as $possibleDirectory) { -- if (file_exists($possibleDirectory)) { -- $directories[] = $possibleDirectory; -- } --} -+$directories = array_filter($possibleDirectories, 'file_exists'); -``` - -
- -### SimplifyForeachToCoalescingRector - -Changes foreach that returns set value to ?? - -- class: [`Rector\CodeQuality\Rector\Foreach_\SimplifyForeachToCoalescingRector`](../rules/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector.php) - -```diff --foreach ($this->oldToNewFunctions as $oldFunction => $newFunction) { -- if ($currentFunction === $oldFunction) { -- return $newFunction; -- } --} -- --return null; -+return $this->oldToNewFunctions[$currentFunction] ?? null; -``` - -
- -### SimplifyFuncGetArgsCountRector - -Simplify count of `func_get_args()` to `func_num_args()` - -- class: [`Rector\CodeQuality\Rector\FuncCall\SimplifyFuncGetArgsCountRector`](../rules/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php) - -```diff --count(func_get_args()); -+func_num_args(); -``` - -
- -### SimplifyIfElseToTernaryRector - -Changes if/else for same value as assign to ternary - -- class: [`Rector\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector`](../rules/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector.php) - -```diff - class SomeClass - { - public function run() - { -- if (empty($value)) { -- $this->arrayBuilt[][$key] = true; -- } else { -- $this->arrayBuilt[][$key] = $value; -- } -+ $this->arrayBuilt[][$key] = empty($value) ? true : $value; - } - } -``` - -
- -### SimplifyIfIssetToNullCoalescingRector - -Simplify binary if to null coalesce - -- class: [`Rector\CodeQuality\Rector\If_\SimplifyIfIssetToNullCoalescingRector`](../rules/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector.php) - -```diff - final class SomeController - { - public function run($possibleStatieYamlFile) - { -- if (isset($possibleStatieYamlFile['import'])) { -- $possibleStatieYamlFile['import'] = array_merge($possibleStatieYamlFile['import'], $filesToImport); -- } else { -- $possibleStatieYamlFile['import'] = $filesToImport; -- } -+ $possibleStatieYamlFile['import'] = array_merge($possibleStatieYamlFile['import'] ?? [], $filesToImport); - } - } -``` - -
- -### SimplifyIfNotNullReturnRector - -Changes redundant null check to instant return - -- class: [`Rector\CodeQuality\Rector\If_\SimplifyIfNotNullReturnRector`](../rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php) - -```diff - $newNode = 'something'; --if ($newNode !== null) { -- return $newNode; --} -- --return null; -+return $newNode; -``` - -
- -### SimplifyIfNullableReturnRector - -Direct return on if nullable check before return - -- class: [`Rector\CodeQuality\Rector\If_\SimplifyIfNullableReturnRector`](../rules/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector.php) - -```diff - class SomeClass - { - public function run() - { -- /** @var \stdClass|null $value */ -- $value = $this->foo->bar(); -- if (! $value instanceof \stdClass) { -- return null; -- } -- -- return $value; -+ return $this->foo->bar(); - } - } -``` - -
- -### SimplifyIfReturnBoolRector - -Shortens if return false/true to direct return - -- class: [`Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector`](../rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php) - -```diff --if (strpos($docToken->getContent(), "\n") === false) { -- return true; --} -- --return false; -+return strpos($docToken->getContent(), "\n") === false; -``` - -
- -### SimplifyInArrayValuesRector - -Removes unneeded `array_values()` in `in_array()` call - -- class: [`Rector\CodeQuality\Rector\FuncCall\SimplifyInArrayValuesRector`](../rules/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector.php) - -```diff --in_array("key", array_values($array), true); -+in_array("key", $array, true); -``` - -
- -### SimplifyRegexPatternRector - -Simplify regex pattern to known ranges - -- class: [`Rector\CodeQuality\Rector\FuncCall\SimplifyRegexPatternRector`](../rules/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector.php) - -```diff - class SomeClass - { - public function run($value) - { -- preg_match('#[a-zA-Z0-9+]#', $value); -+ preg_match('#[\w\d+]#', $value); - } - } -``` - -
- -### SimplifyStrposLowerRector - -Simplify `strpos(strtolower()`, "...") calls - -- class: [`Rector\CodeQuality\Rector\FuncCall\SimplifyStrposLowerRector`](../rules/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector.php) - -```diff --strpos(strtolower($var), "...") -+stripos($var, "...") -``` - -
- -### SimplifyTautologyTernaryRector - -Simplify tautology ternary to value - -- class: [`Rector\CodeQuality\Rector\Ternary\SimplifyTautologyTernaryRector`](../rules/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector.php) - -```diff --$value = ($fullyQualifiedTypeHint !== $typeHint) ? $fullyQualifiedTypeHint : $typeHint; -+$value = $fullyQualifiedTypeHint; -``` - -
- -### SimplifyUselessVariableRector - -Removes useless variable assigns - -- class: [`Rector\CodeQuality\Rector\Return_\SimplifyUselessVariableRector`](../rules/CodeQuality/Rector/Return_/SimplifyUselessVariableRector.php) - -```diff - function () { -- $a = true; -- return $a; -+ return true; - }; -``` - -
- -### SingleInArrayToCompareRector - -Changes `in_array()` with single element to === - -- class: [`Rector\CodeQuality\Rector\FuncCall\SingleInArrayToCompareRector`](../rules/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector.php) - -```diff - class SomeClass - { - public function run() - { -- if (in_array(strtolower($type), ['$this'], true)) { -+ if (strtolower($type) === '$this') { - return strtolower($type); - } - } - } -``` - -
- -### SingularSwitchToIfRector - -Change switch with only 1 check to if - -- class: [`Rector\CodeQuality\Rector\Switch_\SingularSwitchToIfRector`](../rules/CodeQuality/Rector/Switch_/SingularSwitchToIfRector.php) - -```diff - class SomeObject - { - public function run($value) - { - $result = 1; -- switch ($value) { -- case 100: -+ if ($value === 100) { - $result = 1000; - } - - return $result; - } - } -``` - -
- -### SplitListAssignToSeparateLineRector - -Splits `[$a, $b] = [5, 10]` scalar assign to standalone lines - -- class: [`Rector\CodeQuality\Rector\Assign\SplitListAssignToSeparateLineRector`](../rules/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector.php) - -```diff - final class SomeClass - { - public function run(): void - { -- [$a, $b] = [1, 2]; -+ $a = 1; -+ $b = 2; - } - } -``` - -
- -### StrlenZeroToIdenticalEmptyStringRector - -Changes strlen comparison to 0 to direct empty string compare - -- class: [`Rector\CodeQuality\Rector\Identical\StrlenZeroToIdenticalEmptyStringRector`](../rules/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector.php) - -```diff - class SomeClass - { - public function run($value) - { -- $empty = strlen($value) === 0; -+ $empty = $value === ''; - } - } -``` - -
- -### SwitchNegatedTernaryRector - -Switch negated ternary condition rector - -- class: [`Rector\CodeQuality\Rector\Ternary\SwitchNegatedTernaryRector`](../rules/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector.php) - -```diff - class SomeClass - { - public function run(bool $upper, string $name) - { -- return ! $upper -- ? $name -- : strtoupper($name); -+ return $upper -+ ? strtoupper($name) -+ : $name; - } - } -``` - -
- -### ThrowWithPreviousExceptionRector - -When throwing into a catch block, checks that the previous exception is passed to the new throw clause - -- class: [`Rector\CodeQuality\Rector\Catch_\ThrowWithPreviousExceptionRector`](../rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php) - -```diff - class SomeClass - { - public function run() - { - try { - $someCode = 1; - } catch (Throwable $throwable) { -- throw new AnotherException('ups'); -+ throw new AnotherException('ups', $throwable->getCode(), $throwable); - } - } - } -``` - -
- -### UnnecessaryTernaryExpressionRector - -Remove unnecessary ternary expressions. - -- class: [`Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector`](../rules/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector.php) - -```diff --$foo === $bar ? true : false; -+$foo === $bar; -``` - -
- -### UnusedForeachValueToArrayKeysRector - -Change foreach with unused `$value` but only `$key,` to `array_keys()` - -- class: [`Rector\CodeQuality\Rector\Foreach_\UnusedForeachValueToArrayKeysRector`](../rules/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector.php) - -```diff - class SomeClass - { - public function run() - { - $items = []; -- foreach ($values as $key => $value) { -+ foreach (array_keys($values) as $key) { - $items[$key] = null; - } - } - } -``` - -
- -### UnwrapSprintfOneArgumentRector - -unwrap `sprintf()` with one argument - -- class: [`Rector\CodeQuality\Rector\FuncCall\UnwrapSprintfOneArgumentRector`](../rules/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector.php) - -```diff --echo sprintf('value'); -+echo 'value'; -``` - -
- -### UseIdenticalOverEqualWithSameTypeRector - -Use ===/!== over ==/!=, it values have the same type - -- class: [`Rector\CodeQuality\Rector\Equal\UseIdenticalOverEqualWithSameTypeRector`](../rules/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector.php) - -```diff - class SomeClass - { - public function run(int $firstValue, int $secondValue) - { -- $isSame = $firstValue == $secondValue; -- $isDiffernt = $firstValue != $secondValue; -+ $isSame = $firstValue === $secondValue; -+ $isDiffernt = $firstValue !== $secondValue; - } - } -``` - -
- -## CodingStyle - -### AddArrayDefaultToArrayPropertyRector - -Adds array default value to property to prevent foreach over null error - -- class: [`Rector\CodingStyle\Rector\Class_\AddArrayDefaultToArrayPropertyRector`](../rules/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector.php) - -```diff - class SomeClass - { - /** - * @var int[] - */ -- private $values; -+ private $values = []; - - public function isEmpty() - { -- return $this->values === null; -+ return $this->values === []; - } - } -``` - -
- -### AddFalseDefaultToBoolPropertyRector - -Add false default to bool properties, to prevent null compare errors - -- class: [`Rector\CodingStyle\Rector\Property\AddFalseDefaultToBoolPropertyRector`](../rules/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector.php) - -```diff - class SomeClass - { - /** - * @var bool - */ -- private $isDisabled; -+ private $isDisabled = false; - } -``` - -
- -### BinarySwitchToIfElseRector - -Changes switch with 2 options to if-else - -- class: [`Rector\CodingStyle\Rector\Switch_\BinarySwitchToIfElseRector`](../rules/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector.php) - -```diff --switch ($foo) { -- case 'my string': -- $result = 'ok'; -- break; -- -- default: -- $result = 'not ok'; -+if ($foo == 'my string') { -+ $result = 'ok'; -+} else { -+ $result = 'not ok'; - } -``` - -
- -### CallUserFuncArrayToVariadicRector - -Replace `call_user_func_array()` with variadic - -- class: [`Rector\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector`](../rules/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector.php) - -```diff - class SomeClass - { - public function run() - { -- call_user_func_array('some_function', $items); -+ some_function(...$items); - } - } -``` - -
- -### CallUserFuncToMethodCallRector - -Refactor `call_user_func()` on known class method to a method call - -- class: [`Rector\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector`](../rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php) - -```diff - final class SomeClass - { - public function run() - { -- $result = \call_user_func([$this->property, 'method'], $args); -+ $result = $this->property->method($args); - } - } -``` - -
- -### CatchExceptionNameMatchingTypeRector - -Type and name of catch exception should match - -- class: [`Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector`](../rules/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector.php) - -```diff - class SomeClass - { - public function run() - { - try { - // ... -- } catch (SomeException $typoException) { -- $typoException->getMessage(); -+ } catch (SomeException $someException) { -+ $someException->getMessage(); - } - } - } -``` - -
- -### ConsistentImplodeRector - -Changes various implode forms to consistent one - -- class: [`Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector`](../rules/CodingStyle/Rector/FuncCall/ConsistentImplodeRector.php) - -```diff - class SomeClass - { - public function run(array $items) - { -- $itemsAsStrings = implode($items); -- $itemsAsStrings = implode($items, '|'); -+ $itemsAsStrings = implode('', $items); -+ $itemsAsStrings = implode('|', $items); - - $itemsAsStrings = implode('|', $items); - } - } -``` - -
- -### ConsistentPregDelimiterRector - -Replace PREG delimiter with configured one - -:wrench: **configure it!** - -- class: [`Rector\CodingStyle\Rector\FuncCall\ConsistentPregDelimiterRector`](../rules/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector.php) - -```php -use Rector\CodingStyle\Rector\FuncCall\ConsistentPregDelimiterRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ConsistentPregDelimiterRector::class) - ->call('configure', [[ - ConsistentPregDelimiterRector::DELIMITER => '#', - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- preg_match('~value~', $value); -- preg_match_all('~value~im', $value); -+ preg_match('#value#', $value); -+ preg_match_all('#value#im', $value); - } - } -``` - -
- -### CountArrayToEmptyArrayComparisonRector - -Change count array comparison to empty array comparison to improve performance - -- class: [`Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector`](../rules/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector.php) - -```diff --count($array) === 0; --count($array) > 0; --! count($array); -+$array === []; -+$array !== []; -+$array === []; -``` - -
- -### EncapsedStringsToSprintfRector - -Convert enscaped {$string} to more readable sprintf - -- class: [`Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector`](../rules/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector.php) - -```diff - final class SomeClass - { - public function run(string $format) - { -- return "Unsupported format {$format}"; -+ return sprintf('Unsupported format %s', $format); - } - } -``` - -
- -### FollowRequireByDirRector - -include/require should be followed by absolute path - -- class: [`Rector\CodingStyle\Rector\Include_\FollowRequireByDirRector`](../rules/CodingStyle/Rector/Include_/FollowRequireByDirRector.php) - -```diff - class SomeClass - { - public function run() - { -- require 'autoload.php'; -+ require __DIR__ . '/autoload.php'; - } - } -``` - -
- -### FuncGetArgsToVariadicParamRector - -Refactor `func_get_args()` in to a variadic param - -- class: [`Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector`](../rules/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector.php) - -```diff --function run() -+function run(...$args) - { -- $args = \func_get_args(); - } -``` - -
- -### MakeInheritedMethodVisibilitySameAsParentRector - -Make method visibility same as parent one - -- class: [`Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector`](../rules/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php) - -```diff - class ChildClass extends ParentClass - { -- public function run() -+ protected function run() - { - } - } - - class ParentClass - { - protected function run() - { - } - } -``` - -
- -### ManualJsonStringToJsonEncodeArrayRector - -Convert manual JSON string to JSON::encode array - -- class: [`Rector\CodingStyle\Rector\Assign\ManualJsonStringToJsonEncodeArrayRector`](../rules/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector.php) - -```diff -+use Nette\Utils\Json; -+ - final class SomeClass - { - public function run() - { -- $someJsonAsString = '{"role_name":"admin","numberz":{"id":"10"}}'; -+ $data = [ -+ 'role_name' => 'admin', -+ 'numberz' => ['id' => 10] -+ ]; -+ $someJsonAsString = Json::encode($data); - } - } -``` - -
- -### NewlineBeforeNewAssignSetRector - -Add extra space before new assign set - -- class: [`Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector`](../rules/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php) - -```diff - final class SomeClass - { - public function run() - { - $value = new Value; - $value->setValue(5); -+ - $value2 = new Value; - $value2->setValue(1); - } - } -``` - -
- -### NullableCompareToNullRector - -Changes negate of empty comparison of nullable value to explicit === or !== compare - -- class: [`Rector\CodingStyle\Rector\If_\NullableCompareToNullRector`](../rules/CodingStyle/Rector/If_/NullableCompareToNullRector.php) - -```diff - /** @var stdClass|null $value */ --if ($value) { -+if ($value !== null) { - } - --if (!$value) { -+if ($value === null) { - } -``` - -
- -### OrderAttributesRector - -Order attributes by desired names - -:wrench: **configure it!** - -- class: [`Rector\CodingStyle\Rector\ClassMethod\OrderAttributesRector`](../rules/CodingStyle/Rector/ClassMethod/OrderAttributesRector.php) - -```php -use Rector\CodingStyle\Rector\ClassMethod\OrderAttributesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(OrderAttributesRector::class) - ->call('configure', [[ - OrderAttributesRector::ATTRIBUTES_ORDER => ['First', 'Second'], - ]]); -}; -``` - -↓ - -```diff -+#[First] - #[Second] --#[First] - class Someclass - { - } -``` - -
- -### PHPStormVarAnnotationRector - -Change various `@var` annotation formats to one PHPStorm understands - -- class: [`Rector\CodingStyle\Rector\Assign\PHPStormVarAnnotationRector`](../rules/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector.php) - -```diff --$config = 5; --/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */ -+/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */ -+$config = 5; -``` - -
- -### PostIncDecToPreIncDecRector - -Use ++$value or --$value instead of `$value++` or `$value--` - -- class: [`Rector\CodingStyle\Rector\PostInc\PostIncDecToPreIncDecRector`](../rules/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector.php) - -```diff - class SomeClass - { - public function run($value = 1) - { -- $value++; echo $value; -- $value--; echo $value; -+ ++$value; echo $value; -+ --$value; echo $value; - } - } -``` - -
- -### PreferThisOrSelfMethodCallRector - -Changes `$this->...` and static:: to self:: or vise versa for given types - -:wrench: **configure it!** - -- class: [`Rector\CodingStyle\Rector\MethodCall\PreferThisOrSelfMethodCallRector`](../rules/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector.php) - -```php -use PHPUnit\Framework\TestCase; -use Rector\CodingStyle\Enum\PreferenceSelfThis; -use Rector\CodingStyle\Rector\MethodCall\PreferThisOrSelfMethodCallRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(PreferThisOrSelfMethodCallRector::class) - ->call('configure', [[ - PreferThisOrSelfMethodCallRector::TYPE_TO_PREFERENCE => [ - TestCase::class => ValueObjectInliner::inline(PreferenceSelfThis::PREFER_SELF()), - - ], ]]); -}; -``` - -↓ - -```diff - class SomeClass extends \PHPUnit\Framework\TestCase - { - public function run() - { -- $this->assertEquals('a', 'a'); -+ self::assertEquals('a', 'a'); - } - } -``` - -
- -### PreslashSimpleFunctionRector - -Add pre-slash to short named functions to improve performance - -- class: [`Rector\CodingStyle\Rector\FuncCall\PreslashSimpleFunctionRector`](../rules/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector.php) - -```diff - class SomeClass - { - public function shorten($value) - { -- return trim($value); -+ return \trim($value); - } - } -``` - -
- -### RemoveDoubleUnderscoreInMethodNameRector - -Non-magic PHP object methods cannot start with "__" - -- class: [`Rector\CodingStyle\Rector\ClassMethod\RemoveDoubleUnderscoreInMethodNameRector`](../rules/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector.php) - -```diff - class SomeClass - { -- public function __getName($anotherObject) -+ public function getName($anotherObject) - { -- $anotherObject->__getSurname(); -+ $anotherObject->getSurname(); - } - } -``` - -
- -### RemoveUnusedAliasRector - -Removes unused use aliases. Keep annotation aliases like "Doctrine\ORM\Mapping as ORM" to keep convention format - -- class: [`Rector\CodingStyle\Rector\Use_\RemoveUnusedAliasRector`](../rules/CodingStyle/Rector/Use_/RemoveUnusedAliasRector.php) - -```diff --use Symfony\Kernel as BaseKernel; -+use Symfony\Kernel; - --class SomeClass extends BaseKernel -+class SomeClass extends Kernel - { - } -``` - -
- -### ReturnArrayClassMethodToYieldRector - -Turns array return to yield return in specific type and method - -:wrench: **configure it!** - -- class: [`Rector\CodingStyle\Rector\ClassMethod\ReturnArrayClassMethodToYieldRector`](../rules/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector.php) - -```php -use Rector\CodingStyle\Rector\ClassMethod\ReturnArrayClassMethodToYieldRector; -use Rector\CodingStyle\ValueObject\ReturnArrayClassMethodToYield; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ReturnArrayClassMethodToYieldRector::class) - ->call('configure', [[ - ReturnArrayClassMethodToYieldRector::METHODS_TO_YIELDS => ValueObjectInliner::inline([ - new ReturnArrayClassMethodToYield('PHPUnit\Framework\TestCase', '*provide*'), - ]), - ]]); -}; -``` - -↓ - -```diff - use PHPUnit\Framework\TestCase; - - final class SomeTest implements TestCase - { - public static function provideData() - { -- return [['some text']]; -+ yield ['some text']; - } - } -``` - -
- -### SeparateMultiUseImportsRector - -Split multi use imports and trait statements to standalone lines - -- class: [`Rector\CodingStyle\Rector\Use_\SeparateMultiUseImportsRector`](../rules/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector.php) - -```diff --use A, B; -+use A; -+use B; - - class SomeClass - { -- use SomeTrait, AnotherTrait; -+ use SomeTrait; -+ use AnotherTrait; - } -``` - -
- -### SplitDoubleAssignRector - -Split multiple inline assigns to each own lines default value, to prevent undefined array issues - -- class: [`Rector\CodingStyle\Rector\Assign\SplitDoubleAssignRector`](../rules/CodingStyle/Rector/Assign/SplitDoubleAssignRector.php) - -```diff - class SomeClass - { - public function run() - { -- $one = $two = 1; -+ $one = 1; -+ $two = 1; - } - } -``` - -
- -### SplitGroupedConstantsAndPropertiesRector - -Separate constant and properties to own lines - -- class: [`Rector\CodingStyle\Rector\ClassConst\SplitGroupedConstantsAndPropertiesRector`](../rules/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector.php) - -```diff - class SomeClass - { -- const HI = true, AHOJ = 'true'; -+ const HI = true; -+ const AHOJ = 'true'; - - /** - * @var string - */ -- public $isIt, $isIsThough; -+ public $isIt; -+ -+ /** -+ * @var string -+ */ -+ public $isIsThough; - } -``` - -
- -### SplitStringClassConstantToClassConstFetchRector - -Separate class constant in a string to class constant fetch and string - -- class: [`Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector`](../rules/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector.php) - -```diff - class SomeClass - { - const HI = true; - } - - class AnotherClass - { - public function get() - { -- return 'SomeClass::HI'; -+ return SomeClass::class . '::HI'; - } - } -``` - -
- -### StrictArraySearchRector - -Makes array_search search for identical elements - -- class: [`Rector\CodingStyle\Rector\FuncCall\StrictArraySearchRector`](../rules/CodingStyle/Rector/FuncCall/StrictArraySearchRector.php) - -```diff --array_search($value, $items); -+array_search($value, $items, true); -``` - -
- -### SymplifyQuoteEscapeRector - -Prefer quote that are not inside the string - -- class: [`Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector`](../rules/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector.php) - -```diff - class SomeClass - { - public function run() - { -- $name = "\" Tom"; -- $name = '\' Sara'; -+ $name = '" Tom'; -+ $name = "' Sara"; - } - } -``` - -
- -### TernaryConditionVariableAssignmentRector - -Assign outcome of ternary condition to variable, where applicable - -- class: [`Rector\CodingStyle\Rector\Ternary\TernaryConditionVariableAssignmentRector`](../rules/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector.php) - -```diff - function ternary($value) - { -- $value ? $a = 1 : $a = 0; -+ $a = $value ? 1 : 0; - } -``` - -
- -### UnSpreadOperatorRector - -Remove spread operator - -- class: [`Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector`](../rules/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector.php) - -```diff - class SomeClass - { -- public function run(...$array) -+ public function run(array $array) - { - } - - public function execute(array $data) - { -- $this->run(...$data); -+ $this->run($data); - } - } -``` - -
- -### UseClassKeywordForClassNameResolutionRector - -Use `class` keyword for class name resolution in string instead of hardcoded string reference - -- class: [`Rector\CodingStyle\Rector\String_\UseClassKeywordForClassNameResolutionRector`](../rules/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector.php) - -```diff --$value = 'App\SomeClass::someMethod()'; -+$value = \App\SomeClass . '::someMethod()'; -``` - -
- -### UseIncrementAssignRector - -Use ++ increment instead of `$var += 1` - -- class: [`Rector\CodingStyle\Rector\Plus\UseIncrementAssignRector`](../rules/CodingStyle/Rector/Plus/UseIncrementAssignRector.php) - -```diff - class SomeClass - { - public function run() - { -- $style += 1; -+ ++$style; - } - } -``` - -
- -### UseMessageVariableForSprintfInSymfonyStyleRector - -Decouple `$message` property from `sprintf()` calls in `$this->symfonyStyle->method()` - -- class: [`Rector\CodingStyle\Rector\MethodCall\UseMessageVariableForSprintfInSymfonyStyleRector`](../rules/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector.php) - -```diff - use Symfony\Component\Console\Style\SymfonyStyle; - - final class SomeClass - { - public function run(SymfonyStyle $symfonyStyle) - { -- $symfonyStyle->info(sprintf('Hi %s', 'Tom')); -+ $message = sprintf('Hi %s', 'Tom'); -+ $symfonyStyle->info($message); - } - } -``` - -
- -### VarConstantCommentRector - -Constant should have a `@var` comment with type - -- class: [`Rector\CodingStyle\Rector\ClassConst\VarConstantCommentRector`](../rules/CodingStyle/Rector/ClassConst/VarConstantCommentRector.php) - -```diff - class SomeClass - { -+ /** -+ * @var string -+ */ - const HI = 'hi'; - } -``` - -
- -### VersionCompareFuncCallToConstantRector - -Changes use of call to version compare function to use of PHP version constant - -- class: [`Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector`](../rules/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php) - -```diff - class SomeClass - { - public function run() - { -- version_compare(PHP_VERSION, '5.3.0', '<'); -+ PHP_VERSION_ID < 50300; - } - } -``` - -
- -### WrapEncapsedVariableInCurlyBracesRector - -Wrap encapsed variables in curly braces - -- class: [`Rector\CodingStyle\Rector\Encapsed\WrapEncapsedVariableInCurlyBracesRector`](../rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php) - -```diff - function run($world) - { -- echo "Hello $world!"; -+ echo "Hello {$world}!"; - } -``` - -
- -## Composer - -### AddPackageToRequireComposerRector - -Add package to "require" in `composer.json` - -:wrench: **configure it!** - -- class: [`Rector\Composer\Rector\AddPackageToRequireComposerRector`](../rules/Composer/Rector/AddPackageToRequireComposerRector.php) - -```php -use Rector\Composer\Rector\AddPackageToRequireComposerRector; -use Rector\Composer\ValueObject\PackageAndVersion; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddPackageToRequireComposerRector::class) - ->call('configure', [[ - AddPackageToRequireComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('symfony/console', '^3.4'), - ]), - ]]); -}; -``` - -↓ - -```diff - { -+ "require": { -+ "symfony/console": "^3.4" -+ } - } -``` - -
- -### AddPackageToRequireDevComposerRector - -Add package to "require-dev" in `composer.json` - -:wrench: **configure it!** - -- class: [`Rector\Composer\Rector\AddPackageToRequireDevComposerRector`](../rules/Composer/Rector/AddPackageToRequireDevComposerRector.php) - -```php -use Rector\Composer\Rector\AddPackageToRequireDevComposerRector; -use Rector\Composer\ValueObject\PackageAndVersion; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddPackageToRequireDevComposerRector::class) - ->call('configure', [[ - AddPackageToRequireDevComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('symfony/console', '^3.4'), - ]), - ]]); -}; -``` - -↓ - -```diff - { -+ "require-dev": { -+ "symfony/console": "^3.4" -+ } - } -``` - -
- -### ChangePackageVersionComposerRector - -Change package version `composer.json` - -:wrench: **configure it!** - -- class: [`Rector\Composer\Rector\ChangePackageVersionComposerRector`](../rules/Composer/Rector/ChangePackageVersionComposerRector.php) - -```php -use Rector\Composer\Rector\ChangePackageVersionComposerRector; -use Rector\Composer\ValueObject\PackageAndVersion; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ChangePackageVersionComposerRector::class) - ->call('configure', [[ - ChangePackageVersionComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('symfony/console', '^4.4'), - ]), - ]]); -}; -``` - -↓ - -```diff - { -- "require-dev": { -- "symfony/console": "^3.4" -+ "require": { -+ "symfony/console": "^4.4" - } - } -``` - -
- -### RemovePackageComposerRector - -Remove package from "require" and "require-dev" in `composer.json` - -:wrench: **configure it!** - -- class: [`Rector\Composer\Rector\RemovePackageComposerRector`](../rules/Composer/Rector/RemovePackageComposerRector.php) - -```php -use Rector\Composer\Rector\RemovePackageComposerRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RemovePackageComposerRector::class) - ->call('configure', [[ - RemovePackageComposerRector::PACKAGE_NAMES => ['symfony/console'], - ]]); -}; -``` - -↓ - -```diff - { -- "require": { -- "symfony/console": "^3.4" -- } - } -``` - -
- -### RenamePackageComposerRector - -Change package name in `composer.json` - -:wrench: **configure it!** - -- class: [`Rector\Composer\Rector\RenamePackageComposerRector`](../rules/Composer/Rector/RenamePackageComposerRector.php) - -```php -use Rector\Composer\Rector\RenamePackageComposerRector; -use Rector\Composer\ValueObject\RenamePackage; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenamePackageComposerRector::class) - ->call('configure', [[ - RenamePackageComposerRector::RENAME_PACKAGES => ValueObjectInliner::inline([ - new RenamePackage('rector/rector', 'rector/rector-src'), - ]), - ]]); -}; -``` - -↓ - -```diff - { - "require": { -- "rector/rector": "dev-main" -+ "rector/rector-src": "dev-main" - } - } -``` - -
- -### ReplacePackageAndVersionComposerRector - -Change package name and version `composer.json` - -:wrench: **configure it!** - -- class: [`Rector\Composer\Rector\ReplacePackageAndVersionComposerRector`](../rules/Composer/Rector/ReplacePackageAndVersionComposerRector.php) - -```php -use Rector\Composer\Rector\ReplacePackageAndVersionComposerRector; -use Rector\Composer\ValueObject\ReplacePackageAndVersion; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ReplacePackageAndVersionComposerRector::class) - ->call('configure', [[ - ReplacePackageAndVersionComposerRector::REPLACE_PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new ReplacePackageAndVersion('symfony/console', 'symfony/http-kernel', '^4.4'), - ]), - ]]); -}; -``` - -↓ - -```diff - { - "require-dev": { -- "symfony/console": "^3.4" -+ "symfony/http-kernel": "^4.4" - } - } -``` - -
- -## DeadCode - -### RecastingRemovalRector - -Removes recasting of the same type - -- class: [`Rector\DeadCode\Rector\Cast\RecastingRemovalRector`](../rules/DeadCode/Rector/Cast/RecastingRemovalRector.php) - -```diff - $string = ''; --$string = (string) $string; -+$string = $string; - - $array = []; --$array = (array) $array; -+$array = $array; -``` - -
- -### RemoveAlwaysTrueIfConditionRector - -Remove if condition that is always true - -- class: [`Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector`](../rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php) - -```diff - final class SomeClass - { - public function go() - { -- if (1 === 1) { -- return 'yes'; -- } -+ return 'yes'; - - return 'no'; - } - } -``` - -
- -### RemoveAndTrueRector - -Remove and true that has no added value - -- class: [`Rector\DeadCode\Rector\BooleanAnd\RemoveAndTrueRector`](../rules/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector.php) - -```diff - class SomeClass - { - public function run() - { -- return true && 5 === 1; -+ return 5 === 1; - } - } -``` - -
- -### RemoveAnnotationRector - -Remove annotation by names - -:wrench: **configure it!** - -- class: [`Rector\DeadCode\Rector\ClassLike\RemoveAnnotationRector`](../rules/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php) - -```php -use Rector\DeadCode\Rector\ClassLike\RemoveAnnotationRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RemoveAnnotationRector::class) - ->call('configure', [[ - RemoveAnnotationRector::ANNOTATIONS_TO_REMOVE => ['method'], - ]]); -}; -``` - -↓ - -```diff --/** -- * @method getName() -- */ - final class SomeClass - { - } -``` - -
- -### RemoveAssignOfVoidReturnFunctionRector - -Remove assign of void function/method to variable - -- class: [`Rector\DeadCode\Rector\Assign\RemoveAssignOfVoidReturnFunctionRector`](../rules/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector.php) - -```diff - class SomeClass - { - public function run() - { -- $value = $this->getOne(); -+ $this->getOne(); - } - - private function getOne(): void - { - } - } -``` - -
- -### RemoveCodeAfterReturnRector - -Remove dead code after return statement - -- class: [`Rector\DeadCode\Rector\FunctionLike\RemoveCodeAfterReturnRector`](../rules/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector.php) - -```diff - class SomeClass - { - public function run(int $a) - { - return $a; -- $a++; - } - } -``` - -
- -### RemoveConcatAutocastRector - -Remove (string) casting when it comes to concat, that does this by default - -- class: [`Rector\DeadCode\Rector\Concat\RemoveConcatAutocastRector`](../rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php) - -```diff - class SomeConcatingClass - { - public function run($value) - { -- return 'hi ' . (string) $value; -+ return 'hi ' . $value; - } - } -``` - -
- -### RemoveDeadConditionAboveReturnRector - -Remove dead condition above return - -- class: [`Rector\DeadCode\Rector\Return_\RemoveDeadConditionAboveReturnRector`](../rules/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector.php) - -```diff - final class SomeClass - { - public function go() - { -- if (1 === 1) { -- return 'yes'; -- } -- - return 'yes'; - } - } -``` - -
- -### RemoveDeadConstructorRector - -Remove empty constructor - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveDeadConstructorRector`](../rules/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector.php) - -```diff - class SomeClass - { -- public function __construct() -- { -- } - } -``` - -
- -### RemoveDeadIfForeachForRector - -Remove if, foreach and for that does not do anything - -- class: [`Rector\DeadCode\Rector\For_\RemoveDeadIfForeachForRector`](../rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php) - -```diff - class SomeClass - { - public function run($someObject) - { - $value = 5; -- if ($value) { -- } -- - if ($someObject->run()) { -- } -- -- foreach ($values as $value) { - } - - return $value; - } - } -``` - -
- -### RemoveDeadInstanceOfRector - -Remove dead instanceof check on type hinted variable - -- class: [`Rector\DeadCode\Rector\If_\RemoveDeadInstanceOfRector`](../rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php) - -```diff - final class SomeClass - { - public function go(stdClass $stdClass) - { -- if (! $stdClass instanceof stdClass) { -- return false; -- } -- - return true; - } - } -``` - -
- -### RemoveDeadLoopRector - -Remove loop with no body - -- class: [`Rector\DeadCode\Rector\For_\RemoveDeadLoopRector`](../rules/DeadCode/Rector/For_/RemoveDeadLoopRector.php) - -```diff - class SomeClass - { - public function run($values) - { -- for ($i=1; $i - -### RemoveDeadReturnRector - -Remove last return in the functions, since does not do anything - -- class: [`Rector\DeadCode\Rector\FunctionLike\RemoveDeadReturnRector`](../rules/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector.php) - -```diff - class SomeClass - { - public function run() - { - $shallWeDoThis = true; - - if ($shallWeDoThis) { - return; - } -- -- return; - } - } -``` - -
- -### RemoveDeadStmtRector - -Removes dead code statements - -- class: [`Rector\DeadCode\Rector\Expression\RemoveDeadStmtRector`](../rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php) - -```diff --$value = 5; --$value; -+$value = 5; -``` - -
- -### RemoveDeadTryCatchRector - -Remove dead try/catch - -- class: [`Rector\DeadCode\Rector\TryCatch\RemoveDeadTryCatchRector`](../rules/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector.php) - -```diff - class SomeClass - { - public function run() - { -- try { -- // some code -- } -- catch (Throwable $throwable) { -- throw $throwable; -- } -+ // some code - } - } -``` - -
- -### RemoveDeadZeroAndOneOperationRector - -Remove operation with 1 and 0, that have no effect on the value - -- class: [`Rector\DeadCode\Rector\Plus\RemoveDeadZeroAndOneOperationRector`](../rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php) - -```diff - class SomeClass - { - public function run() - { -- $value = 5 * 1; -- $value = 5 + 0; -+ $value = 5; -+ $value = 5; - } - } -``` - -
- -### RemoveDelegatingParentCallRector - -Removed dead parent call, that does not change anything - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveDelegatingParentCallRector`](../rules/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector.php) - -```diff - class SomeClass - { -- public function prettyPrint(array $stmts): string -- { -- return parent::prettyPrint($stmts); -- } - } -``` - -
- -### RemoveDoubleAssignRector - -Simplify useless double assigns - -- class: [`Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector`](../rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php) - -```diff --$value = 1; - $value = 1; -``` - -
- -### RemoveDuplicatedArrayKeyRector - -Remove duplicated key in defined arrays. - -- class: [`Rector\DeadCode\Rector\Array_\RemoveDuplicatedArrayKeyRector`](../rules/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector.php) - -```diff - $item = [ -- 1 => 'A', - 1 => 'B' - ]; -``` - -
- -### RemoveDuplicatedCaseInSwitchRector - -2 following switch keys with identical will be reduced to one result - -- class: [`Rector\DeadCode\Rector\Switch_\RemoveDuplicatedCaseInSwitchRector`](../rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php) - -```diff - class SomeClass - { - public function run() - { - switch ($name) { - case 'clearHeader': - return $this->modifyHeader($node, 'remove'); - case 'clearAllHeaders': -- return $this->modifyHeader($node, 'replace'); - case 'clearRawHeaders': - return $this->modifyHeader($node, 'replace'); - case '...': - return 5; - } - } - } -``` - -
- -### RemoveDuplicatedIfReturnRector - -Remove duplicated if stmt with return in function/method body - -- class: [`Rector\DeadCode\Rector\FunctionLike\RemoveDuplicatedIfReturnRector`](../rules/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector.php) - -```diff - class SomeClass - { - public function run($value) - { - if ($value) { - return true; - } - - $value2 = 100; -- -- if ($value) { -- return true; -- } - } - } -``` - -
- -### RemoveDuplicatedInstanceOfRector - -Remove duplicated instanceof in one call - -- class: [`Rector\DeadCode\Rector\BinaryOp\RemoveDuplicatedInstanceOfRector`](../rules/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector.php) - -```diff - class SomeClass - { -- public function run($value) -+ public function run($value): void - { -- $isIt = $value instanceof A || $value instanceof A; -- $isIt = $value instanceof A && $value instanceof A; -+ $isIt = $value instanceof A; -+ $isIt = $value instanceof A; - } - } -``` - -
- -### RemoveEmptyClassMethodRector - -Remove empty class methods not required by parents - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveEmptyClassMethodRector`](../rules/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector.php) - -```diff - class OrphanClass - { -- public function __construct() -- { -- } - } -``` - -
- -### RemoveEmptyMethodCallRector - -Remove empty method call - -- class: [`Rector\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector`](../rules/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector.php) - -```diff - class SomeClass - { - public function callThis() - { - } - } - --$some = new SomeClass(); --$some->callThis(); -+$some = new SomeClass(); -``` - -
- -### RemoveLastReturnRector - -Remove very last `return` that has no meaning - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveLastReturnRector`](../rules/DeadCode/Rector/ClassMethod/RemoveLastReturnRector.php) - -```diff - function some_function($value) - { - if ($value === 1000) { - return; - } - - if ($value) { -- return; - } - } -``` - -
- -### RemoveNonExistingVarAnnotationRector - -Removes non-existing `@var` annotations above the code - -- class: [`Rector\DeadCode\Rector\Node\RemoveNonExistingVarAnnotationRector`](../rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php) - -```diff - class SomeClass - { - public function get() - { -- /** @var Training[] $trainings */ - return $this->getData(); - } - } -``` - -
- -### RemoveNullPropertyInitializationRector - -Remove initialization with null value from property declarations - -- class: [`Rector\DeadCode\Rector\PropertyProperty\RemoveNullPropertyInitializationRector`](../rules/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector.php) - -```diff - class SunshineCommand extends ParentClassWithNewConstructor - { -- private $myVar = null; -+ private $myVar; - } -``` - -
- -### RemoveOverriddenValuesRector - -Remove initial assigns of overridden values - -- class: [`Rector\DeadCode\Rector\FunctionLike\RemoveOverriddenValuesRector`](../rules/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector.php) - -```diff - final class SomeController - { - public function run() - { -- $directories = []; - $possibleDirectories = []; - $directories = array_filter($possibleDirectories, 'file_exists'); - } - } -``` - -
- -### RemoveParentCallWithoutParentRector - -Remove unused parent call with no parent class - -- class: [`Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector`](../rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php) - -```diff - class OrphanClass - { - public function __construct() - { -- parent::__construct(); - } - } -``` - -
- -### RemovePhpVersionIdCheckRector - -Remove unneded PHP_VERSION_ID check - -:wrench: **configure it!** - -- class: [`Rector\DeadCode\Rector\ConstFetch\RemovePhpVersionIdCheckRector`](../rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php) - -```php -use Rector\DeadCode\Rector\ConstFetch\RemovePhpVersionIdCheckRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RemovePhpVersionIdCheckRector::class) - ->call('configure', [[ - RemovePhpVersionIdCheckRector::PHP_VERSION_CONSTRAINT => 80000, - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- if (PHP_VERSION_ID < 80000) { -- return; -- } - echo 'do something'; - } - } -``` - -
- -### RemoveUnreachableStatementRector - -Remove unreachable statements - -- class: [`Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector`](../rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php) - -```diff - class SomeClass - { - public function run() - { - return 5; -- -- $removeMe = 10; - } - } -``` - -
- -### RemoveUnusedAssignVariableRector - -Remove assigned unused variable - -- class: [`Rector\DeadCode\Rector\Assign\RemoveUnusedAssignVariableRector`](../rules/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector.php) - -```diff - class SomeClass - { - public function run() - { -- $value = $this->process(); -+ $this->process(); - } - - public function process() - { - // something going on - return 5; - } - } -``` - -
- -### RemoveUnusedConstructorParamRector - -Remove unused parameter in constructor - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveUnusedConstructorParamRector`](../rules/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector.php) - -```diff - final class SomeClass - { - private $hey; - -- public function __construct($hey, $man) -+ public function __construct($hey) - { - $this->hey = $hey; - } - } -``` - -
- -### RemoveUnusedForeachKeyRector - -Remove unused key in foreach - -- class: [`Rector\DeadCode\Rector\Foreach_\RemoveUnusedForeachKeyRector`](../rules/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector.php) - -```diff - $items = []; --foreach ($items as $key => $value) { -+foreach ($items as $value) { - $result = $value; - } -``` - -
- -### RemoveUnusedNonEmptyArrayBeforeForeachRector - -Remove unused if check to non-empty array before foreach of the array - -- class: [`Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector`](../rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php) - -```diff - class SomeClass - { - public function run() - { - $values = []; -- if ($values !== []) { -- foreach ($values as $value) { -- echo $value; -- } -+ foreach ($values as $value) { -+ echo $value; - } - } - } -``` - -
- -### RemoveUnusedPrivateClassConstantRector - -Remove unused class constants - -- class: [`Rector\DeadCode\Rector\ClassConst\RemoveUnusedPrivateClassConstantRector`](../rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php) - -```diff - class SomeClass - { -- private const SOME_CONST = 'dead'; -- - public function run() - { - } - } -``` - -
- -### RemoveUnusedPrivateMethodParameterRector - -Remove unused parameter, if not required by interface or parent class - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodParameterRector`](../rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector.php) - -```diff - class SomeClass - { -- private function run($value, $value2) -+ private function run($value) - { - $this->value = $value; - } - } -``` - -
- -### RemoveUnusedPrivateMethodRector - -Remove unused private method - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector`](../rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php) - -```diff - final class SomeController - { - public function run() - { - return 5; - } -- -- private function skip() -- { -- return 10; -- } - } -``` - -
- -### RemoveUnusedPrivatePropertyRector - -Remove unused private properties - -- class: [`Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector`](../rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php) - -```diff - class SomeClass - { -- private $property; - } -``` - -
- -### RemoveUnusedPromotedPropertyRector - -Remove unused promoted property - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector`](../rules/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector.php) - -```diff - class SomeClass - { - public function __construct( -- private $someUnusedDependency, - private $usedDependency - ) { - } - - public function getUsedDependency() - { - return $this->usedDependency; - } - } -``` - -
- -### RemoveUnusedVariableAssignRector - -Remove unused assigns to variables - -- class: [`Rector\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector`](../rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php) - -```diff - class SomeClass - { - public function run() - { -- $value = 5; - } - } -``` - -
- -### RemoveUselessParamTagRector - -Remove `@param` docblock with same type as parameter type - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector`](../rules/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector.php) - -```diff - class SomeClass - { - /** -- * @param string $a - * @param string $b description - */ - public function foo(string $a, string $b) - { - } - } -``` - -
- -### RemoveUselessReturnTagRector - -Remove `@return` docblock with same type as defined in PHP - -- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnTagRector`](../rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php) - -```diff - use stdClass; - - class SomeClass - { -- /** -- * @return stdClass -- */ - public function foo(): stdClass - { - } - } -``` - -
- -### RemoveUselessVarTagRector - -Remove unused `@var` annotation for properties - -- class: [`Rector\DeadCode\Rector\Property\RemoveUselessVarTagRector`](../rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php) - -```diff - final class SomeClass - { -- /** -- * @var string -- */ - public string $name = 'name'; - } -``` - -
- -### SimplifyIfElseWithSameContentRector - -Remove if/else if they have same content - -- class: [`Rector\DeadCode\Rector\If_\SimplifyIfElseWithSameContentRector`](../rules/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector.php) - -```diff - class SomeClass - { - public function run() - { -- if (true) { -- return 1; -- } else { -- return 1; -- } -+ return 1; - } - } -``` - -
- -### SimplifyMirrorAssignRector - -Removes unneeded $a = $a assigns - -- class: [`Rector\DeadCode\Rector\Expression\SimplifyMirrorAssignRector`](../rules/DeadCode/Rector/Expression/SimplifyMirrorAssignRector.php) - -```diff - function run() { -- $a = $a; - } -``` - -
- -### TernaryToBooleanOrFalseToBooleanAndRector - -Change ternary of bool : false to && bool - -- class: [`Rector\DeadCode\Rector\Ternary\TernaryToBooleanOrFalseToBooleanAndRector`](../rules/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector.php) - -```diff - class SomeClass - { - public function go() - { -- return $value ? $this->getBool() : false; -+ return $value && $this->getBool(); - } - - private function getBool(): bool - { - return (bool) 5; - } - } -``` - -
- -### UnwrapFutureCompatibleIfFunctionExistsRector - -Remove functions exists if with else for always existing - -- class: [`Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfFunctionExistsRector`](../rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector.php) - -```diff - class SomeClass - { - public function run() - { - // session locking trough other addons -- if (function_exists('session_abort')) { -- session_abort(); -- } else { -- session_write_close(); -- } -+ session_abort(); - } - } -``` - -
- -### UnwrapFutureCompatibleIfPhpVersionRector - -Remove php version checks if they are passed - -- class: [`Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector`](../rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php) - -```diff - // current PHP: 7.2 --if (version_compare(PHP_VERSION, '7.2', '<')) { -- return 'is PHP 7.1-'; --} else { -- return 'is PHP 7.2+'; --} -+return 'is PHP 7.2+'; -``` - -
- -## Defluent - -### DefluentReturnMethodCallRector - -Turns return of fluent, to standalone call line and return of value - -- class: [`Rector\Defluent\Rector\Return_\DefluentReturnMethodCallRector`](../rules/Defluent/Rector/Return_/DefluentReturnMethodCallRector.php) - -```diff - $someClass = new SomeClass(); --return $someClass->someFunction(); -+$someClass->someFunction(); -+return $someClass; -``` - -
- -### FluentChainMethodCallToNormalMethodCallRector - -Turns fluent interface calls to classic ones. - -- class: [`Rector\Defluent\Rector\MethodCall\FluentChainMethodCallToNormalMethodCallRector`](../rules/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector.php) - -```diff - $someClass = new SomeClass(); --$someClass->someFunction() -- ->otherFunction(); -+$someClass->someFunction(); -+$someClass->otherFunction(); -``` - -
- -### InArgFluentChainMethodCallToStandaloneMethodCallRector - -Turns fluent interface calls to classic ones. - -- class: [`Rector\Defluent\Rector\MethodCall\InArgFluentChainMethodCallToStandaloneMethodCallRector`](../rules/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector.php) - -```diff - class UsedAsParameter - { - public function someFunction(FluentClass $someClass) - { -- $this->processFluentClass($someClass->someFunction()->otherFunction()); -+ $someClass->someFunction(); -+ $someClass->otherFunction(); -+ $this->processFluentClass($someClass); - } - - public function processFluentClass(FluentClass $someClass) - { - } - } -``` - -
- -### MethodCallOnSetterMethodCallToStandaloneAssignRector - -Change method call on setter to standalone assign before the setter - -- class: [`Rector\Defluent\Rector\MethodCall\MethodCallOnSetterMethodCallToStandaloneAssignRector`](../rules/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector.php) - -```diff - class SomeClass - { - public function some() - { -- $this->anotherMethod(new AnotherClass()) -- ->someFunction(); -+ $anotherClass = new AnotherClass(); -+ $anotherClass->someFunction(); -+ $this->anotherMethod($anotherClass); - } - - public function anotherMethod(AnotherClass $anotherClass) - { - } - } -``` - -
- -### NewFluentChainMethodCallToNonFluentRector - -Turns fluent interface calls to classic ones. - -- class: [`Rector\Defluent\Rector\MethodCall\NewFluentChainMethodCallToNonFluentRector`](../rules/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector.php) - -```diff --(new SomeClass())->someFunction() -- ->otherFunction(); -+$someClass = new SomeClass(); -+$someClass->someFunction(); -+$someClass->otherFunction(); -``` - -
- -### NormalToFluentRector - -Turns fluent interface calls to classic ones. - -:wrench: **configure it!** - -- class: [`Rector\Defluent\Rector\ClassMethod\NormalToFluentRector`](../rules/Defluent/Rector/ClassMethod/NormalToFluentRector.php) - -```php -use Rector\Defluent\Rector\ClassMethod\NormalToFluentRector; -use Rector\Defluent\ValueObject\NormalToFluent; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(NormalToFluentRector::class) - ->call('configure', [[ - NormalToFluentRector::CALLS_TO_FLUENT => ValueObjectInliner::inline([ - new NormalToFluent('SomeClass', ['someFunction', 'otherFunction']), ] - ), - ]]); -}; -``` - -↓ - -```diff - $someObject = new SomeClass(); --$someObject->someFunction(); --$someObject->otherFunction(); -+$someObject->someFunction() -+ ->otherFunction(); -``` - -
- -### ReturnFluentChainMethodCallToNormalMethodCallRector - -Turns fluent interface calls to classic ones. - -- class: [`Rector\Defluent\Rector\Return_\ReturnFluentChainMethodCallToNormalMethodCallRector`](../rules/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector.php) - -```diff - $someClass = new SomeClass(); -+$someClass->someFunction(); -+$someClass->otherFunction(); - --return $someClass->someFunction() -- ->otherFunction(); -+return $someClass; -``` - -
- -### ReturnNewFluentChainMethodCallToNonFluentRector - -Turns fluent interface calls to classic ones. - -- class: [`Rector\Defluent\Rector\Return_\ReturnNewFluentChainMethodCallToNonFluentRector`](../rules/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector.php) - -```diff --return (new SomeClass())->someFunction() -- ->otherFunction(); -+$someClass = new SomeClass(); -+$someClass->someFunction(); -+$someClass->otherFunction(); -+return $someClass; -``` - -
- -### ReturnThisRemoveRector - -Removes "return `$this;"` from *fluent interfaces* for specified classes. - -- class: [`Rector\Defluent\Rector\ClassMethod\ReturnThisRemoveRector`](../rules/Defluent/Rector/ClassMethod/ReturnThisRemoveRector.php) - -```diff - class SomeExampleClass - { - public function someFunction() - { -- return $this; - } - - public function otherFunction() - { -- return $this; - } - } -``` - -
- -## DependencyInjection - -### ActionInjectionToConstructorInjectionRector - -Turns action injection in Controllers to constructor injection - -- class: [`Rector\DependencyInjection\Rector\Class_\ActionInjectionToConstructorInjectionRector`](../rules/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector.php) - -```diff - final class SomeController - { -- public function default(ProductRepository $productRepository) -+ /** -+ * @var ProductRepository -+ */ -+ private $productRepository; -+ public function __construct(ProductRepository $productRepository) - { -- $products = $productRepository->fetchAll(); -+ $this->productRepository = $productRepository; -+ } -+ -+ public function default() -+ { -+ $products = $this->productRepository->fetchAll(); - } - } -``` - -
- -### AddMethodParentCallRector - -Add method parent call, in case new parent method is added - -:wrench: **configure it!** - -- class: [`Rector\DependencyInjection\Rector\ClassMethod\AddMethodParentCallRector`](../rules/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector.php) - -```php -use Rector\DependencyInjection\Rector\ClassMethod\AddMethodParentCallRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddMethodParentCallRector::class) - ->call('configure', [[ - AddMethodParentCallRector::METHODS_BY_PARENT_TYPES => [ - 'ParentClassWithNewConstructor' => '__construct', - - ], ]]); -}; -``` - -↓ - -```diff - class SunshineCommand extends ParentClassWithNewConstructor - { - public function __construct() - { - $value = 5; -+ -+ parent::__construct(); - } - } -``` - -
- -### ReplaceVariableByPropertyFetchRector - -Turns variable in controller action to property fetch, as follow up to action injection variable to property change. - -- class: [`Rector\DependencyInjection\Rector\Variable\ReplaceVariableByPropertyFetchRector`](../rules/DependencyInjection/Rector/Variable/ReplaceVariableByPropertyFetchRector.php) - -```diff - final class SomeController - { - /** - * @var ProductRepository - */ - private $productRepository; - - public function __construct(ProductRepository $productRepository) - { - $this->productRepository = $productRepository; - } - - public function default() - { -- $products = $productRepository->fetchAll(); -+ $products = $this->productRepository->fetchAll(); - } - } -``` - -
- -## DowngradePhp53 - -### DirConstToFileConstRector - -Refactor __DIR__ to dirname(__FILE__) - -- class: [`Rector\DowngradePhp53\Rector\Dir\DirConstToFileConstRector`](../rules/DowngradePhp53/Rector/Dir/DirConstToFileConstRector.php) - -```diff - final class SomeClass - { - public function run() - { -- return __DIR__; -+ return dirname(__FILE__); - } - } -``` - -
- -## DowngradePhp70 - -### DowngradeAnonymousClassRector - -Remove anonymous class - -- class: [`Rector\DowngradePhp70\Rector\New_\DowngradeAnonymousClassRector`](../rules/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector.php) - -```diff -+class Anonymous -+{ -+ public function execute() -+ { -+ } -+} - class SomeClass - { - public function run() - { -- return new class { -- public function execute() -- { -- } -- }; -+ return new Anonymous(); - } - } -``` - -
- -### DowngradeDefineArrayConstantRector - -Change array contant definition via define to const - -- class: [`Rector\DowngradePhp70\Rector\Expression\DowngradeDefineArrayConstantRector`](../rules/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector.php) - -```diff --define('ANIMALS', [ -+const ANIMALS = [ - 'dog', - 'cat', - 'bird' --]); -+]; -``` - -
- -### DowngradeGeneratedScalarTypesRector - -Refactor scalar types in PHP code in string snippets, e.g. generated container code from symfony/dependency-injection - -- class: [`Rector\DowngradePhp70\Rector\String_\DowngradeGeneratedScalarTypesRector`](../rules/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector.php) - -```diff - $code = <<<'EOF' -- public function getParameter(string $name) -+ /** -+ * @param string $name -+ */ -+ public function getParameter($name) - { - return $name; - } - EOF; -``` - -
- -### DowngradeNullCoalesceRector - -Change null coalesce to isset ternary check - -- class: [`Rector\DowngradePhp70\Rector\Coalesce\DowngradeNullCoalesceRector`](../rules/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector.php) - -```diff --$username = $_GET['user'] ?? 'nobody'; -+$username = isset($_GET['user']) ? $_GET['user'] : 'nobody'; -``` - -
- -### DowngradeParentTypeDeclarationRector - -Remove "parent" return type, add a `"@return` parent" tag instead - -- class: [`Rector\DowngradePhp70\Rector\ClassMethod\DowngradeParentTypeDeclarationRector`](../rules/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector.php) - -```diff - class ParentClass - { - } - - class SomeClass extends ParentClass - { -- public function foo(): parent -+ /** -+ * @return parent -+ */ -+ public function foo() - { - return $this; - } - } -``` - -
- -### DowngradeScalarTypeDeclarationRector - -Remove the type params and return type, add `@param` and `@return` tags instead - -- class: [`Rector\DowngradePhp70\Rector\FunctionLike\DowngradeScalarTypeDeclarationRector`](../rules/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function run(string $input): string -+ /** -+ * @param string $input -+ * @return string -+ */ -+ public function run($input) - { - } - } -``` - -
- -### DowngradeSelfTypeDeclarationRector - -Remove "self" return type, add a `"@return` self" tag instead - -- class: [`Rector\DowngradePhp70\Rector\ClassMethod\DowngradeSelfTypeDeclarationRector`](../rules/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function foo(): self -+ public function foo() - { - return $this; - } - } -``` - -
- -### DowngradeSessionStartArrayOptionsRector - -Move array option of session_start($options) to before statement's `ini_set()` - -- class: [`Rector\DowngradePhp70\Rector\FuncCall\DowngradeSessionStartArrayOptionsRector`](../rules/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector.php) - -```diff --session_start([ -- 'cache_limiter' => 'private', --]); -+ini_set('session.cache_limiter', 'private'); -+session_start(); -``` - -
- -### DowngradeSpaceshipRector - -Change spaceship with check equal, and ternary to result 0, -1, 1 - -- class: [`Rector\DowngradePhp70\Rector\Spaceship\DowngradeSpaceshipRector`](../rules/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector.php) - -```diff --return $a <=> $b; -+$battleShipcompare = function ($left, $right) { -+ if ($left === $right) { -+ return 0; -+ } -+ return $left < $right ? -1 : 1; -+}; -+return $battleShipcompare($a, $b); -``` - -
- -### DowngradeStrictTypeDeclarationRector - -Remove the declare(strict_types=1) - -- class: [`Rector\DowngradePhp70\Rector\Declare_\DowngradeStrictTypeDeclarationRector`](../rules/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector.php) - -```diff --declare(strict_types=1); - echo 'something'; -``` - -
- -### SplitGroupedUseImportsRector - -Refactor grouped use imports to standalone lines - -- class: [`Rector\DowngradePhp70\Rector\GroupUse\SplitGroupedUseImportsRector`](../rules/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector.php) - -```diff --use SomeNamespace\{ -- First, -- Second --}; -+use SomeNamespace\First; -+use SomeNamespace\Second; -``` - -
- -## DowngradePhp71 - -### DowngradeClassConstantVisibilityRector - -Downgrade class constant visibility - -- class: [`Rector\DowngradePhp71\Rector\ClassConst\DowngradeClassConstantVisibilityRector`](../rules/DowngradePhp71/Rector/ClassConst/DowngradeClassConstantVisibilityRector.php) - -```diff - class SomeClass - { -- public const PUBLIC_CONST_B = 2; -- protected const PROTECTED_CONST = 3; -- private const PRIVATE_CONST = 4; -+ const PUBLIC_CONST_B = 2; -+ const PROTECTED_CONST = 3; -+ const PRIVATE_CONST = 4; - } -``` - -
- -### DowngradeIsIterableRector - -Change is_iterable with array and Traversable object type check - -- class: [`Rector\DowngradePhp71\Rector\FuncCall\DowngradeIsIterableRector`](../rules/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector.php) - -```diff - class SomeClass - { - public function run($obj) - { -- is_iterable($obj); -+ is_array($obj) || $obj instanceof \Traversable; - } - } -``` - -
- -### DowngradeIterablePseudoTypeDeclarationRector - -Remove the iterable pseudo type params and returns, add `@param` and `@return` tags instead - -- class: [`Rector\DowngradePhp71\Rector\FunctionLike\DowngradeIterablePseudoTypeDeclarationRector`](../rules/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function run(iterable $iterator): iterable -+ /** -+ * @param mixed[]|\Traversable $iterator -+ * @return mixed[]|\Traversable -+ */ -+ public function run($iterator) - { - // do something - } - } -``` - -
- -### DowngradeKeysInListRector - -Extract keys in list to its own variable assignment - -- class: [`Rector\DowngradePhp71\Rector\List_\DowngradeKeysInListRector`](../rules/DowngradePhp71/Rector/List_/DowngradeKeysInListRector.php) - -```diff - class SomeClass - { - public function run(): void - { - $data = [ - ["id" => 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; -- list("id" => $id1, "name" => $name1) = $data[0]; -+ $id1 = $data[0]["id"]; -+ $name1 = $data[0]["name"]; - } - } -``` - -
- -### DowngradeNegativeStringOffsetToStrlenRector - -Downgrade negative string offset to strlen - -- class: [`Rector\DowngradePhp71\Rector\String_\DowngradeNegativeStringOffsetToStrlenRector`](../rules/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector.php) - -```diff --echo 'abcdef'[-2]; --echo strpos('aabbcc', 'b', -3); --echo strpos($var, 'b', -3); -+echo 'abcdef'[strlen('abcdef') - 2]; -+echo strpos('aabbcc', 'b', strlen('aabbcc') - 3); -+echo strpos($var, 'b', strlen($var) - 3); -``` - -
- -### DowngradeNullableTypeDeclarationRector - -Remove the nullable type params, add `@param` tags instead - -- class: [`Rector\DowngradePhp71\Rector\FunctionLike\DowngradeNullableTypeDeclarationRector`](../rules/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function run(?string $input): ?string -+ /** -+ * @param string|null $input -+ * @return string|null -+ */ -+ public function run($input) - { - } - } -``` - -
- -### DowngradePipeToMultiCatchExceptionRector - -Downgrade single one | separated to multi catch exception - -- class: [`Rector\DowngradePhp71\Rector\TryCatch\DowngradePipeToMultiCatchExceptionRector`](../rules/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector.php) - -```diff - try { - // Some code... --} catch (ExceptionType1 | ExceptionType2 $exception) { -+} catch (ExceptionType1 $exception) { -+ $sameCode; -+} catch (ExceptionType2 $exception) { - $sameCode; - } -``` - -
- -### DowngradeVoidTypeDeclarationRector - -Remove "void" return type, add a `"@return` void" tag instead - -- class: [`Rector\DowngradePhp71\Rector\FunctionLike\DowngradeVoidTypeDeclarationRector`](../rules/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function run(): void -+ /** -+ * @return void -+ */ -+ public function run() - { - } - } -``` - -
- -### SymmetricArrayDestructuringToListRector - -Downgrade Symmetric array destructuring to `list()` function - -- class: [`Rector\DowngradePhp71\Rector\Array_\SymmetricArrayDestructuringToListRector`](../rules/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector.php) - -```diff --[$id1, $name1] = $data; -+list($id1, $name1) = $data; -``` - -
- -## DowngradePhp72 - -### DowngradeObjectTypeDeclarationRector - -Remove the "object" param and return type, add a `@param` and `@return` tags instead - -- class: [`Rector\DowngradePhp72\Rector\FunctionLike\DowngradeObjectTypeDeclarationRector`](../rules/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function someFunction(object $someObject): object -+ /** -+ * @param object $someObject -+ * @return object -+ */ -+ public function someFunction($someObject) - { - } - } -``` - -
- -### DowngradeParameterTypeWideningRector - -Change param type to match the lowest type in whole family tree - -:wrench: **configure it!** - -- class: [`Rector\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector`](../rules/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector.php) - -```php -use Rector\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(DowngradeParameterTypeWideningRector::class) - ->call('configure', [[ - DowngradeParameterTypeWideningRector::SAFE_TYPES => [], - DowngradeParameterTypeWideningRector::SAFE_TYPES_TO_METHODS => [], - ]]); -}; -``` - -↓ - -```diff - interface SomeInterface - { -- public function test(array $input); -+ /** -+ * @param mixed[] $input -+ */ -+ public function test($input); - } - - final class SomeClass implements SomeInterface - { - public function test($input) - { - } - } -``` - -
- -### DowngradePregUnmatchedAsNullConstantRector - -Remove PREG_UNMATCHED_AS_NULL from preg_match and set null value on empty string matched on each match - -- class: [`Rector\DowngradePhp72\Rector\FuncCall\DowngradePregUnmatchedAsNullConstantRector`](../rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php) - -```diff - class SomeClass - { - public function run() - { -- preg_match('/(a)(b)*(c)/', 'ac', $matches, PREG_UNMATCHED_AS_NULL); -+ preg_match('/(a)(b)*(c)/', 'ac', $matches); -+ array_walk_recursive($matches, function (&$value) { -+ if ($value === '') { -+ $value = null; -+ } -+ }); - } - } -``` - -
- -### DowngradeStreamIsattyRector - -Downgrade `stream_isatty()` function - -- class: [`Rector\DowngradePhp72\Rector\FuncCall\DowngradeStreamIsattyRector`](../rules/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector.php) - -```diff - class SomeClass - { - public function run($stream) - { -- $isStream = stream_isatty($stream); -+ $streamIsatty = function ($stream) { -+ if (\function_exists('stream_isatty')) { -+ return stream_isatty($stream); -+ } -+ -+ if (!\is_resource($stream)) { -+ trigger_error('stream_isatty() expects parameter 1 to be resource, '.\gettype($stream).' given', \E_USER_WARNING); -+ -+ return false; -+ } -+ -+ if ('\\' === \DIRECTORY_SEPARATOR) { -+ $stat = @fstat($stream); -+ // Check if formatted mode is S_IFCHR -+ return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; -+ } -+ -+ return \function_exists('posix_isatty') && @posix_isatty($stream); -+ }; -+ $isStream = $streamIsatty($stream); - } - } -``` - -
- -## DowngradePhp73 - -### DowngradeArrayKeyFirstLastRector - -Downgrade `array_key_first()` and `array_key_last()` functions - -- class: [`Rector\DowngradePhp73\Rector\FuncCall\DowngradeArrayKeyFirstLastRector`](../rules/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector.php) - -```diff - class SomeClass - { - public function run($items) - { -- $firstItemKey = array_key_first($items); -+ reset($items); -+ $firstItemKey = key($items); - } - } -``` - -
- -### DowngradeFlexibleHeredocSyntaxRector - -Remove indentation from heredoc/nowdoc - -- class: [`Rector\DowngradePhp73\Rector\String_\DowngradeFlexibleHeredocSyntaxRector`](../rules/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector.php) - -```diff - $query = << - -### DowngradeIsCountableRector - -Downgrade `is_countable()` to former version - -- class: [`Rector\DowngradePhp73\Rector\FuncCall\DowngradeIsCountableRector`](../rules/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector.php) - -```diff - $items = []; --return is_countable($items); -+return is_array($items) || $items instanceof Countable; -``` - -
- -### DowngradeListReferenceAssignmentRector - -Convert the list reference assignment to its equivalent PHP 7.2 code - -- class: [`Rector\DowngradePhp73\Rector\List_\DowngradeListReferenceAssignmentRector`](../rules/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector.php) - -```diff - class SomeClass - { - public function run($string) - { -- $array = [1, 2, 3]; -- list($a, &$b) = $array; -+ $array = [1, 2]; -+ list($a) = $array; -+ $b =& $array[1]; - -- [&$c, $d, &$e] = $array; -+ [$c, $d, $e] = $array; -+ $c =& $array[0]; -+ $e =& $array[2]; - -- list(&$a, &$b) = $array; -+ $a =& $array[0]; -+ $b =& $array[1]; - } - } -``` - -
- -### DowngradeTrailingCommasInFunctionCallsRector - -Remove trailing commas in function calls - -- class: [`Rector\DowngradePhp73\Rector\FuncCall\DowngradeTrailingCommasInFunctionCallsRector`](../rules/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector.php) - -```diff - class SomeClass - { - public function __construct(string $value) - { - $compacted = compact( - 'posts', -- 'units', -+ 'units' - ); - } - } -``` - -
- -### SetCookieOptionsArrayToArgumentsRector - -Convert setcookie option array to arguments - -- class: [`Rector\DowngradePhp73\Rector\FuncCall\SetCookieOptionsArrayToArgumentsRector`](../rules/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector.php) - -```diff --setcookie('name', $value, ['expires' => 360]); -+setcookie('name', $value, 360); -``` - -
- -## DowngradePhp74 - -### ArrowFunctionToAnonymousFunctionRector - -Replace arrow functions with anonymous functions - -- class: [`Rector\DowngradePhp74\Rector\ArrowFunction\ArrowFunctionToAnonymousFunctionRector`](../rules/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector.php) - -```diff - class SomeClass - { - public function run() - { - $delimiter = ","; -- $callable = fn($matches) => $delimiter . strtolower($matches[1]); -+ $callable = function ($matches) use ($delimiter) { -+ return $delimiter . strtolower($matches[1]); -+ }; - } - } -``` - -
- -### DowngradeArrayMergeCallWithoutArgumentsRector - -Add missing param to `array_merge` and `array_merge_recursive` - -- class: [`Rector\DowngradePhp74\Rector\FuncCall\DowngradeArrayMergeCallWithoutArgumentsRector`](../rules/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector.php) - -```diff - class SomeClass - { - public function run() - { -- array_merge(); -- array_merge_recursive(); -+ array_merge([]); -+ array_merge_recursive([]); - } - } -``` - -
- -### DowngradeArraySpreadRector - -Replace array spread with array_merge function - -- class: [`Rector\DowngradePhp74\Rector\Array_\DowngradeArraySpreadRector`](../rules/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector.php) - -```diff - class SomeClass - { - public function run() - { - $parts = ['apple', 'pear']; -- $fruits = ['banana', 'orange', ...$parts, 'watermelon']; -+ $fruits = array_merge(['banana', 'orange'], $parts, ['watermelon']); - } - - public function runWithIterable() - { -- $fruits = ['banana', 'orange', ...new ArrayIterator(['durian', 'kiwi']), 'watermelon']; -+ $item0Unpacked = new ArrayIterator(['durian', 'kiwi']); -+ $fruits = array_merge(['banana', 'orange'], is_array($item0Unpacked) ? $item0Unpacked : iterator_to_array($item0Unpacked), ['watermelon']); - } - } -``` - -
- -### DowngradeContravariantArgumentTypeRector - -Remove contravariant argument type declarations - -- class: [`Rector\DowngradePhp74\Rector\ClassMethod\DowngradeContravariantArgumentTypeRector`](../rules/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector.php) - -```diff - class ParentType {} - class ChildType extends ParentType {} - - class A - { - public function contraVariantArguments(ChildType $type) - { - } - } - - class B extends A - { -- public function contraVariantArguments(ParentType $type) -+ /** -+ * @param ParentType $type -+ */ -+ public function contraVariantArguments($type) - { - } - } -``` - -
- -### DowngradeCovariantReturnTypeRector - -Make method return same type as parent - -- class: [`Rector\DowngradePhp74\Rector\ClassMethod\DowngradeCovariantReturnTypeRector`](../rules/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector.php) - -```diff - class ParentType {} - class ChildType extends ParentType {} - - class A - { - public function covariantReturnTypes(): ParentType - { - } - } - - class B extends A - { -- public function covariantReturnTypes(): ChildType -+ /** -+ * @return ChildType -+ */ -+ public function covariantReturnTypes(): ParentType - { - } - } -``` - -
- -### DowngradeFreadFwriteFalsyToNegationRector - -Changes `fread()` or `fwrite()` compare to false to negation check - -- class: [`Rector\DowngradePhp74\Rector\Identical\DowngradeFreadFwriteFalsyToNegationRector`](../rules/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector.php) - -```diff --fread($handle, $length) === false; --fwrite($fp, '1') === false; -+!fread($handle, $length); -+!fwrite($fp, '1'); -``` - -
- -### DowngradeNullCoalescingOperatorRector - -Remove null coalescing operator ??= - -- class: [`Rector\DowngradePhp74\Rector\Coalesce\DowngradeNullCoalescingOperatorRector`](../rules/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector.php) - -```diff - $array = []; --$array['user_id'] ??= 'value'; -+$array['user_id'] = $array['user_id'] ?? 'value'; -``` - -
- -### DowngradeNumericLiteralSeparatorRector - -Remove "_" as thousands separator in numbers - -- class: [`Rector\DowngradePhp74\Rector\LNumber\DowngradeNumericLiteralSeparatorRector`](../rules/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector.php) - -```diff - class SomeClass - { - public function run() - { -- $int = 1_000; -- $float = 1_000_500.001; -+ $int = 1000; -+ $float = 1000500.001; - } - } -``` - -
- -### DowngradeStripTagsCallWithArrayRector - -Convert 2nd param to `strip_tags` from array to string - -- class: [`Rector\DowngradePhp74\Rector\FuncCall\DowngradeStripTagsCallWithArrayRector`](../rules/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector.php) - -```diff - class SomeClass - { - public function run($string) - { - // Arrays: change to string -- strip_tags($string, ['a', 'p']); -+ strip_tags($string, '<' . implode('><', ['a', 'p']) . '>'); - - // Variables/consts/properties: if array, change to string - $tags = ['a', 'p']; -- strip_tags($string, $tags); -+ strip_tags($string, $tags !== null && is_array($tags) ? '<' . implode('><', $tags) . '>' : $tags); - - // Default case (eg: function call): externalize to var, then if array, change to string -- strip_tags($string, getTags()); -+ $expr = getTags(); -+ strip_tags($string, is_array($expr) ? '<' . implode('><', $expr) . '>' : $expr); - } - } -``` - -
- -### DowngradeTypedPropertyRector - -Changes property type definition from type definitions to `@var` annotations. - -- class: [`Rector\DowngradePhp74\Rector\Property\DowngradeTypedPropertyRector`](../rules/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector.php) - -```diff - class SomeClass - { -- private string $property; -+ /** -+ * @var string -+ */ -+ private $property; - } -``` - -
- -## DowngradePhp80 - -### DowngradeAbstractPrivateMethodInTraitRector - -Remove "abstract" from private methods in traits and adds an empty function body - -- class: [`Rector\DowngradePhp80\Rector\ClassMethod\DowngradeAbstractPrivateMethodInTraitRector`](../rules/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector.php) - -```diff - trait SomeTrait - { -- abstract private function someAbstractPrivateFunction(); -+ private function someAbstractPrivateFunction() {} - } -``` - -
- -### DowngradeAttributeToAnnotationRector - -Refactor PHP attribute markers to annotations notation - -:wrench: **configure it!** - -- class: [`Rector\DowngradePhp80\Rector\Class_\DowngradeAttributeToAnnotationRector`](../rules/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector.php) - -```php -use Rector\DowngradePhp80\Rector\Class_\DowngradeAttributeToAnnotationRector; -use Rector\DowngradePhp80\ValueObject\DowngradeAttributeToAnnotation; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(DowngradeAttributeToAnnotationRector::class) - ->call('configure', [[ - DowngradeAttributeToAnnotationRector::ATTRIBUTE_TO_ANNOTATION => ValueObjectInliner::inline([ - new DowngradeAttributeToAnnotation('Symfony\Component\Routing\Annotation\Route', null), - ]), - ]]); -}; -``` - -↓ - -```diff - use Symfony\Component\Routing\Annotation\Route; - - class SymfonyRoute - { -- #[Route(path: '/path', name: 'action')] -+ /** -+ * @Route("/path", name="action") -+ */ - public function action() - { - } - } -``` - -
- -### DowngradeClassOnObjectToGetClassRector - -Change `$object::class` to get_class($object) - -- class: [`Rector\DowngradePhp80\Rector\ClassConstFetch\DowngradeClassOnObjectToGetClassRector`](../rules/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector.php) - -```diff - class SomeClass - { - public function run($object) - { -- return $object::class; -+ return get_class($object); - } - } -``` - -
- -### DowngradeMatchToSwitchRector - -Downgrade `match()` to `switch()` - -- class: [`Rector\DowngradePhp80\Rector\Expression\DowngradeMatchToSwitchRector`](../rules/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector.php) - -```diff - class SomeClass - { - public function run() - { -- $message = match ($statusCode) { -- 200, 300 => null, -- 400 => 'not found', -- default => 'unknown status code', -- }; -+ switch ($statusCode) { -+ case 200: -+ case 300: -+ $message = null; -+ break; -+ case 400: -+ $message = 'not found'; -+ break; -+ default: -+ $message = 'unknown status code'; -+ break; -+ } - } - } -``` - -
- -### DowngradeMixedTypeDeclarationRector - -Remove the "mixed" param and return type, add a `@param` and `@return` tag instead - -- class: [`Rector\DowngradePhp80\Rector\FunctionLike\DowngradeMixedTypeDeclarationRector`](../rules/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function someFunction(mixed $anything): mixed -+ /** -+ * @param mixed $anything -+ * @return mixed -+ */ -+ public function someFunction($anything) - { - } - } -``` - -
- -### DowngradeNamedArgumentRector - -Remove named argument - -- class: [`Rector\DowngradePhp80\Rector\MethodCall\DowngradeNamedArgumentRector`](../rules/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector.php) - -```diff - class SomeClass - { - public function run() - { -- $this->execute(b: 100); -+ $this->execute(null, 100); - } - - private function execute($a = null, $b = null) - { - } - } -``` - -
- -### DowngradeNonCapturingCatchesRector - -Downgrade catch () without variable to one - -- class: [`Rector\DowngradePhp80\Rector\Catch_\DowngradeNonCapturingCatchesRector`](../rules/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector.php) - -```diff - class SomeClass - { - public function run() - { - try { - // code -- } catch (\Exception) { -+ } catch (\Exception $exception) { - // error - } - } - } -``` - -
- -### DowngradeNullsafeToTernaryOperatorRector - -Change nullsafe operator to ternary operator rector - -- class: [`Rector\DowngradePhp80\Rector\NullsafeMethodCall\DowngradeNullsafeToTernaryOperatorRector`](../rules/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector.php) - -```diff --$dateAsString = $booking->getStartDate()?->asDateTimeString(); --$dateAsString = $booking->startDate?->dateTimeString; -+$dateAsString = ($bookingGetStartDate = $booking->getStartDate()) ? $bookingGetStartDate->asDateTimeString() : null; -+$dateAsString = ($bookingGetStartDate = $booking->startDate) ? $bookingGetStartDate->dateTimeString : null; -``` - -
- -### DowngradePhpTokenRector - -`"something()"` will be renamed to `"somethingElse()"` - -- class: [`Rector\DowngradePhp80\Rector\StaticCall\DowngradePhpTokenRector`](../rules/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector.php) - -```diff --$tokens = \PhpToken::tokenize($code); -+$tokens = token_get_all($code); - --foreach ($tokens as $phpToken) { -- $name = $phpToken->getTokenName(); -- $text = $phpToken->text; -+foreach ($tokens as $token) { -+ $name = is_array($token) ? token_name($token[0]) : null; -+ $text = is_array($token) ? $token[1] : $token; - } -``` - -
- -### DowngradePropertyPromotionRector - -Change constructor property promotion to property asssign - -- class: [`Rector\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector`](../rules/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector.php) - -```diff - class SomeClass - { -- public function __construct(public float $value = 0.0) -+ public float $value; -+ -+ public function __construct(float $value = 0.0) - { -+ $this->value = $value; - } - } -``` - -
- -### DowngradeStaticTypeDeclarationRector - -Remove "static" return and param type, add a `"@param` `$this"` and `"@return` `$this"` tag instead - -- class: [`Rector\DowngradePhp80\Rector\ClassMethod\DowngradeStaticTypeDeclarationRector`](../rules/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function getStatic(): static -+ /** -+ * @return static -+ */ -+ public function getStatic() - { - return new static(); - } - } -``` - -
- -### DowngradeStrContainsRector - -Replace `str_contains()` with `strpos()` !== false - -- class: [`Rector\DowngradePhp80\Rector\FuncCall\DowngradeStrContainsRector`](../rules/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector.php) - -```diff - class SomeClass - { - public function run() - { -- return str_contains('abc', 'a'); -+ return strpos('abc', 'a') !== false; - } - } -``` - -
- -### DowngradeStrEndsWithRector - -Downgrade `str_ends_with()` to `strncmp()` version - -- class: [`Rector\DowngradePhp80\Rector\FuncCall\DowngradeStrEndsWithRector`](../rules/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector.php) - -```diff --str_ends_with($haystack, $needle); -+"" === $needle || ("" !== $haystack && 0 === substr_compare($haystack, $needle, -\strlen($needle))); -``` - -
- -### DowngradeStrStartsWithRector - -Downgrade `str_starts_with()` to `strncmp()` version - -- class: [`Rector\DowngradePhp80\Rector\FuncCall\DowngradeStrStartsWithRector`](../rules/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector.php) - -```diff --str_starts_with($haystack, $needle); -+strncmp($haystack, $needle, strlen($needle)) === 0; -``` - -
- -### DowngradeThrowExprRector - -Downgrade throw as expr - -- class: [`Rector\DowngradePhp80\Rector\Expression\DowngradeThrowExprRector`](../rules/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector.php) - -```diff - class SomeClass - { - public function run() - { -- $id = $somethingNonexistent ?? throw new RuntimeException(); -+ if (!isset($somethingNonexistent)) { -+ throw new RuntimeException(); -+ } -+ $id = $somethingNonexistent; - } - } -``` - -
- -### DowngradeTrailingCommasInParamUseRector - -Remove trailing commas in param or use list - -- class: [`Rector\DowngradePhp80\Rector\ClassMethod\DowngradeTrailingCommasInParamUseRector`](../rules/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector.php) - -```diff - class SomeClass - { -- public function __construct(string $value1, string $value2,) -+ public function __construct(string $value1, string $value2) - { -- function (string $value1, string $value2,) { -+ function (string $value1, string $value2) { - }; - -- function () use ($value1, $value2,) { -+ function () use ($value1, $value2) { - }; - } - } - --function inFunction(string $value1, string $value2,) -+function inFunction(string $value1, string $value2) - { - } -``` - -
- -### DowngradeUnionTypeDeclarationRector - -Remove the union type params and returns, add `@param/@return` tags instead - -- class: [`Rector\DowngradePhp80\Rector\FunctionLike\DowngradeUnionTypeDeclarationRector`](../rules/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector.php) - -```diff - class SomeClass - { -- public function echoInput(string|int $input): int|bool -+ /** -+ * @param string|int $input -+ * @return int|bool -+ */ -+ public function echoInput($input) - { - echo $input; - } - } -``` - -
- -### DowngradeUnionTypeTypedPropertyRector - -Removes union type property type definition, adding `@var` annotations instead. - -- class: [`Rector\DowngradePhp80\Rector\Property\DowngradeUnionTypeTypedPropertyRector`](../rules/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector.php) - -```diff - class SomeClass - { -- private string|int $property; -+ /** -+ * @var string|int -+ */ -+ private $property; - } -``` - -
- -## DowngradePhp81 - -### DowngradeFinalizePublicClassConstantRector - -Remove final from class constants - -- class: [`Rector\DowngradePhp81\Rector\ClassConst\DowngradeFinalizePublicClassConstantRector`](../rules/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector.php) - -```diff - class SomeClass - { -- final public const NAME = 'value'; -+ public const NAME = 'value'; - } -``` - -
- -## EarlyReturn - -### ChangeAndIfToEarlyReturnRector - -Changes if && to early return - -- class: [`Rector\EarlyReturn\Rector\If_\ChangeAndIfToEarlyReturnRector`](../rules/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector.php) - -```diff - class SomeClass - { - public function canDrive(Car $car) - { -- if ($car->hasWheels && $car->hasFuel) { -- return true; -+ if (!$car->hasWheels) { -+ return false; - } - -- return false; -+ if (!$car->hasFuel) { -+ return false; -+ } -+ -+ return true; - } - } -``` - -
- -### ChangeIfElseValueAssignToEarlyReturnRector - -Change if/else value to early return - -- class: [`Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector`](../rules/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector.php) - -```diff - class SomeClass - { - public function run() - { - if ($this->hasDocBlock($tokens, $index)) { -- $docToken = $tokens[$this->getDocBlockIndex($tokens, $index)]; -- } else { -- $docToken = null; -+ return $tokens[$this->getDocBlockIndex($tokens, $index)]; - } -- -- return $docToken; -+ return null; - } - } -``` - -
- -### ChangeNestedForeachIfsToEarlyContinueRector - -Change nested ifs to foreach with continue - -- class: [`Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector`](../rules/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector.php) - -```diff - class SomeClass - { - public function run() - { - $items = []; - - foreach ($values as $value) { -- if ($value === 5) { -- if ($value2 === 10) { -- $items[] = 'maybe'; -- } -+ if ($value !== 5) { -+ continue; - } -+ if ($value2 !== 10) { -+ continue; -+ } -+ -+ $items[] = 'maybe'; - } - } - } -``` - -
- -### ChangeNestedIfsToEarlyReturnRector - -Change nested ifs to early return - -- class: [`Rector\EarlyReturn\Rector\If_\ChangeNestedIfsToEarlyReturnRector`](../rules/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector.php) - -```diff - class SomeClass - { - public function run() - { -- if ($value === 5) { -- if ($value2 === 10) { -- return 'yes'; -- } -+ if ($value !== 5) { -+ return 'no'; -+ } -+ -+ if ($value2 === 10) { -+ return 'yes'; - } - - return 'no'; - } - } -``` - -
- -### ChangeOrIfContinueToMultiContinueRector - -Changes if || to early return - -- class: [`Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector`](../rules/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector.php) - -```diff - class SomeClass - { - public function canDrive(Car $newCar) - { - foreach ($cars as $car) { -- if ($car->hasWheels() || $car->hasFuel()) { -+ if ($car->hasWheels()) { -+ continue; -+ } -+ if ($car->hasFuel()) { - continue; - } - - $car->setWheel($newCar->wheel); - $car->setFuel($newCar->fuel); - } - } - } -``` - -
- -### ChangeOrIfReturnToEarlyReturnRector - -Changes if || with return to early return - -- class: [`Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector`](../rules/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector.php) - -```diff - class SomeClass - { - public function run($a, $b) - { -- if ($a || $b) { -+ if ($a) { -+ return null; -+ } -+ if ($b) { - return null; - } - - return 'another'; - } - } -``` - -
- -### PreparedValueToEarlyReturnRector - -Return early prepared value in ifs - -- class: [`Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector`](../rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php) - -```diff - class SomeClass - { - public function run() - { -- $var = null; -- - if (rand(0,1)) { -- $var = 1; -+ return 1; - } - - if (rand(0,1)) { -- $var = 2; -+ return 2; - } - -- return $var; -+ return null; - } - } -``` - -
- -### RemoveAlwaysElseRector - -Split if statement, when if condition always break execution flow - -- class: [`Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector`](../rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php) - -```diff - class SomeClass - { - public function run($value) - { - if ($value) { - throw new \InvalidStateException; -- } else { -- return 10; - } -+ -+ return 10; - } - } -``` - -
- -### ReturnAfterToEarlyOnBreakRector - -Change return after foreach to early return in foreach on break - -- class: [`Rector\EarlyReturn\Rector\Foreach_\ReturnAfterToEarlyOnBreakRector`](../rules/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector.php) - -```diff - class SomeClass - { - public function run(array $pathConstants, string $allowedPath) - { -- $pathOK = false; -- - foreach ($pathConstants as $allowedPath) { - if ($dirPath == $allowedPath) { -- $pathOK = true; -- break; -+ return true; - } - } - -- return $pathOK; -+ return false; - } - } -``` - -
- -### ReturnBinaryAndToEarlyReturnRector - -Changes Single return of && to early returns - -- class: [`Rector\EarlyReturn\Rector\Return_\ReturnBinaryAndToEarlyReturnRector`](../rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php) - -```diff - class SomeClass - { - public function accept() - { -- return $this->something() && $this->somethingelse(); -+ if (!$this->something()) { -+ return false; -+ } -+ return (bool) $this->somethingelse(); - } - } -``` - -
- -### ReturnBinaryOrToEarlyReturnRector - -Changes Single return of || to early returns - -- class: [`Rector\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector`](../rules/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector.php) - -```diff - class SomeClass - { - public function accept() - { -- return $this->something() || $this->somethingElse(); -+ if ($this->something()) { -+ return true; -+ } -+ return (bool) $this->somethingElse(); - } - } -``` - -
- -## LeagueEvent - -### DispatchStringToObjectRector - -Change string events to anonymous class which implement \League\Event\HasEventName - -- class: [`Rector\LeagueEvent\Rector\MethodCall\DispatchStringToObjectRector`](../rules/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector.php) - -```diff - final class SomeClass - { - /** @var \League\Event\EventDispatcher */ - private $dispatcher; - - public function run() - { -- $this->dispatcher->dispatch('my-event'); -+ $this->dispatcher->dispatch(new class implements \League\Event\HasEventName -+ { -+ public function eventName(): string -+ { -+ return 'my-event'; -+ } -+ }); - } - } -``` - -
- -## MockeryToProphecy - -### MockeryCloseRemoveRector - -Removes mockery close from test classes - -- class: [`Rector\MockeryToProphecy\Rector\StaticCall\MockeryCloseRemoveRector`](../rules/MockeryToProphecy/Rector/StaticCall/MockeryCloseRemoveRector.php) - -```diff - public function tearDown() : void - { -- \Mockery::close(); - } -``` - -
- -### MockeryCreateMockToProphizeRector - -Changes mockery mock creation to Prophesize - -- class: [`Rector\MockeryToProphecy\Rector\ClassMethod\MockeryCreateMockToProphizeRector`](../rules/MockeryToProphecy/Rector/ClassMethod/MockeryCreateMockToProphizeRector.php) - -```diff --$mock = \Mockery::mock('MyClass'); -+$mock = $this->prophesize('MyClass'); -+ - $service = new Service(); --$service->injectDependency($mock); -+$service->injectDependency($mock->reveal()); -``` - -
- -## MysqlToMysqli - -### MysqlAssignToMysqliRector - -Converts more complex mysql functions to mysqli - -- class: [`Rector\MysqlToMysqli\Rector\Assign\MysqlAssignToMysqliRector`](../rules/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector.php) - -```diff --$data = mysql_db_name($result, $row); -+mysqli_data_seek($result, $row); -+$fetch = mysql_fetch_row($result); -+$data = $fetch[0]; -``` - -
- -### MysqlFuncCallToMysqliRector - -Converts more complex mysql functions to mysqli - -- class: [`Rector\MysqlToMysqli\Rector\FuncCall\MysqlFuncCallToMysqliRector`](../rules/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector.php) - -```diff --mysql_drop_db($database); -+mysqli_query('DROP DATABASE ' . $database); -``` - -
- -### MysqlPConnectToMysqliConnectRector - -Replace `mysql_pconnect()` with `mysqli_connect()` with host p: prefix - -- class: [`Rector\MysqlToMysqli\Rector\FuncCall\MysqlPConnectToMysqliConnectRector`](../rules/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector.php) - -```diff - final class SomeClass - { - public function run($host, $username, $password) - { -- return mysql_pconnect($host, $username, $password); -+ return mysqli_connect('p:' . $host, $username, $password); - } - } -``` - -
- -### MysqlQueryMysqlErrorWithLinkRector - -Add mysql_query and mysql_error with connection - -- class: [`Rector\MysqlToMysqli\Rector\FuncCall\MysqlQueryMysqlErrorWithLinkRector`](../rules/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector.php) - -```diff - class SomeClass - { - public function run() - { - $conn = mysqli_connect('host', 'user', 'pass'); - -- mysql_error(); -+ mysqli_error($conn); - $sql = 'SELECT'; - -- return mysql_query($sql); -+ return mysqli_query($conn, $sql); - } - } -``` - -
- -## Naming - -### RenameForeachValueVariableToMatchExprVariableRector - -Renames value variable name in foreach loop to match expression variable - -- class: [`Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector`](../rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector.php) - -```diff - class SomeClass - { - public function run() - { - $array = []; -- foreach ($variables as $property) { -- $array[] = $property; -+ foreach ($variables as $variable) { -+ $array[] = $variable; - } - } - } -``` - -
- -### RenameForeachValueVariableToMatchMethodCallReturnTypeRector - -Renames value variable name in foreach loop to match method type - -- class: [`Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchMethodCallReturnTypeRector`](../rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector.php) - -```diff - class SomeClass - { - public function run() - { - $array = []; -- foreach ($object->getMethods() as $property) { -- $array[] = $property; -+ foreach ($object->getMethods() as $method) { -+ $array[] = $method; - } - } - } -``` - -
- -### RenameParamToMatchTypeRector - -Rename param to match ClassType - -- class: [`Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector`](../rules/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector.php) - -```diff - final class SomeClass - { -- public function run(Apple $pie) -+ public function run(Apple $apple) - { -- $food = $pie; -+ $food = $apple; - } - } -``` - -
- -### RenamePropertyToMatchTypeRector - -Rename property and method param to match its type - -- class: [`Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector`](../rules/Naming/Rector/Class_/RenamePropertyToMatchTypeRector.php) - -```diff - class SomeClass - { - /** - * @var EntityManager - */ -- private $eventManager; -+ private $entityManager; - -- public function __construct(EntityManager $eventManager) -+ public function __construct(EntityManager $entityManager) - { -- $this->eventManager = $eventManager; -+ $this->entityManager = $entityManager; - } - } -``` - -
- -### RenameVariableToMatchMethodCallReturnTypeRector - -Rename variable to match method return type - -- class: [`Rector\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector`](../rules/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector.php) - -```diff - class SomeClass - { - public function run() - { -- $a = $this->getRunner(); -+ $runner = $this->getRunner(); - } - - public function getRunner(): Runner - { - return new Runner(); - } - } -``` - -
- -### RenameVariableToMatchNewTypeRector - -Rename variable to match new ClassType - -- class: [`Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector`](../rules/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector.php) - -```diff - final class SomeClass - { - public function run() - { -- $search = new DreamSearch(); -- $search->advance(); -+ $dreamSearch = new DreamSearch(); -+ $dreamSearch->advance(); - } - } -``` - -
- -## Order - -### OrderPrivateMethodsByUseRector - -Order private methods in order of their use - -- class: [`Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector`](../rules/Order/Rector/Class_/OrderPrivateMethodsByUseRector.php) - -```diff - class SomeClass - { - public function run() - { - $this->call1(); - $this->call2(); - } - -- private function call2() -+ private function call1() - { - } - -- private function call1() -+ private function call2() - { - } - } -``` - -
- -## PSR4 - -### MultipleClassFileToPsr4ClassesRector - -Change multiple classes in one file to standalone PSR-4 classes. - -- class: [`Rector\PSR4\Rector\Namespace_\MultipleClassFileToPsr4ClassesRector`](../rules/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector.php) - -```diff -+// new file: "app/Exceptions/FirstException.php" - namespace App\Exceptions; - - use Exception; - - final class FirstException extends Exception - { - } -+ -+// new file: "app/Exceptions/SecondException.php" -+namespace App\Exceptions; -+ -+use Exception; - - final class SecondException extends Exception - { - } -``` - -
- -### NormalizeNamespaceByPSR4ComposerAutoloadRector - -Adds namespace to namespace-less files or correct namespace to match PSR-4 in `composer.json` autoload section. Run with combination with "Rector\PSR4\Rector\Namespace_\MultipleClassFileToPsr4ClassesRector" - -- class: [`Rector\PSR4\Rector\FileWithoutNamespace\NormalizeNamespaceByPSR4ComposerAutoloadRector`](../rules/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector.php) - -- with `composer.json`: - -```json -{ - "autoload": { - "psr-4": { - "App\\CustomNamespace\\": "src" - } - } -} -``` - -↓ - -```diff - // src/SomeClass.php - -+namespace App\CustomNamespace; -+ - class SomeClass - { - } -``` - -
- -## Php52 - -### ContinueToBreakInSwitchRector - -Use break instead of continue in switch statements - -- class: [`Rector\Php52\Rector\Switch_\ContinueToBreakInSwitchRector`](../rules/Php52/Rector/Switch_/ContinueToBreakInSwitchRector.php) - -```diff - function some_run($value) - { - switch ($value) { - case 1: - echo 'Hi'; -- continue; -+ break; - case 2: - echo 'Hello'; - break; - } - } -``` - -
- -### VarToPublicPropertyRector - -Change property modifier from `var` to `public` - -- class: [`Rector\Php52\Rector\Property\VarToPublicPropertyRector`](../rules/Php52/Rector/Property/VarToPublicPropertyRector.php) - -```diff - final class SomeController - { -- var $name = 'Tom'; -+ public $name = 'Tom'; - } -``` - -
- -## Php53 - -### ClearReturnNewByReferenceRector - -Remove reference from "$assign = &new Value;" - -- class: [`Rector\Php53\Rector\AssignRef\ClearReturnNewByReferenceRector`](../rules/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector.php) - -```diff --$assign = &new Value; -+$assign = new Value; -``` - -
- -### DirNameFileConstantToDirConstantRector - -Convert dirname(__FILE__) to __DIR__ - -- class: [`Rector\Php53\Rector\FuncCall\DirNameFileConstantToDirConstantRector`](../rules/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector.php) - -```diff - class SomeClass - { - public function run() - { -- return dirname(__FILE__); -+ return __DIR__; - } - } -``` - -
- -### ReplaceHttpServerVarsByServerRector - -Rename old `$HTTP_*` variable names to new replacements - -- class: [`Rector\Php53\Rector\Variable\ReplaceHttpServerVarsByServerRector`](../rules/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector.php) - -```diff --$serverVars = $HTTP_SERVER_VARS; -+$serverVars = $_SERVER; -``` - -
- -### TernaryToElvisRector - -Use ?: instead of ?, where useful - -- class: [`Rector\Php53\Rector\Ternary\TernaryToElvisRector`](../rules/Php53/Rector/Ternary/TernaryToElvisRector.php) - -```diff - function elvis() - { -- $value = $a ? $a : false; -+ $value = $a ?: false; - } -``` - -
- -## Php54 - -### RemoveReferenceFromCallRector - -Remove & from function and method calls - -- class: [`Rector\Php54\Rector\FuncCall\RemoveReferenceFromCallRector`](../rules/Php54/Rector/FuncCall/RemoveReferenceFromCallRector.php) - -```diff - final class SomeClass - { - public function run($one) - { -- return strlen(&$one); -+ return strlen($one); - } - } -``` - -
- -### RemoveZeroBreakContinueRector - -Remove 0 from break and continue - -- class: [`Rector\Php54\Rector\Break_\RemoveZeroBreakContinueRector`](../rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php) - -```diff - class SomeClass - { - public function run($random) - { -- continue 0; -- break 0; -+ continue; -+ break; - - $five = 5; -- continue $five; -+ continue 5; - -- break $random; -+ break; - } - } -``` - -
- -## Php55 - -### ClassConstantToSelfClassRector - -Change `__CLASS__` to self::class - -- class: [`Rector\Php55\Rector\Class_\ClassConstantToSelfClassRector`](../rules/Php55/Rector/Class_/ClassConstantToSelfClassRector.php) - -```diff - class SomeClass - { - public function callOnMe() - { -- var_dump(__CLASS__); -+ var_dump(self::class); - } - } -``` - -
- -### PregReplaceEModifierRector - -The /e modifier is no longer supported, use preg_replace_callback instead - -- class: [`Rector\Php55\Rector\FuncCall\PregReplaceEModifierRector`](../rules/Php55/Rector/FuncCall/PregReplaceEModifierRector.php) - -```diff - class SomeClass - { - public function run() - { -- $comment = preg_replace('~\b(\w)(\w+)~e', '"$1".strtolower("$2")', $comment); -+ $comment = preg_replace_callback('~\b(\w)(\w+)~', function ($matches) { -+ return($matches[1].strtolower($matches[2])); -+ }, $comment); - } - } -``` - -
- -### StringClassNameToClassConstantRector - -Replace string class names by ::class constant - -:wrench: **configure it!** - -- class: [`Rector\Php55\Rector\String_\StringClassNameToClassConstantRector`](../rules/Php55/Rector/String_/StringClassNameToClassConstantRector.php) - -```php -use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(StringClassNameToClassConstantRector::class) - ->call('configure', [[ - StringClassNameToClassConstantRector::CLASSES_TO_SKIP => ['ClassName', 'AnotherClassName'], - ]]); -}; -``` - -↓ - -```diff - class AnotherClass - { - } - - class SomeClass - { - public function run() - { -- return 'AnotherClass'; -+ return \AnotherClass::class; - } - } -``` - -
- -## Php56 - -### AddDefaultValueForUndefinedVariableRector - -Adds default value for undefined variable - -- class: [`Rector\Php56\Rector\FunctionLike\AddDefaultValueForUndefinedVariableRector`](../rules/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector.php) - -```diff - class SomeClass - { - public function run() - { -+ $a = null; - if (rand(0, 1)) { - $a = 5; - } - echo $a; - } - } -``` - -
- -### PowToExpRector - -Changes pow(val, val2) to ** (exp) parameter - -- class: [`Rector\Php56\Rector\FuncCall\PowToExpRector`](../rules/Php56/Rector/FuncCall/PowToExpRector.php) - -```diff --pow(1, 2); -+1**2; -``` - -
- -## Php70 - -### BreakNotInLoopOrSwitchToReturnRector - -Convert break outside for/foreach/switch context to return - -- class: [`Rector\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector`](../rules/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector.php) - -```diff - class SomeClass - { - public function run() - { - if ($isphp5) - return 1; - else - return 2; -- break; -+ return; - } - } -``` - -
- -### CallUserMethodRector - -Changes `call_user_method()/call_user_method_array()` to `call_user_func()/call_user_func_array()` - -- class: [`Rector\Php70\Rector\FuncCall\CallUserMethodRector`](../rules/Php70/Rector/FuncCall/CallUserMethodRector.php) - -```diff --call_user_method($method, $obj, "arg1", "arg2"); -+call_user_func(array(&$obj, "method"), "arg1", "arg2"); -``` - -
- -### EmptyListRector - -`list()` cannot be empty - -- class: [`Rector\Php70\Rector\List_\EmptyListRector`](../rules/Php70/Rector/List_/EmptyListRector.php) - -```diff --'list() = $values;' -+'list($unusedGenerated) = $values;' -``` - -
- -### EregToPregMatchRector - -Changes ereg*() to preg*() calls - -- class: [`Rector\Php70\Rector\FuncCall\EregToPregMatchRector`](../rules/Php70/Rector/FuncCall/EregToPregMatchRector.php) - -```diff --ereg("hi") -+preg_match("#hi#"); -``` - -
- -### ExceptionHandlerTypehintRector - -Change typehint from `Exception` to `Throwable`. - -- class: [`Rector\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector`](../rules/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector.php) - -```diff --function handler(Exception $exception) { ... } -+function handler(Throwable $exception) { ... } - set_exception_handler('handler'); -``` - -
- -### IfToSpaceshipRector - -Changes if/else to spaceship <=> where useful - -- class: [`Rector\Php70\Rector\If_\IfToSpaceshipRector`](../rules/Php70/Rector/If_/IfToSpaceshipRector.php) - -```diff - class SomeClass - { - public function run() - { - usort($languages, function ($a, $b) { -- if ($a[0] === $b[0]) { -- return 0; -- } -- -- return ($a[0] < $b[0]) ? 1 : -1; -+ return $b[0] <=> $a[0]; - }); - } - } -``` - -
- -### ListSplitStringRector - -`list()` cannot split string directly anymore, use `str_split()` - -- class: [`Rector\Php70\Rector\Assign\ListSplitStringRector`](../rules/Php70/Rector/Assign/ListSplitStringRector.php) - -```diff --list($foo) = "string"; -+list($foo) = str_split("string"); -``` - -
- -### ListSwapArrayOrderRector - -`list()` assigns variables in reverse order - relevant in array assign - -- class: [`Rector\Php70\Rector\Assign\ListSwapArrayOrderRector`](../rules/Php70/Rector/Assign/ListSwapArrayOrderRector.php) - -```diff --list($a[], $a[]) = [1, 2]; -+list($a[], $a[]) = array_reverse([1, 2]); -``` - -
- -### MultiDirnameRector - -Changes multiple `dirname()` calls to one with nesting level - -- class: [`Rector\Php70\Rector\FuncCall\MultiDirnameRector`](../rules/Php70/Rector/FuncCall/MultiDirnameRector.php) - -```diff --dirname(dirname($path)); -+dirname($path, 2); -``` - -
- -### NonVariableToVariableOnFunctionCallRector - -Transform non variable like arguments to variable where a function or method expects an argument passed by reference - -- class: [`Rector\Php70\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector`](../rules/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php) - -```diff --reset(a()); -+$a = a(); reset($a); -``` - -
- -### Php4ConstructorRector - -Changes PHP 4 style constructor to __construct. - -- class: [`Rector\Php70\Rector\ClassMethod\Php4ConstructorRector`](../rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php) - -```diff - class SomeClass - { -- public function SomeClass() -+ public function __construct() - { - } - } -``` - -
- -### RandomFunctionRector - -Changes rand, srand and getrandmax by new mt_* alternatives. - -- class: [`Rector\Php70\Rector\FuncCall\RandomFunctionRector`](../rules/Php70/Rector/FuncCall/RandomFunctionRector.php) - -```diff --rand(); -+mt_rand(); -``` - -
- -### ReduceMultipleDefaultSwitchRector - -Remove first default switch, that is ignored - -- class: [`Rector\Php70\Rector\Switch_\ReduceMultipleDefaultSwitchRector`](../rules/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector.php) - -```diff - switch ($expr) { - default: -- echo "Hello World"; -- -- default: - echo "Goodbye Moon!"; - break; - } -``` - -
- -### RenameMktimeWithoutArgsToTimeRector - -Renames `mktime()` without arguments to `time()` - -- class: [`Rector\Php70\Rector\FuncCall\RenameMktimeWithoutArgsToTimeRector`](../rules/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector.php) - -```diff - class SomeClass - { - public function run() - { - $time = mktime(1, 2, 3); -- $nextTime = mktime(); -+ $nextTime = time(); - } - } -``` - -
- -### StaticCallOnNonStaticToInstanceCallRector - -Changes static call to instance call, where not useful - -- class: [`Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector`](../rules/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector.php) - -```diff - class Something - { - public function doWork() - { - } - } - - class Another - { - public function run() - { -- return Something::doWork(); -+ return (new Something)->doWork(); - } - } -``` - -
- -### TernaryToNullCoalescingRector - -Changes unneeded null check to ?? operator - -- class: [`Rector\Php70\Rector\Ternary\TernaryToNullCoalescingRector`](../rules/Php70/Rector/Ternary/TernaryToNullCoalescingRector.php) - -```diff --$value === null ? 10 : $value; -+$value ?? 10; -``` - -
- -```diff --isset($value) ? $value : 10; -+$value ?? 10; -``` - -
- -### TernaryToSpaceshipRector - -Use <=> spaceship instead of ternary with same effect - -- class: [`Rector\Php70\Rector\Ternary\TernaryToSpaceshipRector`](../rules/Php70/Rector/Ternary/TernaryToSpaceshipRector.php) - -```diff - function order_func($a, $b) { -- return ($a < $b) ? -1 : (($a > $b) ? 1 : 0); -+ return $a <=> $b; - } -``` - -
- -### ThisCallOnStaticMethodToStaticCallRector - -Changes `$this->call()` to static method to static call - -- class: [`Rector\Php70\Rector\MethodCall\ThisCallOnStaticMethodToStaticCallRector`](../rules/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector.php) - -```diff - class SomeClass - { - public static function run() - { -- $this->eat(); -+ static::eat(); - } - - public static function eat() - { - } - } -``` - -
- -### WrapVariableVariableNameInCurlyBracesRector - -Ensure variable variables are wrapped in curly braces - -- class: [`Rector\Php70\Rector\Variable\WrapVariableVariableNameInCurlyBracesRector`](../rules/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector.php) - -```diff - function run($foo) - { -- global $$foo->bar; -+ global ${$foo->bar}; - } -``` - -
- -## Php71 - -### AssignArrayToStringRector - -String cannot be turned into array by assignment anymore - -- class: [`Rector\Php71\Rector\Assign\AssignArrayToStringRector`](../rules/Php71/Rector/Assign/AssignArrayToStringRector.php) - -```diff --$string = ''; -+$string = []; - $string[] = 1; -``` - -
- -### BinaryOpBetweenNumberAndStringRector - -Change binary operation between some number + string to PHP 7.1 compatible version - -- class: [`Rector\Php71\Rector\BinaryOp\BinaryOpBetweenNumberAndStringRector`](../rules/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector.php) - -```diff - class SomeClass - { - public function run() - { -- $value = 5 + ''; -- $value = 5.0 + 'hi'; -+ $value = 5 + 0; -+ $value = 5.0 + 0; - } - } -``` - -
- -### CountOnNullRector - -Changes `count()` on null to safe ternary check - -- class: [`Rector\Php71\Rector\FuncCall\CountOnNullRector`](../rules/Php71/Rector/FuncCall/CountOnNullRector.php) - -```diff - $values = null; --$count = count($values); -+$count = count((array) $values); -``` - -
- -### IsIterableRector - -Changes is_array + Traversable check to is_iterable - -- class: [`Rector\Php71\Rector\BooleanOr\IsIterableRector`](../rules/Php71/Rector/BooleanOr/IsIterableRector.php) - -```diff --is_array($foo) || $foo instanceof Traversable; -+is_iterable($foo); -``` - -
- -### ListToArrayDestructRector - -Change `list()` to array destruct - -- class: [`Rector\Php71\Rector\List_\ListToArrayDestructRector`](../rules/Php71/Rector/List_/ListToArrayDestructRector.php) - -```diff - class SomeClass - { - public function run() - { -- list($id1, $name1) = $data; -+ [$id1, $name1] = $data; - -- foreach ($data as list($id, $name)) { -+ foreach ($data as [$id, $name]) { - } - } - } -``` - -
- -### MultiExceptionCatchRector - -Changes multi catch of same exception to single one | separated. - -- class: [`Rector\Php71\Rector\TryCatch\MultiExceptionCatchRector`](../rules/Php71/Rector/TryCatch/MultiExceptionCatchRector.php) - -```diff - try { - // Some code... --} catch (ExceptionType1 $exception) { -- $sameCode; --} catch (ExceptionType2 $exception) { -+} catch (ExceptionType1 | ExceptionType2 $exception) { - $sameCode; - } -``` - -
- -### PublicConstantVisibilityRector - -Add explicit public constant visibility. - -- class: [`Rector\Php71\Rector\ClassConst\PublicConstantVisibilityRector`](../rules/Php71/Rector/ClassConst/PublicConstantVisibilityRector.php) - -```diff - class SomeClass - { -- const HEY = 'you'; -+ public const HEY = 'you'; - } -``` - -
- -### RemoveExtraParametersRector - -Remove extra parameters - -- class: [`Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector`](../rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php) - -```diff --strlen("asdf", 1); -+strlen("asdf"); -``` - -
- -### ReservedObjectRector - -Changes reserved "Object" name to "Object" where can be configured - -:wrench: **configure it!** - -- class: [`Rector\Php71\Rector\Name\ReservedObjectRector`](../rules/Php71/Rector/Name/ReservedObjectRector.php) - -```php -use Rector\Php71\Rector\Name\ReservedObjectRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ReservedObjectRector::class) - ->call('configure', [[ - ReservedObjectRector::RESERVED_KEYWORDS_TO_REPLACEMENTS => [ - 'ReservedObject' => 'SmartObject', - 'Object' => 'AnotherSmartObject', - - ], ]]); -}; -``` - -↓ - -```diff --class Object -+class SmartObject - { - } -``` - -
- -## Php72 - -### CreateFunctionToAnonymousFunctionRector - -Use anonymous functions instead of deprecated `create_function()` - -- class: [`Rector\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector`](../rules/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector.php) - -```diff - class ClassWithCreateFunction - { - public function run() - { -- $callable = create_function('$matches', "return '$delimiter' . strtolower(\$matches[1]);"); -+ $callable = function($matches) use ($delimiter) { -+ return $delimiter . strtolower($matches[1]); -+ }; - } - } -``` - -
- -### GetClassOnNullRector - -Null is no more allowed in `get_class()` - -- class: [`Rector\Php72\Rector\FuncCall\GetClassOnNullRector`](../rules/Php72/Rector/FuncCall/GetClassOnNullRector.php) - -```diff - final class SomeClass - { - public function getItem() - { - $value = null; -- return get_class($value); -+ return $value !== null ? get_class($value) : self::class; - } - } -``` - -
- -### IsObjectOnIncompleteClassRector - -Incomplete class returns inverted bool on `is_object()` - -- class: [`Rector\Php72\Rector\FuncCall\IsObjectOnIncompleteClassRector`](../rules/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector.php) - -```diff - $incompleteObject = new __PHP_Incomplete_Class; --$isObject = is_object($incompleteObject); -+$isObject = ! is_object($incompleteObject); -``` - -
- -### ListEachRector - -`each()` function is deprecated, use `key()` and `current()` instead - -- class: [`Rector\Php72\Rector\Assign\ListEachRector`](../rules/Php72/Rector/Assign/ListEachRector.php) - -```diff --list($key, $callback) = each($callbacks); -+$key = key($callbacks); -+$callback = current($callbacks); -+next($callbacks); -``` - -
- -### ParseStrWithResultArgumentRector - -Use `$result` argument in `parse_str()` function - -- class: [`Rector\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector`](../rules/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector.php) - -```diff --parse_str($this->query); --$data = get_defined_vars(); -+parse_str($this->query, $result); -+$data = $result; -``` - -
- -### ReplaceEachAssignmentWithKeyCurrentRector - -Replace `each()` assign outside loop - -- class: [`Rector\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector`](../rules/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector.php) - -```diff - $array = ['b' => 1, 'a' => 2]; --$eachedArray = each($array); -+$eachedArray[1] = current($array); -+$eachedArray['value'] = current($array); -+$eachedArray[0] = key($array); -+$eachedArray['key'] = key($array); -+next($array); -``` - -
- -### StringifyDefineRector - -Make first argument of `define()` string - -- class: [`Rector\Php72\Rector\FuncCall\StringifyDefineRector`](../rules/Php72/Rector/FuncCall/StringifyDefineRector.php) - -```diff - class SomeClass - { - public function run(int $a) - { -- define(CONSTANT_2, 'value'); -+ define('CONSTANT_2', 'value'); - define('CONSTANT', 'value'); - } - } -``` - -
- -### StringsAssertNakedRector - -String asserts must be passed directly to `assert()` - -- class: [`Rector\Php72\Rector\FuncCall\StringsAssertNakedRector`](../rules/Php72/Rector/FuncCall/StringsAssertNakedRector.php) - -```diff - function nakedAssert() - { -- assert('true === true'); -- assert("true === true"); -+ assert(true === true); -+ assert(true === true); - } -``` - -
- -### UnsetCastRector - -Removes (unset) cast - -- class: [`Rector\Php72\Rector\Unset_\UnsetCastRector`](../rules/Php72/Rector/Unset_/UnsetCastRector.php) - -```diff --$different = (unset) $value; -+$different = null; - --$value = (unset) $value; -+unset($value); -``` - -
- -### WhileEachToForeachRector - -`each()` function is deprecated, use `foreach()` instead. - -- class: [`Rector\Php72\Rector\While_\WhileEachToForeachRector`](../rules/Php72/Rector/While_/WhileEachToForeachRector.php) - -```diff --while (list($key, $callback) = each($callbacks)) { -+foreach ($callbacks as $key => $callback) { - // ... - } -``` - -
- -```diff --while (list($key) = each($callbacks)) { -+foreach (array_keys($callbacks) as $key) { - // ... - } -``` - -
- -## Php73 - -### ArrayKeyFirstLastRector - -Make use of `array_key_first()` and `array_key_last()` - -- class: [`Rector\Php73\Rector\FuncCall\ArrayKeyFirstLastRector`](../rules/Php73/Rector/FuncCall/ArrayKeyFirstLastRector.php) - -```diff --reset($items); --$firstKey = key($items); -+$firstKey = array_key_first($items); -``` - -
- -```diff --end($items); --$lastKey = key($items); -+$lastKey = array_key_last($items); -``` - -
- -### IsCountableRector - -Changes is_array + Countable check to is_countable - -- class: [`Rector\Php73\Rector\BooleanOr\IsCountableRector`](../rules/Php73/Rector/BooleanOr/IsCountableRector.php) - -```diff --is_array($foo) || $foo instanceof Countable; -+is_countable($foo); -``` - -
- -### JsonThrowOnErrorRector - -Adds JSON_THROW_ON_ERROR to `json_encode()` and `json_decode()` to throw JsonException on error - -- class: [`Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector`](../rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php) - -```diff --json_encode($content); --json_decode($json); -+json_encode($content, JSON_THROW_ON_ERROR); -+json_decode($json, null, 512, JSON_THROW_ON_ERROR); -``` - -
- -### RegexDashEscapeRector - -Escape - in some cases - -- class: [`Rector\Php73\Rector\FuncCall\RegexDashEscapeRector`](../rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php) - -```diff --preg_match("#[\w-()]#", 'some text'); -+preg_match("#[\w\-()]#", 'some text'); -``` - -
- -### SensitiveConstantNameRector - -Changes case insensitive constants to sensitive ones. - -- class: [`Rector\Php73\Rector\ConstFetch\SensitiveConstantNameRector`](../rules/Php73/Rector/ConstFetch/SensitiveConstantNameRector.php) - -```diff - define('FOO', 42, true); - var_dump(FOO); --var_dump(foo); -+var_dump(FOO); -``` - -
- -### SensitiveDefineRector - -Changes case insensitive constants to sensitive ones. - -- class: [`Rector\Php73\Rector\FuncCall\SensitiveDefineRector`](../rules/Php73/Rector/FuncCall/SensitiveDefineRector.php) - -```diff --define('FOO', 42, true); -+define('FOO', 42); -``` - -
- -### SensitiveHereNowDocRector - -Changes heredoc/nowdoc that contains closing word to safe wrapper name - -- class: [`Rector\Php73\Rector\String_\SensitiveHereNowDocRector`](../rules/Php73/Rector/String_/SensitiveHereNowDocRector.php) - -```diff --$value = << - -### SetCookieRector - -Convert setcookie argument to PHP7.3 option array - -- class: [`Rector\Php73\Rector\FuncCall\SetCookieRector`](../rules/Php73/Rector/FuncCall/SetCookieRector.php) - -```diff --setcookie('name', $value, 360); -+setcookie('name', $value, ['expires' => 360]); -``` - -
- -```diff --setcookie('name', $name, 0, '', '', true, true); -+setcookie('name', $name, ['expires' => 0, 'path' => '', 'domain' => '', 'secure' => true, 'httponly' => true]); -``` - -
- -### StringifyStrNeedlesRector - -Makes needles explicit strings - -- class: [`Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector`](../rules/Php73/Rector/FuncCall/StringifyStrNeedlesRector.php) - -```diff - $needle = 5; --$fivePosition = strpos('725', $needle); -+$fivePosition = strpos('725', (string) $needle); -``` - -
- -## Php74 - -### AddLiteralSeparatorToNumberRector - -Add "_" as thousands separator in numbers for higher or equals to limitValue config - -:wrench: **configure it!** - -- class: [`Rector\Php74\Rector\LNumber\AddLiteralSeparatorToNumberRector`](../rules/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector.php) - -```php -use Rector\Php74\Rector\LNumber\AddLiteralSeparatorToNumberRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddLiteralSeparatorToNumberRector::class) - ->call('configure', [[ - AddLiteralSeparatorToNumberRector::LIMIT_VALUE => 1000000, - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- $int = 500000; -- $float = 1000500.001; -+ $int = 500_000; -+ $float = 1_000_500.001; - } - } -``` - -
- -### ArrayKeyExistsOnPropertyRector - -Change `array_key_exists()` on property to `property_exists()` - -- class: [`Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector`](../rules/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector.php) - -```diff - class SomeClass - { - public $value; - } - $someClass = new SomeClass; - --array_key_exists('value', $someClass); -+property_exists($someClass, 'value'); -``` - -
- -### ArraySpreadInsteadOfArrayMergeRector - -Change `array_merge()` to spread operator, except values with possible string key values - -- class: [`Rector\Php74\Rector\FuncCall\ArraySpreadInsteadOfArrayMergeRector`](../rules/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector.php) - -```diff - class SomeClass - { - public function run($iter1, $iter2) - { -- $values = array_merge(iterator_to_array($iter1), iterator_to_array($iter2)); -+ $values = [...$iter1, ...$iter2]; - - // Or to generalize to all iterables -- $anotherValues = array_merge( -- is_array($iter1) ? $iter1 : iterator_to_array($iter1), -- is_array($iter2) ? $iter2 : iterator_to_array($iter2) -- ); -+ $anotherValues = [...$iter1, ...$iter2]; - } - } -``` - -
- -### ChangeReflectionTypeToStringToGetNameRector - -Change string calls on ReflectionType - -- class: [`Rector\Php74\Rector\MethodCall\ChangeReflectionTypeToStringToGetNameRector`](../rules/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector.php) - -```diff - class SomeClass - { - public function go(ReflectionFunction $reflectionFunction) - { - $parameterReflection = $reflectionFunction->getParameters()[0]; - -- $paramType = (string) $parameterReflection->getType(); -+ $paramType = (string) ($parameterReflection->getType() ? $parameterReflection->getType()->getName() : null); - -- $stringValue = 'hey' . $reflectionFunction->getReturnType(); -+ $stringValue = 'hey' . ($reflectionFunction->getReturnType() ? $reflectionFunction->getReturnType()->getName() : null); - - // keep - return $reflectionFunction->getReturnType(); - } - } -``` - -
- -### ClosureToArrowFunctionRector - -Change closure to arrow function - -- class: [`Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector`](../rules/Php74/Rector/Closure/ClosureToArrowFunctionRector.php) - -```diff - class SomeClass - { - public function run($meetups) - { -- return array_filter($meetups, function (Meetup $meetup) { -- return is_object($meetup); -- }); -+ return array_filter($meetups, fn(Meetup $meetup) => is_object($meetup)); - } - } -``` - -
- -### ExportToReflectionFunctionRector - -Change `export()` to ReflectionFunction alternatives - -- class: [`Rector\Php74\Rector\StaticCall\ExportToReflectionFunctionRector`](../rules/Php74/Rector/StaticCall/ExportToReflectionFunctionRector.php) - -```diff --$reflectionFunction = ReflectionFunction::export('foo'); --$reflectionFunctionAsString = ReflectionFunction::export('foo', true); -+$reflectionFunction = new ReflectionFunction('foo'); -+$reflectionFunctionAsString = (string) new ReflectionFunction('foo'); -``` - -
- -### FilterVarToAddSlashesRector - -Change `filter_var()` with slash escaping to `addslashes()` - -- class: [`Rector\Php74\Rector\FuncCall\FilterVarToAddSlashesRector`](../rules/Php74/Rector/FuncCall/FilterVarToAddSlashesRector.php) - -```diff - $var= "Satya's here!"; --filter_var($var, FILTER_SANITIZE_MAGIC_QUOTES); -+addslashes($var); -``` - -
- -### GetCalledClassToStaticClassRector - -Change `get_called_class()` to static::class - -- class: [`Rector\Php74\Rector\FuncCall\GetCalledClassToStaticClassRector`](../rules/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector.php) - -```diff - class SomeClass - { - public function callOnMe() - { -- var_dump(get_called_class()); -+ var_dump(static::class); - } - } -``` - -
- -### MbStrrposEncodingArgumentPositionRector - -Change `mb_strrpos()` encoding argument position - -- class: [`Rector\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector`](../rules/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector.php) - -```diff --mb_strrpos($text, "abc", "UTF-8"); -+mb_strrpos($text, "abc", 0, "UTF-8"); -``` - -
- -### NullCoalescingOperatorRector - -Use null coalescing operator ??= - -- class: [`Rector\Php74\Rector\Assign\NullCoalescingOperatorRector`](../rules/Php74/Rector/Assign/NullCoalescingOperatorRector.php) - -```diff - $array = []; --$array['user_id'] = $array['user_id'] ?? 'value'; -+$array['user_id'] ??= 'value'; -``` - -
- -### RealToFloatTypeCastRector - -Change deprecated (real) to (float) - -- class: [`Rector\Php74\Rector\Double\RealToFloatTypeCastRector`](../rules/Php74/Rector/Double/RealToFloatTypeCastRector.php) - -```diff - class SomeClass - { - public function run() - { -- $number = (real) 5; -+ $number = (float) 5; - $number = (float) 5; - $number = (double) 5; - } - } -``` - -
- -### ReservedFnFunctionRector - -Change `fn()` function name, since it will be reserved keyword - -:wrench: **configure it!** - -- class: [`Rector\Php74\Rector\Function_\ReservedFnFunctionRector`](../rules/Php74/Rector/Function_/ReservedFnFunctionRector.php) - -```php -use Rector\Php74\Rector\Function_\ReservedFnFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ReservedFnFunctionRector::class) - ->call('configure', [[ - ReservedFnFunctionRector::RESERVED_NAMES_TO_NEW_ONES => [ - 'fn' => 'someFunctionName', - - ], ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- function fn($value) -+ function f($value) - { - return $value; - } - -- fn(5); -+ f(5); - } - } -``` - -
- -### RestoreDefaultNullToNullableTypePropertyRector - -Add null default to properties with PHP 7.4 property nullable type - -- class: [`Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector`](../rules/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector.php) - -```diff - class SomeClass - { -- public ?string $name; -+ public ?string $name = null; - } -``` - -
- -### TypedPropertyRector - -Changes property `@var` annotations from annotation to type. - -:wrench: **configure it!** - -- class: [`Rector\Php74\Rector\Property\TypedPropertyRector`](../rules/Php74/Rector/Property/TypedPropertyRector.php) - -```php -use Rector\Php74\Rector\Property\TypedPropertyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(TypedPropertyRector::class) - ->call('configure', [[ - TypedPropertyRector::CLASS_LIKE_TYPE_ONLY => false, - ]]); -}; -``` - -↓ - -```diff - final class SomeClass - { -- /** -- * @var int -- */ -- private $count; -+ private int $count; - } -``` - -
- -## Php80 - -### AnnotationToAttributeRector - -Change annotation to attribute - -:wrench: **configure it!** - -- class: [`Rector\Php80\Rector\Class_\AnnotationToAttributeRector`](../rules/Php80/Rector/Class_/AnnotationToAttributeRector.php) - -```php -use Rector\Php80\Rector\Class_\AnnotationToAttributeRector; -use Rector\Php80\ValueObject\AnnotationToAttribute; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AnnotationToAttributeRector::class) - ->call('configure', [[ - AnnotationToAttributeRector::ANNOTATION_TO_ATTRIBUTE => ValueObjectInliner::inline([ - new AnnotationToAttribute('Symfony\Component\Routing\Annotation\Route', null), - ]), - ]]); -}; -``` - -↓ - -```diff - use Symfony\Component\Routing\Annotation\Route; - - class SymfonyRoute - { -- /** -- * @Route("/path", name="action") -- */ -+ #[Route(path: '/path', name: 'action')] - public function action() - { - } - } -``` - -
- -### ChangeSwitchToMatchRector - -Change `switch()` to `match()` - -- class: [`Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector`](../rules/Php80/Rector/Switch_/ChangeSwitchToMatchRector.php) - -```diff --switch ($input) { -- case Lexer::T_SELECT: -- $statement = 'select'; -- break; -- case Lexer::T_UPDATE: -- $statement = 'update'; -- break; -- default: -- $statement = 'error'; --} -+$statement = match ($input) { -+ Lexer::T_SELECT => 'select', -+ Lexer::T_UPDATE => 'update', -+ default => 'error', -+}; -``` - -
- -### ClassOnObjectRector - -Change get_class($object) to faster `$object::class` - -- class: [`Rector\Php80\Rector\FuncCall\ClassOnObjectRector`](../rules/Php80/Rector/FuncCall/ClassOnObjectRector.php) - -```diff - class SomeClass - { - public function run($object) - { -- return get_class($object); -+ return $object::class; - } - } -``` - -
- -### ClassPropertyAssignToConstructorPromotionRector - -Change simple property init and assign to constructor promotion - -- class: [`Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector`](../rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php) - -```diff - class SomeClass - { -- public float $someVariable; -- -- public function __construct(float $someVariable = 0.0) -+ public function __construct(private float $someVariable = 0.0) - { -- $this->someVariable = $someVariable; - } - } -``` - -
- -### DoctrineAnnotationClassToAttributeRector - -Refactor Doctrine `@annotation` annotated class to a PHP 8.0 attribute class - -:wrench: **configure it!** - -- class: [`Rector\Php80\Rector\Class_\DoctrineAnnotationClassToAttributeRector`](../rules/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector.php) - -```php -use Rector\Php80\Rector\Class_\DoctrineAnnotationClassToAttributeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(DoctrineAnnotationClassToAttributeRector::class) - ->call('configure', [[ - DoctrineAnnotationClassToAttributeRector::REMOVE_ANNOTATIONS => true, - ]]); -}; -``` - -↓ - -```diff --use Doctrine\Common\Annotations\Annotation\Target; -+use Attribute; - --/** -- * @Annotation -- * @Target({"METHOD"}) -- */ -+#[Attribute(Attribute::TARGET_METHOD)] - class SomeAnnotation - { - } -``` - -
- -### FinalPrivateToPrivateVisibilityRector - -Changes method visibility from final private to only private - -- class: [`Rector\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector`](../rules/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector.php) - -```diff - class SomeClass - { -- final private function getter() { -+ private function getter() { - return $this; - } - } -``` - -
- -### GetDebugTypeRector - -Change ternary type resolve to `get_debug_type()` - -- class: [`Rector\Php80\Rector\Ternary\GetDebugTypeRector`](../rules/Php80/Rector/Ternary/GetDebugTypeRector.php) - -```diff - class SomeClass - { - public function run($value) - { -- return is_object($value) ? get_class($value) : gettype($value); -+ return get_debug_type($value); - } - } -``` - -
- -### NullsafeOperatorRector - -Change if null check with nullsafe operator ?-> with full short circuiting - -- class: [`Rector\Php80\Rector\If_\NullsafeOperatorRector`](../rules/Php80/Rector/If_/NullsafeOperatorRector.php) - -```diff - class SomeClass - { - public function run($someObject) - { -- $someObject2 = $someObject->mayFail1(); -- if ($someObject2 === null) { -- return null; -- } -- -- return $someObject2->mayFail2(); -+ return $someObject->mayFail1()?->mayFail2(); - } - } -``` - -
- -### OptionalParametersAfterRequiredRector - -Move required parameters after optional ones - -- class: [`Rector\Php80\Rector\ClassMethod\OptionalParametersAfterRequiredRector`](../rules/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php) - -```diff - class SomeObject - { -- public function run($optional = 1, $required) -+ public function run($required, $optional = 1) - { - } - } -``` - -
- -### RemoveUnusedVariableInCatchRector - -Remove unused variable in `catch()` - -- class: [`Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector`](../rules/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector.php) - -```diff - final class SomeClass - { - public function run() - { - try { -- } catch (Throwable $notUsedThrowable) { -+ } catch (Throwable) { - } - } - } -``` - -
- -### SetStateToStaticRector - -Adds static visibility to `__set_state()` methods - -- class: [`Rector\Php80\Rector\ClassMethod\SetStateToStaticRector`](../rules/Php80/Rector/ClassMethod/SetStateToStaticRector.php) - -```diff - class SomeClass - { -- public function __set_state($properties) { -+ public static function __set_state($properties) { - - } - } -``` - -
- -### StrContainsRector - -Replace `strpos()` !== false and `strstr()` with `str_contains()` - -- class: [`Rector\Php80\Rector\NotIdentical\StrContainsRector`](../rules/Php80/Rector/NotIdentical/StrContainsRector.php) - -```diff - class SomeClass - { - public function run() - { -- return strpos('abc', 'a') !== false; -+ return str_contains('abc', 'a'); - } - } -``` - -
- -### StrEndsWithRector - -Change helper functions to `str_ends_with()` - -- class: [`Rector\Php80\Rector\Identical\StrEndsWithRector`](../rules/Php80/Rector/Identical/StrEndsWithRector.php) - -```diff - class SomeClass - { - public function run() - { -- $isMatch = substr($haystack, -strlen($needle)) === $needle; -+ $isMatch = str_ends_with($haystack, $needle); - -- $isNotMatch = substr($haystack, -strlen($needle)) !== $needle; -+ $isNotMatch = !str_ends_with($haystack, $needle); - } - } -``` - -
- -```diff - class SomeClass - { - public function run() - { -- $isMatch = substr($haystack, -9) === 'hardcoded; -+ $isMatch = str_ends_with($haystack, 'hardcoded'); - -- $isNotMatch = substr($haystack, -9) !== 'hardcoded'; -+ $isNotMatch = !str_ends_with($haystack, 'hardcoded'); - } - } -``` - -
- -### StrStartsWithRector - -Change helper functions to `str_starts_with()` - -- class: [`Rector\Php80\Rector\Identical\StrStartsWithRector`](../rules/Php80/Rector/Identical/StrStartsWithRector.php) - -```diff - class SomeClass - { - public function run() - { -- $isMatch = substr($haystack, 0, strlen($needle)) === $needle; -+ $isMatch = str_starts_with($haystack, $needle); - -- $isNotMatch = substr($haystack, 0, strlen($needle)) !== $needle; -+ $isNotMatch = ! str_starts_with($haystack, $needle); - } - } -``` - -
- -### StringableForToStringRector - -Add `Stringable` interface to classes with `__toString()` method - -- class: [`Rector\Php80\Rector\Class_\StringableForToStringRector`](../rules/Php80/Rector/Class_/StringableForToStringRector.php) - -```diff --class SomeClass -+class SomeClass implements Stringable - { -- public function __toString() -+ public function __toString(): string - { - return 'I can stringz'; - } - } -``` - -
- -### TokenGetAllToObjectRector - -Convert `token_get_all` to `PhpToken::tokenize` - -- class: [`Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector`](../rules/Php80/Rector/FuncCall/TokenGetAllToObjectRector.php) - -```diff - final class SomeClass - { - public function run() - { -- $tokens = token_get_all($code); -- foreach ($tokens as $token) { -- if (is_array($token)) { -- $name = token_name($token[0]); -- $text = $token[1]; -- } else { -- $name = null; -- $text = $token; -- } -+ $tokens = \PhpToken::tokenize($code); -+ foreach ($tokens as $phpToken) { -+ $name = $phpToken->getTokenName(); -+ $text = $phpToken->text; - } - } - } -``` - -
- -### UnionTypesRector - -Change docs types to union types, where possible (properties are covered by TypedPropertiesRector) - -- class: [`Rector\Php80\Rector\FunctionLike\UnionTypesRector`](../rules/Php80/Rector/FunctionLike/UnionTypesRector.php) - -```diff - class SomeClass - { -- /** -- * @param array|int $number -- * @return bool|float -- */ -- public function go($number) -+ public function go(array|int $number): bool|float - { - } - } -``` - -
- -## Php81 - -### FinalizePublicClassConstantRector - -Add final to constants that - -- class: [`Rector\Php81\Rector\ClassConst\FinalizePublicClassConstantRector`](../rules/Php81/Rector/ClassConst/FinalizePublicClassConstantRector.php) - -```diff - class SomeClass - { -- public const NAME = 'value'; -+ public final const NAME = 'value'; - } -``` - -
- -### MyCLabsClassToEnumRector - -Refactor MyCLabs enum class to native Enum - -- class: [`Rector\Php81\Rector\Class_\MyCLabsClassToEnumRector`](../rules/Php81/Rector/Class_/MyCLabsClassToEnumRector.php) - -```diff --use MyCLabs\Enum\Enum; -- --final class Action extends Enum -+enum Action - { -- private const VIEW = 'view'; -- private const EDIT = 'edit'; -+ case VIEW = 'view'; -+ case EDIT = 'edit'; - } -``` - -
- -### MyCLabsMethodCallToEnumConstRector - -Refactor MyCLabs enum fetch to Enum const - -- class: [`Rector\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector`](../rules/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector.php) - -```diff --$name = SomeEnum::VALUE()->getKey(); -+$name = SomeEnum::VALUE; -``` - -
- -### ReadOnlyPropertyRector - -Decorate read-only property with `readonly` attribute - -- class: [`Rector\Php81\Rector\Property\ReadOnlyPropertyRector`](../rules/Php81/Rector/Property/ReadOnlyPropertyRector.php) - -```diff - class SomeClass - { - public function __construct( -- private string $name -+ private readonly string $name - ) { - } - - public function getName() - { - return $this->name; - } - } -``` - -
- -### SpatieEnumClassToEnumRector - -Refactor Spatie enum class to native Enum - -- class: [`Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector`](../rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php) - -```diff --use \Spatie\Enum\Enum; -- --/** -- * @method static self draft() -- * @method static self published() -- * @method static self archived() -- */ --class StatusEnum extends Enum -+enum StatusEnum - { -+ case draft = 'draft'; -+ case published = 'published'; -+ case archived = 'archived'; - } -``` - -
- -## PhpSpecToPHPUnit - -### AddMockPropertiesRector - -Migrate PhpSpec behavior to PHPUnit test - -- class: [`Rector\PhpSpecToPHPUnit\Rector\Class_\AddMockPropertiesRector`](../rules/PhpSpecToPHPUnit/Rector/Class_/AddMockPropertiesRector.php) - -```diff - namespace spec\SomeNamespaceForThisTest; - --use PhpSpec\ObjectBehavior; -- - class OrderSpec extends ObjectBehavior - { -- public function let(OrderFactory $factory, ShippingMethod $shippingMethod): void -+ /** -+ * @var \SomeNamespaceForThisTest\Order -+ */ -+ private $order; -+ protected function setUp() - { -- $factory->createShippingMethodFor(Argument::any())->shouldBeCalled()->willReturn($shippingMethod); -+ /** @var OrderFactory|\PHPUnit\Framework\MockObject\MockObject $factory */ -+ $factory = $this->createMock(OrderFactory::class); -+ -+ /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ -+ $shippingMethod = $this->createMock(ShippingMethod::class); -+ -+ $factory->expects($this->once())->method('createShippingMethodFor')->willReturn($shippingMethod); - } - } -``` - -
- -### MockVariableToPropertyFetchRector - -Migrate PhpSpec behavior to PHPUnit test - -- class: [`Rector\PhpSpecToPHPUnit\Rector\Variable\MockVariableToPropertyFetchRector`](../rules/PhpSpecToPHPUnit/Rector/Variable/MockVariableToPropertyFetchRector.php) - -```diff - namespace spec\SomeNamespaceForThisTest; - --use PhpSpec\ObjectBehavior; -- - class OrderSpec extends ObjectBehavior - { -- public function let(OrderFactory $factory, ShippingMethod $shippingMethod): void -+ /** -+ * @var \SomeNamespaceForThisTest\Order -+ */ -+ private $order; -+ protected function setUp() - { -- $factory->createShippingMethodFor(Argument::any())->shouldBeCalled()->willReturn($shippingMethod); -+ /** @var OrderFactory|\PHPUnit\Framework\MockObject\MockObject $factory */ -+ $factory = $this->createMock(OrderFactory::class); -+ -+ /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ -+ $shippingMethod = $this->createMock(ShippingMethod::class); -+ -+ $factory->expects($this->once())->method('createShippingMethodFor')->willReturn($shippingMethod); - } - } -``` - -
- -### PhpSpecClassToPHPUnitClassRector - -Migrate PhpSpec behavior to PHPUnit test - -- class: [`Rector\PhpSpecToPHPUnit\Rector\Class_\PhpSpecClassToPHPUnitClassRector`](../rules/PhpSpecToPHPUnit/Rector/Class_/PhpSpecClassToPHPUnitClassRector.php) - -```diff - namespace spec\SomeNamespaceForThisTest; - --use PhpSpec\ObjectBehavior; -- - class OrderSpec extends ObjectBehavior - { -- public function let(OrderFactory $factory, ShippingMethod $shippingMethod): void -+ /** -+ * @var \SomeNamespaceForThisTest\Order -+ */ -+ private $order; -+ protected function setUp() - { -- $factory->createShippingMethodFor(Argument::any())->shouldBeCalled()->willReturn($shippingMethod); -+ /** @var OrderFactory|\PHPUnit\Framework\MockObject\MockObject $factory */ -+ $factory = $this->createMock(OrderFactory::class); -+ -+ /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ -+ $shippingMethod = $this->createMock(ShippingMethod::class); -+ -+ $factory->expects($this->once())->method('createShippingMethodFor')->willReturn($shippingMethod); - } - } -``` - -
- -### PhpSpecMethodToPHPUnitMethodRector - -Migrate PhpSpec behavior to PHPUnit test - -- class: [`Rector\PhpSpecToPHPUnit\Rector\ClassMethod\PhpSpecMethodToPHPUnitMethodRector`](../rules/PhpSpecToPHPUnit/Rector/ClassMethod/PhpSpecMethodToPHPUnitMethodRector.php) - -```diff - namespace spec\SomeNamespaceForThisTest; - --use PhpSpec\ObjectBehavior; -- - class OrderSpec extends ObjectBehavior - { -- public function let(OrderFactory $factory, ShippingMethod $shippingMethod): void -+ /** -+ * @var \SomeNamespaceForThisTest\Order -+ */ -+ private $order; -+ protected function setUp() - { -- $factory->createShippingMethodFor(Argument::any())->shouldBeCalled()->willReturn($shippingMethod); -+ /** @var OrderFactory|\PHPUnit\Framework\MockObject\MockObject $factory */ -+ $factory = $this->createMock(OrderFactory::class); -+ -+ /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ -+ $shippingMethod = $this->createMock(ShippingMethod::class); -+ -+ $factory->expects($this->once())->method('createShippingMethodFor')->willReturn($shippingMethod); - } - } -``` - -
- -### PhpSpecMocksToPHPUnitMocksRector - -Migrate PhpSpec behavior to PHPUnit test - -- class: [`Rector\PhpSpecToPHPUnit\Rector\MethodCall\PhpSpecMocksToPHPUnitMocksRector`](../rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecMocksToPHPUnitMocksRector.php) - -```diff - namespace spec\SomeNamespaceForThisTest; - --use PhpSpec\ObjectBehavior; -- - class OrderSpec extends ObjectBehavior - { -- public function let(OrderFactory $factory, ShippingMethod $shippingMethod): void -+ /** -+ * @var \SomeNamespaceForThisTest\Order -+ */ -+ private $order; -+ protected function setUp() - { -- $factory->createShippingMethodFor(Argument::any())->shouldBeCalled()->willReturn($shippingMethod); -+ /** @var OrderFactory|\PHPUnit\Framework\MockObject\MockObject $factory */ -+ $factory = $this->createMock(OrderFactory::class); -+ -+ /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ -+ $shippingMethod = $this->createMock(ShippingMethod::class); -+ -+ $factory->expects($this->once())->method('createShippingMethodFor')->willReturn($shippingMethod); - } - } -``` - -
- -### PhpSpecPromisesToPHPUnitAssertRector - -Migrate PhpSpec behavior to PHPUnit test - -- class: [`Rector\PhpSpecToPHPUnit\Rector\MethodCall\PhpSpecPromisesToPHPUnitAssertRector`](../rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecPromisesToPHPUnitAssertRector.php) - -```diff - namespace spec\SomeNamespaceForThisTest; - --use PhpSpec\ObjectBehavior; -- - class OrderSpec extends ObjectBehavior - { -- public function let(OrderFactory $factory, ShippingMethod $shippingMethod): void -+ /** -+ * @var \SomeNamespaceForThisTest\Order -+ */ -+ private $order; -+ protected function setUp() - { -- $factory->createShippingMethodFor(Argument::any())->shouldBeCalled()->willReturn($shippingMethod); -+ /** @var OrderFactory|\PHPUnit\Framework\MockObject\MockObject $factory */ -+ $factory = $this->createMock(OrderFactory::class); -+ -+ /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ -+ $shippingMethod = $this->createMock(ShippingMethod::class); -+ -+ $factory->expects($this->once())->method('createShippingMethodFor')->willReturn($shippingMethod); - } - } -``` - -
- -### RenameSpecFileToTestFileRector - -Rename "*Spec.php" file to "*Test.php" file - -- class: [`Rector\PhpSpecToPHPUnit\Rector\Class_\RenameSpecFileToTestFileRector`](../rules/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector.php) - -```diff --// tests/SomeSpec.php -+// tests/SomeTest.php -``` - -
- -## PostRector - -### ClassRenamingPostRector - -Rename references for classes that were renamed during Rector run - -- class: [`Rector\PostRector\Rector\ClassRenamingPostRector`](../packages/PostRector/Rector/ClassRenamingPostRector.php) - -```diff --function (OriginalClass $someClass) -+function (RenamedClass $someClass) - { - } -``` - -
- -### NameImportingPostRector - -Imports fully qualified names - -- class: [`Rector\PostRector\Rector\NameImportingPostRector`](../packages/PostRector/Rector/NameImportingPostRector.php) - -```diff -+use App\AnotherClass; -+ - class SomeClass - { -- public function run(App\AnotherClass $anotherClass) -+ public function run(AnotherClass $anotherClass) - { - } - } -``` - -
- -### NodeAddingPostRector - -Add nodes on weird positions - -- class: [`Rector\PostRector\Rector\NodeAddingPostRector`](../packages/PostRector/Rector/NodeAddingPostRector.php) - -```diff - class SomeClass - { - public function run($value) - { -- return 1; -+ if ($value) { -+ return 1; -+ } - } - } -``` - -
- -### NodeRemovingPostRector - -Remove nodes from weird positions - -- class: [`Rector\PostRector\Rector\NodeRemovingPostRector`](../packages/PostRector/Rector/NodeRemovingPostRector.php) - -```diff - class SomeClass - { - public function run($value) - { -- if ($value) { -- return 1; -- } -+ return 1; - } - } -``` - -
- -### NodeToReplacePostRector - -Replaces nodes on weird positions - -- class: [`Rector\PostRector\Rector\NodeToReplacePostRector`](../packages/PostRector/Rector/NodeToReplacePostRector.php) - -```diff - class SomeClass - { - public function run($value) - { -- return 1; -+ return $value; - } - } -``` - -
- -### PropertyAddingPostRector - -Add dependency properties - -- class: [`Rector\PostRector\Rector\PropertyAddingPostRector`](../packages/PostRector/Rector/PropertyAddingPostRector.php) - -```diff - class SomeClass - { -+ private $value; - public function run() - { - return $this->value; - } - } -``` - -
- -### UseAddingPostRector - -Add unique use imports collected during Rector run - -- class: [`Rector\PostRector\Rector\UseAddingPostRector`](../packages/PostRector/Rector/UseAddingPostRector.php) - -```diff -+use App\AnotherClass; -+ - class SomeClass - { - public function run(AnotherClass $anotherClass) - { - } - } -``` - -
- -## Privatization - -### ChangeGlobalVariablesToPropertiesRector - -Change global `$variables` to private properties - -- class: [`Rector\Privatization\Rector\ClassMethod\ChangeGlobalVariablesToPropertiesRector`](../rules/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector.php) - -```diff - class SomeClass - { -+ private $variable; - public function go() - { -- global $variable; -- $variable = 5; -+ $this->variable = 5; - } - - public function run() - { -- global $variable; -- var_dump($variable); -+ var_dump($this->variable); - } - } -``` - -
- -### ChangeLocalPropertyToVariableRector - -Change local property used in single method to local variable - -- class: [`Rector\Privatization\Rector\Class_\ChangeLocalPropertyToVariableRector`](../rules/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector.php) - -```diff - class SomeClass - { -- private $count; - public function run() - { -- $this->count = 5; -- return $this->count; -+ $count = 5; -+ return $count; - } - } -``` - -
- -### ChangeReadOnlyPropertyWithDefaultValueToConstantRector - -Change property with read only status with default value to constant - -- class: [`Rector\Privatization\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector`](../rules/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector.php) - -```diff - class SomeClass - { - /** - * @var string[] - */ -- private $magicMethods = [ -+ private const MAGIC_METHODS = [ - '__toString', - '__wakeup', - ]; - - public function run() - { -- foreach ($this->magicMethods as $magicMethod) { -+ foreach (self::MAGIC_METHODS as $magicMethod) { - echo $magicMethod; - } - } - } -``` - -
- -### ChangeReadOnlyVariableWithDefaultValueToConstantRector - -Change variable with read only status with default value to constant - -- class: [`Rector\Privatization\Rector\Class_\ChangeReadOnlyVariableWithDefaultValueToConstantRector`](../rules/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector.php) - -```diff - class SomeClass - { -+ /** -+ * @var string[] -+ */ -+ private const REPLACEMENTS = [ -+ 'PHPUnit\Framework\TestCase\Notice' => 'expectNotice', -+ 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', -+ ]; -+ - public function run() - { -- $replacements = [ -- 'PHPUnit\Framework\TestCase\Notice' => 'expectNotice', -- 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', -- ]; -- -- foreach ($replacements as $class => $method) { -+ foreach (self::REPLACEMENTS as $class => $method) { - } - } - } -``` - -
- -### FinalizeClassesWithoutChildrenRector - -Finalize every class that has no children - -- class: [`Rector\Privatization\Rector\Class_\FinalizeClassesWithoutChildrenRector`](../rules/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector.php) - -```diff --class FirstClass -+final class FirstClass - { - } - - class SecondClass - { - } - --class ThirdClass extends SecondClass -+final class ThirdClass extends SecondClass - { - } -``` - -
- -### PrivatizeFinalClassMethodRector - -Change protected class method to private if possible - -- class: [`Rector\Privatization\Rector\ClassMethod\PrivatizeFinalClassMethodRector`](../rules/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector.php) - -```diff - final class SomeClass - { -- protected function someMethod() -+ private function someMethod() - { - } - } -``` - -
- -### PrivatizeFinalClassPropertyRector - -Change property to private if possible - -- class: [`Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector`](../rules/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector.php) - -```diff - final class SomeClass - { -- protected $value; -+ private $value; - } -``` - -
- -### PrivatizeLocalGetterToPropertyRector - -Privatize getter of local property to property - -- class: [`Rector\Privatization\Rector\MethodCall\PrivatizeLocalGetterToPropertyRector`](../rules/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector.php) - -```diff - class SomeClass - { - private $some; - - public function run() - { -- return $this->getSome() + 5; -+ return $this->some + 5; - } - - private function getSome() - { - return $this->some; - } - } -``` - -
- -### RepeatedLiteralToClassConstantRector - -Replace repeated strings with constant - -- class: [`Rector\Privatization\Rector\Class_\RepeatedLiteralToClassConstantRector`](../rules/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector.php) - -```diff - class SomeClass - { -+ /** -+ * @var string -+ */ -+ private const REQUIRES = 'requires'; - public function run($key, $items) - { -- if ($key === 'requires') { -- return $items['requires']; -+ if ($key === self::REQUIRES) { -+ return $items[self::REQUIRES]; - } - } - } -``` - -
- -### ReplaceStringWithClassConstantRector - -Replace string values in specific method call by constant of provided class - -:wrench: **configure it!** - -- class: [`Rector\Privatization\Rector\MethodCall\ReplaceStringWithClassConstantRector`](../rules/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector.php) - -```php -use Rector\Privatization\Rector\MethodCall\ReplaceStringWithClassConstantRector; -use Rector\Privatization\ValueObject\ReplaceStringWithClassConstant; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ReplaceStringWithClassConstantRector::class) - ->call('configure', [[ - ReplaceStringWithClassConstantRector::REPLACE_STRING_WITH_CLASS_CONSTANT => ValueObjectInliner::inline([ - new ReplaceStringWithClassConstant('SomeClass', 'call', 0, 'Placeholder'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- $this->call('name'); -+ $this->call(Placeholder::NAME); - } - } -``` - -
- -## Removing - -### ArgumentRemoverRector - -Removes defined arguments in defined methods and their calls. - -:wrench: **configure it!** - -- class: [`Rector\Removing\Rector\ClassMethod\ArgumentRemoverRector`](../rules/Removing/Rector/ClassMethod/ArgumentRemoverRector.php) - -```php -use Rector\Removing\Rector\ClassMethod\ArgumentRemoverRector; -use Rector\Removing\ValueObject\ArgumentRemover; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ArgumentRemoverRector::class) - ->call('configure', [[ - ArgumentRemoverRector::REMOVED_ARGUMENTS => ValueObjectInliner::inline([ - new ArgumentRemover('ExampleClass', 'someMethod', 0, [true]), ] - ), - ]]); -}; -``` - -↓ - -```diff - $someObject = new SomeClass; --$someObject->someMethod(true); -+$someObject->someMethod(); -``` - -
- -### RemoveFuncCallArgRector - -Remove argument by position by function name - -:wrench: **configure it!** - -- class: [`Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector`](../rules/Removing/Rector/FuncCall/RemoveFuncCallArgRector.php) - -```php -use Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector; -use Rector\Removing\ValueObject\RemoveFuncCallArg; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RemoveFuncCallArgRector::class) - ->call('configure', [[ - RemoveFuncCallArgRector::REMOVED_FUNCTION_ARGUMENTS => ValueObjectInliner::inline([ - new RemoveFuncCallArg('remove_last_arg', 1), - ]), - ]]); -}; -``` - -↓ - -```diff --remove_last_arg(1, 2); -+remove_last_arg(1); -``` - -
- -### RemoveFuncCallRector - -Remove ini_get by configuration - -:wrench: **configure it!** - -- class: [`Rector\Removing\Rector\FuncCall\RemoveFuncCallRector`](../rules/Removing/Rector/FuncCall/RemoveFuncCallRector.php) - -```php -use Rector\Removing\Rector\FuncCall\RemoveFuncCallRector; -use Rector\Removing\ValueObject\RemoveFuncCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RemoveFuncCallRector::class) - ->call('configure', [[ - RemoveFuncCallRector::REMOVE_FUNC_CALLS => ValueObjectInliner::inline([ - new RemoveFuncCall('ini_get', [['y2k_compliance']]), ] - ), - ]]); -}; -``` - -↓ - -```diff --ini_get('y2k_compliance'); - ini_get('keep_me'); -``` - -
- -### RemoveInterfacesRector - -Removes interfaces usage from class. - -:wrench: **configure it!** - -- class: [`Rector\Removing\Rector\Class_\RemoveInterfacesRector`](../rules/Removing/Rector/Class_/RemoveInterfacesRector.php) - -```php -use Rector\Removing\Rector\Class_\RemoveInterfacesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RemoveInterfacesRector::class) - ->call('configure', [[ - RemoveInterfacesRector::INTERFACES_TO_REMOVE => ['SomeInterface'], - ]]); -}; -``` - -↓ - -```diff --class SomeClass implements SomeInterface -+class SomeClass - { - } -``` - -
- -### RemoveParentRector - -Removes extends class by name - -:wrench: **configure it!** - -- class: [`Rector\Removing\Rector\Class_\RemoveParentRector`](../rules/Removing/Rector/Class_/RemoveParentRector.php) - -```php -use Rector\Removing\Rector\Class_\RemoveParentRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RemoveParentRector::class) - ->call('configure', [[ - RemoveParentRector::PARENT_TYPES_TO_REMOVE => ['SomeParentClass'], - ]]); -}; -``` - -↓ - -```diff --final class SomeClass extends SomeParentClass -+final class SomeClass - { - } -``` - -
- -### RemoveTraitUseRector - -Remove specific traits from code - -:wrench: **configure it!** - -- class: [`Rector\Removing\Rector\Class_\RemoveTraitUseRector`](../rules/Removing/Rector/Class_/RemoveTraitUseRector.php) - -```php -use Rector\Removing\Rector\Class_\RemoveTraitUseRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RemoveTraitUseRector::class) - ->call('configure', [[ - RemoveTraitUseRector::TRAITS_TO_REMOVE => ['TraitNameToRemove'], - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -- use SomeTrait; - } -``` - -
- -## RemovingStatic - -### DesiredClassTypeToDynamicRector - -Change full static service, to dynamic one - -- class: [`Rector\RemovingStatic\Rector\Class_\DesiredClassTypeToDynamicRector`](../rules/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector.php) - -```diff - class AnotherClass - { -+ /** -+ * @var SomeClass -+ */ -+ private $someClass; -+ -+ public fuction __construct(SomeClass $someClass) -+ { -+ $this->someClass = $someClass; -+ } -+ - public function run() - { - SomeClass::someStatic(); - } - } - - class SomeClass - { -- public static function run() -+ public function run() - { -- self::someStatic(); -+ $this->someStatic(); - } - -- private static function someStatic() -+ private function someStatic() - { - } - } -``` - -
- -### DesiredPropertyClassMethodTypeToDynamicRector - -Change defined static properties and methods to dynamic - -- class: [`Rector\RemovingStatic\Rector\Property\DesiredPropertyClassMethodTypeToDynamicRector`](../rules/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector.php) - -```diff - final class SomeClass - { -- public static $name; -+ public $name; - -- public static function go() -+ public function go() - { - } - } -``` - -
- -### DesiredStaticCallTypeToDynamicRector - -Change defined static service to dynamic one - -- class: [`Rector\RemovingStatic\Rector\StaticCall\DesiredStaticCallTypeToDynamicRector`](../rules/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector.php) - -```diff - final class SomeClass - { - public function run() - { -- SomeStaticMethod::someStatic(); -+ $this->someStaticMethod->someStatic(); - } - } -``` - -
- -### DesiredStaticPropertyFetchTypeToDynamicRector - -Change defined static service to dynamic one - -- class: [`Rector\RemovingStatic\Rector\StaticPropertyFetch\DesiredStaticPropertyFetchTypeToDynamicRector`](../rules/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector.php) - -```diff - final class SomeClass - { - public function run() - { -- SomeStaticMethod::$someStatic; -+ $this->someStaticMethod->someStatic; - } - } -``` - -
- -### LocallyCalledStaticMethodToNonStaticRector - -Change static method and local-only calls to non-static - -- class: [`Rector\RemovingStatic\Rector\ClassMethod\LocallyCalledStaticMethodToNonStaticRector`](../rules/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector.php) - -```diff - class SomeClass - { - public function run() - { -- self::someStatic(); -+ $this->someStatic(); - } - -- private static function someStatic() -+ private function someStatic() - { - } - } -``` - -
- -### NewUniqueObjectToEntityFactoryRector - -Convert new X to new factories - -:wrench: **configure it!** - -- class: [`Rector\RemovingStatic\Rector\Class_\NewUniqueObjectToEntityFactoryRector`](../rules/RemovingStatic/Rector/Class_/NewUniqueObjectToEntityFactoryRector.php) - -```php -use Rector\RemovingStatic\Rector\Class_\NewUniqueObjectToEntityFactoryRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(NewUniqueObjectToEntityFactoryRector::class) - ->call('configure', [[ - NewUniqueObjectToEntityFactoryRector::TYPES_TO_SERVICES => ['ClassName'], - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -+ public function __construct(AnotherClassFactory $anotherClassFactory) -+ { -+ $this->anotherClassFactory = $anotherClassFactory; -+ } -+ - public function run() - { -- return new AnotherClass; -+ return $this->anotherClassFactory->create(); - } - } - - class AnotherClass - { - public function someFun() - { - return StaticClass::staticMethod(); - } - } -``` - -
- -### PassFactoryToUniqueObjectRector - -Convert new `X/Static::call()` to factories in entities, pass them via constructor to each other - -:wrench: **configure it!** - -- class: [`Rector\RemovingStatic\Rector\Class_\PassFactoryToUniqueObjectRector`](../rules/RemovingStatic/Rector/Class_/PassFactoryToUniqueObjectRector.php) - -```php -use Rector\RemovingStatic\Rector\Class_\PassFactoryToUniqueObjectRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(PassFactoryToUniqueObjectRector::class) - ->call('configure', [[ - PassFactoryToUniqueObjectRector::TYPES_TO_SERVICES => ['StaticClass'], - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -+ public function __construct(AnotherClassFactory $anotherClassFactory) -+ { -+ $this->anotherClassFactory = $anotherClassFactory; -+ } -+ - public function run() - { -- return new AnotherClass; -+ return $this->anotherClassFactory->create(); - } - } - - class AnotherClass - { -+ public function __construct(StaticClass $staticClass) -+ { -+ $this->staticClass = $staticClass; -+ } -+ - public function someFun() - { -- return StaticClass::staticMethod(); -+ return $this->staticClass->staticMethod(); -+ } -+} -+ -+final class AnotherClassFactory -+{ -+ /** -+ * @var StaticClass -+ */ -+ private $staticClass; -+ -+ public function __construct(StaticClass $staticClass) -+ { -+ $this->staticClass = $staticClass; -+ } -+ -+ public function create(): AnotherClass -+ { -+ return new AnotherClass($this->staticClass); - } - } -``` - -
- -### StaticTypeToSetterInjectionRector - -Changes types to setter injection - -:wrench: **configure it!** - -- class: [`Rector\RemovingStatic\Rector\Class_\StaticTypeToSetterInjectionRector`](../rules/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector.php) - -```php -use Rector\RemovingStatic\Rector\Class_\StaticTypeToSetterInjectionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(StaticTypeToSetterInjectionRector::class) - ->call('configure', [[ - StaticTypeToSetterInjectionRector::STATIC_TYPES => ['SomeStaticClass'], - ]]); -}; -``` - -↓ - -```diff - final class CheckoutEntityFactory - { -+ /** -+ * @var SomeStaticClass -+ */ -+ private $someStaticClass; -+ -+ public function setSomeStaticClass(SomeStaticClass $someStaticClass) -+ { -+ $this->someStaticClass = $someStaticClass; -+ } -+ - public function run() - { -- return SomeStaticClass::go(); -+ return $this->someStaticClass->go(); - } - } -``` - -
- -## Renaming - -### PseudoNamespaceToNamespaceRector - -Replaces defined Pseudo_Namespaces by Namespace\Ones. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\FileWithoutNamespace\PseudoNamespaceToNamespaceRector`](../rules/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector.php) - -```php -use Rector\Renaming\Rector\FileWithoutNamespace\PseudoNamespaceToNamespaceRector; -use Rector\Renaming\ValueObject\PseudoNamespaceToNamespace; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(PseudoNamespaceToNamespaceRector::class) - ->call('configure', [[ - PseudoNamespaceToNamespaceRector::NAMESPACE_PREFIXES_WITH_EXCLUDED_CLASSES => ValueObjectInliner::inline([ - new PseudoNamespaceToNamespace('Some_', ['Some_Class_To_Keep']), ] - ), - ]]); -}; -``` - -↓ - -```diff --/** @var Some_Chicken $someService */ --$someService = new Some_Chicken; -+/** @var Some\Chicken $someService */ -+$someService = new Some\Chicken; - $someClassToKeep = new Some_Class_To_Keep; -``` - -
- -### RenameAnnotationRector - -Turns defined annotations above properties and methods to their new values. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\ClassMethod\RenameAnnotationRector`](../rules/Renaming/Rector/ClassMethod/RenameAnnotationRector.php) - -```php -use Rector\Renaming\Rector\ClassMethod\RenameAnnotationRector; -use Rector\Renaming\ValueObject\RenameAnnotation; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameAnnotationRector::class) - ->call('configure', [[ - RenameAnnotationRector::RENAMED_ANNOTATIONS_IN_TYPES => ValueObjectInliner::inline([ - new RenameAnnotation('PHPUnit\Framework\TestCase', 'test', 'scenario'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeTest extends PHPUnit\Framework\TestCase - { - /** -- * @test -+ * @scenario - */ - public function someMethod() - { - } - } -``` - -
- -### RenameClassConstFetchRector - -Replaces defined class constants in their calls. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector`](../rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php) - -```php -use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector; -use Rector\Renaming\ValueObject\RenameClassAndConstFetch; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameClassConstFetchRector::class) - ->call('configure', [[ - RenameClassConstFetchRector::CLASS_CONSTANT_RENAME => ValueObjectInliner::inline([ - new RenameClassAndConstFetch('SomeClass', 'OTHER_OLD_CONSTANT', 'DifferentClass', 'NEW_CONSTANT'), - ]), - ]]); -}; -``` - -↓ - -```diff --$value = SomeClass::OLD_CONSTANT; --$value = SomeClass::OTHER_OLD_CONSTANT; -+$value = SomeClass::NEW_CONSTANT; -+$value = DifferentClass::NEW_CONSTANT; -``` - -
- -### RenameClassRector - -Replaces defined classes by new ones. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\Name\RenameClassRector`](../rules/Renaming/Rector/Name/RenameClassRector.php) - -```php -use Rector\Renaming\Rector\Name\RenameClassRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'App\SomeOldClass' => 'App\SomeNewClass', - - ], ]]); -}; -``` - -↓ - -```diff - namespace App; - --use SomeOldClass; -+use SomeNewClass; - --function someFunction(SomeOldClass $someOldClass): SomeOldClass -+function someFunction(SomeNewClass $someOldClass): SomeNewClass - { -- if ($someOldClass instanceof SomeOldClass) { -- return new SomeOldClass; -+ if ($someOldClass instanceof SomeNewClass) { -+ return new SomeNewClass; - } - } -``` - -
- -### RenameConstantRector - -Replace constant by new ones - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\ConstFetch\RenameConstantRector`](../rules/Renaming/Rector/ConstFetch/RenameConstantRector.php) - -```php -use Rector\Renaming\Rector\ConstFetch\RenameConstantRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameConstantRector::class) - ->call('configure', [[ - RenameConstantRector::OLD_TO_NEW_CONSTANTS => [ - 'MYSQL_ASSOC' => 'MYSQLI_ASSOC', - 'OLD_CONSTANT' => 'NEW_CONSTANT', - - ], ]]); -}; -``` - -↓ - -```diff - final class SomeClass - { - public function run() - { -- return MYSQL_ASSOC; -+ return MYSQLI_ASSOC; - } - } -``` - -
- -### RenameFunctionRector - -Turns defined function call new one. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\FuncCall\RenameFunctionRector`](../rules/Renaming/Rector/FuncCall/RenameFunctionRector.php) - -```php -use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'view' => 'Laravel\Templating\render', - - ], ]]); -}; -``` - -↓ - -```diff --view("...", []); -+Laravel\Templating\render("...", []); -``` - -
- -### RenameMethodRector - -Turns method names to new ones. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\MethodCall\RenameMethodRector`](../rules/Renaming/Rector/MethodCall/RenameMethodRector.php) - -```php -use Rector\Renaming\Rector\MethodCall\RenameMethodRector; -use Rector\Renaming\ValueObject\MethodCallRename; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - new MethodCallRename('SomeExampleClass', 'oldMethod', 'newMethod'), - ]), - ]]); -}; -``` - -↓ - -```diff - $someObject = new SomeExampleClass; --$someObject->oldMethod(); -+$someObject->newMethod(); -``` - -
- -### RenameNamespaceRector - -Replaces old namespace by new one. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\Namespace_\RenameNamespaceRector`](../rules/Renaming/Rector/Namespace_/RenameNamespaceRector.php) - -```php -use Rector\Renaming\Rector\Namespace_\RenameNamespaceRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameNamespaceRector::class) - ->call('configure', [[ - RenameNamespaceRector::OLD_TO_NEW_NAMESPACES => [ - 'SomeOldNamespace' => 'SomeNewNamespace', - - ], ]]); -}; -``` - -↓ - -```diff --$someObject = new SomeOldNamespace\SomeClass; -+$someObject = new SomeNewNamespace\SomeClass; -``` - -
- -### RenamePropertyRector - -Replaces defined old properties by new ones. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\PropertyFetch\RenamePropertyRector`](../rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php) - -```php -use Rector\Renaming\Rector\PropertyFetch\RenamePropertyRector; -use Rector\Renaming\ValueObject\RenameProperty; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenamePropertyRector::class) - ->call('configure', [[ - RenamePropertyRector::RENAMED_PROPERTIES => ValueObjectInliner::inline([ - new RenameProperty('SomeClass', 'someOldProperty', 'someNewProperty'), - ]), - ]]); -}; -``` - -↓ - -```diff --$someObject->someOldProperty; -+$someObject->someNewProperty; -``` - -
- -### RenameStaticMethodRector - -Turns method names to new ones. - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector`](../rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php) - -```php -use Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector; -use Rector\Renaming\ValueObject\RenameStaticMethod; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameStaticMethodRector::class) - ->call('configure', [[ - RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => ValueObjectInliner::inline([ - new RenameStaticMethod('SomeClass', 'oldMethod', 'AnotherExampleClass', 'newStaticMethod'), - ]), - ]]); -}; -``` - -↓ - -```diff --SomeClass::oldStaticMethod(); -+AnotherExampleClass::newStaticMethod(); -``` - -
- -```php -use Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector; -use Rector\Renaming\ValueObject\RenameStaticMethod; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameStaticMethodRector::class) - ->call('configure', [[ - RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => ValueObjectInliner::inline([ - new RenameStaticMethod('SomeClass', 'oldMethod', 'SomeClass', 'newStaticMethod'), - ]), - ]]); -}; -``` - -↓ - -```diff --SomeClass::oldStaticMethod(); -+SomeClass::newStaticMethod(); -``` - -
- -### RenameStringRector - -Change string value - -:wrench: **configure it!** - -- class: [`Rector\Renaming\Rector\String_\RenameStringRector`](../rules/Renaming/Rector/String_/RenameStringRector.php) - -```php -use Rector\Renaming\Rector\String_\RenameStringRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(RenameStringRector::class) - ->call('configure', [[ - RenameStringRector::STRING_CHANGES => [ - 'ROLE_PREVIOUS_ADMIN' => 'IS_IMPERSONATOR', - - ], ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- return 'ROLE_PREVIOUS_ADMIN'; -+ return 'IS_IMPERSONATOR'; - } - } -``` - -
- -## Restoration - -### CompleteImportForPartialAnnotationRector - -In case you have accidentally removed use imports but code still contains partial use statements, this will save you - -:wrench: **configure it!** - -- class: [`Rector\Restoration\Rector\Namespace_\CompleteImportForPartialAnnotationRector`](../rules/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector.php) - -```php -use Rector\Restoration\Rector\Namespace_\CompleteImportForPartialAnnotationRector; -use Rector\Restoration\ValueObject\CompleteImportForPartialAnnotation; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(CompleteImportForPartialAnnotationRector::class) - ->call('configure', [[ - CompleteImportForPartialAnnotationRector::USE_IMPORTS_TO_RESTORE => ValueObjectInliner::inline([ - new CompleteImportForPartialAnnotation('Doctrine\ORM\Mapping', 'ORM'), - ]), - ]]); -}; -``` - -↓ - -```diff -+use Doctrine\ORM\Mapping as ORM; -+ - class SomeClass - { - /** - * @ORM\Id - */ - public $id; - } -``` - -
- -### InferParamFromClassMethodReturnRector - -Change `@param` doc based on another method return type - -:wrench: **configure it!** - -- class: [`Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector`](../rules/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector.php) - -```php -use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector; -use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(InferParamFromClassMethodReturnRector::class) - ->call('configure', [[ - InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => ValueObjectInliner::inline([ - new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function getNodeTypes(): array - { - return [String_::class]; - } - -+ /** -+ * @param String_ $node -+ */ - public function process(Node $node) - { - } - } -``` - -
- -### MakeTypedPropertyNullableIfCheckedRector - -Make typed property nullable if checked - -- class: [`Rector\Restoration\Rector\Property\MakeTypedPropertyNullableIfCheckedRector`](../rules/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector.php) - -```diff - final class SomeClass - { -- private AnotherClass $anotherClass; -+ private ?AnotherClass $anotherClass = null; - - public function run() - { - if ($this->anotherClass === null) { - $this->anotherClass = new AnotherClass; - } - } - } -``` - -
- -### MissingClassConstantReferenceToStringRector - -Convert missing class reference to string - -- class: [`Rector\Restoration\Rector\ClassConstFetch\MissingClassConstantReferenceToStringRector`](../rules/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector.php) - -```diff - class SomeClass - { - public function run() - { -- return NonExistingClass::class; -+ return 'NonExistingClass'; - } - } -``` - -
- -### RemoveFinalFromEntityRector - -Remove final from Doctrine entities - -- class: [`Rector\Restoration\Rector\Class_\RemoveFinalFromEntityRector`](../rules/Restoration/Rector/Class_/RemoveFinalFromEntityRector.php) - -```diff - use Doctrine\ORM\Mapping as ORM; - - /** - * @ORM\Entity - */ --final class SomeClass -+class SomeClass - { - } -``` - -
- -### UpdateFileNameByClassNameFileSystemRector - -Rename file to respect class name - -- class: [`Rector\Restoration\Rector\ClassLike\UpdateFileNameByClassNameFileSystemRector`](../rules/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector.php) - -```diff --// app/SomeClass.php -+// app/AnotherClass.php - class AnotherClass - { - } -``` - -
- -## Transform - -### AddInterfaceByParentRector - -Add interface by parent - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Class_\AddInterfaceByParentRector`](../rules/Transform/Rector/Class_/AddInterfaceByParentRector.php) - -```php -use Rector\Transform\Rector\Class_\AddInterfaceByParentRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddInterfaceByParentRector::class) - ->call('configure', [[ - AddInterfaceByParentRector::INTERFACE_BY_PARENT => [ - 'SomeParent' => 'SomeInterface', - - ], ]]); -}; -``` - -↓ - -```diff --class SomeClass extends SomeParent -+class SomeClass extends SomeParent implements SomeInterface - { - - } -``` - -
- -### AddInterfaceByTraitRector - -Add interface by used trait - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Class_\AddInterfaceByTraitRector`](../rules/Transform/Rector/Class_/AddInterfaceByTraitRector.php) - -```php -use Rector\Transform\Rector\Class_\AddInterfaceByTraitRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddInterfaceByTraitRector::class) - ->call('configure', [[ - AddInterfaceByTraitRector::INTERFACE_BY_TRAIT => [ - 'SomeTrait' => 'SomeInterface', - - ], ]]); -}; -``` - -↓ - -```diff --class SomeClass -+class SomeClass implements SomeInterface - { - use SomeTrait; - } -``` - -
- -### ArgumentFuncCallToMethodCallRector - -Move help facade-like function calls to constructor injection - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\FuncCall\ArgumentFuncCallToMethodCallRector`](../rules/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector.php) - -```php -use Rector\Transform\Rector\FuncCall\ArgumentFuncCallToMethodCallRector; -use Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ArgumentFuncCallToMethodCallRector::class) - ->call('configure', [[ - ArgumentFuncCallToMethodCallRector::FUNCTIONS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new ArgumentFuncCallToMethodCall('view', 'Illuminate\Contracts\View\Factory', 'make', null), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeController - { -+ /** -+ * @var \Illuminate\Contracts\View\Factory -+ */ -+ private $viewFactory; -+ -+ public function __construct(\Illuminate\Contracts\View\Factory $viewFactory) -+ { -+ $this->viewFactory = $viewFactory; -+ } -+ - public function action() - { -- $template = view('template.blade'); -- $viewFactory = view(); -+ $template = $this->viewFactory->make('template.blade'); -+ $viewFactory = $this->viewFactory; - } - } -``` - -
- -### CallableInMethodCallToVariableRector - -Change a callable in method call to standalone variable assign - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\MethodCall\CallableInMethodCallToVariableRector`](../rules/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector.php) - -```php -use Rector\Transform\Rector\MethodCall\CallableInMethodCallToVariableRector; -use Rector\Transform\ValueObject\CallableInMethodCallToVariable; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(CallableInMethodCallToVariableRector::class) - ->call('configure', [[ - CallableInMethodCallToVariableRector::CALLABLE_IN_METHOD_CALL_TO_VARIABLE => ValueObjectInliner::inline([ - new CallableInMethodCallToVariable('Nette\Caching\Cache', 'save', 1), - ]), - ]]); -}; -``` - -↓ - -```diff - final class SomeClass - { - public function run() - { - /** @var \Nette\Caching\Cache $cache */ -- $cache->save($key, function () use ($container) { -- return 100; -- }); -+ $result = 100; -+ $cache->save($key, $result); - } - } -``` - -
- -### ChangeSingletonToServiceRector - -Change singleton class to normal class that can be registered as a service - -- class: [`Rector\Transform\Rector\Class_\ChangeSingletonToServiceRector`](../rules/Transform/Rector/Class_/ChangeSingletonToServiceRector.php) - -```diff - class SomeClass - { -- private static $instance; -- -- private function __construct() -+ public function __construct() - { -- } -- -- public static function getInstance() -- { -- if (null === static::$instance) { -- static::$instance = new static(); -- } -- -- return static::$instance; - } - } -``` - -
- -### ClassConstFetchToValueRector - -Replaces constant by value - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\ClassConstFetch\ClassConstFetchToValueRector`](../rules/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector.php) - -```php -use Rector\Transform\Rector\ClassConstFetch\ClassConstFetchToValueRector; -use Rector\Transform\ValueObject\ClassConstFetchToValue; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ClassConstFetchToValueRector::class) - ->call('configure', [[ - ClassConstFetchToValueRector::CLASS_CONST_FETCHES_TO_VALUES => ValueObjectInliner::inline([ - new ClassConstFetchToValue('Nette\Configurator', 'PRODUCTION', 'production'), - ]), - ]]); -}; -``` - -↓ - -```diff --$value === Nette\Configurator::DEVELOPMENT -+$value === "development" -``` - -
- -### DimFetchAssignToMethodCallRector - -Change magic array access add to `$list[],` to explicit `$list->addMethod(...)` - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Assign\DimFetchAssignToMethodCallRector`](../rules/Transform/Rector/Assign/DimFetchAssignToMethodCallRector.php) - -```php -use Rector\Transform\Rector\Assign\DimFetchAssignToMethodCallRector; -use Rector\Transform\ValueObject\DimFetchAssignToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(DimFetchAssignToMethodCallRector::class) - ->call('configure', [[ - DimFetchAssignToMethodCallRector::DIM_FETCH_ASSIGN_TO_METHOD_CALL => ValueObjectInliner::inline([ - new DimFetchAssignToMethodCall( - 'Nette\Application\Routers\RouteList', - 'Nette\Application\Routers\Route', - 'addRoute' - ), - ]), - ]]); -}; -``` - -↓ - -```diff --use Nette\Application\Routers\Route; - use Nette\Application\Routers\RouteList; - - class RouterFactory - { - public static function createRouter() - { - $routeList = new RouteList(); -- $routeList[] = new Route('...'); -+ $routeList->addRoute('...'); - } - } -``` - -
- -### FuncCallToConstFetchRector - -Changes use of function calls to use constants - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\FuncCall\FuncCallToConstFetchRector`](../rules/Transform/Rector/FuncCall/FuncCallToConstFetchRector.php) - -```php -use Rector\Transform\Rector\FuncCall\FuncCallToConstFetchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(FuncCallToConstFetchRector::class) - ->call('configure', [[ - FuncCallToConstFetchRector::FUNCTIONS_TO_CONSTANTS => [ - 'php_sapi_name' => 'PHP_SAPI', - - ], ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- $value = php_sapi_name(); -+ $value = PHP_SAPI; - } - } -``` - -
- -### FuncCallToMethodCallRector - -Turns defined function calls to local method calls. - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\FuncCall\FuncCallToMethodCallRector`](../rules/Transform/Rector/FuncCall/FuncCallToMethodCallRector.php) - -```php -use Rector\Transform\Rector\FuncCall\FuncCallToMethodCallRector; -use Rector\Transform\ValueObject\FuncCallToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(FuncCallToMethodCallRector::class) - ->call('configure', [[ - FuncCallToMethodCallRector::FUNC_CALL_TO_CLASS_METHOD_CALL => ValueObjectInliner::inline([ - new FuncCallToMethodCall('view', 'Namespaced\SomeRenderer', 'render'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -+ /** -+ * @var \Namespaced\SomeRenderer -+ */ -+ private $someRenderer; -+ -+ public function __construct(\Namespaced\SomeRenderer $someRenderer) -+ { -+ $this->someRenderer = $someRenderer; -+ } -+ - public function run() - { -- view('...'); -+ $this->someRenderer->view('...'); - } - } -``` - -
- -### FuncCallToNewRector - -Change configured function calls to new Instance - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\FuncCall\FuncCallToNewRector`](../rules/Transform/Rector/FuncCall/FuncCallToNewRector.php) - -```php -use Rector\Transform\Rector\FuncCall\FuncCallToNewRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(FuncCallToNewRector::class) - ->call('configure', [[ - FuncCallToNewRector::FUNCTIONS_TO_NEWS => [ - 'collection' => ['Collection'], - - ], ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- $array = collection([]); -+ $array = new \Collection([]); - } - } -``` - -
- -### FuncCallToStaticCallRector - -Turns defined function call to static method call. - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\FuncCall\FuncCallToStaticCallRector`](../rules/Transform/Rector/FuncCall/FuncCallToStaticCallRector.php) - -```php -use Rector\Transform\Rector\FuncCall\FuncCallToStaticCallRector; -use Rector\Transform\ValueObject\FuncCallToStaticCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(FuncCallToStaticCallRector::class) - ->call('configure', [[ - FuncCallToStaticCallRector::FUNC_CALLS_TO_STATIC_CALLS => ValueObjectInliner::inline([ - new FuncCallToStaticCall('view', 'SomeStaticClass', 'render'), - ]), - ]]); -}; -``` - -↓ - -```diff --view("...", []); -+SomeClass::render("...", []); -``` - -
- -### GetAndSetToMethodCallRector - -Turns defined `__get`/`__set` to specific method calls. - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Assign\GetAndSetToMethodCallRector`](../rules/Transform/Rector/Assign/GetAndSetToMethodCallRector.php) - -```php -use Rector\Transform\Rector\Assign\GetAndSetToMethodCallRector; -use Rector\Transform\ValueObject\GetAndSetToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(GetAndSetToMethodCallRector::class) - ->call('configure', [[ - GetAndSetToMethodCallRector::TYPE_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new GetAndSetToMethodCall('SomeContainer', 'addService', 'getService'), - ]), - ]]); -}; -``` - -↓ - -```diff - $container = new SomeContainer; --$container->someService = $someService; -+$container->setService("someService", $someService); -``` - -
- -### MergeInterfacesRector - -Merges old interface to a new one, that already has its methods - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Class_\MergeInterfacesRector`](../rules/Transform/Rector/Class_/MergeInterfacesRector.php) - -```php -use Rector\Transform\Rector\Class_\MergeInterfacesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(MergeInterfacesRector::class) - ->call('configure', [[ - MergeInterfacesRector::OLD_TO_NEW_INTERFACES => [ - 'SomeOldInterface' => 'SomeInterface', - - ], ]]); -}; -``` - -↓ - -```diff --class SomeClass implements SomeInterface, SomeOldInterface -+class SomeClass implements SomeInterface - { - } -``` - -
- -### MethodCallToAnotherMethodCallWithArgumentsRector - -Turns old method call with specific types to new one with arguments - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\MethodCall\MethodCallToAnotherMethodCallWithArgumentsRector`](../rules/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector.php) - -```php -use Rector\Transform\Rector\MethodCall\MethodCallToAnotherMethodCallWithArgumentsRector; -use Rector\Transform\ValueObject\MethodCallToAnotherMethodCallWithArguments; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(MethodCallToAnotherMethodCallWithArgumentsRector::class) - ->call('configure', [[ - MethodCallToAnotherMethodCallWithArgumentsRector::METHOD_CALL_RENAMES_WITH_ADDED_ARGUMENTS => ValueObjectInliner::inline([ - new MethodCallToAnotherMethodCallWithArguments('Nette\DI\ServiceDefinition', 'setInject', 'addTag', [ - 'inject', - ]), ] - ), - ]]); -}; -``` - -↓ - -```diff - $serviceDefinition = new Nette\DI\ServiceDefinition; --$serviceDefinition->setInject(); -+$serviceDefinition->addTag('inject'); -``` - -
- -### MethodCallToMethodCallRector - -Change method one method from one service to a method call to in another service - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\MethodCall\MethodCallToMethodCallRector`](../rules/Transform/Rector/MethodCall/MethodCallToMethodCallRector.php) - -```php -use Rector\Transform\Rector\MethodCall\MethodCallToMethodCallRector; -use Rector\Transform\ValueObject\MethodCallToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(MethodCallToMethodCallRector::class) - ->call('configure', [[ - MethodCallToMethodCallRector::METHOD_CALLS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new MethodCallToMethodCall('FirstDependency', 'go', 'SecondDependency', 'away'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function __construct( -- private FirstDependency $firstDependency -+ private SecondDependency $secondDependency - ) { - } - - public function run() - { -- $this->firstDependency->go(); -+ $this->secondDependency->away(); - } - } -``` - -
- -### MethodCallToPropertyFetchRector - -Turns method call `"$this->something()"` to property fetch "$this->something" - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector`](../rules/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector.php) - -```php -use Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(MethodCallToPropertyFetchRector::class) - ->call('configure', [[ - MethodCallToPropertyFetchRector::METHOD_CALL_TO_PROPERTY_FETCHES => [ - 'someMethod' => 'someProperty', - - ], ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- $this->someMethod(); -+ $this->someProperty; - } - } -``` - -
- -### MethodCallToStaticCallRector - -Change method call to desired static call - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\MethodCall\MethodCallToStaticCallRector`](../rules/Transform/Rector/MethodCall/MethodCallToStaticCallRector.php) - -```php -use Rector\Transform\Rector\MethodCall\MethodCallToStaticCallRector; -use Rector\Transform\ValueObject\MethodCallToStaticCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(MethodCallToStaticCallRector::class) - ->call('configure', [[ - MethodCallToStaticCallRector::METHOD_CALLS_TO_STATIC_CALLS => ValueObjectInliner::inline([ - new MethodCallToStaticCall('AnotherDependency', 'process', 'StaticCaller', 'anotherMethod'), - ]), - ]]); -}; -``` - -↓ - -```diff - final class SomeClass - { - private $anotherDependency; - - public function __construct(AnotherDependency $anotherDependency) - { - $this->anotherDependency = $anotherDependency; - } - - public function loadConfiguration() - { -- return $this->anotherDependency->process('value'); -+ return StaticCaller::anotherMethod('value'); - } - } -``` - -
- -### NewArgToMethodCallRector - -Change new with specific argument to method call - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\New_\NewArgToMethodCallRector`](../rules/Transform/Rector/New_/NewArgToMethodCallRector.php) - -```php -use Rector\Transform\Rector\New_\NewArgToMethodCallRector; -use Rector\Transform\ValueObject\NewArgToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(NewArgToMethodCallRector::class) - ->call('configure', [[ - NewArgToMethodCallRector::NEW_ARGS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new NewArgToMethodCall('Dotenv', true, 'usePutenv'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- $dotenv = new Dotenv(true); -+ $dotenv = new Dotenv(); -+ $dotenv->usePutenv(); - } - } -``` - -
- -### NewToConstructorInjectionRector - -Change defined new type to constructor injection - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\New_\NewToConstructorInjectionRector`](../rules/Transform/Rector/New_/NewToConstructorInjectionRector.php) - -```php -use Rector\Transform\Rector\New_\NewToConstructorInjectionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(NewToConstructorInjectionRector::class) - ->call('configure', [[ - NewToConstructorInjectionRector::TYPES_TO_CONSTRUCTOR_INJECTION => ['Validator'], - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -+ /** -+ * @var Validator -+ */ -+ private $validator; -+ -+ public function __construct(Validator $validator) -+ { -+ $this->validator = $validator; -+ } -+ - public function run() - { -- $validator = new Validator(); -- $validator->validate(1000); -+ $this->validator->validate(1000); - } - } -``` - -
- -### NewToMethodCallRector - -Replaces creating object instances with "new" keyword with factory method. - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\New_\NewToMethodCallRector`](../rules/Transform/Rector/New_/NewToMethodCallRector.php) - -```php -use Rector\Transform\Rector\New_\NewToMethodCallRector; -use Rector\Transform\ValueObject\NewToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(NewToMethodCallRector::class) - ->call('configure', [[ - NewToMethodCallRector::NEWS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new NewToMethodCall('MyClass', 'MyClassFactory', 'create'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -+ /** -+ * @var \MyClassFactory -+ */ -+ private $myClassFactory; -+ - public function example() { -- new MyClass($argument); -+ $this->myClassFactory->create($argument); - } - } -``` - -
- -### NewToStaticCallRector - -Change new Object to static call - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\New_\NewToStaticCallRector`](../rules/Transform/Rector/New_/NewToStaticCallRector.php) - -```php -use Rector\Transform\Rector\New_\NewToStaticCallRector; -use Rector\Transform\ValueObject\NewToStaticCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(NewToStaticCallRector::class) - ->call('configure', [[ - NewToStaticCallRector::TYPE_TO_STATIC_CALLS => ValueObjectInliner::inline([ - new NewToStaticCall('Cookie', 'Cookie', 'create'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- new Cookie($name); -+ Cookie::create($name); - } - } -``` - -
- -### ParentClassToTraitsRector - -Replaces parent class to specific traits - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Class_\ParentClassToTraitsRector`](../rules/Transform/Rector/Class_/ParentClassToTraitsRector.php) - -```php -use Rector\Transform\Rector\Class_\ParentClassToTraitsRector; -use Rector\Transform\ValueObject\ParentClassToTraits; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ParentClassToTraitsRector::class) - ->call('configure', [[ - ParentClassToTraitsRector::PARENT_CLASS_TO_TRAITS => ValueObjectInliner::inline([ - new ParentClassToTraits('Nette\Object', ['Nette\SmartObject']), ] - ), - ]]); -}; -``` - -↓ - -```diff --class SomeClass extends Nette\Object -+class SomeClass - { -+ use Nette\SmartObject; - } -``` - -
- -### PropertyAssignToMethodCallRector - -Turns property assign of specific type and property name to method call - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Assign\PropertyAssignToMethodCallRector`](../rules/Transform/Rector/Assign/PropertyAssignToMethodCallRector.php) - -```php -use Rector\Transform\Rector\Assign\PropertyAssignToMethodCallRector; -use Rector\Transform\ValueObject\PropertyAssignToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(PropertyAssignToMethodCallRector::class) - ->call('configure', [[ - PropertyAssignToMethodCallRector::PROPERTY_ASSIGNS_TO_METHODS_CALLS => ValueObjectInliner::inline([ - new PropertyAssignToMethodCall('SomeClass', 'oldProperty', 'newMethodCall'), - ]), - ]]); -}; -``` - -↓ - -```diff - $someObject = new SomeClass; --$someObject->oldProperty = false; -+$someObject->newMethodCall(false); -``` - -
- -### PropertyFetchToMethodCallRector - -Replaces properties assign calls be defined methods. - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Assign\PropertyFetchToMethodCallRector`](../rules/Transform/Rector/Assign/PropertyFetchToMethodCallRector.php) - -```php -use Rector\Transform\Rector\Assign\PropertyFetchToMethodCallRector; -use Rector\Transform\ValueObject\PropertyFetchToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(PropertyFetchToMethodCallRector::class) - ->call('configure', [[ - PropertyFetchToMethodCallRector::PROPERTIES_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new PropertyFetchToMethodCall('SomeObject', 'property', 'getProperty', 'setProperty', []), ] - ), - ]]); -}; -``` - -↓ - -```diff --$result = $object->property; --$object->property = $value; -+$result = $object->getProperty(); -+$object->setProperty($value); -``` - -
- -```php -use Rector\Transform\Rector\Assign\PropertyFetchToMethodCallRector; -use Rector\Transform\ValueObject\PropertyFetchToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(PropertyFetchToMethodCallRector::class) - ->call('configure', [[ - PropertyFetchToMethodCallRector::PROPERTIES_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new PropertyFetchToMethodCall('SomeObject', 'property', 'getConfig', null, ['someArg']), ] - ), - ]]); -}; -``` - -↓ - -```diff --$result = $object->property; -+$result = $object->getProperty('someArg'); -``` - -
- -### ReplaceParentCallByPropertyCallRector - -Changes method calls in child of specific types to defined property method call - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\MethodCall\ReplaceParentCallByPropertyCallRector`](../rules/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector.php) - -```php -use Rector\Transform\Rector\MethodCall\ReplaceParentCallByPropertyCallRector; -use Rector\Transform\ValueObject\ReplaceParentCallByPropertyCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ReplaceParentCallByPropertyCallRector::class) - ->call('configure', [[ - ReplaceParentCallByPropertyCallRector::PARENT_CALLS_TO_PROPERTIES => ValueObjectInliner::inline([ - new ReplaceParentCallByPropertyCall('SomeTypeToReplace', 'someMethodCall', 'someProperty'), - ]), - ]]); -}; -``` - -↓ - -```diff - final class SomeClass - { - public function run(SomeTypeToReplace $someTypeToReplace) - { -- $someTypeToReplace->someMethodCall(); -+ $this->someProperty->someMethodCall(); - } - } -``` - -
- -### ServiceGetterToConstructorInjectionRector - -Get service call to constructor injection - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\MethodCall\ServiceGetterToConstructorInjectionRector`](../rules/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector.php) - -```php -use Rector\Transform\Rector\MethodCall\ServiceGetterToConstructorInjectionRector; -use Rector\Transform\ValueObject\ServiceGetterToConstructorInjection; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ServiceGetterToConstructorInjectionRector::class) - ->call('configure', [[ - ServiceGetterToConstructorInjectionRector::METHOD_CALL_TO_SERVICES => ValueObjectInliner::inline([ - new ServiceGetterToConstructorInjection('FirstService', 'getAnotherService', 'AnotherService'), - ]), - ]]); -}; -``` - -↓ - -```diff - final class SomeClass - { - /** - * @var FirstService - */ - private $firstService; - -- public function __construct(FirstService $firstService) -- { -- $this->firstService = $firstService; -- } -- -- public function run() -- { -- $anotherService = $this->firstService->getAnotherService(); -- $anotherService->run(); -- } --} -- --class FirstService --{ - /** - * @var AnotherService - */ - private $anotherService; - -- public function __construct(AnotherService $anotherService) -+ public function __construct(FirstService $firstService, AnotherService $anotherService) - { -+ $this->firstService = $firstService; - $this->anotherService = $anotherService; - } - -- public function getAnotherService(): AnotherService -+ public function run() - { -- return $this->anotherService; -+ $anotherService = $this->anotherService; -+ $anotherService->run(); - } - } -``` - -
- -### SingleToManyMethodRector - -Change method that returns single value to multiple values - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\ClassMethod\SingleToManyMethodRector`](../rules/Transform/Rector/ClassMethod/SingleToManyMethodRector.php) - -```php -use Rector\Transform\Rector\ClassMethod\SingleToManyMethodRector; -use Rector\Transform\ValueObject\SingleToManyMethod; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(SingleToManyMethodRector::class) - ->call('configure', [[ - SingleToManyMethodRector::SINGLES_TO_MANY_METHODS => ValueObjectInliner::inline([ - new SingleToManyMethod('SomeClass', 'getNode', 'getNodes'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -- public function getNode(): string -+ /** -+ * @return string[] -+ */ -+ public function getNodes(): array - { -- return 'Echo_'; -+ return ['Echo_']; - } - } -``` - -
- -### StaticCallToFuncCallRector - -Turns static call to function call. - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\StaticCall\StaticCallToFuncCallRector`](../rules/Transform/Rector/StaticCall/StaticCallToFuncCallRector.php) - -```php -use Rector\Transform\Rector\StaticCall\StaticCallToFuncCallRector; -use Rector\Transform\ValueObject\StaticCallToFuncCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(StaticCallToFuncCallRector::class) - ->call('configure', [[ - StaticCallToFuncCallRector::STATIC_CALLS_TO_FUNCTIONS => ValueObjectInliner::inline([ - new StaticCallToFuncCall('OldClass', 'oldMethod', 'new_function'), - ]), - ]]); -}; -``` - -↓ - -```diff --OldClass::oldMethod("args"); -+new_function("args"); -``` - -
- -### StaticCallToMethodCallRector - -Change static call to service method via constructor injection - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\StaticCall\StaticCallToMethodCallRector`](../rules/Transform/Rector/StaticCall/StaticCallToMethodCallRector.php) - -```php -use Rector\Transform\Rector\StaticCall\StaticCallToMethodCallRector; -use Rector\Transform\ValueObject\StaticCallToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(StaticCallToMethodCallRector::class) - ->call('configure', [[ - StaticCallToMethodCallRector::STATIC_CALLS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new StaticCallToMethodCall( - 'Nette\Utils\FileSystem', - 'write', - 'Symplify\SmartFileSystem\SmartFileSystem', - 'dumpFile' - ), - ]), - ]]); -}; -``` - -↓ - -```diff --use Nette\Utils\FileSystem; -+use Symplify\SmartFileSystem\SmartFileSystem; - - class SomeClass - { -+ /** -+ * @var SmartFileSystem -+ */ -+ private $smartFileSystem; -+ -+ public function __construct(SmartFileSystem $smartFileSystem) -+ { -+ $this->smartFileSystem = $smartFileSystem; -+ } -+ - public function run() - { -- return FileSystem::write('file', 'content'); -+ return $this->smartFileSystem->dumpFile('file', 'content'); - } - } -``` - -
- -### StaticCallToNewRector - -Change static call to new instance - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\StaticCall\StaticCallToNewRector`](../rules/Transform/Rector/StaticCall/StaticCallToNewRector.php) - -```php -use Rector\Transform\Rector\StaticCall\StaticCallToNewRector; -use Rector\Transform\ValueObject\StaticCallToNew; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(StaticCallToNewRector::class) - ->call('configure', [[ - StaticCallToNewRector::STATIC_CALLS_TO_NEWS => ValueObjectInliner::inline([ - new StaticCallToNew('JsonResponse', 'create'), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { - public function run() - { -- $dotenv = JsonResponse::create(true); -+ $dotenv = new JsonResponse(); - } - } -``` - -
- -### StringToClassConstantRector - -Changes strings to specific constants - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\String_\StringToClassConstantRector`](../rules/Transform/Rector/String_/StringToClassConstantRector.php) - -```php -use Rector\Transform\Rector\String_\StringToClassConstantRector; -use Rector\Transform\ValueObject\StringToClassConstant; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(StringToClassConstantRector::class) - ->call('configure', [[ - StringToClassConstantRector::STRINGS_TO_CLASS_CONSTANTS => ValueObjectInliner::inline([ - new StringToClassConstant('compiler.post_dump', 'Yet\AnotherClass', 'CONSTANT'), - ]), - ]]); -}; -``` - -↓ - -```diff - final class SomeSubscriber - { - public static function getSubscribedEvents() - { -- return ['compiler.post_dump' => 'compile']; -+ return [\Yet\AnotherClass::CONSTANT => 'compile']; - } - } -``` - -
- -### ToStringToMethodCallRector - -Turns defined code uses of `"__toString()"` method to specific method calls. - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\String_\ToStringToMethodCallRector`](../rules/Transform/Rector/String_/ToStringToMethodCallRector.php) - -```php -use Rector\Transform\Rector\String_\ToStringToMethodCallRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ToStringToMethodCallRector::class) - ->call('configure', [[ - ToStringToMethodCallRector::METHOD_NAMES_BY_TYPE => [ - 'SomeObject' => 'getPath', - - ], ]]); -}; -``` - -↓ - -```diff - $someValue = new SomeObject; --$result = (string) $someValue; --$result = $someValue->__toString(); -+$result = $someValue->getPath(); -+$result = $someValue->getPath(); -``` - -
- -### UnsetAndIssetToMethodCallRector - -Turns defined `__isset`/`__unset` calls to specific method calls. - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\Isset_\UnsetAndIssetToMethodCallRector`](../rules/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector.php) - -```php -use Rector\Transform\Rector\Isset_\UnsetAndIssetToMethodCallRector; -use Rector\Transform\ValueObject\UnsetAndIssetToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(UnsetAndIssetToMethodCallRector::class) - ->call('configure', [[ - UnsetAndIssetToMethodCallRector::ISSET_UNSET_TO_METHOD_CALL => ValueObjectInliner::inline([ - new UnsetAndIssetToMethodCall('SomeContainer', 'hasService', 'removeService'), - ]), - ]]); -}; -``` - -↓ - -```diff - $container = new SomeContainer; --isset($container["someKey"]); --unset($container["someKey"]); -+$container->hasService("someKey"); -+$container->removeService("someKey"); -``` - -
- -### WrapReturnRector - -Wrap return value of specific method - -:wrench: **configure it!** - -- class: [`Rector\Transform\Rector\ClassMethod\WrapReturnRector`](../rules/Transform/Rector/ClassMethod/WrapReturnRector.php) - -```php -use Rector\Transform\Rector\ClassMethod\WrapReturnRector; -use Rector\Transform\ValueObject\WrapReturn; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(WrapReturnRector::class) - ->call('configure', [[ - WrapReturnRector::TYPE_METHOD_WRAPS => ValueObjectInliner::inline([ - new WrapReturn('SomeClass', 'getItem', true), - ]), - ]]); -}; -``` - -↓ - -```diff - final class SomeClass - { - public function getItem() - { -- return 1; -+ return [1]; - } - } -``` - -
- -## TypeDeclaration - -### AddArrayParamDocTypeRector - -Adds `@param` annotation to array parameters inferred from the rest of the code - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\AddArrayParamDocTypeRector`](../rules/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector.php) - -```diff - class SomeClass - { - /** - * @var int[] - */ - private $values; - -+ /** -+ * @param int[] $values -+ */ - public function __construct(array $values) - { - $this->values = $values; - } - } -``` - -
- -### AddArrayReturnDocTypeRector - -Adds `@return` annotation to array parameters inferred from the rest of the code - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector`](../rules/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector.php) - -```diff - class SomeClass - { - /** - * @var int[] - */ - private $values; - -+ /** -+ * @return int[] -+ */ - public function getValues(): array - { - return $this->values; - } - } -``` - -
- -### AddClosureReturnTypeRector - -Add known return type to functions - -- class: [`Rector\TypeDeclaration\Rector\Closure\AddClosureReturnTypeRector`](../rules/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector.php) - -```diff - class SomeClass - { - public function run($meetups) - { -- return array_filter($meetups, function (Meetup $meetup) { -+ return array_filter($meetups, function (Meetup $meetup): bool { - return is_object($meetup); - }); - } - } -``` - -
- -### AddMethodCallBasedStrictParamTypeRector - -Change param type to strict type of passed expression - -:wrench: **configure it!** - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector`](../rules/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php) - -```php -use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddMethodCallBasedStrictParamTypeRector::class) - ->call('configure', [[ - AddMethodCallBasedStrictParamTypeRector::TRUST_DOC_BLOCKS => false, - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -- public function getById($id) -+ public function getById(int $id) - { - } - } - - class CallerClass - { - public function run(SomeClass $someClass) - { - $someClass->getById($this->getId()); - } - - public function getId(): int - { - return 1000; - } - } -``` - -
- -### AddParamTypeDeclarationRector - -Add param types where needed - -:wrench: **configure it!** - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector`](../rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector.php) - -```php -use PHPStan\Type\StringType; -use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector; -use Rector\TypeDeclaration\ValueObject\AddParamTypeDeclaration; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddParamTypeDeclarationRector::class) - ->call('configure', [[ - AddParamTypeDeclarationRector::PARAMETER_TYPEHINTS => ValueObjectInliner::inline([ - new AddParamTypeDeclaration('SomeClass', 'process', 0, new StringType()), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -- public function process($name) -+ public function process(string $name) - { - } - } -``` - -
- -### AddReturnTypeDeclarationRector - -Changes defined return typehint of method and class. - -:wrench: **configure it!** - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector`](../rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php) - -```php -use PHPStan\Type\ArrayType; -use PHPStan\Type\MixedType; -use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector; -use Rector\TypeDeclaration\ValueObject\AddReturnTypeDeclaration; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(AddReturnTypeDeclarationRector::class) - ->call('configure', [[ - AddReturnTypeDeclarationRector::METHOD_RETURN_TYPES => ValueObjectInliner::inline([ - new AddReturnTypeDeclaration('SomeClass', 'getData', new ArrayType(new MixedType( - false, - null - ), new MixedType( - false, - null - ))), - ]), - ]]); -}; -``` - -↓ - -```diff - class SomeClass - { -- public function getData() -+ public function getData(): array - { - } - } -``` - -
- -### AddVoidReturnTypeWhereNoReturnRector - -Add return type void to function like without any return - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector`](../rules/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector.php) - -```diff - final class SomeClass - { -- public function getValues() -+ public function getValues(): void - { - $value = 1000; - return; - } - } -``` - -
- -### CompleteVarDocTypePropertyRector - -Complete property `@var` annotations or correct the old ones - -- class: [`Rector\TypeDeclaration\Rector\Property\CompleteVarDocTypePropertyRector`](../rules/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector.php) - -```diff - final class SomeClass - { -+ /** -+ * @var EventDispatcher -+ */ - private $eventDispatcher; - - public function __construct(EventDispatcher $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - } - } -``` - -
- -### FormerNullableArgumentToScalarTypedRector - -Change null in argument, that is now not nullable anymore - -- class: [`Rector\TypeDeclaration\Rector\MethodCall\FormerNullableArgumentToScalarTypedRector`](../rules/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector.php) - -```diff - final class SomeClass - { - public function run() - { -- $this->setValue(null); -+ $this->setValue(''); - } - - public function setValue(string $value) - { - } - } -``` - -
- -### ParamTypeByMethodCallTypeRector - -Change param type based on passed method call type - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector`](../rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php) - -```diff - class SomeTypedService - { - public function run(string $name) - { - } - } - - final class UseDependency - { - public function __construct( - private SomeTypedService $someTypedService - ) { - } - -- public function go($value) -+ public function go(string $value) - { - $this->someTypedService->run($value); - } - } -``` - -
- -### ParamTypeByParentCallTypeRector - -Change param type based on parent param type - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector`](../rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector.php) - -```diff - class SomeControl - { - public function __construct(string $name) - { - } - } - - class VideoControl extends SomeControl - { -- public function __construct($name) -+ public function __construct(string $name) - { - parent::__construct($name); - } - } -``` - -
- -### ParamTypeDeclarationRector - -Change `@param` types to type declarations if not a BC-break - -- class: [`Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector`](../rules/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector.php) - -```diff - abstract class VendorParentClass - { - /** - * @param int $number - */ - public function keep($number) - { - } - } - - final class ChildClass extends VendorParentClass - { - /** - * @param int $number - */ - public function keep($number) - { - } - -- /** -- * @param int $number -- */ -- public function change($number) -+ public function change(int $number) - { - } - } -``` - -
- -### ParamTypeFromStrictTypedPropertyRector - -Add param type from `$param` set to typed property - -- class: [`Rector\TypeDeclaration\Rector\Param\ParamTypeFromStrictTypedPropertyRector`](../rules/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector.php) - -```diff - final class SomeClass - { - private int $age; - -- public function setAge($age) -+ public function setAge(int $age) - { - $this->age = $age; - } - } -``` - -
- -### PropertyTypeDeclarationRector - -Add `@var` to properties that are missing it - -- class: [`Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector`](../rules/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector.php) - -```diff - class SomeClass - { -+ /** -+ * @var int -+ */ - private $value; - - public function run() - { - $this->value = 123; - } - } -``` - -
- -### ReturnNeverTypeRector - -Add "never" return-type for methods that never return anything - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector`](../rules/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector.php) - -```diff - final class SomeClass - { -+ /** -+ * @return never -+ */ - public function run() - { - throw new InvalidException(); - } - } -``` - -
- -### ReturnTypeDeclarationRector - -Change `@return` types and type from static analysis to type declarations if not a BC-break - -- class: [`Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector`](../rules/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector.php) - -```diff - class SomeClass - { -- /** -- * @return int -- */ -- public function getCount() -+ public function getCount(): int - { - } - } -``` - -
- -### ReturnTypeFromReturnNewRector - -Add return type to function like with return new - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector`](../rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector.php) - -```diff - final class SomeClass - { -- public function action() -+ public function action(): Response - { - return new Response(); - } - } -``` - -
- -### ReturnTypeFromStrictTypedCallRector - -Add return type from strict return type of call - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector`](../rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector.php) - -```diff - final class SomeClass - { -- public function getData() -+ public function getData(): int - { - return $this->getNumber(); - } - - private function getNumber(): int - { - return 1000; - } - } -``` - -
- -### ReturnTypeFromStrictTypedPropertyRector - -Add return method return type based on strict typed property - -- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedPropertyRector`](../rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector.php) - -```diff - final class SomeClass - { - private int $age = 100; - -- public function getAge() -+ public function getAge(): int - { - return $this->age; - } - } -``` - -
- -### TypedPropertyFromStrictConstructorRector - -Add typed properties based only on strict constructor types - -- class: [`Rector\TypeDeclaration\Rector\Property\TypedPropertyFromStrictConstructorRector`](../rules/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector.php) - -```diff - class SomeObject - { -- private $name; -+ private string $name; - - public function __construct(string $name) - { - $this->name = $name; - } - } -``` - -
- -## Visibility - -### ChangeConstantVisibilityRector - -Change visibility of constant from parent class. - -:wrench: **configure it!** - -- class: [`Rector\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector`](../rules/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector.php) - -```php -use Rector\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector; -use Rector\Visibility\ValueObject\ChangeConstantVisibility; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ChangeConstantVisibilityRector::class) - ->call('configure', [[ - ChangeConstantVisibilityRector::CLASS_CONSTANT_VISIBILITY_CHANGES => ValueObjectInliner::inline([ - new ChangeConstantVisibility('ParentObject', 'SOME_CONSTANT', 2), - ]), - ]]); -}; -``` - -↓ - -```diff - class FrameworkClass - { - protected const SOME_CONSTANT = 1; - } - - class MyClass extends FrameworkClass - { -- public const SOME_CONSTANT = 1; -+ protected const SOME_CONSTANT = 1; - } -``` - -
- -### ChangeMethodVisibilityRector - -Change visibility of method from parent class. - -:wrench: **configure it!** - -- class: [`Rector\Visibility\Rector\ClassMethod\ChangeMethodVisibilityRector`](../rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php) - -```php -use Rector\Visibility\Rector\ClassMethod\ChangeMethodVisibilityRector; -use Rector\Visibility\ValueObject\ChangeMethodVisibility; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - - $services->set(ChangeMethodVisibilityRector::class) - ->call('configure', [[ - ChangeMethodVisibilityRector::METHOD_VISIBILITIES => ValueObjectInliner::inline([ - new ChangeMethodVisibility('FrameworkClass', 'someMethod', 2), - ]), - ]]); -}; -``` - -↓ - -```diff - class FrameworkClass - { - protected function someMethod() - { - } - } - - class MyClass extends FrameworkClass - { -- public function someMethod() -+ protected function someMethod() - { - } - } -``` - -
diff --git a/build/target-repository/docs/static_reflection_and_autoload.md b/build/target-repository/docs/static_reflection_and_autoload.md deleted file mode 100644 index 68f7a82f85f..00000000000 --- a/build/target-repository/docs/static_reflection_and_autoload.md +++ /dev/null @@ -1,54 +0,0 @@ -# Static Reflection and Autoload - - -Rector is using static reflection to load code without running it since version 0.10. That means your classes are found **without composer autoload and without running them**. Rector will find them and work with them as you have PSR-4 autoload properly setup. This comes very useful in legacy projects or projects with custom autoload. - -Do you want to know more about it? Continue here: - -- [From Doctrine Annotations Parser to Static Reflection](https://getrector.org/blog/from-doctrine-annotations-parser-to-static-reflection) -- [Legacy Refactoring made Easy with Static Reflection](https://getrector.org/blog/2021/03/15/legacy-refactoring-made-easy-with-static-reflection) -- [Zero Config Analysis with Static Reflection](https://phpstan.org/blog/zero-config-analysis-with-static-reflection) - from PHPStan - -```php -// rector.php - -use Rector\Core\Configuration\Option; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - - // Rector is using static reflection to load code without running it - see https://phpstan.org/blog/zero-config-analysis-with-static-reflection - $parameters->set(Option::AUTOLOAD_PATHS, [ - // discover specific file - __DIR__ . '/file-with-functions.php', - // or full directory - __DIR__ . '/project-without-composer', - ]); -``` - -## Include Files - -Do you need to include constants, class aliases or custom autoloader? Use `BOOTSTRAP_FILES` parameter: - -```php -// rector.php - -use Rector\Core\Configuration\Option; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::BOOTSTRAP_FILES, [ - __DIR__ . '/constants.php', - __DIR__ . '/project/special/autoload.php', - ]); -}; -``` - -Listed files will be executed like: - -```php -include $filePath; -``` diff --git a/build/target-repository/e2e/attributes/rector.php b/build/target-repository/e2e/attributes/rector.php index f2ea9e04f5d..027d3fda920 100644 --- a/build/target-repository/e2e/attributes/rector.php +++ b/build/target-repository/e2e/attributes/rector.php @@ -2,15 +2,13 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersion; +use Rector\Config\RectorConfig; use Rector\Symfony\Set\SymfonySetList; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import(SymfonySetList::SYMFONY_52); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->sets([SymfonySetList::SYMFONY_52]); - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_80); - $parameters->set(Option::SOURCE, [__DIR__ . '/src']); + $rectorConfig->phpVersion(PhpVersion::PHP_80); + $rectorConfig->paths([__DIR__ . '/src']); }; diff --git a/build/target-repository/e2e/define-constant/composer.json b/build/target-repository/e2e/define-constant/composer.json new file mode 100644 index 00000000000..441fe971bf2 --- /dev/null +++ b/build/target-repository/e2e/define-constant/composer.json @@ -0,0 +1,4 @@ +{ + "autoload": { + } +} diff --git a/build/target-repository/e2e/define-constant/rector.php b/build/target-repository/e2e/define-constant/rector.php new file mode 100644 index 00000000000..3c447108ba1 --- /dev/null +++ b/build/target-repository/e2e/define-constant/rector.php @@ -0,0 +1,11 @@ +paths([__DIR__ . '/src']); + + $rectorConfig->sets([\Rector\Set\ValueObject\SetList::CODE_QUALITY]); +}; diff --git a/build/target-repository/e2e/define-constant/src/DefineConstant.php b/build/target-repository/e2e/define-constant/src/DefineConstant.php new file mode 100644 index 00000000000..dc1379fce00 --- /dev/null +++ b/build/target-repository/e2e/define-constant/src/DefineConstant.php @@ -0,0 +1,7 @@ +appDirectory, '\\/ ')) . DIRECTORY_SEPARATOR); +} diff --git a/build/target-repository/e2e/dont-execute-code/rector.php b/build/target-repository/e2e/dont-execute-code/rector.php index bde91796b2c..11af563f5c7 100644 --- a/build/target-repository/e2e/dont-execute-code/rector.php +++ b/build/target-repository/e2e/dont-execute-code/rector.php @@ -2,13 +2,11 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; +use Rector\Config\RectorConfig; use Rector\Set\ValueObject\SetList; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PATHS, [__DIR__.'/src']); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->paths([__DIR__.'/src']); - $containerConfigurator->import(SetList::PHP_53); + $rectorConfig->sets([SetList::PHP_53]); }; diff --git a/build/target-repository/e2e/finalize/composer.json b/build/target-repository/e2e/finalize/composer.json deleted file mode 100644 index cebce1733e6..00000000000 --- a/build/target-repository/e2e/finalize/composer.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "autoload": { - "psr-4": { - "Rector\\e2e\\": "src" - } - }, - "require-dev": { - "doctrine/orm": "^2.7" - } -} diff --git a/build/target-repository/e2e/finalize/rector.php b/build/target-repository/e2e/finalize/rector.php deleted file mode 100644 index 290907417b1..00000000000 --- a/build/target-repository/e2e/finalize/rector.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(FinalizeClassesWithoutChildrenRector::class); -}; diff --git a/build/target-repository/e2e/finalize/src/SkipSomeEntity.php b/build/target-repository/e2e/finalize/src/SkipSomeEntity.php deleted file mode 100644 index 11baf123bec..00000000000 --- a/build/target-repository/e2e/finalize/src/SkipSomeEntity.php +++ /dev/null @@ -1,14 +0,0 @@ -paths([__DIR__ . '/src/']); + + $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class); +}; diff --git a/build/target-repository/e2e/global-install/src/MyException.php b/build/target-repository/e2e/global-install/src/MyException.php new file mode 100644 index 00000000000..411a6153ca9 --- /dev/null +++ b/build/target-repository/e2e/global-install/src/MyException.php @@ -0,0 +1,13 @@ +paths([__DIR__.'/src']); + + $rectorConfig->sets([SetList::DEAD_CODE]); +}; diff --git a/build/target-repository/e2e/parse-match-class-on-php74/src/QueryMatching.php b/build/target-repository/e2e/parse-match-class-on-php74/src/QueryMatching.php new file mode 100644 index 00000000000..3d1172260dd --- /dev/null +++ b/build/target-repository/e2e/parse-match-class-on-php74/src/QueryMatching.php @@ -0,0 +1,15 @@ +parameters(); - $parameters->set(Option::PATHS, [__DIR__.'/src']); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->paths([__DIR__.'/src']); - $containerConfigurator->import(SetList::PHP_53); + $rectorConfig->sets([SetList::PHP_53]); }; diff --git a/build/target-repository/e2e/parse-php8-code/rector.php b/build/target-repository/e2e/parse-php8-code/rector.php index bde91796b2c..11af563f5c7 100644 --- a/build/target-repository/e2e/parse-php8-code/rector.php +++ b/build/target-repository/e2e/parse-php8-code/rector.php @@ -2,13 +2,11 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; +use Rector\Config\RectorConfig; use Rector\Set\ValueObject\SetList; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PATHS, [__DIR__.'/src']); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->paths([__DIR__.'/src']); - $containerConfigurator->import(SetList::PHP_53); + $rectorConfig->sets([SetList::PHP_53]); }; diff --git a/build/target-repository/e2e/rector-prefixed-rule-test/.gitignore b/build/target-repository/e2e/rector-prefixed-rule-test/.gitignore new file mode 100644 index 00000000000..0cf304cb7e5 --- /dev/null +++ b/build/target-repository/e2e/rector-prefixed-rule-test/.gitignore @@ -0,0 +1,3 @@ +/.phpunit.result.cache +/vendor +/composer.lock \ No newline at end of file diff --git a/build/target-repository/e2e/rector-prefixed-rule-test/composer.json b/build/target-repository/e2e/rector-prefixed-rule-test/composer.json new file mode 100644 index 00000000000..6d324aff191 --- /dev/null +++ b/build/target-repository/e2e/rector-prefixed-rule-test/composer.json @@ -0,0 +1,15 @@ +{ + "require": { + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "rector/rector": "dev-main" + }, + "autoload-dev": { + "psr-4": { + "Utils\\Rector\\": "utils/rector/src", + "Utils\\Rector\\Tests\\": "utils/rector/tests" + } + } +} diff --git a/build/target-repository/e2e/rector-prefixed-rule-test/phpunit.xml b/build/target-repository/e2e/rector-prefixed-rule-test/phpunit.xml new file mode 100644 index 00000000000..30a11ec1808 --- /dev/null +++ b/build/target-repository/e2e/rector-prefixed-rule-test/phpunit.xml @@ -0,0 +1,13 @@ + + + + + utils/rector/tests + + + diff --git a/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/src/Rector/RenameSimpleRector.php b/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/src/Rector/RenameSimpleRector.php new file mode 100644 index 00000000000..298e3ba9956 --- /dev/null +++ b/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/src/Rector/RenameSimpleRector.php @@ -0,0 +1,38 @@ +> + */ + public function getNodeTypes(): array + { + return [Variable::class]; + } + + /** + * @param Variable $node + */ + public function refactor(Node $node): ?Node + { + $node->name = 'newValue'; + return $node; + } + + public function getRuleDefinition(): RuleDefinition + { + // needed only for simple test only + } +} diff --git a/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/Fixture/rename_variable.php.inc b/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/Fixture/rename_variable.php.inc new file mode 100644 index 00000000000..1055f6bf05b --- /dev/null +++ b/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/Fixture/rename_variable.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/RenameSimpleRectorTest.php b/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/RenameSimpleRectorTest.php new file mode 100644 index 00000000000..67e7f1e9e34 --- /dev/null +++ b/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/RenameSimpleRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/config/configured_rule.php b/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/config/configured_rule.php new file mode 100644 index 00000000000..4e2b0a55284 --- /dev/null +++ b/build/target-repository/e2e/rector-prefixed-rule-test/utils/rector/tests/Rector/RenameSimpleRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(RenameSimpleRector::class); +}; diff --git a/build/target-repository/stubs-rector/Internal/Constants.php b/build/target-repository/stubs-rector/Internal/Constants.php new file mode 100644 index 00000000000..eab3c121c2a --- /dev/null +++ b/build/target-repository/stubs-rector/Internal/Constants.php @@ -0,0 +1,19 @@ +flags = $flags; + } + + } +} + +if (PHP_VERSION_ID < 80100 && ! class_exists('ReturnTypeWillChange', false)) { + #[Attribute(Attribute::TARGET_METHOD)] + final class ReturnTypeWillChange + { + } +} + +if (PHP_VERSION_ID < 80100 && ! class_exists('ReflectionIntersectionType', false)) { + class ReflectionIntersectionType extends ReflectionType + { + + /** @return ReflectionType[] */ + public function getTypes() + { + return []; + } + + } +} diff --git a/build/target-repository/stubs-rector/PHPUnit/Framework/TestCase.php b/build/target-repository/stubs-rector/PHPUnit/Framework/TestCase.php new file mode 100644 index 00000000000..e7bd1841693 --- /dev/null +++ b/build/target-repository/stubs-rector/PHPUnit/Framework/TestCase.php @@ -0,0 +1,24 @@ + $originalClassName + * + * @psalm-return MockObject&RealInstanceType + */ + protected function createMock(string $originalClassName): MockObject + { + } + } +} diff --git a/composer-dependency-analyser.php b/composer-dependency-analyser.php new file mode 100644 index 00000000000..9b360269026 --- /dev/null +++ b/composer-dependency-analyser.php @@ -0,0 +1,27 @@ +addPathToScan(__DIR__ . '/build/config', false) + ->addPathToScan('bin', false) + // prepared test tooling + ->ignoreErrorsOnPackage('phpunit/phpunit', [ErrorType::DEV_DEPENDENCY_IN_PROD]) + // pinned v3.x version + ->ignoreErrorsOnPackage('react/promise', [ErrorType::UNUSED_DEPENDENCY]) + // ensure use version ^3.2.0 + ->ignoreErrorsOnPackage('composer/pcre', [ErrorType::UNUSED_DEPENDENCY]) + + ->ignoreErrorsOnPaths([ + __DIR__ . '/stubs', + __DIR__ . '/tests', + __DIR__ . '/rules-tests', + __DIR__ . '/build/config/config-downgrade.php', + ], [ErrorType::UNKNOWN_CLASS]) + + ->disableExtensionsAnalysis(); diff --git a/composer.json b/composer.json index a5036dccb91..e7172771349 100644 --- a/composer.json +++ b/composer.json @@ -1,73 +1,66 @@ { "name": "rector/rector-src", - "description": "Instant upgrade and refactoring of your PHP code", + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com", "bin": [ "bin/rector" ], "license": "MIT", - "homepage": "https://getrector.org", + "keywords": [ + "refactoring", + "automation", + "migration" + ], "require": { - "php": "^8.0", - "ext-dom": "*", - "ext-json": "*", - "composer/semver": "^3.2", - "composer/xdebug-handler": "^2.0", - "danielstjules/stringy": "^3.1", - "doctrine/inflector": "^2.0", - "ergebnis/json-printer": "^3.1", - "idiosyncratic/editorconfig": "^0.1.3", - "myclabs/php-enum": "^1.8", - "nette/utils": "^3.2", - "nikic/php-parser": "4.12.0", - "phpstan/phpdoc-parser": "^0.5.5", - "phpstan/phpstan": "0.12.96", - "phpstan/phpstan-phpunit": "^0.12.22", - "rector/extension-installer": "^0.11.0", - "rector/rector-cakephp": "^0.11.3", - "rector/rector-doctrine": "^0.11.17", - "rector/rector-laravel": "^0.11.5", - "rector/rector-nette": "^0.11.21", - "rector/rector-phpunit": "^0.11.7", - "rector/rector-symfony": "^0.11.18", - "rector/rector-phpoffice": "^0.11.2", - "sebastian/diff": "^4.0.4", - "ssch/typo3-rector": "^0.11.22", - "symfony/console": "5.3.x-dev", - "symfony/dependency-injection": "5.3.x-dev", - "symfony/finder": "^5.3", - "symfony/http-kernel": "^5.3", - "symfony/process": "^5.3", - "symfony/yaml": "^5.3", - "symplify/astral": "^9.4.40", - "symplify/autowire-array-parameter": "^9.4.40", - "symplify/composer-json-manipulator": "^9.4.40", - "symplify/console-color-diff": "^9.4.40", - "symplify/package-builder": "^9.4.40", - "symplify/rule-doc-generator-contracts": "^9.4.40", - "symplify/simple-php-doc-parser": "^9.4.40", - "symplify/skipper": "^9.4.40", - "symplify/smart-file-system": "^9.4.40", - "symplify/symfony-php-config": "^9.4.40", - "tracy/tracy": "^2.8", - "webmozart/assert": "^1.10" + "php": "^8.3", + "clue/ndjson-react": "^1.3", + "composer/pcre": "^3.3.2", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.5", + "doctrine/inflector": "^2.1", + "illuminate/container": "12.39.*", + "nette/utils": "^4.1.3", + "nikic/php-parser": "^5.7", + "ondram/ci-detector": "^4.2", + "phpstan/phpdoc-parser": "^2.3", + "phpstan/phpstan": "^2.1.41", + "react/event-loop": "^1.6", + "react/promise": "^3.3", + "react/socket": "^1.17", + "rector/extension-installer": "^0.11.2", + "rector/rector-doctrine": "dev-main", + "rector/rector-downgrade-php": "dev-main", + "rector/rector-phpunit": "dev-main", + "rector/rector-symfony": "dev-main", + "sebastian/diff": "^6.0|^7.0", + "symfony/console": "^6.4.24", + "symfony/filesystem": "^7.0", + "symfony/finder": "^6.4", + "symfony/process": "^6.4|^7.4", + "symplify/easy-parallel": "^11.2.2", + "symplify/rule-doc-generator-contracts": "^11.2", + "webmozart/assert": "^2.1" }, "require-dev": { - "brianium/paratest": "^6.3", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-nette": "^0.12.19", - "phpunit/phpunit": "^9.5", - "rector/phpstan-rules": "^0.3.5", - "rector/rector-generator": "^0.3", - "spatie/enum": "^3.9", - "symplify/coding-standard": "^9.4.40", - "symplify/easy-ci": "^9.4.40", - "symplify/easy-coding-standard": "^9.4.40", - "symplify/easy-testing": "^9.4.40", - "symplify/monorepo-builder": "^9.4.40", - "symplify/phpstan-extensions": "^9.4.40", - "symplify/phpstan-rules": "^9.4.40", - "symplify/rule-doc-generator": "^9.4.40", - "timeweb/phpstan-enum": "^2.3" + "nette/robot-loader": "^4.1", + "php-parallel-lint/php-parallel-lint": "^1.4", + "symplify/easy-coding-standard": "^13.0", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-webmozart-assert": "^2.0", + "phpunit/phpunit": "^12.5.6", + "rector/jack": "^0.5", + "rector/release-notes-generator": "^0.5.1", + "rector/swiss-knife": "^2.3", + "rector/type-perfect": "^2.1.2", + "shipmonk/composer-dependency-analyser": "^1.8", + "symplify/phpstan-extensions": "^12.0.2", + "symplify/phpstan-rules": "^14.9.11", + "symplify/vendor-patches": "^11.5", + "tomasvotruba/class-leak": "^2.1", + "tomasvotruba/unused-public": "^2.2", + "tracy/tracy": "^2.11" }, "replace": { "rector/rector": "self.version" @@ -75,79 +68,89 @@ "autoload": { "psr-4": { "Rector\\": [ - "packages", - "rules" + "rules", + "src" ], - "Rector\\Core\\": "src", - "Rector\\Compiler\\": "utils/compiler/src" + "Rector\\Utils\\": "utils" }, "files": [ - "src/functions/node_helper.php", - "src/constants.php" + "src/functions/node_helper.php" ] }, "autoload-dev": { "psr-4": { "Rector\\Tests\\": [ - "packages-tests", - "rules-tests" + "rules-tests", + "tests" + ], + "Rector\\Utils\\Tests\\": "utils-tests", + "E2e\\Parallel\\Reflection\\Resolver\\": [ + "e2e/parallel-reflection-resolver/src/", + "e2e/no-parallel-reflection-resolver/src" ], - "Rector\\Core\\Tests\\": "tests", - "Rector\\RuleDocGenerator\\": "utils/rule-doc-generator/src" + "Rector\\Scripts\\": "scripts/src" }, "classmap": [ "stubs", - "rules-tests/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector/Expected", - "rules-tests/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector/Expected", - "rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source", - "rules-tests/Renaming/Rector/Name/RenameClassRector/Source", - "rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Source", "rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source", "rules-tests/Renaming/Rector/Name/RenameClassRector/Source" ], "files": [ - "stubs/Doctrine/Persistence/ObjectManager.php", - "stubs/Doctrine/Common/Persistence/ObjectManager.php", + "tests/debug_functions.php", "rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/Source/some_view_function.php", - "rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/MyBar.php", - "rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Source/EventDispatcher.php", - "rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Source/FunctionTyped.php" + "rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Source/FunctionTyped.php", + "rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Source/functions.php", + "rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Source/ParentClass.php", + "rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Source/functions.php" ] }, "scripts": { "complete-check": [ "@check-cs", "@phpstan", - "@docs", "phpunit" ], - "check-cs": [ - "vendor/bin/ecs check --ansi", - "vendor/bin/ecs check-markdown README.md --ansi" - ], - "fix-cs": [ - "vendor/bin/ecs check --fix --ansi", - "vendor/bin/ecs check-markdown README.md --fix --ansi" - ], - "phpstan": "vendor/bin/phpstan analyse --ansi --error-format symplify", - "phpstan-config": "vendor/bin/phpstan analyse config --ansi --error-format symplify", - "docs": [ - "vendor/bin/rule-doc-generator generate packages rules --output-file build/rector_rules_overview.md --ansi --categorize", - "mv build/rector_rules_overview.md build/target-repository/docs/rector_rules_overview.md", - "vendor/bin/ecs check-markdown build/target-repository/docs/rector_rules_overview.md --ansi --fix" - ], + "check-cs": "vendor/bin/ecs check --ansi", + "fix-cs": "vendor/bin/ecs check --fix --ansi", + "phpstan": "vendor/bin/phpstan analyse --ansi --memory-limit=512M", "rector": "bin/rector process --ansi", "preload": "php build/build-preload.php .", - "release": "vendor/bin/monorepo-builder release patch --ansi" + "release": "vendor/bin/rng --from-commit X --to-commit Y --remote-repository rectorphp/rector-symfony --remote-repository rectorphp/rector-doctrine --remote-repository rectorphp/rector-phpunit" }, "extra": { - "branch-alias": { - "dev-main": "0.11-dev" - } + "patches": { + "illuminate/container": [ + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/illuminate-container-container-php.patch" + ], + "nikic/php-parser": [ + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-expr-closure-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-finally-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-function-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-do-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-catch-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-trycatch-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-for-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-classmethod-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-else-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-while-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-foreach-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-if-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-case-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-elseif-php.patch", + "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-namespace-php.patch" + ] + }, + "composer-exit-on-patch-failure": true, + "enable-patching": true }, "config": { "sort-packages": true, - "platform-check": false + "platform-check": false, + "allow-plugins": { + "phpstan/extension-installer": true, + "rector/extension-installer": true, + "cweagans/composer-patches": true + } }, "minimum-stability": "dev", "prefer-stable": true diff --git a/config/config.php b/config/config.php index 63a152a3004..fa62bbed1f5 100644 --- a/config/config.php +++ b/config/config.php @@ -2,21 +2,48 @@ declare(strict_types=1); -use Rector\Core\Bootstrap\ExtensionConfigResolver; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use OndraM\CiDetector\CiDetector; +use Rector\Bootstrap\ExtensionConfigResolver; +use Rector\Caching\ValueObject\Storage\MemoryCacheStorage; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import(__DIR__ . '/services.php'); - $containerConfigurator->import(__DIR__ . '/services-rules.php'); - $containerConfigurator->import(__DIR__ . '/services-packages.php'); - $containerConfigurator->import(__DIR__ . '/parameters.php'); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->paths([]); + $rectorConfig->skip([]); + $rectorConfig->autoloadPaths([]); + $rectorConfig->bootstrapFiles([]); + $rectorConfig->parallel(); + + // to avoid autoimporting out of the box + $rectorConfig->importNames(false, false); + $rectorConfig->removeUnusedImports(false); + + $rectorConfig->importShortClasses(); + $rectorConfig->indent(' ', 4); + + $rectorConfig->fileExtensions(['php']); + + $rectorConfig->cacheDirectory(sys_get_temp_dir() . '/rector_cached_files'); + $rectorConfig->containerCacheDirectory(sys_get_temp_dir()); + + // use faster in-memory cache in CI. + // CI always starts from scratch, therefore IO intensive caching is not worth it + if ((new CiDetector())->isCiDetected()) { + $rectorConfig->cacheClass(MemoryCacheStorage::class); + } + + // load internal rector-* extension configs $extensionConfigResolver = new ExtensionConfigResolver(); - $extensionConfigFiles = $extensionConfigResolver->provide(); - foreach ($extensionConfigFiles as $extensionConfigFile) { - $containerConfigurator->import($extensionConfigFile->getRealPath()); + foreach ($extensionConfigResolver->provide() as $extensionConfigFile) { + $rectorConfig->import($extensionConfigFile); } - // require only in dev - $containerConfigurator->import(__DIR__ . '/../utils/compiler/config/config.php', null, 'not_found'); + // use original php-parser printer to avoid BC break on fluent call + $rectorConfig->newLineOnFluentCall(false); + + // allow real paths in output formatters + $rectorConfig->reportingRealPath(false); + + $rectorConfig->treatClassesAsFinal(false); }; diff --git a/config/parameters.php b/config/parameters.php deleted file mode 100644 index d3d1593e160..00000000000 --- a/config/parameters.php +++ /dev/null @@ -1,40 +0,0 @@ -parameters(); - - // paths and extensions - $parameters->set(Option::PATHS, []); - $parameters->set(Option::FILE_EXTENSIONS, ['php']); - $parameters->set(Option::AUTOLOAD_PATHS, []); - - // these files will be executed, useful e.g. for constant definitions - $parameters->set(Option::BOOTSTRAP_FILES, []); - - // FQN class importing - $parameters->set(Option::AUTO_IMPORT_NAMES, false); - $parameters->set(Option::IMPORT_SHORT_CLASSES, true); - $parameters->set(Option::IMPORT_DOC_BLOCKS, true); - - $parameters->set(Option::PHP_VERSION_FEATURES, null); - $parameters->set(Option::NESTED_CHAIN_METHOD_CALL_LIMIT, 60); - $parameters->set(Option::SKIP, []); - - $parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, null); - - // cache - $parameters->set(Option::CACHE_DIR, sys_get_temp_dir() . '/rector_cached_files'); - - // use faster in-memory cache in CI. - // CI always starts from scratch, therefore IO intensive caching is not worth it - $runsInGithubAction = getenv('GITHUB_ACTION'); - if ($runsInGithubAction !== false) { - $parameters->set(Option::CACHE_CLASS, MemoryCacheStorage::class); - } -}; diff --git a/config/phpstan/parser.neon b/config/phpstan/parser.neon new file mode 100644 index 00000000000..3b0f385dce2 --- /dev/null +++ b/config/phpstan/parser.neon @@ -0,0 +1,27 @@ +# see original config.neon in phpstan.neon - https://github.com/phpstan/phpstan-src/blob/386eb913abb6ac05886c5642fd48b5d99db66a20/conf/config.neon#L1582 +# this file overrides definitions from the config above +services: + defaultAnalysisParser: + factory: @pathRoutingParser + arguments!: [] + + cachedRectorParser: + class: PHPStan\Parser\CachedParser + arguments: + originalParser: @rectorParser + cachedNodesByStringCountMax: %cache.nodesByStringCountMax% + autowired: false + + pathRoutingParser: + class: PHPStan\Parser\PathRoutingParser + arguments: + currentPhpVersionRichParser: @cachedRectorParser + currentPhpVersionSimpleParser: @cachedRectorParser + php8Parser: @php8Parser + autowired: false + + rectorParser: + class: PHPStan\Parser\RichParser + arguments: + parser: @currentPhpVersionPhpParser + autowired: no diff --git a/config/phpstan/static-reflection.neon b/config/phpstan/static-reflection.neon index 740ca36b7dd..238529fc630 100644 --- a/config/phpstan/static-reflection.neon +++ b/config/phpstan/static-reflection.neon @@ -1,8 +1,3 @@ -parameters: - # see https://github.com/rectorphp/rector/issues/3490#issue-634342324 - featureToggles: - disableRuntimeReflectionProvider: false - services: - Rector\NodeTypeResolver\Reflection\BetterReflection\RectorBetterReflectionSourceLocatorFactory - Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocator\IntermediateSourceLocator diff --git a/config/services-packages.php b/config/services-packages.php deleted file mode 100644 index 56319e665fd..00000000000 --- a/config/services-packages.php +++ /dev/null @@ -1,28 +0,0 @@ -services(); - - $services->defaults() - ->public() - ->autowire() - ->autoconfigure(); - - $services->load('Rector\\', __DIR__ . '/../packages') - ->exclude([ - __DIR__ . '/../packages/*/{ValueObject,Contract,Exception}', - __DIR__ . '/../packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php', - __DIR__ . '/../packages/Testing/PHPUnit', - __DIR__ . '/../packages/BetterPhpDocParser/PhpDoc', - __DIR__ . '/../packages/Caching/Cache.php', - __DIR__ . '/../packages/NodeTypeResolver/NodeVisitor/FileNodeVisitor.php', - - // used in PHPStan - __DIR__ . '/../packages/NodeTypeResolver/Reflection/BetterReflection/RectorBetterReflectionSourceLocatorFactory.php', - __DIR__ . '/../packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php', - ]); -}; diff --git a/config/services-rules.php b/config/services-rules.php deleted file mode 100644 index 777d82be912..00000000000 --- a/config/services-rules.php +++ /dev/null @@ -1,26 +0,0 @@ -parameters(); - $parameters->set(Option::TYPES_TO_REMOVE_STATIC_FROM, []); - - $services = $containerConfigurator->services(); - - $services->defaults() - ->public() - ->autowire() - ->autoconfigure(); - - // psr-4 - $services->alias(PSR4AutoloadNamespaceMatcherInterface::class, PSR4NamespaceMatcher::class); - - $services->load('Rector\\', __DIR__ . '/../rules') - ->exclude([__DIR__ . '/../rules/*/{ValueObject,Rector,Contract,Exception,Enum}']); -}; diff --git a/config/services.php b/config/services.php deleted file mode 100644 index 33f3733480d..00000000000 --- a/config/services.php +++ /dev/null @@ -1,159 +0,0 @@ -services(); - - $services->defaults() - ->public() - ->autowire() - ->autoconfigure(); - - $services->load('Rector\Core\\', __DIR__ . '/../src') - ->exclude([ - __DIR__ . '/../src/Rector', - __DIR__ . '/../src/Exception', - __DIR__ . '/../src/DependencyInjection/CompilerPass', - __DIR__ . '/../src/DependencyInjection/Loader', - __DIR__ . '/../src/HttpKernel', - __DIR__ . '/../src/ValueObject', - __DIR__ . '/../src/Bootstrap', - __DIR__ . '/../src/Enum', - __DIR__ . '/../src/PhpParser/Node/CustomNode', - __DIR__ . '/../src/functions', - __DIR__ . '/../src/constants.php', - __DIR__ . '/../src/PhpParser/NodeVisitor/CreatedByRuleNodeVisitor.php', - ]); - - $services->alias(SymfonyApplication::class, ConsoleApplication::class); - - $services->set(FileSystemGuard::class); - - $services->set(SimpleCallableNodeTraverser::class); - - $services->set(ParserFactory::class); - $services->set(BuilderFactory::class); - $services->set(CloningVisitor::class); - $services->set(NodeFinder::class); - - $services->set(Parser::class) - ->factory([service(NikicPhpParserFactory::class), 'create']); - $services->set(Lexer::class) - ->factory([service(PhpParserLexerFactory::class), 'create']); - - // symplify/package-builder - $services->set(PrivatesAccessor::class); - $services->set(PrivatesCaller::class); - $services->set(FinderSanitizer::class); - $services->set(FileSystemFilter::class); - - $services->set(ParameterProvider::class) - ->arg('$container', service('service_container')); - - $services->set(CommandNaming::class); - $services->set(SmartFileSystem::class); - - $services->set(StringFormatConverter::class); - - $services->set(SymfonyStyleFactory::class); - $services->set(SymfonyStyle::class) - ->factory([service(SymfonyStyleFactory::class), 'create']); - - $services->set(JsonFileSystem::class); - $services->set(NodeConnectingVisitor::class); - - $services->set(InflectorFactory::class); - $services->set(Inflector::class) - ->factory([service(InflectorFactory::class), 'build']); - - $services->set(VersionParser::class); - $services->set(TypeChecker::class); - - // phpdoc parser - $services->set(\PHPStan\PhpDocParser\Lexer\Lexer::class); - $services->alias(PhpDocParser::class, BetterPhpDocParser::class); - - // cache - $services->set(DependencyResolver::class) - ->factory([service(PHPStanServicesFactory::class), 'createDependencyResolver']); - $services->set(FileHelper::class) - ->factory([service(PHPStanServicesFactory::class), 'createFileHelper']); - - $services->set(Cache::class) - ->factory([service(CacheFactory::class), 'create']); - - // type resolving - $services->set(IntermediateSourceLocator::class); - $services->alias(TypeParser::class, BetterTypeParser::class); - - // PHPStan services - $services->set(ReflectionProvider::class) - ->factory([service(PHPStanServicesFactory::class), 'createReflectionProvider']); - - $services->set(NodeScopeResolver::class) - ->factory([service(PHPStanServicesFactory::class), 'createNodeScopeResolver']); - - $services->set(ScopeFactory::class) - ->factory([service(PHPStanServicesFactory::class), 'createScopeFactory']); - - $services->set(TypeNodeResolver::class) - ->factory([service(PHPStanServicesFactory::class), 'createTypeNodeResolver']); - - $services->set(DynamicSourceLocatorProvider::class) - ->factory([service(PHPStanServicesFactory::class), 'createDynamicSourceLocatorProvider']); - - $services->set(Printer::class); - $services->alias(PrinterInterface::class, Printer::class); - - $services->set(EditorConfig::class); -}; diff --git a/config/set/action-injection-to-constructor-injection.php b/config/set/action-injection-to-constructor-injection.php deleted file mode 100644 index 7f3f39ac464..00000000000 --- a/config/set/action-injection-to-constructor-injection.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(ActionInjectionToConstructorInjectionRector::class); - $services->set(ReplaceVariableByPropertyFetchRector::class); -}; diff --git a/config/set/assert.php b/config/set/assert.php new file mode 100644 index 00000000000..c98e4dc42d9 --- /dev/null +++ b/config/set/assert.php @@ -0,0 +1,10 @@ +rules([AddAssertArrayFromClassMethodDocblockRector::class]); +}; diff --git a/config/set/behat-annotations-to-attributes.php b/config/set/behat-annotations-to-attributes.php new file mode 100644 index 00000000000..96379d523d0 --- /dev/null +++ b/config/set/behat-annotations-to-attributes.php @@ -0,0 +1,24 @@ +ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Given', 'Behat\Step\Given', useValueAsAttributeArgument: true), + new AnnotationToAttribute('When', 'Behat\Step\When', useValueAsAttributeArgument: true), + new AnnotationToAttribute('Then', 'Behat\Step\Then', useValueAsAttributeArgument: true), + new AnnotationToAttribute('BeforeSuite', 'Behat\Hook\BeforeSuite', useValueAsAttributeArgument: true), + new AnnotationToAttribute('AfterSuite', 'Behat\Hook\AfterSuite', useValueAsAttributeArgument: true), + new AnnotationToAttribute('BeforeFeature', 'Behat\Hook\BeforeFeature', useValueAsAttributeArgument: true), + new AnnotationToAttribute('AfterFeature', 'Behat\Hook\AfterFeature', useValueAsAttributeArgument: true), + new AnnotationToAttribute('BeforeScenario', 'Behat\Hook\BeforeScenario', useValueAsAttributeArgument: true), + new AnnotationToAttribute('AfterScenario', 'Behat\Hook\AfterScenario', useValueAsAttributeArgument: true), + new AnnotationToAttribute('BeforeStep', 'Behat\Hook\BeforeStep', useValueAsAttributeArgument: true), + new AnnotationToAttribute('AfterStep', 'Behat\Hook\AfterStep', useValueAsAttributeArgument: true), + new AnnotationToAttribute('Transform', 'Behat\Transformation\Transform', useValueAsAttributeArgument: true), + ]); +}; diff --git a/config/set/carbon-2.php b/config/set/carbon-2.php deleted file mode 100644 index b3bf448f534..00000000000 --- a/config/set/carbon-2.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(ChangeDiffForHumansArgsRector::class); - $services->set(ChangeCarbonSingularMethodCallToPluralRector::class); -}; diff --git a/config/set/code-quality-strict.php b/config/set/code-quality-strict.php deleted file mode 100644 index 9c07f0e9193..00000000000 --- a/config/set/code-quality-strict.php +++ /dev/null @@ -1,15 +0,0 @@ -services(); - $services->set(CountArrayToEmptyArrayComparisonRector::class); - $services->set(UseMessageVariableForSprintfInSymfonyStyleRector::class); - $services->set(FlipTypeControlToUseExclusiveTypeRector::class); -}; diff --git a/config/set/code-quality.php b/config/set/code-quality.php index e68094241c7..3880450839b 100644 --- a/config/set/code-quality.php +++ b/config/set/code-quality.php @@ -2,183 +2,15 @@ declare(strict_types=1); -use Rector\CodeQuality\Rector\Array_\ArrayThisCallToThisMethodCallRector; -use Rector\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector; -use Rector\CodeQuality\Rector\Assign\CombinedAssignRector; -use Rector\CodeQuality\Rector\Assign\SplitListAssignToSeparateLineRector; -use Rector\CodeQuality\Rector\BooleanAnd\SimplifyEmptyArrayCheckRector; -use Rector\CodeQuality\Rector\BooleanNot\SimplifyDeMorganBinaryRector; -use Rector\CodeQuality\Rector\Catch_\ThrowWithPreviousExceptionRector; -use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector; -use Rector\CodeQuality\Rector\ClassMethod\DateTimeToDateTimeInterfaceRector; -use Rector\CodeQuality\Rector\ClassMethod\NarrowUnionTypeDocRector; -use Rector\CodeQuality\Rector\Concat\JoinStringConcatRector; -use Rector\CodeQuality\Rector\Equal\UseIdenticalOverEqualWithSameTypeRector; -use Rector\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector; -use Rector\CodeQuality\Rector\For_\ForRepeatedCountToOwnVariableRector; -use Rector\CodeQuality\Rector\For_\ForToForeachRector; -use Rector\CodeQuality\Rector\Foreach_\ForeachItemsAssignToEmptyArrayToAssignRector; -use Rector\CodeQuality\Rector\Foreach_\ForeachToInArrayRector; -use Rector\CodeQuality\Rector\Foreach_\SimplifyForeachToArrayFilterRector; -use Rector\CodeQuality\Rector\Foreach_\SimplifyForeachToCoalescingRector; -use Rector\CodeQuality\Rector\Foreach_\UnusedForeachValueToArrayKeysRector; -use Rector\CodeQuality\Rector\FuncCall\AddPregQuoteDelimiterRector; -use Rector\CodeQuality\Rector\FuncCall\ArrayKeysAndInArrayToArrayKeyExistsRector; -use Rector\CodeQuality\Rector\FuncCall\ArrayMergeOfNonArraysToSimpleArrayRector; -use Rector\CodeQuality\Rector\FuncCall\CallUserFuncWithArrowFunctionToInlineRector; -use Rector\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector; -use Rector\CodeQuality\Rector\FuncCall\CompactToVariablesRector; -use Rector\CodeQuality\Rector\FuncCall\InArrayAndArrayKeysToArrayKeyExistsRector; -use Rector\CodeQuality\Rector\FuncCall\IntvalToTypeCastRector; -use Rector\CodeQuality\Rector\FuncCall\IsAWithStringWithThirdArgumentRector; -use Rector\CodeQuality\Rector\FuncCall\RemoveSoleValueSprintfRector; -use Rector\CodeQuality\Rector\FuncCall\SetTypeToCastRector; -use Rector\CodeQuality\Rector\FuncCall\SimplifyFuncGetArgsCountRector; -use Rector\CodeQuality\Rector\FuncCall\SimplifyInArrayValuesRector; -use Rector\CodeQuality\Rector\FuncCall\SimplifyRegexPatternRector; -use Rector\CodeQuality\Rector\FuncCall\SimplifyStrposLowerRector; -use Rector\CodeQuality\Rector\FuncCall\SingleInArrayToCompareRector; -use Rector\CodeQuality\Rector\FuncCall\UnwrapSprintfOneArgumentRector; -use Rector\CodeQuality\Rector\FunctionLike\RemoveAlwaysTrueConditionSetInConstructorRector; -use Rector\CodeQuality\Rector\Identical\BooleanNotIdenticalToNotIdenticalRector; -use Rector\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector; -use Rector\CodeQuality\Rector\Identical\GetClassToInstanceOfRector; -use Rector\CodeQuality\Rector\Identical\SimplifyArraySearchRector; -use Rector\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector; -use Rector\CodeQuality\Rector\Identical\SimplifyConditionsRector; -use Rector\CodeQuality\Rector\Identical\StrlenZeroToIdenticalEmptyStringRector; -use Rector\CodeQuality\Rector\If_\CombineIfRector; -use Rector\CodeQuality\Rector\If_\ConsecutiveNullCompareReturnsToNullCoalesceQueueRector; -use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector; -use Rector\CodeQuality\Rector\If_\ShortenElseIfRector; -use Rector\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector; -use Rector\CodeQuality\Rector\If_\SimplifyIfIssetToNullCoalescingRector; -use Rector\CodeQuality\Rector\If_\SimplifyIfNotNullReturnRector; -use Rector\CodeQuality\Rector\If_\SimplifyIfNullableReturnRector; -use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector; -use Rector\CodeQuality\Rector\Include_\AbsolutizeRequireAndIncludePathRector; -use Rector\CodeQuality\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector; -use Rector\CodeQuality\Rector\LogicalAnd\AndAssignsToSeparateLinesRector; -use Rector\CodeQuality\Rector\LogicalAnd\LogicalToBooleanRector; -use Rector\CodeQuality\Rector\Name\FixClassCaseSensitivityNameRector; -use Rector\CodeQuality\Rector\New_\NewStaticToNewSelfRector; -use Rector\CodeQuality\Rector\NotEqual\CommonNotEqualRector; -use Rector\CodeQuality\Rector\Return_\SimplifyUselessVariableRector; -use Rector\CodeQuality\Rector\Switch_\SingularSwitchToIfRector; -use Rector\CodeQuality\Rector\Ternary\ArrayKeyExistsTernaryThenValueToCoalescingRector; -use Rector\CodeQuality\Rector\Ternary\SimplifyDuplicatedTernaryRector; -use Rector\CodeQuality\Rector\Ternary\SimplifyTautologyTernaryRector; -use Rector\CodeQuality\Rector\Ternary\SwitchNegatedTernaryRector; -use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector; -use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; -use Rector\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector; -use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; -use Rector\Php52\Rector\Property\VarToPublicPropertyRector; -use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector; -use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\Level\CodeQualityLevel; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CombinedAssignRector::class); - $services->set(SimplifyEmptyArrayCheckRector::class); - $services->set(ForeachToInArrayRector::class); - $services->set(SimplifyForeachToCoalescingRector::class); - $services->set(InArrayAndArrayKeysToArrayKeyExistsRector::class); - $services->set(SimplifyFuncGetArgsCountRector::class); - $services->set(SimplifyInArrayValuesRector::class); - $services->set(SimplifyStrposLowerRector::class); - $services->set(GetClassToInstanceOfRector::class); - $services->set(SimplifyArraySearchRector::class); - $services->set(SimplifyConditionsRector::class); - $services->set(SimplifyIfNotNullReturnRector::class); - $services->set(SimplifyIfReturnBoolRector::class); - $services->set(SimplifyUselessVariableRector::class); - $services->set(UnnecessaryTernaryExpressionRector::class); - $services->set(RemoveExtraParametersRector::class); - $services->set(SimplifyDeMorganBinaryRector::class); - $services->set(SimplifyTautologyTernaryRector::class); - $services->set(SimplifyForeachToArrayFilterRector::class); - $services->set(SingleInArrayToCompareRector::class); - $services->set(SimplifyIfElseToTernaryRector::class); - $services->set(JoinStringConcatRector::class); - $services->set(ConsecutiveNullCompareReturnsToNullCoalesceQueueRector::class); - $services->set(SimplifyIfIssetToNullCoalescingRector::class); - $services->set(ExplicitBoolCompareRector::class); - $services->set(CombineIfRector::class); - $services->set(UseIdenticalOverEqualWithSameTypeRector::class); - $services->set(SimplifyDuplicatedTernaryRector::class); - $services->set(SimplifyBoolIdenticalTrueRector::class); - $services->set(SimplifyRegexPatternRector::class); - $services->set(BooleanNotIdenticalToNotIdenticalRector::class); - $services->set(CallableThisArrayToAnonymousFunctionRector::class); - $services->set(AndAssignsToSeparateLinesRector::class); - $services->set(ForToForeachRector::class); - $services->set(CompactToVariablesRector::class); - $services->set(CompleteDynamicPropertiesRector::class); - $services->set(IsAWithStringWithThirdArgumentRector::class); - $services->set(StrlenZeroToIdenticalEmptyStringRector::class); - $services->set(RemoveAlwaysTrueConditionSetInConstructorRector::class); - $services->set(ThrowWithPreviousExceptionRector::class); - $services->set(RemoveSoleValueSprintfRector::class); - $services->set(ShortenElseIfRector::class); - $services->set(AddPregQuoteDelimiterRector::class); - $services->set(ArrayMergeOfNonArraysToSimpleArrayRector::class); - $services->set(IntvalToTypeCastRector::class); - $services->set(ArrayKeyExistsTernaryThenValueToCoalescingRector::class); - $services->set(AbsolutizeRequireAndIncludePathRector::class); - $services->set(ChangeArrayPushToArrayAssignRector::class); - $services->set(ForRepeatedCountToOwnVariableRector::class); - $services->set(ForeachItemsAssignToEmptyArrayToAssignRector::class); - $services->set(InlineIfToExplicitIfRector::class); - $services->set(ArrayKeysAndInArrayToArrayKeyExistsRector::class); - $services->set(SplitListAssignToSeparateLineRector::class); - $services->set(UnusedForeachValueToArrayKeysRector::class); - $services->set(ArrayThisCallToThisMethodCallRector::class); - $services->set(CommonNotEqualRector::class); - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'split' => 'explode', - 'join' => 'implode', - 'sizeof' => 'count', - # https://www.php.net/manual/en/aliases.php - 'chop' => 'rtrim', - 'doubleval' => 'floatval', - 'gzputs' => 'gzwrites', - 'fputs' => 'fwrite', - 'ini_alter' => 'ini_set', - 'is_double' => 'is_float', - 'is_integer' => 'is_int', - 'is_long' => 'is_int', - 'is_real' => 'is_float', - 'is_writeable' => 'is_writable', - 'key_exists' => 'array_key_exists', - 'pos' => 'current', - 'strchr' => 'strstr', - # mb - 'mbstrcut' => 'mb_strcut', - 'mbstrlen' => 'mb_strlen', - 'mbstrpos' => 'mb_strpos', - 'mbstrrpos' => 'mb_strrpos', - 'mbsubstr' => 'mb_substr', - ], - ]]); - $services->set(SetTypeToCastRector::class); - $services->set(LogicalToBooleanRector::class); - $services->set(VarToPublicPropertyRector::class); - $services->set(FixClassCaseSensitivityNameRector::class); - $services->set(IssetOnPropertyObjectToPropertyExistsRector::class); - $services->set(NewStaticToNewSelfRector::class); - $services->set(DateTimeToDateTimeInterfaceRector::class); - $services->set(UnwrapSprintfOneArgumentRector::class); - $services->set(SwitchNegatedTernaryRector::class); - $services->set(SingularSwitchToIfRector::class); - $services->set(SimplifyIfNullableReturnRector::class); - $services->set(NarrowUnionTypeDocRector::class); - $services->set(FuncGetArgsToVariadicParamRector::class); - $services->set(CallUserFuncToMethodCallRector::class); - $services->set(CallUserFuncWithArrowFunctionToInlineRector::class); - $services->set(CountArrayToEmptyArrayComparisonRector::class); - $services->set(FlipTypeControlToUseExclusiveTypeRector::class); +return static function (RectorConfig $rectorConfig): void { + foreach (CodeQualityLevel::RULES_WITH_CONFIGURATION as $rectorClass => $configuration) { + $rectorConfig->ruleWithConfiguration($rectorClass, $configuration); + } + + // the rule order matters, as its used in withCodeQualityLevel() method + // place the safest rules first, follow by more complex ones + $rectorConfig->rules(CodeQualityLevel::RULES); }; diff --git a/config/set/coding-style-advanced.php b/config/set/coding-style-advanced.php deleted file mode 100644 index bf2cae8b74b..00000000000 --- a/config/set/coding-style-advanced.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(UseMessageVariableForSprintfInSymfonyStyleRector::class); -}; diff --git a/config/set/coding-style.php b/config/set/coding-style.php index 58b47727daa..f3e5b87c833 100644 --- a/config/set/coding-style.php +++ b/config/set/coding-style.php @@ -2,78 +2,15 @@ declare(strict_types=1); -use Rector\CodingStyle\Rector\Assign\ManualJsonStringToJsonEncodeArrayRector; -use Rector\CodingStyle\Rector\Assign\PHPStormVarAnnotationRector; -use Rector\CodingStyle\Rector\Assign\SplitDoubleAssignRector; -use Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector; -use Rector\CodingStyle\Rector\Class_\AddArrayDefaultToArrayPropertyRector; -use Rector\CodingStyle\Rector\ClassConst\SplitGroupedConstantsAndPropertiesRector; -use Rector\CodingStyle\Rector\ClassConst\VarConstantCommentRector; -use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector; -use Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector; -use Rector\CodingStyle\Rector\ClassMethod\RemoveDoubleUnderscoreInMethodNameRector; -use Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector; -use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector; -use Rector\CodingStyle\Rector\Encapsed\WrapEncapsedVariableInCurlyBracesRector; -use Rector\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector; -use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector; -use Rector\CodingStyle\Rector\FuncCall\ConsistentPregDelimiterRector; -use Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector; -use Rector\CodingStyle\Rector\If_\NullableCompareToNullRector; -use Rector\CodingStyle\Rector\Include_\FollowRequireByDirRector; -use Rector\CodingStyle\Rector\MethodCall\UseMessageVariableForSprintfInSymfonyStyleRector; -use Rector\CodingStyle\Rector\Plus\UseIncrementAssignRector; -use Rector\CodingStyle\Rector\PostInc\PostIncDecToPreIncDecRector; -use Rector\CodingStyle\Rector\Property\AddFalseDefaultToBoolPropertyRector; -use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector; -use Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector; -use Rector\CodingStyle\Rector\Switch_\BinarySwitchToIfElseRector; -use Rector\CodingStyle\Rector\Ternary\TernaryConditionVariableAssignmentRector; -use Rector\CodingStyle\Rector\Use_\RemoveUnusedAliasRector; -use Rector\CodingStyle\Rector\Use_\SeparateMultiUseImportsRector; -use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; -use Rector\Transform\Rector\FuncCall\FuncCallToConstFetchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\Level\CodingStyleLevel; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(PHPStormVarAnnotationRector::class); - $services->set(NullableCompareToNullRector::class); - $services->set(BinarySwitchToIfElseRector::class); - $services->set(ConsistentImplodeRector::class); - $services->set(TernaryConditionVariableAssignmentRector::class); - $services->set(RemoveUnusedAliasRector::class); - $services->set(SymplifyQuoteEscapeRector::class); - $services->set(SplitGroupedConstantsAndPropertiesRector::class); - $services->set(SplitStringClassConstantToClassConstFetchRector::class); - $services->set(StringClassNameToClassConstantRector::class); - $services->set(ConsistentPregDelimiterRector::class); - $services->set(FollowRequireByDirRector::class); - $services->set(CatchExceptionNameMatchingTypeRector::class); - $services->set(UseIncrementAssignRector::class); - $services->set(SplitDoubleAssignRector::class); - $services->set(VarConstantCommentRector::class); - $services->set(EncapsedStringsToSprintfRector::class); - $services->set(WrapEncapsedVariableInCurlyBracesRector::class); - $services->set(NewlineBeforeNewAssignSetRector::class); - $services->set(ManualJsonStringToJsonEncodeArrayRector::class); - $services->set(AddArrayDefaultToArrayPropertyRector::class); - $services->set(AddFalseDefaultToBoolPropertyRector::class); - $services->set(MakeInheritedMethodVisibilitySameAsParentRector::class); - $services->set(CallUserFuncArrayToVariadicRector::class); - $services->set(VersionCompareFuncCallToConstantRector::class); - $services->set(UseMessageVariableForSprintfInSymfonyStyleRector::class); +return static function (RectorConfig $rectorConfig): void { + foreach (CodingStyleLevel::RULES_WITH_CONFIGURATION as $rectorClass => $configuration) { + $rectorConfig->ruleWithConfiguration($rectorClass, $configuration); + } - $services->set(FuncCallToConstFetchRector::class) - ->call('configure', [[ - FuncCallToConstFetchRector::FUNCTIONS_TO_CONSTANTS => [ - 'php_sapi_name' => 'PHP_SAPI', - 'pi' => 'M_PI', - ], - ]]); - - $services->set(SeparateMultiUseImportsRector::class); - $services->set(RemoveDoubleUnderscoreInMethodNameRector::class); - $services->set(PostIncDecToPreIncDecRector::class); - $services->set(UnSpreadOperatorRector::class); + // the rule order matters, as its used in withCodingStyleLevel() method + // place the safest rules first, follow by more complex ones + $rectorConfig->rules(CodingStyleLevel::RULES); }; diff --git a/config/set/datetime-to-carbon.php b/config/set/datetime-to-carbon.php new file mode 100644 index 00000000000..705aa0839fa --- /dev/null +++ b/config/set/datetime-to-carbon.php @@ -0,0 +1,18 @@ +rules([ + DateFuncCallToCarbonRector::class, + DateTimeInstanceToCarbonRector::class, + DateTimeMethodCallToCarbonRector::class, + TimeFuncCallToCarbonRector::class, + ]); +}; diff --git a/config/set/dead-code.php b/config/set/dead-code.php index dfbb8f50dea..2f354616b86 100644 --- a/config/set/dead-code.php +++ b/config/set/dead-code.php @@ -2,100 +2,9 @@ declare(strict_types=1); -use Rector\CodeQuality\Rector\Return_\SimplifyUselessVariableRector; -use Rector\DeadCode\Rector\Array_\RemoveDuplicatedArrayKeyRector; -use Rector\DeadCode\Rector\Assign\RemoveAssignOfVoidReturnFunctionRector; -use Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector; -use Rector\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector; -use Rector\DeadCode\Rector\BinaryOp\RemoveDuplicatedInstanceOfRector; -use Rector\DeadCode\Rector\BooleanAnd\RemoveAndTrueRector; -use Rector\DeadCode\Rector\Cast\RecastingRemovalRector; -use Rector\DeadCode\Rector\ClassConst\RemoveUnusedPrivateClassConstantRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveDeadConstructorRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveDelegatingParentCallRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveEmptyClassMethodRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveLastReturnRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedConstructorParamRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodParameterRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnTagRector; -use Rector\DeadCode\Rector\Concat\RemoveConcatAutocastRector; -use Rector\DeadCode\Rector\Expression\RemoveDeadStmtRector; -use Rector\DeadCode\Rector\Expression\SimplifyMirrorAssignRector; -use Rector\DeadCode\Rector\For_\RemoveDeadIfForeachForRector; -use Rector\DeadCode\Rector\For_\RemoveDeadLoopRector; -use Rector\DeadCode\Rector\Foreach_\RemoveUnusedForeachKeyRector; -use Rector\DeadCode\Rector\FunctionLike\RemoveCodeAfterReturnRector; -use Rector\DeadCode\Rector\FunctionLike\RemoveDeadReturnRector; -use Rector\DeadCode\Rector\FunctionLike\RemoveDuplicatedIfReturnRector; -use Rector\DeadCode\Rector\FunctionLike\RemoveOverriddenValuesRector; -use Rector\DeadCode\Rector\If_\RemoveDeadInstanceOfRector; -use Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector; -use Rector\DeadCode\Rector\If_\SimplifyIfElseWithSameContentRector; -use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfFunctionExistsRector; -use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector; -use Rector\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector; -use Rector\DeadCode\Rector\Node\RemoveNonExistingVarAnnotationRector; -use Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector; -use Rector\DeadCode\Rector\PropertyProperty\RemoveNullPropertyInitializationRector; -use Rector\DeadCode\Rector\Return_\RemoveDeadConditionAboveReturnRector; -use Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector; -use Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector; -use Rector\DeadCode\Rector\Switch_\RemoveDuplicatedCaseInSwitchRector; -use Rector\DeadCode\Rector\Ternary\TernaryToBooleanOrFalseToBooleanAndRector; -use Rector\DeadCode\Rector\TryCatch\RemoveDeadTryCatchRector; -use Rector\PHPUnit\Rector\ClassMethod\RemoveEmptyTestMethodRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\Level\DeadCodeLevel; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UnwrapFutureCompatibleIfFunctionExistsRector::class); - $services->set(UnwrapFutureCompatibleIfPhpVersionRector::class); - $services->set(RecastingRemovalRector::class); - $services->set(RemoveDeadStmtRector::class); - $services->set(RemoveDuplicatedArrayKeyRector::class); - $services->set(RemoveUnusedForeachKeyRector::class); - $services->set(RemoveParentCallWithoutParentRector::class); - $services->set(RemoveEmptyClassMethodRector::class); - $services->set(RemoveDoubleAssignRector::class); - $services->set(SimplifyMirrorAssignRector::class); - $services->set(RemoveOverriddenValuesRector::class); - $services->set(RemoveUnusedPrivatePropertyRector::class); - $services->set(RemoveUnusedPrivateClassConstantRector::class); - $services->set(RemoveUnusedPrivateMethodRector::class); - $services->set(RemoveCodeAfterReturnRector::class); - $services->set(RemoveDeadConstructorRector::class); - $services->set(RemoveDeadReturnRector::class); - $services->set(RemoveDeadIfForeachForRector::class); - $services->set(RemoveAndTrueRector::class); - $services->set(RemoveConcatAutocastRector::class); - $services->set(SimplifyUselessVariableRector::class); - $services->set(RemoveDelegatingParentCallRector::class); - $services->set(RemoveDuplicatedInstanceOfRector::class); - $services->set(RemoveDuplicatedCaseInSwitchRector::class); - $services->set(RemoveNullPropertyInitializationRector::class); - $services->set(RemoveUnreachableStatementRector::class); - $services->set(SimplifyIfElseWithSameContentRector::class); - $services->set(TernaryToBooleanOrFalseToBooleanAndRector::class); - $services->set(RemoveEmptyTestMethodRector::class); - $services->set(RemoveDeadTryCatchRector::class); - $services->set(RemoveUnusedVariableAssignRector::class); - $services->set(RemoveDuplicatedIfReturnRector::class); - $services->set(RemoveUnusedNonEmptyArrayBeforeForeachRector::class); - $services->set(RemoveAssignOfVoidReturnFunctionRector::class); - $services->set(RemoveEmptyMethodCallRector::class); - $services->set(RemoveDeadConditionAboveReturnRector::class); - $services->set(RemoveUnusedConstructorParamRector::class); - $services->set(RemoveDeadInstanceOfRector::class); - $services->set(RemoveDeadLoopRector::class); - $services->set(RemoveUnusedPrivateMethodParameterRector::class); - - // docblock - $services->set(RemoveUselessParamTagRector::class); - $services->set(RemoveUselessReturnTagRector::class); - $services->set(RemoveNonExistingVarAnnotationRector::class); - $services->set(RemoveUnusedPromotedPropertyRector::class); - $services->set(RemoveLastReturnRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules(DeadCodeLevel::RULES); }; diff --git a/config/set/defluent.php b/config/set/defluent.php deleted file mode 100644 index 16ce9b8e2a6..00000000000 --- a/config/set/defluent.php +++ /dev/null @@ -1,33 +0,0 @@ -services(); - - // variable/property - $services->set(FluentChainMethodCallToNormalMethodCallRector::class); - $services->set(ReturnFluentChainMethodCallToNormalMethodCallRector::class); - - // new - $services->set(NewFluentChainMethodCallToNonFluentRector::class); - $services->set(ReturnNewFluentChainMethodCallToNonFluentRector::class); - - $services->set(ReturnThisRemoveRector::class); - $services->set(DefluentReturnMethodCallRector::class); - $services->set(MethodCallOnSetterMethodCallToStandaloneAssignRector::class); - $services->set(InArgFluentChainMethodCallToStandaloneMethodCallRector::class); -}; diff --git a/config/set/downgrade-php53.php b/config/set/downgrade-php53.php deleted file mode 100644 index 403fc5f414a..00000000000 --- a/config/set/downgrade-php53.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_53); - - $services = $containerConfigurator->services(); - $services->set(DirConstToFileConstRector::class); -}; diff --git a/config/set/downgrade-php70.php b/config/set/downgrade-php70.php deleted file mode 100644 index cac6e235ccf..00000000000 --- a/config/set/downgrade-php70.php +++ /dev/null @@ -1,36 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_56); - - $services = $containerConfigurator->services(); - $services->set(DowngradeScalarTypeDeclarationRector::class); - $services->set(DowngradeStrictTypeDeclarationRector::class); - $services->set(DowngradeSelfTypeDeclarationRector::class); - $services->set(DowngradeAnonymousClassRector::class); - $services->set(DowngradeNullCoalesceRector::class); - $services->set(DowngradeSpaceshipRector::class); - $services->set(DowngradeDefineArrayConstantRector::class); - $services->set(DowngradeSessionStartArrayOptionsRector::class); - $services->set(SplitGroupedUseImportsRector::class); - $services->set(DowngradeGeneratedScalarTypesRector::class); - $services->set(DowngradeParentTypeDeclarationRector::class); -}; diff --git a/config/set/downgrade-php71.php b/config/set/downgrade-php71.php deleted file mode 100644 index 69e3a236131..00000000000 --- a/config/set/downgrade-php71.php +++ /dev/null @@ -1,32 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_70); - - $services = $containerConfigurator->services(); - $services->set(DowngradeNullableTypeDeclarationRector::class); - $services->set(DowngradeVoidTypeDeclarationRector::class); - $services->set(DowngradeClassConstantVisibilityRector::class); - $services->set(DowngradePipeToMultiCatchExceptionRector::class); - $services->set(SymmetricArrayDestructuringToListRector::class); - $services->set(DowngradeNegativeStringOffsetToStrlenRector::class); - $services->set(DowngradeKeysInListRector::class); - $services->set(DowngradeIterablePseudoTypeDeclarationRector::class); - $services->set(DowngradeIsIterableRector::class); -}; diff --git a/config/set/downgrade-php72.php b/config/set/downgrade-php72.php deleted file mode 100644 index f507482513d..00000000000 --- a/config/set/downgrade-php72.php +++ /dev/null @@ -1,22 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_71); - - $services = $containerConfigurator->services(); - $services->set(DowngradeObjectTypeDeclarationRector::class); - $services->set(DowngradeParameterTypeWideningRector::class); - $services->set(DowngradePregUnmatchedAsNullConstantRector::class); - $services->set(DowngradeStreamIsattyRector::class); -}; diff --git a/config/set/downgrade-php73.php b/config/set/downgrade-php73.php deleted file mode 100644 index 6464eb5271b..00000000000 --- a/config/set/downgrade-php73.php +++ /dev/null @@ -1,27 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_72); - - $services = $containerConfigurator->services(); - $services->set(DowngradeFlexibleHeredocSyntaxRector::class); - $services->set(DowngradeListReferenceAssignmentRector::class); - $services->set(DowngradeTrailingCommasInFunctionCallsRector::class); - $services->set(DowngradeArrayKeyFirstLastRector::class); - $services->set(SetCookieOptionsArrayToArgumentsRector::class); - $services->set(DowngradeIsCountableRector::class); -}; diff --git a/config/set/downgrade-php74.php b/config/set/downgrade-php74.php deleted file mode 100644 index a1ba41a5d25..00000000000 --- a/config/set/downgrade-php74.php +++ /dev/null @@ -1,34 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_73); - - $services = $containerConfigurator->services(); - $services->set(DowngradeTypedPropertyRector::class); - $services->set(ArrowFunctionToAnonymousFunctionRector::class); - $services->set(DowngradeCovariantReturnTypeRector::class); - $services->set(DowngradeContravariantArgumentTypeRector::class); - $services->set(DowngradeNullCoalescingOperatorRector::class); - $services->set(DowngradeNumericLiteralSeparatorRector::class); - $services->set(DowngradeStripTagsCallWithArrayRector::class); - $services->set(DowngradeArraySpreadRector::class); - $services->set(DowngradeArrayMergeCallWithoutArgumentsRector::class); - $services->set(DowngradeFreadFwriteFalsyToNegationRector::class); -}; diff --git a/config/set/downgrade-php80.php b/config/set/downgrade-php80.php deleted file mode 100644 index eed3ae01e38..00000000000 --- a/config/set/downgrade-php80.php +++ /dev/null @@ -1,71 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_74); - - $services = $containerConfigurator->services(); - $services->set(RemoveInterfacesRector::class) - ->call('configure', [[ - RemoveInterfacesRector::INTERFACES_TO_REMOVE => [ - // @see https://wiki.php.net/rfc/stringable - 'Stringable', - ], - ]]); - - $services->set(DowngradeNamedArgumentRector::class); - - $services->set(DowngradeAttributeToAnnotationRector::class) - ->call('configure', [[ - DowngradeAttributeToAnnotationRector::ATTRIBUTE_TO_ANNOTATION => ValueObjectInliner::inline([ - // Symfony - new DowngradeAttributeToAnnotation('Symfony\Contracts\Service\Attribute\Required', 'required'), - // Nette - new DowngradeAttributeToAnnotation('Nette\DI\Attributes\Inject', 'inject'), - ]), - ]]); - - $services->set(DowngradeUnionTypeTypedPropertyRector::class); - $services->set(DowngradeUnionTypeDeclarationRector::class); - $services->set(DowngradeMixedTypeDeclarationRector::class); - $services->set(DowngradeStaticTypeDeclarationRector::class); - $services->set(DowngradeAbstractPrivateMethodInTraitRector::class); - $services->set(DowngradePropertyPromotionRector::class); - $services->set(DowngradeNonCapturingCatchesRector::class); - $services->set(DowngradeStrContainsRector::class); - $services->set(DowngradeMatchToSwitchRector::class); - $services->set(DowngradeClassOnObjectToGetClassRector::class); - $services->set(DowngradeNullsafeToTernaryOperatorRector::class); - $services->set(DowngradeTrailingCommasInParamUseRector::class); - $services->set(DowngradeStrStartsWithRector::class); - $services->set(DowngradeStrEndsWithRector::class); - $services->set(DowngradePhpTokenRector::class); - $services->set(DowngradeThrowExprRector::class); -}; diff --git a/config/set/downgrade-php81.php b/config/set/downgrade-php81.php deleted file mode 100644 index 8a362c2d14f..00000000000 --- a/config/set/downgrade-php81.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_74); - - $services = $containerConfigurator->services(); - $services->set(DowngradeFinalizePublicClassConstantRector::class); -}; diff --git a/config/set/early-return.php b/config/set/early-return.php index 70be964cbe5..582b6f3c69d 100644 --- a/config/set/early-return.php +++ b/config/set/early-return.php @@ -2,30 +2,25 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector; -use Rector\EarlyReturn\Rector\Foreach_\ReturnAfterToEarlyOnBreakRector; -use Rector\EarlyReturn\Rector\If_\ChangeAndIfToEarlyReturnRector; use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector; use Rector\EarlyReturn\Rector\If_\ChangeNestedIfsToEarlyReturnRector; use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector; -use Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector; use Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector; use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector; -use Rector\EarlyReturn\Rector\Return_\ReturnBinaryAndToEarlyReturnRector; use Rector\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\EarlyReturn\Rector\StmtsAwareInterface\ReturnEarlyIfVariableRector; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ChangeNestedForeachIfsToEarlyContinueRector::class); - $services->set(ChangeAndIfToEarlyReturnRector::class); - $services->set(ChangeIfElseValueAssignToEarlyReturnRector::class); - $services->set(ChangeNestedIfsToEarlyReturnRector::class); - $services->set(RemoveAlwaysElseRector::class); - $services->set(ReturnBinaryAndToEarlyReturnRector::class); - $services->set(ChangeOrIfReturnToEarlyReturnRector::class); - $services->set(ChangeOrIfContinueToMultiContinueRector::class); - $services->set(ReturnAfterToEarlyOnBreakRector::class); - $services->set(PreparedValueToEarlyReturnRector::class); - $services->set(ReturnBinaryOrToEarlyReturnRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + ChangeNestedForeachIfsToEarlyContinueRector::class, + ChangeIfElseValueAssignToEarlyReturnRector::class, + ChangeNestedIfsToEarlyReturnRector::class, + RemoveAlwaysElseRector::class, + ChangeOrIfContinueToMultiContinueRector::class, + PreparedValueToEarlyReturnRector::class, + ReturnBinaryOrToEarlyReturnRector::class, + ReturnEarlyIfVariableRector::class, + ]); }; diff --git a/config/set/flysystem-20.php b/config/set/flysystem-20.php deleted file mode 100644 index de0708efc9e..00000000000 --- a/config/set/flysystem-20.php +++ /dev/null @@ -1,35 +0,0 @@ -services(); - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - // Rename is now move, specific for files. - new MethodCallRename('League\Flysystem\FilesystemInterface', 'rename', 'move'), - - // No arbitrary abbreviations - new MethodCallRename('League\Flysystem\FilesystemInterface', 'createDir', 'createDirectory'), - - // Writes are now deterministic - new MethodCallRename('League\Flysystem\FilesystemInterface', 'update', 'write'), - new MethodCallRename('League\Flysystem\FilesystemInterface', 'updateStream', 'writeStream'), - new MethodCallRename('League\Flysystem\FilesystemInterface', 'put', 'write'), - new MethodCallRename('League\Flysystem\FilesystemInterface', 'putStream', 'writeStream'), - - // Metadata getters are renamed - new MethodCallRename('League\Flysystem\FilesystemInterface', 'getTimestamp', 'lastModified'), - new MethodCallRename('League\Flysystem\FilesystemInterface', 'has', 'fileExists'), - new MethodCallRename('League\Flysystem\FilesystemInterface', 'getMimetype', 'mimeType'), - new MethodCallRename('League\Flysystem\FilesystemInterface', 'getSize', 'fileSize'), - new MethodCallRename('League\Flysystem\FilesystemInterface', 'getVisibility', 'visibility'), - ]), - ]]); -}; diff --git a/config/set/framework-extra-bundle-40.php b/config/set/framework-extra-bundle-40.php deleted file mode 100644 index 370ad991e02..00000000000 --- a/config/set/framework-extra-bundle-40.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(ReplaceSensioRouteAnnotationWithSymfonyRector::class); - - $services->set(RemoveServiceFromSensioRouteRector::class); -}; diff --git a/config/set/framework-extra-bundle-50.php b/config/set/framework-extra-bundle-50.php deleted file mode 100644 index c1e4fb90e73..00000000000 --- a/config/set/framework-extra-bundle-50.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(TemplateAnnotationToThisRenderRector::class); -}; diff --git a/config/set/gmagick-to-imagick.php b/config/set/gmagick-to-imagick.php new file mode 100644 index 00000000000..b179f01f482 --- /dev/null +++ b/config/set/gmagick-to-imagick.php @@ -0,0 +1,161 @@ +ruleWithConfiguration(RenameClassRector::class, [ + 'Gmagick' => 'Imagick', + 'GmagickPixel' => 'ImagickPixel', + ]); + + $rectorConfig + ->ruleWithConfiguration(RenameMethodRector::class, [ + new MethodCallRename('Gmagick', 'addimage', 'addImage'), + new MethodCallRename('Gmagick', 'addnoiseimage', 'addNoiseImage'), + new MethodCallRename('Gmagick', 'annotateimage', 'annotateImage'), + new MethodCallRename('Gmagick', 'blurimage', 'blurImage'), + new MethodCallRename('Gmagick', 'borderimage', 'borderImage'), + new MethodCallRename('Gmagick', 'charcoalimage', 'charcoalImage'), + new MethodCallRename('Gmagick', 'chopimage', 'chopImage'), + new MethodCallRename('Gmagick', 'commentimage', 'commentImage'), + new MethodCallRename('Gmagick', 'compositeimage', 'compositeImage'), + new MethodCallRename('Gmagick', 'cropimage', 'cropImage'), + new MethodCallRename('Gmagick', 'cropthumbnailimage', 'cropThumbnailImage'), + new MethodCallRename('Gmagick', 'cyclecolormapimage', 'cycleColormapImage'), + new MethodCallRename('Gmagick', 'deconstructimages', 'deconstructImages'), + new MethodCallRename('Gmagick', 'despeckleimage', 'despeckleImage'), + new MethodCallRename('Gmagick', 'drawimage', 'drawImage'), + new MethodCallRename('Gmagick', 'edgeimage', 'edgeImage'), + new MethodCallRename('Gmagick', 'embossimage', 'embossImage'), + new MethodCallRename('Gmagick', 'enhanceimage', 'enhanceImage'), + new MethodCallRename('Gmagick', 'equalizeimage', 'equalizeImage'), + new MethodCallRename('Gmagick', 'flipimage', 'flipImage'), + new MethodCallRename('Gmagick', 'flopimage', 'flopImage'), + new MethodCallRename('Gmagick', 'frameimage', 'frameImage'), + new MethodCallRename('Gmagick', 'gammaimage', 'gammaImage'), + new MethodCallRename('Gmagick', 'getcopyright', 'getCopyright'), + new MethodCallRename('Gmagick', 'getfilename', 'getFilename'), + new MethodCallRename('Gmagick', 'getimagebackgroundcolor', 'getImageBackgroundColor'), + new MethodCallRename('Gmagick', 'getimageblueprimary', 'getImageBluePrimary'), + new MethodCallRename('Gmagick', 'getimagebordercolor', 'getImageBorderColor'), + new MethodCallRename('Gmagick', 'getimagechanneldepth', 'getImageChannelDepth'), + new MethodCallRename('Gmagick', 'getimagecolors', 'getImageColors'), + new MethodCallRename('Gmagick', 'getimagecolorspace', 'getImageColorspace'), + new MethodCallRename('Gmagick', 'getimagecompose', 'getImageCompose'), + new MethodCallRename('Gmagick', 'getimagedelay', 'getImageDelay'), + new MethodCallRename('Gmagick', 'getimagedepth', 'getImageDepth'), + new MethodCallRename('Gmagick', 'getimagedispose', 'getImageDispose'), + new MethodCallRename('Gmagick', 'getimageextrema', 'getImageExtrema'), + new MethodCallRename('Gmagick', 'getimagefilename', 'getImageFilename'), + new MethodCallRename('Gmagick', 'getimageformat', 'getImageFormat'), + new MethodCallRename('Gmagick', 'getimagegamma', 'getImageGamma'), + new MethodCallRename('Gmagick', 'getimagegreenprimary', 'getImageGreenPrimary'), + new MethodCallRename('Gmagick', 'getimageheight', 'getImageHeight'), + new MethodCallRename('Gmagick', 'getimagehistogram', 'getImageHistogram'), + new MethodCallRename('Gmagick', 'getimageindex', 'getImageIndex'), + new MethodCallRename('Gmagick', 'getimageinterlacescheme', 'getImageInterlaceScheme'), + new MethodCallRename('Gmagick', 'getimageiterations', 'getImageIterations'), + new MethodCallRename('Gmagick', 'getimagematte', 'getImageMatte'), + new MethodCallRename('Gmagick', 'getimagemattecolor', 'getImageMatteColor'), + new MethodCallRename('Gmagick', 'getimageprofile', 'getImageProfile'), + new MethodCallRename('Gmagick', 'getimageredprimary', 'getImageRedPrimary'), + new MethodCallRename('Gmagick', 'getimagerenderingintent', 'getImageRenderingIntent'), + new MethodCallRename('Gmagick', 'getimageresolution', 'getImageResolution'), + new MethodCallRename('Gmagick', 'getimagescene', 'getImageScene'), + new MethodCallRename('Gmagick', 'getimagesignature', 'getImageSignature'), + new MethodCallRename('Gmagick', 'getimagetype', 'getImageType'), + new MethodCallRename('Gmagick', 'getimageunits', 'getImageUnits'), + new MethodCallRename('Gmagick', 'getimagewhitepoint', 'getImageWhitePoint'), + new MethodCallRename('Gmagick', 'getimagewidth', 'getImageWidth'), + new MethodCallRename('Gmagick', 'getpackagename', 'getPackageName'), + new MethodCallRename('Gmagick', 'getquantumdepth', 'getQuantumDepth'), + new MethodCallRename('Gmagick', 'getreleasedate', 'getReleaseDate'), + new MethodCallRename('Gmagick', 'getsamplingfactors', 'getSamplingFactors'), + new MethodCallRename('Gmagick', 'getsize', 'getSize'), + new MethodCallRename('Gmagick', 'getversion', 'getVersion'), + new MethodCallRename('Gmagick', 'hasnextimage', 'hasNextImage'), + new MethodCallRename('Gmagick', 'haspreviousimage', 'hasPreviousImage'), + new MethodCallRename('Gmagick', 'implodeimage', 'implodeImage'), + new MethodCallRename('Gmagick', 'labelimage', 'labelImage'), + new MethodCallRename('Gmagick', 'levelimage', 'levelImage'), + new MethodCallRename('Gmagick', 'magnifyimage', 'magnifyImage'), + new MethodCallRename('Gmagick', 'mapimage', 'mapImage'), + new MethodCallRename('Gmagick', 'medianfilterimage', 'medianFilterImage'), + new MethodCallRename('Gmagick', 'minifyimage', 'minifyImage'), + new MethodCallRename('Gmagick', 'modulateimage', 'modulateImage'), + new MethodCallRename('Gmagick', 'motionblurimage', 'motionBlurImage'), + new MethodCallRename('Gmagick', 'newimage', 'newImage'), + new MethodCallRename('Gmagick', 'nextimage', 'nextImage'), + new MethodCallRename('Gmagick', 'normalizeimage', 'normalizeImage'), + new MethodCallRename('Gmagick', 'oilpaintimage', 'oilPaintImage'), + new MethodCallRename('Gmagick', 'previousimage', 'previousImage'), + new MethodCallRename('Gmagick', 'profileimage', 'profileImage'), + new MethodCallRename('Gmagick', 'quantizeimage', 'quantizeImage'), + new MethodCallRename('Gmagick', 'quantizeimages', 'quantizeImages'), + new MethodCallRename('Gmagick', 'queryfontmetrics', 'queryFontMetrics'), + new MethodCallRename('Gmagick', 'queryfonts', 'queryFonts'), + new MethodCallRename('Gmagick', 'queryformats', 'queryFormats'), + new MethodCallRename('Gmagick', 'radialblurimage', 'radialBlurImage'), + new MethodCallRename('Gmagick', 'raiseimage', 'raiseImage'), + new MethodCallRename('Gmagick', 'readimage', 'readimages'), + new MethodCallRename('Gmagick', 'readimageblob', 'readImageBlob'), + new MethodCallRename('Gmagick', 'readimagefile', 'readImageFile'), + new MethodCallRename('Gmagick', 'reducenoiseimage', 'reduceNoiseImage'), + new MethodCallRename('Gmagick', 'removeimage', 'removeImage'), + new MethodCallRename('Gmagick', 'removeimageprofile', 'removeImageProfile'), + new MethodCallRename('Gmagick', 'resampleimage', 'resampleImage'), + new MethodCallRename('Gmagick', 'resizeimage', 'resizeImage'), + new MethodCallRename('Gmagick', 'rollimage', 'rollImage'), + new MethodCallRename('Gmagick', 'rotateimage', 'rotateImage'), + new MethodCallRename('Gmagick', 'scaleimage', 'scaleImage'), + new MethodCallRename('Gmagick', 'separateimagechannel', 'separateImageChannel'), + new MethodCallRename('Gmagick', 'setCompressionQuality', 'getCompressionQuality'), + new MethodCallRename('Gmagick', 'setfilename', 'setFilename'), + new MethodCallRename('Gmagick', 'setimagebackgroundcolor', 'setImageBackgroundColor'), + new MethodCallRename('Gmagick', 'setimageblueprimary', 'setImageBluePrimary'), + new MethodCallRename('Gmagick', 'setimagebordercolor', 'setImageBorderColor'), + new MethodCallRename('Gmagick', 'setimagechanneldepth', 'setImageChannelDepth'), + new MethodCallRename('Gmagick', 'setimagecolorspace', 'setImageColorspace'), + new MethodCallRename('Gmagick', 'setimagecompose', 'setImageCompose'), + new MethodCallRename('Gmagick', 'setimagedelay', 'setImageDelay'), + new MethodCallRename('Gmagick', 'setimagedepth', 'setImageDepth'), + new MethodCallRename('Gmagick', 'setimagedispose', 'setImageDispose'), + new MethodCallRename('Gmagick', 'setimagefilename', 'setImageFilename'), + new MethodCallRename('Gmagick', 'setimageformat', 'setImageFormat'), + new MethodCallRename('Gmagick', 'setimagegamma', 'setImageGamma'), + new MethodCallRename('Gmagick', 'setimagegreenprimary', 'setImageGreenPrimary'), + new MethodCallRename('Gmagick', 'setimageindex', 'setImageIndex'), + new MethodCallRename('Gmagick', 'setimageinterlacescheme', 'setImageInterlaceScheme'), + new MethodCallRename('Gmagick', 'setimageiterations', 'setImageIterations'), + new MethodCallRename('Gmagick', 'setimageprofile', 'setImageProfile'), + new MethodCallRename('Gmagick', 'setimageredprimary', 'setImageRedPrimary'), + new MethodCallRename('Gmagick', 'setimagerenderingintent', 'setImageRenderingIntent'), + new MethodCallRename('Gmagick', 'setimageresolution', 'setImageResolution'), + new MethodCallRename('Gmagick', 'setimagescene', 'setImageScene'), + new MethodCallRename('Gmagick', 'setimagetype', 'setImageType'), + new MethodCallRename('Gmagick', 'setimageunits', 'setImageUnits'), + new MethodCallRename('Gmagick', 'setimagewhitepoint', 'setImageWhitePoint'), + new MethodCallRename('Gmagick', 'setsamplingfactors', 'setSamplingFactors'), + new MethodCallRename('Gmagick', 'setsize', 'setSize'), + new MethodCallRename('Gmagick', 'shearimage', 'shearImage'), + new MethodCallRename('Gmagick', 'solarizeimage', 'solarizeImage'), + new MethodCallRename('Gmagick', 'spreadimage', 'spreadImage'), + new MethodCallRename('Gmagick', 'stripimage', 'stripImage'), + new MethodCallRename('Gmagick', 'swirlimage', 'swirlImage'), + new MethodCallRename('Gmagick', 'thumbnailimage', 'thumbnailImage'), + new MethodCallRename('Gmagick', 'trimimage', 'trimImage'), + new MethodCallRename('Gmagick', 'writeimage', 'writeImage'), + new MethodCallRename('GmagickPixel', 'getcolor', 'getColor'), + new MethodCallRename('GmagickPixel', 'getcolorcount', 'getColorCount'), + new MethodCallRename('GmagickPixel', 'getcolorvalue', 'getColorValue'), + new MethodCallRename('GmagickPixel', 'setcolor', 'setColor'), + new MethodCallRename('GmagickPixel', 'setcolorvalue', 'setColorValue'), + ]); +}; diff --git a/config/set/gmagick_to_imagick.php b/config/set/gmagick_to_imagick.php deleted file mode 100644 index 0b599b7421c..00000000000 --- a/config/set/gmagick_to_imagick.php +++ /dev/null @@ -1,167 +0,0 @@ -services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'Gmagick' => 'Imagick', - 'GmagickPixel' => 'ImagickPixel', - ], - ]]); - - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - new MethodCallRename('Gmagick', 'addimage', 'addImage'), - new MethodCallRename('Gmagick', 'addnoiseimage', 'addNoiseImage'), - new MethodCallRename('Gmagick', 'annotateimage', 'annotateImage'), - new MethodCallRename('Gmagick', 'blurimage', 'blurImage'), - new MethodCallRename('Gmagick', 'borderimage', 'borderImage'), - new MethodCallRename('Gmagick', 'charcoalimage', 'charcoalImage'), - new MethodCallRename('Gmagick', 'chopimage', 'chopImage'), - new MethodCallRename('Gmagick', 'commentimage', 'commentImage'), - new MethodCallRename('Gmagick', 'compositeimage', 'compositeImage'), - new MethodCallRename('Gmagick', 'cropimage', 'cropImage'), - new MethodCallRename('Gmagick', 'cropthumbnailimage', 'cropThumbnailImage'), - new MethodCallRename('Gmagick', 'cyclecolormapimage', 'cycleColormapImage'), - new MethodCallRename('Gmagick', 'deconstructimages', 'deconstructImages'), - new MethodCallRename('Gmagick', 'despeckleimage', 'despeckleImage'), - new MethodCallRename('Gmagick', 'drawimage', 'drawImage'), - new MethodCallRename('Gmagick', 'edgeimage', 'edgeImage'), - new MethodCallRename('Gmagick', 'embossimage', 'embossImage'), - new MethodCallRename('Gmagick', 'enhanceimage', 'enhanceImage'), - new MethodCallRename('Gmagick', 'equalizeimage', 'equalizeImage'), - new MethodCallRename('Gmagick', 'flipimage', 'flipImage'), - new MethodCallRename('Gmagick', 'flopimage', 'flopImage'), - new MethodCallRename('Gmagick', 'frameimage', 'frameImage'), - new MethodCallRename('Gmagick', 'gammaimage', 'gammaImage'), - new MethodCallRename('Gmagick', 'getcopyright', 'getCopyright'), - new MethodCallRename('Gmagick', 'getfilename', 'getFilename'), - new MethodCallRename('Gmagick', 'getimagebackgroundcolor', 'getImageBackgroundColor'), - new MethodCallRename('Gmagick', 'getimageblueprimary', 'getImageBluePrimary'), - new MethodCallRename('Gmagick', 'getimagebordercolor', 'getImageBorderColor'), - new MethodCallRename('Gmagick', 'getimagechanneldepth', 'getImageChannelDepth'), - new MethodCallRename('Gmagick', 'getimagecolors', 'getImageColors'), - new MethodCallRename('Gmagick', 'getimagecolorspace', 'getImageColorspace'), - new MethodCallRename('Gmagick', 'getimagecompose', 'getImageCompose'), - new MethodCallRename('Gmagick', 'getimagedelay', 'getImageDelay'), - new MethodCallRename('Gmagick', 'getimagedepth', 'getImageDepth'), - new MethodCallRename('Gmagick', 'getimagedispose', 'getImageDispose'), - new MethodCallRename('Gmagick', 'getimageextrema', 'getImageExtrema'), - new MethodCallRename('Gmagick', 'getimagefilename', 'getImageFilename'), - new MethodCallRename('Gmagick', 'getimageformat', 'getImageFormat'), - new MethodCallRename('Gmagick', 'getimagegamma', 'getImageGamma'), - new MethodCallRename('Gmagick', 'getimagegreenprimary', 'getImageGreenPrimary'), - new MethodCallRename('Gmagick', 'getimageheight', 'getImageHeight'), - new MethodCallRename('Gmagick', 'getimagehistogram', 'getImageHistogram'), - new MethodCallRename('Gmagick', 'getimageindex', 'getImageIndex'), - new MethodCallRename('Gmagick', 'getimageinterlacescheme', 'getImageInterlaceScheme'), - new MethodCallRename('Gmagick', 'getimageiterations', 'getImageIterations'), - new MethodCallRename('Gmagick', 'getimagematte', 'getImageMatte'), - new MethodCallRename('Gmagick', 'getimagemattecolor', 'getImageMatteColor'), - new MethodCallRename('Gmagick', 'getimageprofile', 'getImageProfile'), - new MethodCallRename('Gmagick', 'getimageredprimary', 'getImageRedPrimary'), - new MethodCallRename('Gmagick', 'getimagerenderingintent', 'getImageRenderingIntent'), - new MethodCallRename('Gmagick', 'getimageresolution', 'getImageResolution'), - new MethodCallRename('Gmagick', 'getimagescene', 'getImageScene'), - new MethodCallRename('Gmagick', 'getimagesignature', 'getImageSignature'), - new MethodCallRename('Gmagick', 'getimagetype', 'getImageType'), - new MethodCallRename('Gmagick', 'getimageunits', 'getImageUnits'), - new MethodCallRename('Gmagick', 'getimagewhitepoint', 'getImageWhitePoint'), - new MethodCallRename('Gmagick', 'getimagewidth', 'getImageWidth'), - new MethodCallRename('Gmagick', 'getpackagename', 'getPackageName'), - new MethodCallRename('Gmagick', 'getquantumdepth', 'getQuantumDepth'), - new MethodCallRename('Gmagick', 'getreleasedate', 'getReleaseDate'), - new MethodCallRename('Gmagick', 'getsamplingfactors', 'getSamplingFactors'), - new MethodCallRename('Gmagick', 'getsize', 'getSize'), - new MethodCallRename('Gmagick', 'getversion', 'getVersion'), - new MethodCallRename('Gmagick', 'hasnextimage', 'hasNextImage'), - new MethodCallRename('Gmagick', 'haspreviousimage', 'hasPreviousImage'), - new MethodCallRename('Gmagick', 'implodeimage', 'implodeImage'), - new MethodCallRename('Gmagick', 'labelimage', 'labelImage'), - new MethodCallRename('Gmagick', 'levelimage', 'levelImage'), - new MethodCallRename('Gmagick', 'magnifyimage', 'magnifyImage'), - new MethodCallRename('Gmagick', 'mapimage', 'mapImage'), - new MethodCallRename('Gmagick', 'medianfilterimage', 'medianFilterImage'), - new MethodCallRename('Gmagick', 'minifyimage', 'minifyImage'), - new MethodCallRename('Gmagick', 'modulateimage', 'modulateImage'), - new MethodCallRename('Gmagick', 'motionblurimage', 'motionBlurImage'), - new MethodCallRename('Gmagick', 'newimage', 'newImage'), - new MethodCallRename('Gmagick', 'nextimage', 'nextImage'), - new MethodCallRename('Gmagick', 'normalizeimage', 'normalizeImage'), - new MethodCallRename('Gmagick', 'oilpaintimage', 'oilPaintImage'), - new MethodCallRename('Gmagick', 'previousimage', 'previousImage'), - new MethodCallRename('Gmagick', 'profileimage', 'profileImage'), - new MethodCallRename('Gmagick', 'quantizeimage', 'quantizeImage'), - new MethodCallRename('Gmagick', 'quantizeimages', 'quantizeImages'), - new MethodCallRename('Gmagick', 'queryfontmetrics', 'queryFontMetrics'), - new MethodCallRename('Gmagick', 'queryfonts', 'queryFonts'), - new MethodCallRename('Gmagick', 'queryformats', 'queryFormats'), - new MethodCallRename('Gmagick', 'radialblurimage', 'radialBlurImage'), - new MethodCallRename('Gmagick', 'raiseimage', 'raiseImage'), - new MethodCallRename('Gmagick', 'readimage', 'readimages'), - new MethodCallRename('Gmagick', 'readimageblob', 'readImageBlob'), - new MethodCallRename('Gmagick', 'readimagefile', 'readImageFile'), - new MethodCallRename('Gmagick', 'reducenoiseimage', 'reduceNoiseImage'), - new MethodCallRename('Gmagick', 'removeimage', 'removeImage'), - new MethodCallRename('Gmagick', 'removeimageprofile', 'removeImageProfile'), - new MethodCallRename('Gmagick', 'resampleimage', 'resampleImage'), - new MethodCallRename('Gmagick', 'resizeimage', 'resizeImage'), - new MethodCallRename('Gmagick', 'rollimage', 'rollImage'), - new MethodCallRename('Gmagick', 'rotateimage', 'rotateImage'), - new MethodCallRename('Gmagick', 'scaleimage', 'scaleImage'), - new MethodCallRename('Gmagick', 'separateimagechannel', 'separateImageChannel'), - new MethodCallRename('Gmagick', 'setCompressionQuality', 'getCompressionQuality'), - new MethodCallRename('Gmagick', 'setfilename', 'setFilename'), - new MethodCallRename('Gmagick', 'setimagebackgroundcolor', 'setImageBackgroundColor'), - new MethodCallRename('Gmagick', 'setimageblueprimary', 'setImageBluePrimary'), - new MethodCallRename('Gmagick', 'setimagebordercolor', 'setImageBorderColor'), - new MethodCallRename('Gmagick', 'setimagechanneldepth', 'setImageChannelDepth'), - new MethodCallRename('Gmagick', 'setimagecolorspace', 'setImageColorspace'), - new MethodCallRename('Gmagick', 'setimagecompose', 'setImageCompose'), - new MethodCallRename('Gmagick', 'setimagedelay', 'setImageDelay'), - new MethodCallRename('Gmagick', 'setimagedepth', 'setImageDepth'), - new MethodCallRename('Gmagick', 'setimagedispose', 'setImageDispose'), - new MethodCallRename('Gmagick', 'setimagefilename', 'setImageFilename'), - new MethodCallRename('Gmagick', 'setimageformat', 'setImageFormat'), - new MethodCallRename('Gmagick', 'setimagegamma', 'setImageGamma'), - new MethodCallRename('Gmagick', 'setimagegreenprimary', 'setImageGreenPrimary'), - new MethodCallRename('Gmagick', 'setimageindex', 'setImageIndex'), - new MethodCallRename('Gmagick', 'setimageinterlacescheme', 'setImageInterlaceScheme'), - new MethodCallRename('Gmagick', 'setimageiterations', 'setImageIterations'), - new MethodCallRename('Gmagick', 'setimageprofile', 'setImageProfile'), - new MethodCallRename('Gmagick', 'setimageredprimary', 'setImageRedPrimary'), - new MethodCallRename('Gmagick', 'setimagerenderingintent', 'setImageRenderingIntent'), - new MethodCallRename('Gmagick', 'setimageresolution', 'setImageResolution'), - new MethodCallRename('Gmagick', 'setimagescene', 'setImageScene'), - new MethodCallRename('Gmagick', 'setimagetype', 'setImageType'), - new MethodCallRename('Gmagick', 'setimageunits', 'setImageUnits'), - new MethodCallRename('Gmagick', 'setimagewhitepoint', 'setImageWhitePoint'), - new MethodCallRename('Gmagick', 'setsamplingfactors', 'setSamplingFactors'), - new MethodCallRename('Gmagick', 'setsize', 'setSize'), - new MethodCallRename('Gmagick', 'shearimage', 'shearImage'), - new MethodCallRename('Gmagick', 'solarizeimage', 'solarizeImage'), - new MethodCallRename('Gmagick', 'spreadimage', 'spreadImage'), - new MethodCallRename('Gmagick', 'stripimage', 'stripImage'), - new MethodCallRename('Gmagick', 'swirlimage', 'swirlImage'), - new MethodCallRename('Gmagick', 'thumbnailimage', 'thumbnailImage'), - new MethodCallRename('Gmagick', 'trimimage', 'trimImage'), - new MethodCallRename('Gmagick', 'writeimage', 'writeImage'), - new MethodCallRename('GmagickPixel', 'getcolor', 'getColor'), - new MethodCallRename('GmagickPixel', 'getcolorcount', 'getColorCount'), - new MethodCallRename('GmagickPixel', 'getcolorvalue', 'getColorValue'), - new MethodCallRename('GmagickPixel', 'setcolor', 'setColor'), - new MethodCallRename('GmagickPixel', 'setcolorvalue', 'setColorValue'), - ]), - ]]); -}; diff --git a/config/set/guzzle50.php b/config/set/guzzle50.php deleted file mode 100644 index 938d87aaf45..00000000000 --- a/config/set/guzzle50.php +++ /dev/null @@ -1,45 +0,0 @@ -services(); - - # both uses "%classes_to_defluent% - $services->set(FluentChainMethodCallToNormalMethodCallRector::class); - - $configuration = [ - new FuncCallToMethodCall('GuzzleHttp\json_decode', 'GuzzleHttp\Utils', 'jsonDecode'), - new FuncCallToMethodCall('GuzzleHttp\get_path', 'GuzzleHttp\Utils', 'getPath'), - ]; - - $services->set(FuncCallToMethodCallRector::class) - ->call('configure', [[ - FuncCallToMethodCallRector::FUNC_CALL_TO_CLASS_METHOD_CALL => ValueObjectInliner::inline($configuration), - ]]); - - $services->set(StaticCallToFuncCallRector::class) - ->call('configure', [[ - StaticCallToFuncCallRector::STATIC_CALLS_TO_FUNCTIONS => ValueObjectInliner::inline([ - new StaticCallToFuncCall('GuzzleHttp\Utils', 'setPath', 'GuzzleHttp\set_path'), - new StaticCallToFuncCall('GuzzleHttp\Pool', 'batch', 'GuzzleHttp\Pool\batch'), - ]), - ]]); - - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - new MethodCallRename('GuzzleHttp\Message\MessageInterface', 'getHeaderLines', 'getHeaderAsArray'), - ]), - ]]); -}; diff --git a/config/set/instanceof.php b/config/set/instanceof.php new file mode 100644 index 00000000000..9305d1c8240 --- /dev/null +++ b/config/set/instanceof.php @@ -0,0 +1,24 @@ +rules([ + EmptyOnNullableObjectToInstanceOfRector::class, + InlineIsAInstanceOfRector::class, + FlipTypeControlToUseExclusiveTypeRector::class, + RemoveDeadInstanceOfRector::class, + FlipNegatedTernaryInstanceofRector::class, + BinaryOpNullableToInstanceofRector::class, + WhileNullableToInstanceofRector::class, + ]); +}; diff --git a/config/set/league-event-30.php b/config/set/league-event-30.php deleted file mode 100644 index fe3db7ac0d9..00000000000 --- a/config/set/league-event-30.php +++ /dev/null @@ -1,103 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(ChangePackageVersionComposerRector::class) - ->call('configure', [[ - ChangePackageVersionComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('league/event', '^3.0'), - ]), - ]]); - - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - new MethodCallRename('League\Event\EventInterface', 'getName', 'eventName'), - new MethodCallRename('League\Event\EmitterInterface', 'emit', 'dispatch'), - new MethodCallRename('League\Event\EmitterInterface', 'addListener', 'subscribeTo'), - new MethodCallRename('League\Event\EmitterInterface', 'addOneTimeListener', 'subscribeOneTo'), - new MethodCallRename('League\Event\EmitterInterface', 'useListenerProvider', 'subscribeListenersFrom'), - new MethodCallRename('League\Event\ListenerInterface', 'handle', '__invoke'), - ]), - ]]); - - $services->set(AddParamTypeDeclarationRector::class) - ->call('configure', [[ - AddParamTypeDeclarationRector::PARAMETER_TYPEHINTS => ValueObjectInliner::inline([ - new AddParamTypeDeclaration( - 'League\Event\ListenerInterface', - '__invoke', - 0, - new ObjectWithoutClassType() - ), - ]), - ]]); - - $services->set(AddReturnTypeDeclarationRector::class) - ->call('configure', [[ - AddReturnTypeDeclarationRector::METHOD_RETURN_TYPES => ValueObjectInliner::inline([ - new AddReturnTypeDeclaration('League\Event\EventInterface', 'eventName', new StringType()), - new AddReturnTypeDeclaration('League\Event\ListenerInterface', '__invoke', new VoidType()), - ]), - ]]); - - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'League\Event\Emitter' => 'League\Event\EventDispatcher', - 'League\Event\ListenerInterface' => 'League\Event\Listener', - 'League\Event\GeneratorInterface' => 'League\Event\EventGenerator', - 'League\Event\ListenerProviderInterface' => 'League\Event\ListenerSubscriber', - 'League\Event\ListenerAcceptorInterface' => 'League\Event\ListenerRegistry', - ], - ]]); - - $services->set(AddInterfaceByParentRector::class) - ->call('configure', [[ - AddInterfaceByParentRector::INTERFACE_BY_PARENT => [ - 'League\Event\Event' => 'League\Event\HasEventName', - 'League\Event\AbstractListener' => 'League\Event\Listener', - ], - ]]); - - $services->set(RemoveInterfacesRector::class) - ->call('configure', [[ - RemoveInterfacesRector::INTERFACES_TO_REMOVE => ['League\Event\EventInterface'], - ]]); - - $services->set(RemoveParentRector::class) - ->call('configure', [[ - RemoveParentRector::PARENT_TYPES_TO_REMOVE => [ - 'League\Event\AbstractEvent', - 'League\Event\Event', - 'League\Event\AbstractListener', - ], - ]]); - - $services->set(DispatchStringToObjectRector::class); -}; diff --git a/config/set/level/up-to-php53.php b/config/set/level/up-to-php53.php new file mode 100644 index 00000000000..19d2a5fc571 --- /dev/null +++ b/config/set/level/up-to-php53.php @@ -0,0 +1,10 @@ +sets([SetList::PHP_53, SetList::PHP_52]); +}; diff --git a/config/set/level/up-to-php54.php b/config/set/level/up-to-php54.php new file mode 100644 index 00000000000..53cd450330e --- /dev/null +++ b/config/set/level/up-to-php54.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_54, LevelSetList::UP_TO_PHP_53]); +}; diff --git a/config/set/level/up-to-php55.php b/config/set/level/up-to-php55.php new file mode 100644 index 00000000000..ae8dcfd2d18 --- /dev/null +++ b/config/set/level/up-to-php55.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_55, LevelSetList::UP_TO_PHP_54]); +}; diff --git a/config/set/level/up-to-php56.php b/config/set/level/up-to-php56.php new file mode 100644 index 00000000000..749c54bbe46 --- /dev/null +++ b/config/set/level/up-to-php56.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_56, LevelSetList::UP_TO_PHP_55]); +}; diff --git a/config/set/level/up-to-php70.php b/config/set/level/up-to-php70.php new file mode 100644 index 00000000000..2849697ae9a --- /dev/null +++ b/config/set/level/up-to-php70.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_70, LevelSetList::UP_TO_PHP_56]); +}; diff --git a/config/set/level/up-to-php71.php b/config/set/level/up-to-php71.php new file mode 100644 index 00000000000..25ca2ef960d --- /dev/null +++ b/config/set/level/up-to-php71.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_71, LevelSetList::UP_TO_PHP_70]); +}; diff --git a/config/set/level/up-to-php72.php b/config/set/level/up-to-php72.php new file mode 100644 index 00000000000..5e08d491b99 --- /dev/null +++ b/config/set/level/up-to-php72.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_72, LevelSetList::UP_TO_PHP_71]); +}; diff --git a/config/set/level/up-to-php73.php b/config/set/level/up-to-php73.php new file mode 100644 index 00000000000..3756c17e2ef --- /dev/null +++ b/config/set/level/up-to-php73.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_73, LevelSetList::UP_TO_PHP_72]); +}; diff --git a/config/set/level/up-to-php74.php b/config/set/level/up-to-php74.php new file mode 100644 index 00000000000..f8e09e8b075 --- /dev/null +++ b/config/set/level/up-to-php74.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_74, LevelSetList::UP_TO_PHP_73]); +}; diff --git a/config/set/level/up-to-php80.php b/config/set/level/up-to-php80.php new file mode 100644 index 00000000000..55e3442ba03 --- /dev/null +++ b/config/set/level/up-to-php80.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_80, LevelSetList::UP_TO_PHP_74]); +}; diff --git a/config/set/level/up-to-php81.php b/config/set/level/up-to-php81.php new file mode 100644 index 00000000000..b524056347b --- /dev/null +++ b/config/set/level/up-to-php81.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_81, LevelSetList::UP_TO_PHP_80]); +}; diff --git a/config/set/level/up-to-php82.php b/config/set/level/up-to-php82.php new file mode 100644 index 00000000000..d7f0431d1ae --- /dev/null +++ b/config/set/level/up-to-php82.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_82, LevelSetList::UP_TO_PHP_81]); +}; diff --git a/config/set/level/up-to-php83.php b/config/set/level/up-to-php83.php new file mode 100644 index 00000000000..6b9040afd49 --- /dev/null +++ b/config/set/level/up-to-php83.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_83, LevelSetList::UP_TO_PHP_82]); +}; diff --git a/config/set/level/up-to-php84.php b/config/set/level/up-to-php84.php new file mode 100644 index 00000000000..a81c02efddf --- /dev/null +++ b/config/set/level/up-to-php84.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_84, LevelSetList::UP_TO_PHP_83]); +}; diff --git a/config/set/level/up-to-php85.php b/config/set/level/up-to-php85.php new file mode 100644 index 00000000000..11503ac18c0 --- /dev/null +++ b/config/set/level/up-to-php85.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_85, LevelSetList::UP_TO_PHP_84]); +}; diff --git a/config/set/level/up-to-php86.php b/config/set/level/up-to-php86.php new file mode 100644 index 00000000000..abd47e742f1 --- /dev/null +++ b/config/set/level/up-to-php86.php @@ -0,0 +1,11 @@ +sets([SetList::PHP_86, LevelSetList::UP_TO_PHP_85]); +}; diff --git a/config/set/monolog20.php b/config/set/monolog20.php deleted file mode 100644 index 2088db9f9c2..00000000000 --- a/config/set/monolog20.php +++ /dev/null @@ -1,30 +0,0 @@ -services(); - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - new MethodCallRename('Monolog\Logger', 'addDebug', 'debug'), - new MethodCallRename('Monolog\Logger', 'addInfo', 'info'), - new MethodCallRename('Monolog\Logger', 'addNotice', 'notice'), - new MethodCallRename('Monolog\Logger', 'addWarning', 'warning'), - new MethodCallRename('Monolog\Logger', 'addError', 'error'), - new MethodCallRename('Monolog\Logger', 'addCritical', 'critical'), - new MethodCallRename('Monolog\Logger', 'addAlert', 'alert'), - new MethodCallRename('Monolog\Logger', 'addEmergency', 'emergency'), - new MethodCallRename('Monolog\Logger', 'warn', 'warning'), - new MethodCallRename('Monolog\Logger', 'err', 'error'), - new MethodCallRename('Monolog\Logger', 'crit', 'critical'), - new MethodCallRename('Monolog\Logger', 'emerg', 'emergency'), - ]), - ]]); -}; diff --git a/config/set/mysql-to-mysqli.php b/config/set/mysql-to-mysqli.php deleted file mode 100644 index 46b178086bf..00000000000 --- a/config/set/mysql-to-mysqli.php +++ /dev/null @@ -1,86 +0,0 @@ -services(); - - # https://stackoverflow.com/a/1390625/1348344 - # https://github.com/philip/MySQLConverterTool/blob/master/Converter.php - # https://www.phpclasses.org/blog/package/9199/post/3-Smoothly-Migrate-your-PHP-Code-using-the-Old-MySQL-extension-to-MySQLi.html - $services->set(MysqlAssignToMysqliRector::class); - - $services->set(MysqlFuncCallToMysqliRector::class); - - $services->set(RemoveFuncCallArgRector::class) - ->call('configure', [[ - RemoveFuncCallArgRector::REMOVED_FUNCTION_ARGUMENTS => ValueObjectInliner::inline([ - new RemoveFuncCallArg('mysql_pconnect', 3), - new RemoveFuncCallArg('mysql_connect', 3), - new RemoveFuncCallArg('mysql_connect', 4), - ]), - ]]); - - $services->set(MysqlPConnectToMysqliConnectRector::class); - - # first swap arguments, then rename - $services->set(SwapFuncCallArgumentsRector::class) - ->call('configure', [[ - SwapFuncCallArgumentsRector::FUNCTION_ARGUMENT_SWAPS => ValueObjectInliner::inline([ - new SwapFuncCallArguments('mysql_query', [1, 0]), - new SwapFuncCallArguments('mysql_real_escape_string', [1, 0]), - new SwapFuncCallArguments('mysql_select_db', [1, 0]), - new SwapFuncCallArguments('mysql_set_charset', [1, 0]), - ]), - ]]); - - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'mysql_connect' => 'mysqli_connect', - 'mysql_data_seek' => 'mysqli_data_seek', - 'mysql_fetch_array' => 'mysqli_fetch_array', - 'mysql_fetch_assoc' => 'mysqli_fetch_assoc', - 'mysql_fetch_lengths' => 'mysqli_fetch_lengths', - 'mysql_fetch_object' => 'mysqli_fetch_object', - 'mysql_fetch_row' => 'mysqli_fetch_row', - 'mysql_field_seek' => 'mysqli_field_seek', - 'mysql_free_result' => 'mysqli_free_result', - 'mysql_get_client_info' => 'mysqli_get_client_info', - 'mysql_num_fields' => 'mysqli_num_fields', - 'mysql_numfields' => 'mysqli_num_fields', - 'mysql_num_rows' => 'mysqli_num_rows', - 'mysql_numrows' => 'mysqli_num_rows', - ], - ]]); - - # http://php.net/manual/en/mysql.constants.php → http://php.net/manual/en/mysqli.constants.php - $services->set(RenameConstantRector::class) - ->call('configure', [[ - RenameConstantRector::OLD_TO_NEW_CONSTANTS => [ - 'MYSQL_ASSOC' => 'MYSQLI_ASSOC', - 'MYSQL_BOTH' => 'MYSQLI_BOTH', - 'MYSQL_CLIENT_COMPRESS' => 'MYSQLI_CLIENT_COMPRESS', - 'MYSQL_CLIENT_IGNORE_SPACE' => 'MYSQLI_CLIENT_IGNORE_SPACE', - 'MYSQL_CLIENT_INTERACTIVE' => 'MYSQLI_CLIENT_INTERACTIVE', - 'MYSQL_CLIENT_SSL' => 'MYSQLI_CLIENT_SSL', - 'MYSQL_NUM' => 'MYSQLI_NUM', - 'MYSQL_PRIMARY_KEY_FLAG' => 'MYSQLI_PRI_KEY_FLAG', - ], - ]]); - - $services->set(MysqlQueryMysqlErrorWithLinkRector::class); -}; diff --git a/config/set/naming.php b/config/set/naming.php index 404241a7ffd..5c2a1f6b38d 100644 --- a/config/set/naming.php +++ b/config/set/naming.php @@ -2,20 +2,21 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector; use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector; use Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector; use Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector; use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector; use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchMethodCallReturnTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameParamToMatchTypeRector::class); - $services->set(RenamePropertyToMatchTypeRector::class); - $services->set(RenameVariableToMatchNewTypeRector::class); - $services->set(RenameVariableToMatchMethodCallReturnTypeRector::class); - $services->set(RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class); - $services->set(RenameForeachValueVariableToMatchExprVariableRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + RenameParamToMatchTypeRector::class, + RenamePropertyToMatchTypeRector::class, + RenameVariableToMatchNewTypeRector::class, + RenameVariableToMatchMethodCallReturnTypeRector::class, + RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class, + RenameForeachValueVariableToMatchExprVariableRector::class, + ]); }; diff --git a/config/set/nette-utils/nette-utils4.php b/config/set/nette-utils/nette-utils4.php new file mode 100644 index 00000000000..d07fdd94e4b --- /dev/null +++ b/config/set/nette-utils/nette-utils4.php @@ -0,0 +1,10 @@ +rules([UtilsJsonStaticCallNamedArgRector::class]); +}; diff --git a/config/set/order.php b/config/set/order.php deleted file mode 100644 index 7a0800c7ed0..00000000000 --- a/config/set/order.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(OrderPrivateMethodsByUseRector::class); -}; diff --git a/config/set/phalcon40.php b/config/set/phalcon40.php deleted file mode 100644 index a89373243e8..00000000000 --- a/config/set/phalcon40.php +++ /dev/null @@ -1,127 +0,0 @@ -services(); - - # for class renames is better - https://docs.phalcon.io/4.0/en/upgrade#cheat-sheet - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'Phalcon\Acl\Adapter' => 'Phalcon\Acl\Adapter\AbstractAdapter', - 'Phalcon\Acl\Resource' => 'Phalcon\Acl\Component', - 'Phalcon\Acl\ResourceInterface' => 'Phalcon\Acl\ComponentInterface', - 'Phalcon\Acl\ResourceAware' => 'Phalcon\Acl\ComponentAware', - 'Phalcon\Assets\ResourceInterface' => 'Phalcon\Assets\AssetInterface', - 'Phalcon\Validation\MessageInterface' => 'Phalcon\Messages\MessageInterface', - 'Phalcon\Mvc\Model\MessageInterface' => 'Phalcon\Messages\MessageInterface', - 'Phalcon\Annotations\Adapter' => 'Phalcon\Annotations\Adapter\AbstractAdapter', - 'Phalcon\Annotations\Factory' => 'Phalcon\Annotations\AnnotationsFactory', - 'Phalcon\Application' => 'Phalcon\Application\AbstractApplication', - 'Phalcon\Assets\Resource' => 'Phalcon\Assets\Asset', - 'Phalcon\Assets\Resource\Css' => 'Phalcon\Assets\Asset\Css', - 'Phalcon\Assets\Resource\Js' => 'Phalcon\Assets\Asset\Js', - 'Phalcon\Cache\Backend' => 'Phalcon\Cache', - 'Phalcon\Cache\Backend\Factory' => 'Phalcon\Cache\AdapterFactory', - 'Phalcon\Cache\Backend\Apcu' => 'Phalcon\Cache\Adapter\Apcu', - 'Phalcon\Cache\Backend\File' => 'Phalcon\Cache\Adapter\Stream', - 'Phalcon\Cache\Backend\Libmemcached' => 'Phalcon\Cache\Adapter\Libmemcached', - 'Phalcon\Cache\Backend\Memory' => 'Phalcon\Cache\Adapter\Memory', - 'Phalcon\Cache\Backend\Redis' => 'Phalcon\Cache\Adapter\Redis', - 'Phalcon\Cache\Exception' => 'Phalcon\Cache\Exception\Exception', - 'Phalcon\Config\Factory' => 'Phalcon\Config\ConfigFactory', - 'Phalcon\Db' => 'Phalcon\Db\AbstractDb', - 'Phalcon\Db\Adapter' => 'Phalcon\Db\Adapter\AbstractAdapter', - 'Phalcon\Db\Adapter\Pdo' => 'Phalcon\Db\Adapter\Pdo\AbstractPdo', - 'Phalcon\Db\Adapter\Pdo\Factory' => 'Phalcon\Db\Adapter\PdoFactory', - 'Phalcon\Dispatcher' => 'Phalcon\Dispatcher\AbstractDispatcher', - 'Phalcon\Factory' => 'Phalcon\Factory\AbstractFactory', - 'Phalcon\Flash' => 'Phalcon\Flash\AbstractFlash', - 'Phalcon\Forms\Element' => 'Phalcon\Forms\Element\AbstractElement', - 'Phalcon\Image\Adapter' => 'Phalcon\Image\Adapter\AbstractAdapter', - 'Phalcon\Image\Factory' => 'Phalcon\Image\ImageFactory', - 'Phalcon\Logger\Adapter' => 'Phalcon\Logger\Adapter\AbstractAdapter', - 'Phalcon\Logger\Adapter\Blackhole' => 'Phalcon\Logger\Adapter\Noop', - 'Phalcon\Logger\Adapter\File' => 'Phalcon\Logger\Adapter\Stream', - 'Phalcon\Logger\Factory' => 'Phalcon\Logger\LoggerFactory', - 'Phalcon\Logger\Formatter' => 'Phalcon\Logger\Formatter\AbstractFormatter', - 'Phalcon\Mvc\Collection' => 'Phalcon\Collection', - 'Phalcon\Mvc\Collection\Exception' => 'Phalcon\Collection\Exception', - 'Phalcon\Mvc\Model\Message' => 'Phalcon\Messages\Message', - 'Phalcon\Mvc\Model\MetaData\Files' => 'Phalcon\Mvc\Model\MetaData\Stream', - 'Phalcon\Mvc\Model\Validator' => 'Phalcon\Validation\Validator', - 'Phalcon\Mvc\Model\Validator\Email' => 'Phalcon\Validation\Validator\Email', - 'Phalcon\Mvc\Model\Validator\Exclusionin' => 'Phalcon\Validation\Validator\ExclusionIn', - 'Phalcon\Mvc\Model\Validator\Inclusionin' => 'Phalcon\Validation\Validator\InclusionIn', - 'Phalcon\Mvc\Model\Validator\Ip' => 'Phalcon\Validation\Validator\Ip', - 'Phalcon\Mvc\Model\Validator\Numericality' => 'Phalcon\Validation\Validator\Numericality', - 'Phalcon\Mvc\Model\Validator\PresenceOf' => 'Phalcon\Validation\Validator\PresenceOf', - 'Phalcon\Mvc\Model\Validator\Regex' => 'Phalcon\Validation\Validator\Regex', - 'Phalcon\Mvc\Model\Validator\StringLength' => 'Phalcon\Validation\Validator\StringLength', - 'Phalcon\Mvc\Model\Validator\Uniqueness' => 'Phalcon\Validation\Validator\Uniqueness', - 'Phalcon\Mvc\Model\Validator\Url' => 'Phalcon\Validation\Validator\Url', - 'Phalcon\Mvc\Url' => 'Phalcon\Url', - 'Phalcon\Mvc\Url\Exception' => 'Phalcon\Url\Exception', - 'Phalcon\Mvc\User\Component' => 'Phalcon\Di\Injectable', - 'Phalcon\Mvc\User\Module' => 'Phalcon\Di\Injectable', - 'Phalcon\Mvc\User\Plugin' => 'Phalcon\Di\Injectable', - 'Phalcon\Mvc\View\Engine' => 'Phalcon\Mvc\View\Engine\AbstractEngine', - 'Phalcon\Paginator\Adapter' => 'Phalcon\Paginator\Adapter\AbstractAdapter', - 'Phalcon\Paginator\Factory' => 'Phalcon\Paginator\PaginatorFactory', - 'Phalcon\Session\Adapter' => 'Phalcon\Session\Adapter\AbstractAdapter', - 'Phalcon\Session\Adapter\Files' => 'Phalcon\Session\Adapter\Stream', - 'Phalcon\Session\Factory' => 'Phalcon\Session\Manager', - 'Phalcon\Translate\Adapter' => 'Phalcon\Translate\Adapter\AbstractAdapter', - 'Phalcon\Translate\Factory' => 'Phalcon\Translate\TranslateFactory', - 'Phalcon\Validation\CombinedFieldsValidator' => 'Phalcon\Validation\AbstractCombinedFieldsValidator', - 'Phalcon\Validation\Message' => 'Phalcon\Messages\Message', - 'Phalcon\Validation\Message\Group' => 'Phalcon\Messages\Messages', - 'Phalcon\Validation\Validator' => 'Phalcon\Validation\AbstractValidator', - 'Phalcon\Text' => 'Phalcon\Helper\Str', - 'Phalcon\Session\AdapterInterface' => 'SessionHandlerInterface', - ], - ]]); - - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - new MethodCallRename('Phalcon\Acl\AdapterInterface', 'isResource', 'isComponent'), - new MethodCallRename('Phalcon\Acl\AdapterInterface', 'addResource', 'addComponent'), - new MethodCallRename('Phalcon\Acl\AdapterInterface', 'addResourceAccess', 'addComponentAccess'), - new MethodCallRename('Phalcon\Acl\AdapterInterface', 'dropResourceAccess', 'dropComponentAccess'), - new MethodCallRename('Phalcon\Acl\AdapterInterface', 'getActiveResource', 'getActiveComponent'), - new MethodCallRename('Phalcon\Acl\AdapterInterface', 'getResources', 'getComponents'), - new MethodCallRename('Phalcon\Acl\Adapter\Memory', 'isResource', 'isComponent'), - new MethodCallRename('Phalcon\Acl\Adapter\Memory', 'addResource', 'addComponent'), - new MethodCallRename('Phalcon\Acl\Adapter\Memory', 'addResourceAccess', 'addComponentAccess'), - new MethodCallRename('Phalcon\Acl\Adapter\Memory', 'dropResourceAccess', 'dropComponentAccess'), - new MethodCallRename('Phalcon\Acl\Adapter\Memory', 'getResources', 'getComponents'), - new MethodCallRename('Phalcon\Cli\Console', 'addModules', 'registerModules'), - new MethodCallRename('Phalcon\Dispatcher', 'setModelBinding', 'setModelBinder'), - new MethodCallRename('Phalcon\Assets\Manager', 'addResource', 'addAsset'), - new MethodCallRename('Phalcon\Assets\Manager', 'addResourceByType', 'addAssetByType'), - new MethodCallRename('Phalcon\Assets\Manager', 'collectionResourcesByType', 'collectionAssetsByType'), - new MethodCallRename('Phalcon\Http\RequestInterface', 'isSecureRequest', 'isSecure'), - new MethodCallRename('Phalcon\Http\RequestInterface', 'isSoapRequested', 'isSoap'), - new MethodCallRename('Phalcon\Paginator', 'getPaginate', 'paginate'), - new MethodCallRename('Phalcon\Mvc\Model\Criteria', 'order', 'orderBy'), - ]), - ]]); - - $services->set(RenameConstantRector::class) - ->call('configure', [[ - RenameConstantRector::OLD_TO_NEW_CONSTANTS => [ - 'FILTER_SPECIAL_CHARS' => 'FILTER_SPECIAL', - 'FILTER_ALPHANUM' => 'FILTER_ALNUM', - ], - ]]); -}; diff --git a/config/set/php-polyfills.php b/config/set/php-polyfills.php new file mode 100644 index 00000000000..8b9bd56921f --- /dev/null +++ b/config/set/php-polyfills.php @@ -0,0 +1,26 @@ +rules([ + ArrayKeyFirstLastRector::class, + IsCountableRector::class, + GetDebugTypeRector::class, + StrStartsWithRector::class, + StrEndsWithRector::class, + StrContainsRector::class, + ]); +}; diff --git a/config/set/php52.php b/config/set/php52.php index 813caae4970..32c11a13f6d 100644 --- a/config/set/php52.php +++ b/config/set/php52.php @@ -2,23 +2,17 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php52\Rector\Property\VarToPublicPropertyRector; use Rector\Php52\Rector\Switch_\ContinueToBreakInSwitchRector; use Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector; use Rector\Removing\ValueObject\RemoveFuncCallArg; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(VarToPublicPropertyRector::class); - $services->set(ContinueToBreakInSwitchRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([VarToPublicPropertyRector::class, ContinueToBreakInSwitchRector::class]); - $services->set(RemoveFuncCallArgRector::class) - ->call('configure', [[ - RemoveFuncCallArgRector::REMOVED_FUNCTION_ARGUMENTS => ValueObjectInliner::inline([ - // see https://www.php.net/manual/en/function.ldap-first-attribute.php - new RemoveFuncCallArg('ldap_first_attribute', 2), - ]), - ]]); + $rectorConfig->ruleWithConfiguration(RemoveFuncCallArgRector::class, [ + // see https://www.php.net/manual/en/function.ldap-first-attribute.php + new RemoveFuncCallArg('ldap_first_attribute', 2), + ]); }; diff --git a/config/set/php53.php b/config/set/php53.php index e34f32ffc64..36c384b4dc9 100644 --- a/config/set/php53.php +++ b/config/set/php53.php @@ -2,19 +2,15 @@ declare(strict_types=1); -use Rector\Php53\Rector\AssignRef\ClearReturnNewByReferenceRector; +use Rector\Config\RectorConfig; use Rector\Php53\Rector\FuncCall\DirNameFileConstantToDirConstantRector; use Rector\Php53\Rector\Ternary\TernaryToElvisRector; use Rector\Php53\Rector\Variable\ReplaceHttpServerVarsByServerRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(TernaryToElvisRector::class); - - $services->set(DirNameFileConstantToDirConstantRector::class); - - $services->set(ClearReturnNewByReferenceRector::class); - - $services->set(ReplaceHttpServerVarsByServerRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + TernaryToElvisRector::class, + DirNameFileConstantToDirConstantRector::class, + ReplaceHttpServerVarsByServerRector::class, + ]); }; diff --git a/config/set/php54.php b/config/set/php54.php index ad658792681..5542edd735e 100644 --- a/config/set/php54.php +++ b/config/set/php54.php @@ -2,21 +2,21 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; +use Rector\Php54\Rector\Array_\LongArrayToShortArrayRector; use Rector\Php54\Rector\Break_\RemoveZeroBreakContinueRector; use Rector\Php54\Rector\FuncCall\RemoveReferenceFromCallRector; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'mysqli_param_count' => 'mysqli_stmt_param_count', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + LongArrayToShortArrayRector::class, + RemoveReferenceFromCallRector::class, + RemoveZeroBreakContinueRector::class, + ]); - $services->set(RemoveReferenceFromCallRector::class); - - $services->set(RemoveZeroBreakContinueRector::class); + $rectorConfig + ->ruleWithConfiguration(RenameFunctionRector::class, [ + 'mysqli_param_count' => 'mysqli_stmt_param_count', + ]); }; diff --git a/config/set/php55.php b/config/set/php55.php index a92ed2619e3..6b2f320e64f 100644 --- a/config/set/php55.php +++ b/config/set/php55.php @@ -2,12 +2,21 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php55\Rector\Class_\ClassConstantToSelfClassRector; +use Rector\Php55\Rector\ClassConstFetch\StaticToSelfOnFinalClassRector; +use Rector\Php55\Rector\FuncCall\GetCalledClassToSelfClassRector; +use Rector\Php55\Rector\FuncCall\GetCalledClassToStaticClassRector; +use Rector\Php55\Rector\FuncCall\PregReplaceEModifierRector; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StringClassNameToClassConstantRector::class); - $services->set(ClassConstantToSelfClassRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + StringClassNameToClassConstantRector::class, + ClassConstantToSelfClassRector::class, + PregReplaceEModifierRector::class, + GetCalledClassToSelfClassRector::class, + GetCalledClassToStaticClassRector::class, + StaticToSelfOnFinalClassRector::class, + ]); }; diff --git a/config/set/php56.php b/config/set/php56.php index b8fc941986a..b2c294b9644 100644 --- a/config/set/php56.php +++ b/config/set/php56.php @@ -2,56 +2,49 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php56\Rector\FuncCall\PowToExpRector; -use Rector\Php56\Rector\FunctionLike\AddDefaultValueForUndefinedVariableRector; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(PowToExpRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(PowToExpRector::class); - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'mcrypt_generic_end' => 'mcrypt_generic_deinit', - 'set_socket_blocking' => 'stream_set_blocking', - 'ocibindbyname' => 'oci_bind_by_name', - 'ocicancel' => 'oci_cancel', - 'ocicolumnisnull' => 'oci_field_is_null', - 'ocicolumnname' => 'oci_field_name', - 'ocicolumnprecision' => 'oci_field_precision', - 'ocicolumnscale' => 'oci_field_scale', - 'ocicolumnsize' => 'oci_field_size', - 'ocicolumntype' => 'oci_field_type', - 'ocicolumntyperaw' => 'oci_field_type_raw', - 'ocicommit' => 'oci_commit', - 'ocidefinebyname' => 'oci_define_by_name', - 'ocierror' => 'oci_error', - 'ociexecute' => 'oci_execute', - 'ocifetch' => 'oci_fetch', - 'ocifetchstatement' => 'oci_fetch_all', - 'ocifreecursor' => 'oci_free_statement', - 'ocifreestatement' => 'oci_free_statement', - 'ociinternaldebug' => 'oci_internal_debug', - 'ocilogoff' => 'oci_close', - 'ocilogon' => 'oci_connect', - 'ocinewcollection' => 'oci_new_collection', - 'ocinewcursor' => 'oci_new_cursor', - 'ocinewdescriptor' => 'oci_new_descriptor', - 'ocinlogon' => 'oci_new_connect', - 'ocinumcols' => 'oci_num_fields', - 'ociparse' => 'oci_parse', - 'ociplogon' => 'oci_pconnect', - 'ociresult' => 'oci_result', - 'ocirollback' => 'oci_rollback', - 'ocirowcount' => 'oci_num_rows', - 'ociserverversion' => 'oci_server_version', - 'ocisetprefetch' => 'oci_set_prefetch', - 'ocistatementtype' => 'oci_statement_type', - ], - ]]); - - # inspired by level in psalm - https://github.com/vimeo/psalm/blob/82e0bcafac723fdf5007a31a7ae74af1736c9f6f/tests/FileManipulationTest.php#L1063 - $services->set(AddDefaultValueForUndefinedVariableRector::class); + $rectorConfig + ->ruleWithConfiguration(RenameFunctionRector::class, [ + 'mcrypt_generic_end' => 'mcrypt_generic_deinit', + 'set_socket_blocking' => 'stream_set_blocking', + 'ocibindbyname' => 'oci_bind_by_name', + 'ocicancel' => 'oci_cancel', + 'ocicolumnisnull' => 'oci_field_is_null', + 'ocicolumnname' => 'oci_field_name', + 'ocicolumnprecision' => 'oci_field_precision', + 'ocicolumnscale' => 'oci_field_scale', + 'ocicolumnsize' => 'oci_field_size', + 'ocicolumntype' => 'oci_field_type', + 'ocicolumntyperaw' => 'oci_field_type_raw', + 'ocicommit' => 'oci_commit', + 'ocidefinebyname' => 'oci_define_by_name', + 'ocierror' => 'oci_error', + 'ociexecute' => 'oci_execute', + 'ocifetch' => 'oci_fetch', + 'ocifetchstatement' => 'oci_fetch_all', + 'ocifreecursor' => 'oci_free_statement', + 'ocifreestatement' => 'oci_free_statement', + 'ociinternaldebug' => 'oci_internal_debug', + 'ocilogoff' => 'oci_close', + 'ocilogon' => 'oci_connect', + 'ocinewcollection' => 'oci_new_collection', + 'ocinewcursor' => 'oci_new_cursor', + 'ocinewdescriptor' => 'oci_new_descriptor', + 'ocinlogon' => 'oci_new_connect', + 'ocinumcols' => 'oci_num_fields', + 'ociparse' => 'oci_parse', + 'ociplogon' => 'oci_pconnect', + 'ociresult' => 'oci_result', + 'ocirollback' => 'oci_rollback', + 'ocirowcount' => 'oci_num_rows', + 'ociserverversion' => 'oci_server_version', + 'ocisetprefetch' => 'oci_set_prefetch', + 'ocistatementtype' => 'oci_statement_type', + ]); }; diff --git a/config/set/php70.php b/config/set/php70.php index 8657511ced3..b16c79ef7a4 100644 --- a/config/set/php70.php +++ b/config/set/php70.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\Assign\ListSplitStringRector; use Rector\Php70\Rector\Assign\ListSwapArrayOrderRector; use Rector\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector; @@ -9,7 +10,6 @@ use Rector\Php70\Rector\FuncCall\CallUserMethodRector; use Rector\Php70\Rector\FuncCall\EregToPregMatchRector; use Rector\Php70\Rector\FuncCall\MultiDirnameRector; -use Rector\Php70\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector; use Rector\Php70\Rector\FuncCall\RandomFunctionRector; use Rector\Php70\Rector\FuncCall\RenameMktimeWithoutArgsToTimeRector; use Rector\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector; @@ -17,52 +17,33 @@ use Rector\Php70\Rector\List_\EmptyListRector; use Rector\Php70\Rector\MethodCall\ThisCallOnStaticMethodToStaticCallRector; use Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector; +use Rector\Php70\Rector\StmtsAwareInterface\IfIssetToCoalescingRector; use Rector\Php70\Rector\Switch_\ReduceMultipleDefaultSwitchRector; use Rector\Php70\Rector\Ternary\TernaryToNullCoalescingRector; use Rector\Php70\Rector\Ternary\TernaryToSpaceshipRector; use Rector\Php70\Rector\Variable\WrapVariableVariableNameInCurlyBracesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import(__DIR__ . '/mysql-to-mysqli.php'); - - $services = $containerConfigurator->services(); - $services->set(Php4ConstructorRector::class); - - $services->set(TernaryToNullCoalescingRector::class); - - $services->set(RandomFunctionRector::class); - - $services->set(ExceptionHandlerTypehintRector::class); - - $services->set(MultiDirnameRector::class); - - $services->set(ListSplitStringRector::class); - - $services->set(EmptyListRector::class); - - # be careful, run this just once, since it can keep swapping order back and forth - $services->set(ListSwapArrayOrderRector::class); - - $services->set(CallUserMethodRector::class); - - $services->set(EregToPregMatchRector::class); - - $services->set(ReduceMultipleDefaultSwitchRector::class); - - $services->set(TernaryToSpaceshipRector::class); - - $services->set(WrapVariableVariableNameInCurlyBracesRector::class); - - $services->set(IfToSpaceshipRector::class); - - $services->set(StaticCallOnNonStaticToInstanceCallRector::class); - - $services->set(ThisCallOnStaticMethodToStaticCallRector::class); - - $services->set(BreakNotInLoopOrSwitchToReturnRector::class); - - $services->set(RenameMktimeWithoutArgsToTimeRector::class); - - $services->set(NonVariableToVariableOnFunctionCallRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + Php4ConstructorRector::class, + TernaryToNullCoalescingRector::class, + RandomFunctionRector::class, + ExceptionHandlerTypehintRector::class, + MultiDirnameRector::class, + ListSplitStringRector::class, + EmptyListRector::class, + // be careful, run this just once, since it can keep swapping order back and forth + ListSwapArrayOrderRector::class, + CallUserMethodRector::class, + EregToPregMatchRector::class, + ReduceMultipleDefaultSwitchRector::class, + TernaryToSpaceshipRector::class, + WrapVariableVariableNameInCurlyBracesRector::class, + IfToSpaceshipRector::class, + StaticCallOnNonStaticToInstanceCallRector::class, + ThisCallOnStaticMethodToStaticCallRector::class, + BreakNotInLoopOrSwitchToReturnRector::class, + RenameMktimeWithoutArgsToTimeRector::class, + IfIssetToCoalescingRector::class, + ]); }; diff --git a/config/set/php71.php b/config/set/php71.php index 9103be0dac3..029b815e8ae 100644 --- a/config/set/php71.php +++ b/config/set/php71.php @@ -2,22 +2,21 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php71\Rector\Assign\AssignArrayToStringRector; use Rector\Php71\Rector\BinaryOp\BinaryOpBetweenNumberAndStringRector; use Rector\Php71\Rector\BooleanOr\IsIterableRector; -use Rector\Php71\Rector\FuncCall\CountOnNullRector; use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector; use Rector\Php71\Rector\List_\ListToArrayDestructRector; use Rector\Php71\Rector\TryCatch\MultiExceptionCatchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(IsIterableRector::class); - $services->set(MultiExceptionCatchRector::class); - $services->set(AssignArrayToStringRector::class); - $services->set(CountOnNullRector::class); - $services->set(RemoveExtraParametersRector::class); - $services->set(BinaryOpBetweenNumberAndStringRector::class); - $services->set(ListToArrayDestructRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + IsIterableRector::class, + MultiExceptionCatchRector::class, + AssignArrayToStringRector::class, + RemoveExtraParametersRector::class, + BinaryOpBetweenNumberAndStringRector::class, + ListToArrayDestructRector::class, + ]); }; diff --git a/config/set/php72.php b/config/set/php72.php index 1fed4b14fcd..7857c06ce80 100644 --- a/config/set/php72.php +++ b/config/set/php72.php @@ -2,53 +2,41 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\Assign\ListEachRector; use Rector\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector; use Rector\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector; use Rector\Php72\Rector\FuncCall\GetClassOnNullRector; -use Rector\Php72\Rector\FuncCall\IsObjectOnIncompleteClassRector; use Rector\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector; use Rector\Php72\Rector\FuncCall\StringifyDefineRector; use Rector\Php72\Rector\FuncCall\StringsAssertNakedRector; use Rector\Php72\Rector\Unset_\UnsetCastRector; use Rector\Php72\Rector\While_\WhileEachToForeachRector; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(WhileEachToForeachRector::class); - - $services->set(ListEachRector::class); - - $services->set(ReplaceEachAssignmentWithKeyCurrentRector::class); - - $services->set(UnsetCastRector::class); - - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - # and imagewbmp - 'jpeg2wbmp' => 'imagecreatefromjpeg', - # or imagewbmp - 'png2wbmp' => 'imagecreatefrompng', - #migration72.deprecated.gmp_random-function - # http://php.net/manual/en/migration72.deprecated.php - # or gmp_random_range - 'gmp_random' => 'gmp_random_bits', - 'read_exif_data' => 'exif_read_data', - ], - ]]); - - $services->set(GetClassOnNullRector::class); - - $services->set(IsObjectOnIncompleteClassRector::class); - - $services->set(ParseStrWithResultArgumentRector::class); - - $services->set(StringsAssertNakedRector::class); - - $services->set(CreateFunctionToAnonymousFunctionRector::class); - - $services->set(StringifyDefineRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenameFunctionRector::class, [ + # and imagewbmp + 'jpeg2wbmp' => 'imagecreatefromjpeg', + # or imagewbmp + 'png2wbmp' => 'imagecreatefrompng', + #migration72.deprecated.gmp_random-function + # http://php.net/manual/en/migration72.deprecated.php + # or gmp_random_range + 'gmp_random' => 'gmp_random_bits', + 'read_exif_data' => 'exif_read_data', + ]); + + $rectorConfig->rules([ + GetClassOnNullRector::class, + ParseStrWithResultArgumentRector::class, + StringsAssertNakedRector::class, + CreateFunctionToAnonymousFunctionRector::class, + StringifyDefineRector::class, + WhileEachToForeachRector::class, + ListEachRector::class, + ReplaceEachAssignmentWithKeyCurrentRector::class, + UnsetCastRector::class, + ]); }; diff --git a/config/set/php73.php b/config/set/php73.php index 763e646ef15..2200f0e88c0 100644 --- a/config/set/php73.php +++ b/config/set/php73.php @@ -2,49 +2,50 @@ declare(strict_types=1); -use Rector\Php52\Rector\Switch_\ContinueToBreakInSwitchRector; +use Rector\Config\RectorConfig; use Rector\Php73\Rector\BooleanOr\IsCountableRector; use Rector\Php73\Rector\ConstFetch\SensitiveConstantNameRector; use Rector\Php73\Rector\FuncCall\ArrayKeyFirstLastRector; -use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector; use Rector\Php73\Rector\FuncCall\RegexDashEscapeRector; use Rector\Php73\Rector\FuncCall\SensitiveDefineRector; +use Rector\Php73\Rector\FuncCall\SetCookieRector; use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector; use Rector\Php73\Rector\String_\SensitiveHereNowDocRector; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(IsCountableRector::class); - $services->set(ArrayKeyFirstLastRector::class); - $services->set(SensitiveDefineRector::class); - $services->set(SensitiveConstantNameRector::class); - $services->set(SensitiveHereNowDocRector::class); - - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - # https://wiki.php.net/rfc/deprecations_php_7_3 - 'image2wbmp' => 'imagewbmp', - 'mbregex_encoding' => 'mb_regex_encoding', - 'mbereg' => 'mb_ereg', - 'mberegi' => 'mb_eregi', - 'mbereg_replace' => 'mb_ereg_replace', - 'mberegi_replace' => 'mb_eregi_replace', - 'mbsplit' => 'mb_split', - 'mbereg_match' => 'mb_ereg_match', - 'mbereg_search' => 'mb_ereg_search', - 'mbereg_search_pos' => 'mb_ereg_search_pos', - 'mbereg_search_regs' => 'mb_ereg_search_regs', - 'mbereg_search_init' => 'mb_ereg_search_init', - 'mbereg_search_getregs' => 'mb_ereg_search_getregs', - 'mbereg_search_getpos' => 'mb_ereg_search_getpos', - ], - ]]); - - $services->set(StringifyStrNeedlesRector::class); - $services->set(JsonThrowOnErrorRector::class); - $services->set(RegexDashEscapeRector::class); - $services->set(ContinueToBreakInSwitchRector::class); + +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenameFunctionRector::class, [ + # https://wiki.php.net/rfc/deprecations_php_7_3 + 'image2wbmp' => 'imagewbmp', + 'mbregex_encoding' => 'mb_regex_encoding', + 'mbereg' => 'mb_ereg', + 'mberegi' => 'mb_eregi', + 'mbereg_replace' => 'mb_ereg_replace', + 'mberegi_replace' => 'mb_eregi_replace', + 'mbsplit' => 'mb_split', + 'mbereg_match' => 'mb_ereg_match', + 'mbereg_search' => 'mb_ereg_search', + 'mbereg_search_pos' => 'mb_ereg_search_pos', + 'mbereg_search_regs' => 'mb_ereg_search_regs', + 'mbereg_search_init' => 'mb_ereg_search_init', + 'mbereg_search_getregs' => 'mb_ereg_search_getregs', + 'mbereg_search_getpos' => 'mb_ereg_search_getpos', + ]); + + $rectorConfig->rules([ + StringifyStrNeedlesRector::class, + RegexDashEscapeRector::class, + SetCookieRector::class, + IsCountableRector::class, + + ArrayKeyFirstLastRector::class, + + SensitiveDefineRector::class, + + SensitiveConstantNameRector::class, + + SensitiveHereNowDocRector::class, + + ]); }; diff --git a/config/set/php74.php b/config/set/php74.php index fef44e0a074..5094749e76f 100644 --- a/config/set/php74.php +++ b/config/set/php74.php @@ -2,63 +2,49 @@ declare(strict_types=1); +use PhpParser\Node\Expr\Cast\Double; +use Rector\Config\RectorConfig; +use Rector\Php74\Rector\ArrayDimFetch\CurlyToSquareBracketArrayStringRector; use Rector\Php74\Rector\Assign\NullCoalescingOperatorRector; use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector; -use Rector\Php74\Rector\Double\RealToFloatTypeCastRector; use Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector; -use Rector\Php74\Rector\FuncCall\ArraySpreadInsteadOfArrayMergeRector; use Rector\Php74\Rector\FuncCall\FilterVarToAddSlashesRector; -use Rector\Php74\Rector\FuncCall\GetCalledClassToStaticClassRector; +use Rector\Php74\Rector\FuncCall\HebrevcToNl2brHebrevRector; use Rector\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector; -use Rector\Php74\Rector\Function_\ReservedFnFunctionRector; -use Rector\Php74\Rector\LNumber\AddLiteralSeparatorToNumberRector; -use Rector\Php74\Rector\MethodCall\ChangeReflectionTypeToStringToGetNameRector; +use Rector\Php74\Rector\FuncCall\MoneyFormatToNumberFormatRector; +use Rector\Php74\Rector\FuncCall\RestoreIncludePathToIniRestoreRector; use Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector; -use Rector\Php74\Rector\Property\TypedPropertyRector; use Rector\Php74\Rector\StaticCall\ExportToReflectionFunctionRector; +use Rector\Php74\Rector\Ternary\ParenthesizeNestedTernaryRector; +use Rector\Renaming\Rector\Cast\RenameCastRector; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(TypedPropertyRector::class); - - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - #the_real_type - # https://wiki.php.net/rfc/deprecations_php_7_4 - 'is_real' => 'is_float', - #apache_request_headers_function - # https://wiki.php.net/rfc/deprecations_php_7_4 - 'apache_request_headers' => 'getallheaders', - //'hebrevc' => ['nl2br', 'hebrev'], - ], - ]]); - - $services->set(ArrayKeyExistsOnPropertyRector::class); - - $services->set(FilterVarToAddSlashesRector::class); - - $services->set(ExportToReflectionFunctionRector::class); - - $services->set(GetCalledClassToStaticClassRector::class); - - $services->set(MbStrrposEncodingArgumentPositionRector::class); - - $services->set(RealToFloatTypeCastRector::class); - - $services->set(NullCoalescingOperatorRector::class); - - $services->set(ReservedFnFunctionRector::class); - - $services->set(ClosureToArrowFunctionRector::class); - - $services->set(ArraySpreadInsteadOfArrayMergeRector::class); - - $services->set(AddLiteralSeparatorToNumberRector::class); - - $services->set(ChangeReflectionTypeToStringToGetNameRector::class); - - $services->set(RestoreDefaultNullToNullableTypePropertyRector::class); +use Rector\Renaming\ValueObject\RenameCast; + +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenameFunctionRector::class, [ + #the_real_type + # https://wiki.php.net/rfc/deprecations_php_7_4 + 'is_real' => 'is_float', + ]); + + $rectorConfig->rules([ + ArrayKeyExistsOnPropertyRector::class, + FilterVarToAddSlashesRector::class, + ExportToReflectionFunctionRector::class, + MbStrrposEncodingArgumentPositionRector::class, + NullCoalescingOperatorRector::class, + ClosureToArrowFunctionRector::class, + RestoreDefaultNullToNullableTypePropertyRector::class, + CurlyToSquareBracketArrayStringRector::class, + MoneyFormatToNumberFormatRector::class, + ParenthesizeNestedTernaryRector::class, + RestoreIncludePathToIniRestoreRector::class, + HebrevcToNl2brHebrevRector::class, + ]); + + $rectorConfig->ruleWithConfiguration( + RenameCastRector::class, + [new RenameCast(Double::class, Double::KIND_REAL, Double::KIND_FLOAT)] + ); }; diff --git a/config/set/php80.php b/config/set/php80.php index 3c7cb83e334..bab1593481a 100644 --- a/config/set/php80.php +++ b/config/set/php80.php @@ -6,16 +6,18 @@ use Rector\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector; use Rector\Arguments\ValueObject\ArgumentAdder; use Rector\Arguments\ValueObject\ReplaceFuncCallArgumentDefaultValue; +use Rector\CodeQuality\Rector\ClassMethod\OptionalParametersAfterRequiredRector; +use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector; +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector; use Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector; use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector; use Rector\Php80\Rector\Class_\StringableForToStringRector; +use Rector\Php80\Rector\ClassConstFetch\ClassOnThisVariableObjectRector; +use Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector; use Rector\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector; -use Rector\Php80\Rector\ClassMethod\OptionalParametersAfterRequiredRector; use Rector\Php80\Rector\ClassMethod\SetStateToStaticRector; use Rector\Php80\Rector\FuncCall\ClassOnObjectRector; -use Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector; -use Rector\Php80\Rector\FunctionLike\UnionTypesRector; use Rector\Php80\Rector\Identical\StrEndsWithRector; use Rector\Php80\Rector\Identical\StrStartsWithRector; use Rector\Php80\Rector\NotIdentical\StrContainsRector; @@ -24,88 +26,80 @@ use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; use Rector\Transform\Rector\StaticCall\StaticCallToFuncCallRector; use Rector\Transform\ValueObject\StaticCallToFuncCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UnionTypesRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + StrContainsRector::class, + StrStartsWithRector::class, + StrEndsWithRector::class, + StringableForToStringRector::class, + ClassOnObjectRector::class, + GetDebugTypeRector::class, + RemoveUnusedVariableInCatchRector::class, + ClassPropertyAssignToConstructorPromotionRector::class, + ChangeSwitchToMatchRector::class, + RemoveParentCallWithoutParentRector::class, + SetStateToStaticRector::class, + FinalPrivateToPrivateVisibilityRector::class, + AddParamBasedOnParentClassMethodRector::class, + ClassOnThisVariableObjectRector::class, + ConsistentImplodeRector::class, + OptionalParametersAfterRequiredRector::class, + ]); - $services->set(StrContainsRector::class); - $services->set(StrStartsWithRector::class); - $services->set(StrEndsWithRector::class); - - $services->set(StaticCallToFuncCallRector::class) - ->call('configure', [[ - StaticCallToFuncCallRector::STATIC_CALLS_TO_FUNCTIONS => ValueObjectInliner::inline([ - new StaticCallToFuncCall('Nette\Utils\Strings', 'startsWith', 'str_starts_with'), - new StaticCallToFuncCall('Nette\Utils\Strings', 'endsWith', 'str_ends_with'), - new StaticCallToFuncCall('Nette\Utils\Strings', 'contains', 'str_contains'), - ]), - ]]); - - $services->set(StringableForToStringRector::class); - $services->set(ClassOnObjectRector::class); - $services->set(GetDebugTypeRector::class); - $services->set(TokenGetAllToObjectRector::class); - $services->set(RemoveUnusedVariableInCatchRector::class); - $services->set(ClassPropertyAssignToConstructorPromotionRector::class); - $services->set(ChangeSwitchToMatchRector::class); + $rectorConfig + ->ruleWithConfiguration(StaticCallToFuncCallRector::class, [ + new StaticCallToFuncCall('Nette\Utils\Strings', 'startsWith', 'str_starts_with'), + new StaticCallToFuncCall('Nette\Utils\Strings', 'endsWith', 'str_ends_with'), + new StaticCallToFuncCall('Nette\Utils\Strings', 'contains', 'str_contains'), + ]); // nette\utils and Strings::replace() - $services->set(ArgumentAdderRector::class) - ->call('configure', [[ - ArgumentAdderRector::ADDED_ARGUMENTS => ValueObjectInliner::inline([ - new ArgumentAdder('Nette\Utils\Strings', 'replace', 2, 'replacement', ''), - ]), - ]]); - $services->set(RemoveParentCallWithoutParentRector::class); - $services->set(SetStateToStaticRector::class); - $services->set(FinalPrivateToPrivateVisibilityRector::class); + $rectorConfig + ->ruleWithConfiguration( + ArgumentAdderRector::class, + [new ArgumentAdder('Nette\Utils\Strings', 'replace', 2, 'replacement', '')] + ); + // @see https://php.watch/versions/8.0/pgsql-aliases-deprecated - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'pg_clientencoding' => 'pg_client_encoding', - 'pg_cmdtuples' => 'pg_affected_rows', - 'pg_errormessage' => 'pg_last_error', - 'pg_fieldisnull' => 'pg_field_is_null', - 'pg_fieldname' => 'pg_field_name', - 'pg_fieldnum' => 'pg_field_num', - 'pg_fieldprtlen' => 'pg_field_prtlen', - 'pg_fieldsize' => 'pg_field_size', - 'pg_fieldtype' => 'pg_field_type', - 'pg_freeresult' => 'pg_free_result', - 'pg_getlastoid' => 'pg_last_oid', - 'pg_loclose' => 'pg_lo_close', - 'pg_locreate' => 'pg_lo_create', - 'pg_loexport' => 'pg_lo_export', - 'pg_loimport' => 'pg_lo_import', - 'pg_loopen' => 'pg_lo_open', - 'pg_loread' => 'pg_lo_read', - 'pg_loreadall' => 'pg_lo_read_all', - 'pg_lounlink' => 'pg_lo_unlink', - 'pg_lowrite' => 'pg_lo_write', - 'pg_numfields' => 'pg_num_fields', - 'pg_numrows' => 'pg_num_rows', - 'pg_result' => 'pg_fetch_result', - 'pg_setclientencoding' => 'pg_set_client_encoding', - ], - ]]); - $services->set(OptionalParametersAfterRequiredRector::class); + $rectorConfig + ->ruleWithConfiguration(RenameFunctionRector::class, [ + 'pg_clientencoding' => 'pg_client_encoding', + 'pg_cmdtuples' => 'pg_affected_rows', + 'pg_errormessage' => 'pg_last_error', + 'pg_fieldisnull' => 'pg_field_is_null', + 'pg_fieldname' => 'pg_field_name', + 'pg_fieldnum' => 'pg_field_num', + 'pg_fieldprtlen' => 'pg_field_prtlen', + 'pg_fieldsize' => 'pg_field_size', + 'pg_fieldtype' => 'pg_field_type', + 'pg_freeresult' => 'pg_free_result', + 'pg_getlastoid' => 'pg_last_oid', + 'pg_loclose' => 'pg_lo_close', + 'pg_locreate' => 'pg_lo_create', + 'pg_loexport' => 'pg_lo_export', + 'pg_loimport' => 'pg_lo_import', + 'pg_loopen' => 'pg_lo_open', + 'pg_loread' => 'pg_lo_read', + 'pg_loreadall' => 'pg_lo_read_all', + 'pg_lounlink' => 'pg_lo_unlink', + 'pg_lowrite' => 'pg_lo_write', + 'pg_numfields' => 'pg_num_fields', + 'pg_numrows' => 'pg_num_rows', + 'pg_result' => 'pg_fetch_result', + 'pg_setclientencoding' => 'pg_set_client_encoding', + ]); - $services->set(FunctionArgumentDefaultValueReplacerRector::class) - ->call('configure', [[ - FunctionArgumentDefaultValueReplacerRector::REPLACED_ARGUMENTS => ValueObjectInliner::inline([ - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '', '!='), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '!', '!='), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'g', 'gt'), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'l', 'lt'), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'n', 'ne'), - ]), - ]]); + $rectorConfig + ->ruleWithConfiguration(FunctionArgumentDefaultValueReplacerRector::class, [ + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'), + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'), + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '', '!='), + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '!', '!='), + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'g', 'gt'), + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'l', 'lt'), + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'n', 'ne'), + new ReplaceFuncCallArgumentDefaultValue('get_headers', 1, 0, false), + new ReplaceFuncCallArgumentDefaultValue('get_headers', 1, 1, true), + ]); }; diff --git a/config/set/php81.php b/config/set/php81.php index 9e7c26a947d..5a08ab3fdfd 100644 --- a/config/set/php81.php +++ b/config/set/php81.php @@ -2,18 +2,43 @@ declare(strict_types=1); +use Rector\CodingStyle\Rector\ArrowFunction\ArrowFunctionDelegatingCallToFirstClassCallableRector; +use Rector\CodingStyle\Rector\Closure\ClosureDelegatingCallToFirstClassCallableRector; +use Rector\CodingStyle\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector; +use Rector\CodingStyle\Rector\FuncCall\FunctionFirstClassCallableRector; +use Rector\Config\RectorConfig; +use Rector\Php81\Rector\Array_\ArrayToFirstClassCallableRector; use Rector\Php81\Rector\Class_\MyCLabsClassToEnumRector; -use Rector\Php81\Rector\ClassConst\FinalizePublicClassConstantRector; +use Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector; +use Rector\Php81\Rector\FuncCall\NullToStrictIntPregSlitFuncCallLimitArgRector; +use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector; use Rector\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector; +use Rector\Php81\Rector\MethodCall\RemoveReflectionSetAccessibleCallsRector; +use Rector\Php81\Rector\MethodCall\SpatieEnumMethodCallToEnumConstRector; +use Rector\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector; use Rector\Php81\Rector\Property\ReadOnlyPropertyRector; use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReturnNeverTypeRector::class); - $services->set(MyCLabsClassToEnumRector::class); - $services->set(MyCLabsMethodCallToEnumConstRector::class); - $services->set(FinalizePublicClassConstantRector::class); - $services->set(ReadOnlyPropertyRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + ReturnNeverTypeRector::class, + MyCLabsClassToEnumRector::class, + MyCLabsMethodCallToEnumConstRector::class, + MyCLabsConstructorCallToEnumFromRector::class, + ReadOnlyPropertyRector::class, + SpatieEnumClassToEnumRector::class, + SpatieEnumMethodCallToEnumConstRector::class, + NullToStrictStringFuncCallArgRector::class, + NullToStrictIntPregSlitFuncCallLimitArgRector::class, + + ArrayToFirstClassCallableRector::class, + + // closure/arrow function + ArrowFunctionDelegatingCallToFirstClassCallableRector::class, + ClosureDelegatingCallToFirstClassCallableRector::class, + + ClosureFromCallableToFirstClassCallableRector::class, + FunctionFirstClassCallableRector::class, + RemoveReflectionSetAccessibleCallsRector::class, + ]); }; diff --git a/config/set/php82.php b/config/set/php82.php new file mode 100644 index 00000000000..f43a67786c1 --- /dev/null +++ b/config/set/php82.php @@ -0,0 +1,18 @@ +rules([ + ReadOnlyClassRector::class, + Utf8DecodeEncodeToMbConvertEncodingRector::class, + FilesystemIteratorSkipDotsRector::class, + VariableInStringInterpolationFixerRector::class, + ]); +}; diff --git a/config/set/php83.php b/config/set/php83.php new file mode 100644 index 00000000000..1a056a87c05 --- /dev/null +++ b/config/set/php83.php @@ -0,0 +1,24 @@ +rules([ + AddOverrideAttributeToOverriddenMethodsRector::class, + AddTypeToConstRector::class, + CombineHostPortLdapUriRector::class, + RemoveGetClassGetParentClassNoArgsRector::class, + ReadOnlyAnonymousClassRector::class, + DynamicClassConstFetchRector::class, + JsonValidateRector::class, + ]); +}; diff --git a/config/set/php84.php b/config/set/php84.php new file mode 100644 index 00000000000..582a9ec5780 --- /dev/null +++ b/config/set/php84.php @@ -0,0 +1,31 @@ +rules([ + ExplicitNullableParamTypeRector::class, + RoundingModeEnumRector::class, + AddEscapeArgumentRector::class, + NewMethodCallWithoutParenthesesRector::class, + DeprecatedAnnotationToDeprecatedAttributeRector::class, + ForeachToArrayFindRector::class, + ForeachToArrayFindKeyRector::class, + ForeachToArrayAllRector::class, + ForeachToArrayAnyRector::class, + + // optional + // \Rector\Php84\Rector\Class_\PropertyHookRector::class, + ]); +}; diff --git a/config/set/php85.php b/config/set/php85.php new file mode 100644 index 00000000000..e860f28b587 --- /dev/null +++ b/config/set/php85.php @@ -0,0 +1,210 @@ +rules([ + ArrayFirstLastRector::class, + RemoveFinfoBufferContextArgRector::class, + NullDebugInfoReturnRector::class, + ConstAndTraitDeprecatedAttributeRector::class, + ColonAfterSwitchCaseRector::class, + ArrayKeyExistsNullToEmptyStringRector::class, + ChrArgModuloRector::class, + SleepToSerializeRector::class, + OrdSingleByteRector::class, + WakeupToUnserializeRector::class, + ShellExecFunctionCallOverBackticksRector::class, + AddOverrideAttributeToOverriddenPropertiesRector::class, + ]); + + $rectorConfig->ruleWithConfiguration( + RemoveFuncCallArgRector::class, + [ + // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_key_length_parameter_of_openssl_pkey_derive + new RemoveFuncCallArg('openssl_pkey_derive', 2), + // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_exclude_disabled_parameter_of_get_defined_functions + new RemoveFuncCallArg('get_defined_functions', 0), + ] + ); + + $rectorConfig->ruleWithConfiguration( + RenameMethodRector::class, + [ + // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_splobjectstoragecontains_splobjectstorageattach_and_splobjectstoragedetach + new MethodCallRename('SplObjectStorage', 'contains', 'offsetExists'), + new MethodCallRename('SplObjectStorage', 'attach', 'offsetSet'), + new MethodCallRename('SplObjectStorage', 'detach', 'offsetUnset'), + + // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_driver_specific_pdo_constants_and_methods + new MethodCallRename('PDO', 'pgsqlCopyFromArray', 'copyFromArray'), + new MethodCallRename('PDO', 'pgsqlCopyFromFile', 'copyFromFile'), + new MethodCallRename('PDO', 'pgsqlCopyToArray', 'copyToArray'), + new MethodCallRename('PDO', 'pgsqlCopyToFile', 'copyToFile'), + new MethodCallRename('PDO', 'pgsqlGetNotify', 'getNotify'), + new MethodCallRename('PDO', 'pgsqlGetPid', 'getPid'), + new MethodCallRename('PDO', 'pgsqlLOBCreate', 'lobCreate'), + new MethodCallRename('PDO', 'pgsqlLOBOpen', 'lobOpen'), + new MethodCallRename('PDO', 'pgsqlLOBUnlink', 'lobUnlink'), + new MethodCallRename('PDO', 'sqliteCreateAggregate', 'createAggregate'), + new MethodCallRename('PDO', 'sqliteCreateCollation', 'createCollation'), + new MethodCallRename('PDO', 'sqliteCreateFunction', 'createFunction'), + ] + ); + + $rectorConfig->ruleWithConfiguration( + RenameFunctionRector::class, + [ + // https://wiki.php.net/rfc/deprecations_php_8_5#formally_deprecate_socket_set_timeout + 'socket_set_timeout' => 'stream_set_timeout', + + // https://wiki.php.net/rfc/deprecations_php_8_5#formally_deprecate_mysqli_execute + 'mysqli_execute' => 'mysqli_stmt_execute', + ] + ); + + // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_driver_specific_pdo_constants_and_methods + $rectorConfig->ruleWithConfiguration( + RenameClassConstFetchRector::class, + [ + new RenameClassAndConstFetch( + 'PDO', + 'DBLIB_ATTR_CONNECTION_TIMEOUT', + 'Pdo\Dblib', + 'ATTR_CONNECTION_TIMEOUT' + ), + new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_QUERY_TIMEOUT', 'Pdo\Dblib', 'ATTR_QUERY_TIMEOUT'), + new RenameClassAndConstFetch( + 'PDO', + 'DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER', + 'Pdo\Dblib', + 'ATTR_STRINGIFY_UNIQUEIDENTIFIER' + ), + new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_VERSION', 'Pdo\Dblib', 'ATTR_VERSION'), + new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_TDS_VERSION', 'Pdo\Dblib', 'ATTR_TDS_VERSION'), + new RenameClassAndConstFetch( + 'PDO', + 'DBLIB_ATTR_SKIP_EMPTY_ROWSETS', + 'Pdo\Dblib', + 'ATTR_SKIP_EMPTY_ROWSETS' + ), + new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_DATETIME_CONVERT', 'Pdo\Dblib', 'ATTR_DATETIME_CONVERT'), + new RenameClassAndConstFetch('PDO', 'FB_ATTR_DATE_FORMAT', 'Pdo\Firebird', 'ATTR_DATE_FORMAT'), + new RenameClassAndConstFetch('PDO', 'FB_ATTR_TIME_FORMAT', 'Pdo\Firebird', 'ATTR_TIME_FORMAT'), + new RenameClassAndConstFetch('PDO', 'FB_ATTR_TIMESTAMP_FORMAT', 'Pdo\Firebird', 'ATTR_TIMESTAMP_FORMAT'), + new RenameClassAndConstFetch( + 'PDO', + 'MYSQL_ATTR_USE_BUFFERED_QUERY', + 'Pdo\Mysql', + 'ATTR_USE_BUFFERED_QUERY' + ), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_LOCAL_INFILE', 'Pdo\Mysql', 'ATTR_LOCAL_INFILE'), + new RenameClassAndConstFetch( + 'PDO', + 'MYSQL_ATTR_LOCAL_INFILE_DIRECTORY', + 'Pdo\Mysql', + 'ATTR_LOCAL_INFILE_DIRECTORY' + ), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_INIT_COMMAND', 'Pdo\Mysql', 'ATTR_INIT_COMMAND'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_MAX_BUFFER_SIZE', 'Pdo\Mysql', 'ATTR_MAX_BUFFER_SIZE'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_READ_DEFAULT_FILE', 'Pdo\Mysql', 'ATTR_READ_DEFAULT_FILE'), + new RenameClassAndConstFetch( + 'PDO', + 'MYSQL_ATTR_READ_DEFAULT_GROUP', + 'Pdo\Mysql', + 'ATTR_READ_DEFAULT_GROUP' + ), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_COMPRESS', 'Pdo\Mysql', 'ATTR_COMPRESS'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_DIRECT_QUERY', 'Pdo\Mysql', 'ATTR_DIRECT_QUERY'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_FOUND_ROWS', 'Pdo\Mysql', 'ATTR_FOUND_ROWS'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_IGNORE_SPACE', 'Pdo\Mysql', 'ATTR_IGNORE_SPACE'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_KEY', 'Pdo\Mysql', 'ATTR_SSL_KEY'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_CERT', 'Pdo\Mysql', 'ATTR_SSL_CERT'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_CA', 'Pdo\Mysql', 'ATTR_SSL_CA'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_CAPATH', 'Pdo\Mysql', 'ATTR_SSL_CAPATH'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_CIPHER', 'Pdo\Mysql', 'ATTR_SSL_CIPHER'), + new RenameClassAndConstFetch( + 'PDO', + 'MYSQL_ATTR_SSL_VERIFY_SERVER_CERT', + 'Pdo\Mysql', + 'ATTR_SSL_VERIFY_SERVER_CERT' + ), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SERVER_PUBLIC_KEY', 'Pdo\Mysql', 'ATTR_SERVER_PUBLIC_KEY'), + new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_MULTI_STATEMENTS', 'Pdo\Mysql', 'ATTR_MULTI_STATEMENTS'), + new RenameClassAndConstFetch('PDO', 'ODBC_ATTR_USE_CURSOR_LIBRARY', 'Pdo\Odbc', 'ATTR_USE_CURSOR_LIBRARY'), + new RenameClassAndConstFetch('PDO', 'ODBC_ATTR_ASSUME_UTF8', 'Pdo\Odbc', 'ATTR_ASSUME_UTF8'), + new RenameClassAndConstFetch('PDO', 'ODBC_SQL_USE_IF_NEEDED', 'Pdo\Odbc', 'SQL_USE_IF_NEEDED'), + new RenameClassAndConstFetch('PDO', 'ODBC_SQL_USE_DRIVER', 'Pdo\Odbc', 'SQL_USE_DRIVER'), + new RenameClassAndConstFetch('PDO', 'ODBC_SQL_USE_ODBC', 'Pdo\Odbc', 'SQL_USE_ODBC'), + new RenameClassAndConstFetch('PDO', 'PGSQL_ATTR_DISABLE_PREPARES', 'Pdo\Pgsql', 'ATTR_DISABLE_PREPARES'), + new RenameClassAndConstFetch( + 'PDO', + 'SQLITE_ATTR_EXTENDED_RESULT_CODES', + 'Pdo\Sqlite', + 'ATTR_EXTENDED_RESULT_CODES' + ), + new RenameClassAndConstFetch('PDO', 'SQLITE_ATTR_OPEN_FLAGS', 'Pdo\Sqlite', 'OPEN_FLAGS'), + new RenameClassAndConstFetch( + 'PDO', + 'SQLITE_ATTR_READONLY_STATEMENT', + 'Pdo\Sqlite', + 'ATTR_READONLY_STATEMENT' + ), + new RenameClassAndConstFetch('PDO', 'SQLITE_DETERMINISTIC', 'Pdo\Sqlite', 'DETERMINISTIC'), + new RenameClassAndConstFetch('PDO', 'SQLITE_OPEN_READONLY', 'Pdo\Sqlite', 'OPEN_READONLY'), + new RenameClassAndConstFetch('PDO', 'SQLITE_OPEN_READWRITE', 'Pdo\Sqlite', 'OPEN_READWRITE'), + new RenameClassAndConstFetch('PDO', 'SQLITE_OPEN_CREATE', 'Pdo\Sqlite', 'OPEN_CREATE'), + ] + ); + + // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_non-standard_cast_names + $rectorConfig->ruleWithConfiguration( + RenameCastRector::class, + [ + new RenameCast(Int_::class, Int_::KIND_INTEGER, Int_::KIND_INT), + new RenameCast(Bool_::class, Bool_::KIND_BOOLEAN, Bool_::KIND_BOOL), + new RenameCast(Double::class, Double::KIND_DOUBLE, Double::KIND_FLOAT), + new RenameCast(String_::class, String_::KIND_BINARY, String_::KIND_STRING), + ] + ); + + // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_no-op_functions_from_the_resource_to_object_conversion + // these function have no effect when use + $rectorConfig->ruleWithConfiguration(RemoveFuncCallRector::class, [ + 'curl_close', 'curl_share_close', 'finfo_close', 'imagedestroy', 'xml_parser_free', + ]); + + // https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_filter_default_constant + $rectorConfig->ruleWithConfiguration(RenameConstantRector::class, [ + 'FILTER_DEFAULT' => 'FILTER_UNSAFE_RAW', + ]); +}; diff --git a/config/set/php86.php b/config/set/php86.php new file mode 100644 index 00000000000..9eb067e51ea --- /dev/null +++ b/config/set/php86.php @@ -0,0 +1,10 @@ +rules([MinMaxToClampRector::class]); +}; diff --git a/config/set/phpspec-to-phpunit.php b/config/set/phpspec-to-phpunit.php deleted file mode 100644 index b593dc18deb..00000000000 --- a/config/set/phpspec-to-phpunit.php +++ /dev/null @@ -1,32 +0,0 @@ -services(); - - # 1. first convert mocks - $services->set(PhpSpecMocksToPHPUnitMocksRector::class); - - $services->set(PhpSpecPromisesToPHPUnitAssertRector::class); - - # 2. then methods - $services->set(PhpSpecMethodToPHPUnitMethodRector::class); - - # 3. then the class itself - $services->set(PhpSpecClassToPHPUnitClassRector::class); - - $services->set(AddMockPropertiesRector::class); - $services->set(MockVariableToPropertyFetchRector::class); - $services->set(RenameSpecFileToTestFileRector::class); -}; diff --git a/config/set/phpspec30.php b/config/set/phpspec30.php deleted file mode 100644 index d3dd7dffb89..00000000000 --- a/config/set/phpspec30.php +++ /dev/null @@ -1,41 +0,0 @@ -services(); - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - // @see http://www.phpspec.net/en/stable/manual/upgrading-to-phpspec-3.html - new MethodCallRename('PhpSpec\ServiceContainer', 'set', 'define'), - new MethodCallRename('PhpSpec\ServiceContainer', 'setShared', 'define'), - ]), - ]]); - - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'PhpSpec\Console\IO' => 'PhpSpec\Console\ConsoleIO', - 'PhpSpec\IO\IOInterface' => 'PhpSpec\IO\IO', - 'PhpSpec\Locator\ResourceInterface' => 'PhpSpec\Locator\Resource', - 'PhpSpec\Locator\ResourceLocatorInterface' => 'PhpSpec\Locator\ResourceLocator', - 'PhpSpec\Formatter\Presenter\PresenterInterface' => 'PhpSpec\Formatter\Presenter\Presenter', - 'PhpSpec\CodeGenerator\Generator\GeneratorInterface' => 'PhpSpec\CodeGenerator\Generator\Generator', - 'PhpSpec\Extension\ExtensionInterface' => 'PhpSpec\Extension', - 'Phpspec\CodeAnalysis\AccessInspectorInterface' => 'Phpspec\CodeAnalysis\AccessInspector', - 'Phpspec\Event\EventInterface' => 'Phpspec\Event\PhpSpecEvent', - 'PhpSpec\Formatter\Presenter\Differ\EngineInterface' => 'PhpSpec\Formatter\Presenter\Differ\DifferEngine', - 'PhpSpec\Matcher\MatcherInterface' => 'PhpSpec\Matcher\Matcher', - 'PhpSpec\Matcher\MatchersProviderInterface' => 'PhpSpec\Matcher\MatchersProvider', - 'PhpSpec\SpecificationInterface' => 'PhpSpec\Specification', - 'PhpSpec\Runner\Maintainer\MaintainerInterface' => 'PhpSpec\Runner\Maintainer\Maintainer', - ], - ]]); -}; diff --git a/config/set/phpspec40.php b/config/set/phpspec40.php deleted file mode 100644 index 43b710fe4c0..00000000000 --- a/config/set/phpspec40.php +++ /dev/null @@ -1,23 +0,0 @@ -services(); - - $arrayType = new ArrayType(new MixedType(), new MixedType()); - - $services->set(AddReturnTypeDeclarationRector::class) - ->call('configure', [[ - AddReturnTypeDeclarationRector::METHOD_RETURN_TYPES => ValueObjectInliner::inline([ - new AddReturnTypeDeclaration('PhpSpec\ObjectBehavior', 'getMatchers', $arrayType), - ]), - ]]); -}; diff --git a/config/set/privatization.php b/config/set/privatization.php index 48efa97bd92..f0c63b7543f 100644 --- a/config/set/privatization.php +++ b/config/set/privatization.php @@ -2,28 +2,17 @@ declare(strict_types=1); -use Rector\Privatization\Rector\Class_\ChangeLocalPropertyToVariableRector; -use Rector\Privatization\Rector\Class_\ChangeReadOnlyVariableWithDefaultValueToConstantRector; -use Rector\Privatization\Rector\Class_\FinalizeClassesWithoutChildrenRector; -use Rector\Privatization\Rector\Class_\RepeatedLiteralToClassConstantRector; -use Rector\Privatization\Rector\ClassMethod\ChangeGlobalVariablesToPropertiesRector; +use Rector\Config\RectorConfig; +use Rector\Privatization\Rector\ClassConst\PrivatizeFinalClassConstantRector; use Rector\Privatization\Rector\ClassMethod\PrivatizeFinalClassMethodRector; use Rector\Privatization\Rector\MethodCall\PrivatizeLocalGetterToPropertyRector; -use Rector\Privatization\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector; use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(FinalizeClassesWithoutChildrenRector::class); - $services->set(ChangeGlobalVariablesToPropertiesRector::class); - $services->set(ChangeReadOnlyPropertyWithDefaultValueToConstantRector::class); - $services->set(ChangeReadOnlyVariableWithDefaultValueToConstantRector::class); - $services->set(RepeatedLiteralToClassConstantRector::class); - $services->set(PrivatizeLocalGetterToPropertyRector::class); - $services->set(PrivatizeFinalClassPropertyRector::class); - $services->set(PrivatizeFinalClassMethodRector::class); - - // buggy, requires more work - // $services->set(ChangeLocalPropertyToVariableRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rules([ + PrivatizeLocalGetterToPropertyRector::class, + PrivatizeFinalClassPropertyRector::class, + PrivatizeFinalClassMethodRector::class, + PrivatizeFinalClassConstantRector::class, + ]); }; diff --git a/config/set/psr-4.php b/config/set/psr-4.php deleted file mode 100644 index 943ba4c8e2b..00000000000 --- a/config/set/psr-4.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(NormalizeNamespaceByPSR4ComposerAutoloadRector::class); - $services->set(MultipleClassFileToPsr4ClassesRector::class); -}; diff --git a/config/set/rector-preset.php b/config/set/rector-preset.php new file mode 100644 index 00000000000..fbbee995050 --- /dev/null +++ b/config/set/rector-preset.php @@ -0,0 +1,16 @@ +rules([ + DeclareStrictTypesRector::class, + PostIncDecToPreIncDecRector::class, + FinalizeTestCaseClassRector::class, + ]); +}; diff --git a/config/set/safe07.php b/config/set/safe07.php deleted file mode 100644 index dd39cd9604e..00000000000 --- a/config/set/safe07.php +++ /dev/null @@ -1,1124 +0,0 @@ -services(); - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'apache_getenv' => 'Safe\apache_getenv', - 'apache_get_version' => 'Safe\apache_get_version', - 'apache_request_headers' => 'Safe\apache_request_headers', - 'apache_reset_timeout' => 'Safe\apache_reset_timeout', - 'apache_response_headers' => 'Safe\apache_response_headers', - 'apache_setenv' => 'Safe\apache_setenv', - 'apcu_cache_info' => 'Safe\apcu_cache_info', - 'apcu_cas' => 'Safe\apcu_cas', - 'apcu_dec' => 'Safe\apcu_dec', - 'apcu_fetch' => 'Safe\apcu_fetch', - 'apcu_inc' => 'Safe\apcu_inc', - 'apcu_sma_info' => 'Safe\apcu_sma_info', - 'apc_cache_info' => 'Safe\apc_cache_info', - 'apc_cas' => 'Safe\apc_cas', - 'apc_compile_file' => 'Safe\apc_compile_file', - 'apc_dec' => 'Safe\apc_dec', - 'apc_define_constants' => 'Safe\apc_define_constants', - 'apc_delete' => 'Safe\apc_delete', - 'apc_delete_file' => 'Safe\apc_delete_file', - 'apc_fetch' => 'Safe\apc_fetch', - 'apc_inc' => 'Safe\apc_inc', - 'apc_load_constants' => 'Safe\apc_load_constants', - 'apc_sma_info' => 'Safe\apc_sma_info', - 'array_combine' => 'Safe\array_combine', - 'array_flip' => 'Safe\array_flip', - 'array_replace' => 'Safe\array_replace', - 'array_replace_recursive' => 'Safe\array_replace_recursive', - 'array_walk_recursive' => 'Safe\array_walk_recursive', - 'arsort' => 'Safe\arsort', - 'asort' => 'Safe\asort', - 'base64_decode' => 'Safe\base64_decode', - 'bzclose' => 'Safe\bzclose', - 'bzflush' => 'Safe\bzflush', - 'bzread' => 'Safe\bzread', - 'bzwrite' => 'Safe\bzwrite', - 'chdir' => 'Safe\chdir', - 'chgrp' => 'Safe\chgrp', - 'chmod' => 'Safe\chmod', - 'chown' => 'Safe\chown', - 'chroot' => 'Safe\chroot', - 'class_alias' => 'Safe\class_alias', - 'class_implements' => 'Safe\class_implements', - 'class_parents' => 'Safe\class_parents', - 'class_uses' => 'Safe\class_uses', - 'cli_set_process_title' => 'Safe\cli_set_process_title', - 'closelog' => 'Safe\closelog', - 'com_event_sink' => 'Safe\com_event_sink', - 'com_load_typelib' => 'Safe\com_load_typelib', - 'com_print_typeinfo' => 'Safe\com_print_typeinfo', - 'convert_uudecode' => 'Safe\convert_uudecode', - 'convert_uuencode' => 'Safe\convert_uuencode', - 'copy' => 'Safe\copy', - 'create_function' => 'Safe\create_function', - 'cubrid_free_result' => 'Safe\cubrid_free_result', - 'cubrid_get_charset' => 'Safe\cubrid_get_charset', - 'cubrid_get_client_info' => 'Safe\cubrid_get_client_info', - 'cubrid_get_db_parameter' => 'Safe\cubrid_get_db_parameter', - 'cubrid_get_server_info' => 'Safe\cubrid_get_server_info', - 'cubrid_insert_id' => 'Safe\cubrid_insert_id', - 'cubrid_lob2_new' => 'Safe\cubrid_lob2_new', - 'cubrid_lob2_size' => 'Safe\cubrid_lob2_size', - 'cubrid_lob2_size64' => 'Safe\cubrid_lob2_size64', - 'cubrid_lob2_tell' => 'Safe\cubrid_lob2_tell', - 'cubrid_lob2_tell64' => 'Safe\cubrid_lob2_tell64', - 'cubrid_set_db_parameter' => 'Safe\cubrid_set_db_parameter', - 'curl_escape' => 'Safe\curl_escape', - 'curl_exec' => 'Safe\curl_exec', - 'curl_getinfo' => 'Safe\curl_getinfo', - 'curl_init' => 'Safe\curl_init', - 'curl_multi_errno' => 'Safe\curl_multi_errno', - 'curl_multi_info_read' => 'Safe\curl_multi_info_read', - 'curl_multi_init' => 'Safe\curl_multi_init', - 'curl_setopt' => 'Safe\curl_setopt', - 'curl_share_errno' => 'Safe\curl_share_errno', - 'curl_share_setopt' => 'Safe\curl_share_setopt', - 'curl_unescape' => 'Safe\curl_unescape', - 'date_parse' => 'Safe\date_parse', - 'date_parse_from_format' => 'Safe\date_parse_from_format', - 'date_sunrise' => 'Safe\date_sunrise', - 'date_sunset' => 'Safe\date_sunset', - 'date_sun_info' => 'Safe\date_sun_info', - 'db2_autocommit' => 'Safe\db2_autocommit', - 'db2_bind_param' => 'Safe\db2_bind_param', - 'db2_client_info' => 'Safe\db2_client_info', - 'db2_close' => 'Safe\db2_close', - 'db2_commit' => 'Safe\db2_commit', - 'db2_execute' => 'Safe\db2_execute', - 'db2_free_result' => 'Safe\db2_free_result', - 'db2_free_stmt' => 'Safe\db2_free_stmt', - 'db2_get_option' => 'Safe\db2_get_option', - 'db2_pclose' => 'Safe\db2_pclose', - 'db2_rollback' => 'Safe\db2_rollback', - 'db2_server_info' => 'Safe\db2_server_info', - 'db2_set_option' => 'Safe\db2_set_option', - 'define' => 'Safe\define', - 'deflate_add' => 'Safe\deflate_add', - 'deflate_init' => 'Safe\deflate_init', - 'disk_free_space' => 'Safe\disk_free_space', - 'disk_total_space' => 'Safe\disk_total_space', - 'dl' => 'Safe\dl', - 'dns_get_record' => 'Safe\dns_get_record', - 'eio_busy' => 'Safe\eio_busy', - 'eio_chmod' => 'Safe\eio_chmod', - 'eio_chown' => 'Safe\eio_chown', - 'eio_close' => 'Safe\eio_close', - 'eio_custom' => 'Safe\eio_custom', - 'eio_dup2' => 'Safe\eio_dup2', - 'eio_event_loop' => 'Safe\eio_event_loop', - 'eio_fallocate' => 'Safe\eio_fallocate', - 'eio_fchmod' => 'Safe\eio_fchmod', - 'eio_fdatasync' => 'Safe\eio_fdatasync', - 'eio_fstat' => 'Safe\eio_fstat', - 'eio_fstatvfs' => 'Safe\eio_fstatvfs', - 'eio_fsync' => 'Safe\eio_fsync', - 'eio_ftruncate' => 'Safe\eio_ftruncate', - 'eio_futime' => 'Safe\eio_futime', - 'eio_grp' => 'Safe\eio_grp', - 'eio_lstat' => 'Safe\eio_lstat', - 'eio_mkdir' => 'Safe\eio_mkdir', - 'eio_mknod' => 'Safe\eio_mknod', - 'eio_nop' => 'Safe\eio_nop', - 'eio_readahead' => 'Safe\eio_readahead', - 'eio_readdir' => 'Safe\eio_readdir', - 'eio_readlink' => 'Safe\eio_readlink', - 'eio_rename' => 'Safe\eio_rename', - 'eio_rmdir' => 'Safe\eio_rmdir', - 'eio_seek' => 'Safe\eio_seek', - 'eio_sendfile' => 'Safe\eio_sendfile', - 'eio_stat' => 'Safe\eio_stat', - 'eio_statvfs' => 'Safe\eio_statvfs', - 'eio_symlink' => 'Safe\eio_symlink', - 'eio_sync' => 'Safe\eio_sync', - 'eio_syncfs' => 'Safe\eio_syncfs', - 'eio_sync_file_range' => 'Safe\eio_sync_file_range', - 'eio_truncate' => 'Safe\eio_truncate', - 'eio_unlink' => 'Safe\eio_unlink', - 'eio_utime' => 'Safe\eio_utime', - 'eio_write' => 'Safe\eio_write', - 'error_log' => 'Safe\error_log', - 'event_add' => 'Safe\event_add', - 'event_base_loopbreak' => 'Safe\event_base_loopbreak', - 'event_base_loopexit' => 'Safe\event_base_loopexit', - 'event_base_new' => 'Safe\event_base_new', - 'event_base_priority_init' => 'Safe\event_base_priority_init', - 'event_base_reinit' => 'Safe\event_base_reinit', - 'event_base_set' => 'Safe\event_base_set', - 'event_buffer_base_set' => 'Safe\event_buffer_base_set', - 'event_buffer_disable' => 'Safe\event_buffer_disable', - 'event_buffer_enable' => 'Safe\event_buffer_enable', - 'event_buffer_new' => 'Safe\event_buffer_new', - 'event_buffer_priority_set' => 'Safe\event_buffer_priority_set', - 'event_buffer_set_callback' => 'Safe\event_buffer_set_callback', - 'event_buffer_write' => 'Safe\event_buffer_write', - 'event_del' => 'Safe\event_del', - 'event_new' => 'Safe\event_new', - 'event_priority_set' => 'Safe\event_priority_set', - 'event_set' => 'Safe\event_set', - 'event_timer_set' => 'Safe\event_timer_set', - 'fastcgi_finish_request' => 'Safe\fastcgi_finish_request', - 'fbird_blob_cancel' => 'Safe\fbird_blob_cancel', - 'fclose' => 'Safe\fclose', - 'fflush' => 'Safe\fflush', - 'file' => 'Safe\file', - 'fileatime' => 'Safe\fileatime', - 'filectime' => 'Safe\filectime', - 'fileinode' => 'Safe\fileinode', - 'filemtime' => 'Safe\filemtime', - 'fileowner' => 'Safe\fileowner', - 'filesize' => 'Safe\filesize', - 'file_get_contents' => 'Safe\file_get_contents', - 'file_put_contents' => 'Safe\file_put_contents', - 'filter_input_array' => 'Safe\filter_input_array', - 'filter_var_array' => 'Safe\filter_var_array', - 'finfo_close' => 'Safe\finfo_close', - 'finfo_open' => 'Safe\finfo_open', - 'flock' => 'Safe\flock', - 'fopen' => 'Safe\fopen', - 'fputcsv' => 'Safe\fputcsv', - 'fread' => 'Safe\fread', - 'fsockopen' => 'Safe\fsockopen', - 'ftp_alloc' => 'Safe\ftp_alloc', - 'ftp_append' => 'Safe\ftp_append', - 'ftp_cdup' => 'Safe\ftp_cdup', - 'ftp_chdir' => 'Safe\ftp_chdir', - 'ftp_chmod' => 'Safe\ftp_chmod', - 'ftp_close' => 'Safe\ftp_close', - 'ftp_connect' => 'Safe\ftp_connect', - 'ftp_delete' => 'Safe\ftp_delete', - 'ftp_fget' => 'Safe\ftp_fget', - 'ftp_fput' => 'Safe\ftp_fput', - 'ftp_get' => 'Safe\ftp_get', - 'ftp_login' => 'Safe\ftp_login', - 'ftp_mkdir' => 'Safe\ftp_mkdir', - 'ftp_mlsd' => 'Safe\ftp_mlsd', - 'ftp_nlist' => 'Safe\ftp_nlist', - 'ftp_pasv' => 'Safe\ftp_pasv', - 'ftp_put' => 'Safe\ftp_put', - 'ftp_pwd' => 'Safe\ftp_pwd', - 'ftp_rename' => 'Safe\ftp_rename', - 'ftp_rmdir' => 'Safe\ftp_rmdir', - 'ftp_site' => 'Safe\ftp_site', - 'ftp_ssl_connect' => 'Safe\ftp_ssl_connect', - 'ftp_systype' => 'Safe\ftp_systype', - 'ftruncate' => 'Safe\ftruncate', - 'fwrite' => 'Safe\fwrite', - 'getallheaders' => 'Safe\getallheaders', - 'getcwd' => 'Safe\getcwd', - 'gethostname' => 'Safe\gethostname', - 'getimagesize' => 'Safe\getimagesize', - 'getlastmod' => 'Safe\getlastmod', - 'getmygid' => 'Safe\getmygid', - 'getmyinode' => 'Safe\getmyinode', - 'getmypid' => 'Safe\getmypid', - 'getmyuid' => 'Safe\getmyuid', - 'getopt' => 'Safe\getopt', - 'getprotobyname' => 'Safe\getprotobyname', - 'getprotobynumber' => 'Safe\getprotobynumber', - 'get_headers' => 'Safe\get_headers', - 'glob' => 'Safe\glob', - 'gmp_binomial' => 'Safe\gmp_binomial', - 'gmp_export' => 'Safe\gmp_export', - 'gmp_import' => 'Safe\gmp_import', - 'gmp_random_seed' => 'Safe\gmp_random_seed', - 'gnupg_adddecryptkey' => 'Safe\gnupg_adddecryptkey', - 'gnupg_addencryptkey' => 'Safe\gnupg_addencryptkey', - 'gnupg_addsignkey' => 'Safe\gnupg_addsignkey', - 'gnupg_cleardecryptkeys' => 'Safe\gnupg_cleardecryptkeys', - 'gnupg_clearencryptkeys' => 'Safe\gnupg_clearencryptkeys', - 'gnupg_clearsignkeys' => 'Safe\gnupg_clearsignkeys', - 'gnupg_setarmor' => 'Safe\gnupg_setarmor', - 'gnupg_setsignmode' => 'Safe\gnupg_setsignmode', - 'gzclose' => 'Safe\gzclose', - 'gzcompress' => 'Safe\gzcompress', - 'gzdecode' => 'Safe\gzdecode', - 'gzdeflate' => 'Safe\gzdeflate', - 'gzencode' => 'Safe\gzencode', - 'gzgets' => 'Safe\gzgets', - 'gzgetss' => 'Safe\gzgetss', - 'gzinflate' => 'Safe\gzinflate', - 'gzpassthru' => 'Safe\gzpassthru', - 'gzrewind' => 'Safe\gzrewind', - 'gzuncompress' => 'Safe\gzuncompress', - 'hash_hkdf' => 'Safe\hash_hkdf', - 'hash_update_file' => 'Safe\hash_update_file', - 'header_register_callback' => 'Safe\header_register_callback', - 'hex2bin' => 'Safe\hex2bin', - 'highlight_file' => 'Safe\highlight_file', - 'highlight_string' => 'Safe\highlight_string', - 'ibase_add_user' => 'Safe\ibase_add_user', - 'ibase_backup' => 'Safe\ibase_backup', - 'ibase_blob_cancel' => 'Safe\ibase_blob_cancel', - 'ibase_blob_create' => 'Safe\ibase_blob_create', - 'ibase_blob_get' => 'Safe\ibase_blob_get', - 'ibase_close' => 'Safe\ibase_close', - 'ibase_commit' => 'Safe\ibase_commit', - 'ibase_commit_ret' => 'Safe\ibase_commit_ret', - 'ibase_connect' => 'Safe\ibase_connect', - 'ibase_delete_user' => 'Safe\ibase_delete_user', - 'ibase_drop_db' => 'Safe\ibase_drop_db', - 'ibase_free_event_handler' => 'Safe\ibase_free_event_handler', - 'ibase_free_query' => 'Safe\ibase_free_query', - 'ibase_free_result' => 'Safe\ibase_free_result', - 'ibase_maintain_db' => 'Safe\ibase_maintain_db', - 'ibase_modify_user' => 'Safe\ibase_modify_user', - 'ibase_name_result' => 'Safe\ibase_name_result', - 'ibase_pconnect' => 'Safe\ibase_pconnect', - 'ibase_restore' => 'Safe\ibase_restore', - 'ibase_rollback' => 'Safe\ibase_rollback', - 'ibase_rollback_ret' => 'Safe\ibase_rollback_ret', - 'ibase_service_attach' => 'Safe\ibase_service_attach', - 'ibase_service_detach' => 'Safe\ibase_service_detach', - 'iconv' => 'Safe\iconv', - 'iconv_get_encoding' => 'Safe\iconv_get_encoding', - 'iconv_set_encoding' => 'Safe\iconv_set_encoding', - 'image2wbmp' => 'Safe\image2wbmp', - 'imageaffine' => 'Safe\imageaffine', - 'imageaffinematrixconcat' => 'Safe\imageaffinematrixconcat', - 'imageaffinematrixget' => 'Safe\imageaffinematrixget', - 'imagealphablending' => 'Safe\imagealphablending', - 'imageantialias' => 'Safe\imageantialias', - 'imagearc' => 'Safe\imagearc', - 'imagebmp' => 'Safe\imagebmp', - 'imagechar' => 'Safe\imagechar', - 'imagecharup' => 'Safe\imagecharup', - 'imagecolorat' => 'Safe\imagecolorat', - 'imagecolordeallocate' => 'Safe\imagecolordeallocate', - 'imagecolormatch' => 'Safe\imagecolormatch', - 'imageconvolution' => 'Safe\imageconvolution', - 'imagecopy' => 'Safe\imagecopy', - 'imagecopymerge' => 'Safe\imagecopymerge', - 'imagecopymergegray' => 'Safe\imagecopymergegray', - 'imagecopyresampled' => 'Safe\imagecopyresampled', - 'imagecopyresized' => 'Safe\imagecopyresized', - 'imagecreate' => 'Safe\imagecreate', - 'imagecreatefrombmp' => 'Safe\imagecreatefrombmp', - 'imagecreatefromgd' => 'Safe\imagecreatefromgd', - 'imagecreatefromgd2' => 'Safe\imagecreatefromgd2', - 'imagecreatefromgd2part' => 'Safe\imagecreatefromgd2part', - 'imagecreatefromgif' => 'Safe\imagecreatefromgif', - 'imagecreatefromjpeg' => 'Safe\imagecreatefromjpeg', - 'imagecreatefrompng' => 'Safe\imagecreatefrompng', - 'imagecreatefromwbmp' => 'Safe\imagecreatefromwbmp', - 'imagecreatefromwebp' => 'Safe\imagecreatefromwebp', - 'imagecreatefromxbm' => 'Safe\imagecreatefromxbm', - 'imagecreatefromxpm' => 'Safe\imagecreatefromxpm', - 'imagecreatetruecolor' => 'Safe\imagecreatetruecolor', - 'imagecrop' => 'Safe\imagecrop', - 'imagecropauto' => 'Safe\imagecropauto', - 'imagedashedline' => 'Safe\imagedashedline', - 'imagedestroy' => 'Safe\imagedestroy', - 'imageellipse' => 'Safe\imageellipse', - 'imagefill' => 'Safe\imagefill', - 'imagefilledarc' => 'Safe\imagefilledarc', - 'imagefilledellipse' => 'Safe\imagefilledellipse', - 'imagefilledpolygon' => 'Safe\imagefilledpolygon', - 'imagefilledrectangle' => 'Safe\imagefilledrectangle', - 'imagefilltoborder' => 'Safe\imagefilltoborder', - 'imagefilter' => 'Safe\imagefilter', - 'imageflip' => 'Safe\imageflip', - 'imagegammacorrect' => 'Safe\imagegammacorrect', - 'imagegd' => 'Safe\imagegd', - 'imagegd2' => 'Safe\imagegd2', - 'imagegif' => 'Safe\imagegif', - 'imagegrabscreen' => 'Safe\imagegrabscreen', - 'imagegrabwindow' => 'Safe\imagegrabwindow', - 'imagejpeg' => 'Safe\imagejpeg', - 'imagelayereffect' => 'Safe\imagelayereffect', - 'imageline' => 'Safe\imageline', - 'imageloadfont' => 'Safe\imageloadfont', - 'imageopenpolygon' => 'Safe\imageopenpolygon', - 'imagepng' => 'Safe\imagepng', - 'imagepolygon' => 'Safe\imagepolygon', - 'imagepsencodefont' => 'Safe\imagepsencodefont', - 'imagepsextendfont' => 'Safe\imagepsextendfont', - 'imagepsfreefont' => 'Safe\imagepsfreefont', - 'imagepsslantfont' => 'Safe\imagepsslantfont', - 'imagerectangle' => 'Safe\imagerectangle', - 'imagerotate' => 'Safe\imagerotate', - 'imagesavealpha' => 'Safe\imagesavealpha', - 'imagescale' => 'Safe\imagescale', - 'imagesetbrush' => 'Safe\imagesetbrush', - 'imagesetclip' => 'Safe\imagesetclip', - 'imagesetinterpolation' => 'Safe\imagesetinterpolation', - 'imagesetpixel' => 'Safe\imagesetpixel', - 'imagesetstyle' => 'Safe\imagesetstyle', - 'imagesetthickness' => 'Safe\imagesetthickness', - 'imagesettile' => 'Safe\imagesettile', - 'imagestring' => 'Safe\imagestring', - 'imagestringup' => 'Safe\imagestringup', - 'imagesx' => 'Safe\imagesx', - 'imagesy' => 'Safe\imagesy', - 'imagetruecolortopalette' => 'Safe\imagetruecolortopalette', - 'imagettfbbox' => 'Safe\imagettfbbox', - 'imagettftext' => 'Safe\imagettftext', - 'imagewbmp' => 'Safe\imagewbmp', - 'imagewebp' => 'Safe\imagewebp', - 'imagexbm' => 'Safe\imagexbm', - 'imap_append' => 'Safe\imap_append', - 'imap_check' => 'Safe\imap_check', - 'imap_clearflag_full' => 'Safe\imap_clearflag_full', - 'imap_close' => 'Safe\imap_close', - 'imap_createmailbox' => 'Safe\imap_createmailbox', - 'imap_deletemailbox' => 'Safe\imap_deletemailbox', - 'imap_gc' => 'Safe\imap_gc', - 'imap_headerinfo' => 'Safe\imap_headerinfo', - 'imap_mail' => 'Safe\imap_mail', - 'imap_mailboxmsginfo' => 'Safe\imap_mailboxmsginfo', - 'imap_mail_copy' => 'Safe\imap_mail_copy', - 'imap_mail_move' => 'Safe\imap_mail_move', - 'imap_mutf7_to_utf8' => 'Safe\imap_mutf7_to_utf8', - 'imap_num_msg' => 'Safe\imap_num_msg', - 'imap_open' => 'Safe\imap_open', - 'imap_renamemailbox' => 'Safe\imap_renamemailbox', - 'imap_savebody' => 'Safe\imap_savebody', - 'imap_setacl' => 'Safe\imap_setacl', - 'imap_setflag_full' => 'Safe\imap_setflag_full', - 'imap_set_quota' => 'Safe\imap_set_quota', - 'imap_subscribe' => 'Safe\imap_subscribe', - 'imap_thread' => 'Safe\imap_thread', - 'imap_timeout' => 'Safe\imap_timeout', - 'imap_undelete' => 'Safe\imap_undelete', - 'imap_unsubscribe' => 'Safe\imap_unsubscribe', - 'imap_utf8_to_mutf7' => 'Safe\imap_utf8_to_mutf7', - 'inet_ntop' => 'Safe\inet_ntop', - 'inflate_add' => 'Safe\inflate_add', - 'inflate_get_read_len' => 'Safe\inflate_get_read_len', - 'inflate_get_status' => 'Safe\inflate_get_status', - 'inflate_init' => 'Safe\inflate_init', - 'ingres_autocommit' => 'Safe\ingres_autocommit', - 'ingres_close' => 'Safe\ingres_close', - 'ingres_commit' => 'Safe\ingres_commit', - 'ingres_connect' => 'Safe\ingres_connect', - 'ingres_execute' => 'Safe\ingres_execute', - 'ingres_field_name' => 'Safe\ingres_field_name', - 'ingres_field_type' => 'Safe\ingres_field_type', - 'ingres_free_result' => 'Safe\ingres_free_result', - 'ingres_pconnect' => 'Safe\ingres_pconnect', - 'ingres_result_seek' => 'Safe\ingres_result_seek', - 'ingres_rollback' => 'Safe\ingres_rollback', - 'ingres_set_environment' => 'Safe\ingres_set_environment', - 'ini_get' => 'Safe\ini_get', - 'ini_set' => 'Safe\ini_set', - 'inotify_init' => 'Safe\inotify_init', - 'inotify_rm_watch' => 'Safe\inotify_rm_watch', - 'iptcembed' => 'Safe\iptcembed', - 'iptcparse' => 'Safe\iptcparse', - 'jpeg2wbmp' => 'Safe\jpeg2wbmp', - 'json_decode' => 'Safe\json_decode', - 'json_encode' => 'Safe\json_encode', - 'json_last_error_msg' => 'Safe\json_last_error_msg', - 'krsort' => 'Safe\krsort', - 'ksort' => 'Safe\ksort', - 'lchgrp' => 'Safe\lchgrp', - 'lchown' => 'Safe\lchown', - 'ldap_add' => 'Safe\ldap_add', - 'ldap_add_ext' => 'Safe\ldap_add_ext', - 'ldap_bind' => 'Safe\ldap_bind', - 'ldap_bind_ext' => 'Safe\ldap_bind_ext', - 'ldap_control_paged_result' => 'Safe\ldap_control_paged_result', - 'ldap_control_paged_result_response' => 'Safe\ldap_control_paged_result_response', - 'ldap_count_entries' => 'Safe\ldap_count_entries', - 'ldap_delete' => 'Safe\ldap_delete', - 'ldap_delete_ext' => 'Safe\ldap_delete_ext', - 'ldap_exop' => 'Safe\ldap_exop', - 'ldap_exop_passwd' => 'Safe\ldap_exop_passwd', - 'ldap_exop_whoami' => 'Safe\ldap_exop_whoami', - 'ldap_explode_dn' => 'Safe\ldap_explode_dn', - 'ldap_first_attribute' => 'Safe\ldap_first_attribute', - 'ldap_first_entry' => 'Safe\ldap_first_entry', - 'ldap_free_result' => 'Safe\ldap_free_result', - 'ldap_get_attributes' => 'Safe\ldap_get_attributes', - 'ldap_get_dn' => 'Safe\ldap_get_dn', - 'ldap_get_entries' => 'Safe\ldap_get_entries', - 'ldap_get_option' => 'Safe\ldap_get_option', - 'ldap_get_values' => 'Safe\ldap_get_values', - 'ldap_get_values_len' => 'Safe\ldap_get_values_len', - 'ldap_list' => 'Safe\ldap_list', - 'ldap_modify_batch' => 'Safe\ldap_modify_batch', - 'ldap_mod_add' => 'Safe\ldap_mod_add', - 'ldap_mod_add_ext' => 'Safe\ldap_mod_add_ext', - 'ldap_mod_del' => 'Safe\ldap_mod_del', - 'ldap_mod_del_ext' => 'Safe\ldap_mod_del_ext', - 'ldap_mod_replace' => 'Safe\ldap_mod_replace', - 'ldap_mod_replace_ext' => 'Safe\ldap_mod_replace_ext', - 'ldap_next_attribute' => 'Safe\ldap_next_attribute', - 'ldap_parse_exop' => 'Safe\ldap_parse_exop', - 'ldap_parse_result' => 'Safe\ldap_parse_result', - 'ldap_read' => 'Safe\ldap_read', - 'ldap_rename' => 'Safe\ldap_rename', - 'ldap_rename_ext' => 'Safe\ldap_rename_ext', - 'ldap_sasl_bind' => 'Safe\ldap_sasl_bind', - 'ldap_search' => 'Safe\ldap_search', - 'ldap_set_option' => 'Safe\ldap_set_option', - 'ldap_unbind' => 'Safe\ldap_unbind', - 'libxml_get_last_error' => 'Safe\libxml_get_last_error', - 'libxml_set_external_entity_loader' => 'Safe\libxml_set_external_entity_loader', - 'link' => 'Safe\link', - 'lzf_compress' => 'Safe\lzf_compress', - 'lzf_decompress' => 'Safe\lzf_decompress', - 'mailparse_msg_extract_part_file' => 'Safe\mailparse_msg_extract_part_file', - 'mailparse_msg_free' => 'Safe\mailparse_msg_free', - 'mailparse_msg_parse' => 'Safe\mailparse_msg_parse', - 'mailparse_msg_parse_file' => 'Safe\mailparse_msg_parse_file', - 'mailparse_stream_encode' => 'Safe\mailparse_stream_encode', - 'mb_chr' => 'Safe\mb_chr', - 'mb_detect_order' => 'Safe\mb_detect_order', - 'mb_encoding_aliases' => 'Safe\mb_encoding_aliases', - 'mb_eregi_replace' => 'Safe\mb_eregi_replace', - 'mb_ereg_replace' => 'Safe\mb_ereg_replace', - 'mb_ereg_replace_callback' => 'Safe\mb_ereg_replace_callback', - 'mb_ereg_search_getregs' => 'Safe\mb_ereg_search_getregs', - 'mb_ereg_search_init' => 'Safe\mb_ereg_search_init', - 'mb_ereg_search_regs' => 'Safe\mb_ereg_search_regs', - 'mb_ereg_search_setpos' => 'Safe\mb_ereg_search_setpos', - 'mb_http_output' => 'Safe\mb_http_output', - 'mb_internal_encoding' => 'Safe\mb_internal_encoding', - 'mb_ord' => 'Safe\mb_ord', - 'mb_parse_str' => 'Safe\mb_parse_str', - 'mb_regex_encoding' => 'Safe\mb_regex_encoding', - 'mb_send_mail' => 'Safe\mb_send_mail', - 'mb_split' => 'Safe\mb_split', - 'md5_file' => 'Safe\md5_file', - 'metaphone' => 'Safe\metaphone', - 'mime_content_type' => 'Safe\mime_content_type', - 'mkdir' => 'Safe\mkdir', - 'mktime' => 'Safe\mktime', - 'msg_queue_exists' => 'Safe\msg_queue_exists', - 'msg_receive' => 'Safe\msg_receive', - 'msg_remove_queue' => 'Safe\msg_remove_queue', - 'msg_send' => 'Safe\msg_send', - 'msg_set_queue' => 'Safe\msg_set_queue', - 'msql_affected_rows' => 'Safe\msql_affected_rows', - 'msql_close' => 'Safe\msql_close', - 'msql_connect' => 'Safe\msql_connect', - 'msql_create_db' => 'Safe\msql_create_db', - 'msql_data_seek' => 'Safe\msql_data_seek', - 'msql_db_query' => 'Safe\msql_db_query', - 'msql_drop_db' => 'Safe\msql_drop_db', - 'msql_field_len' => 'Safe\msql_field_len', - 'msql_field_name' => 'Safe\msql_field_name', - 'msql_field_seek' => 'Safe\msql_field_seek', - 'msql_field_table' => 'Safe\msql_field_table', - 'msql_field_type' => 'Safe\msql_field_type', - 'msql_free_result' => 'Safe\msql_free_result', - 'msql_pconnect' => 'Safe\msql_pconnect', - 'msql_query' => 'Safe\msql_query', - 'msql_select_db' => 'Safe\msql_select_db', - 'mssql_bind' => 'Safe\mssql_bind', - 'mssql_close' => 'Safe\mssql_close', - 'mssql_connect' => 'Safe\mssql_connect', - 'mssql_data_seek' => 'Safe\mssql_data_seek', - 'mssql_field_length' => 'Safe\mssql_field_length', - 'mssql_field_name' => 'Safe\mssql_field_name', - 'mssql_field_seek' => 'Safe\mssql_field_seek', - 'mssql_field_type' => 'Safe\mssql_field_type', - 'mssql_free_result' => 'Safe\mssql_free_result', - 'mssql_free_statement' => 'Safe\mssql_free_statement', - 'mssql_init' => 'Safe\mssql_init', - 'mssql_pconnect' => 'Safe\mssql_pconnect', - 'mssql_query' => 'Safe\mssql_query', - 'mssql_select_db' => 'Safe\mssql_select_db', - 'mysqli_get_cache_stats' => 'Safe\mysqli_get_cache_stats', - 'mysqli_get_client_stats' => 'Safe\mysqli_get_client_stats', - 'mysqlnd_ms_dump_servers' => 'Safe\mysqlnd_ms_dump_servers', - 'mysqlnd_ms_fabric_select_global' => 'Safe\mysqlnd_ms_fabric_select_global', - 'mysqlnd_ms_fabric_select_shard' => 'Safe\mysqlnd_ms_fabric_select_shard', - 'mysqlnd_ms_get_last_used_connection' => 'Safe\mysqlnd_ms_get_last_used_connection', - 'mysqlnd_qc_clear_cache' => 'Safe\mysqlnd_qc_clear_cache', - 'mysqlnd_qc_set_is_select' => 'Safe\mysqlnd_qc_set_is_select', - 'mysqlnd_qc_set_storage_handler' => 'Safe\mysqlnd_qc_set_storage_handler', - 'mysql_close' => 'Safe\mysql_close', - 'mysql_connect' => 'Safe\mysql_connect', - 'mysql_create_db' => 'Safe\mysql_create_db', - 'mysql_data_seek' => 'Safe\mysql_data_seek', - 'mysql_db_name' => 'Safe\mysql_db_name', - 'mysql_db_query' => 'Safe\mysql_db_query', - 'mysql_drop_db' => 'Safe\mysql_drop_db', - 'mysql_fetch_lengths' => 'Safe\mysql_fetch_lengths', - 'mysql_field_flags' => 'Safe\mysql_field_flags', - 'mysql_field_len' => 'Safe\mysql_field_len', - 'mysql_field_name' => 'Safe\mysql_field_name', - 'mysql_field_seek' => 'Safe\mysql_field_seek', - 'mysql_free_result' => 'Safe\mysql_free_result', - 'mysql_get_host_info' => 'Safe\mysql_get_host_info', - 'mysql_get_proto_info' => 'Safe\mysql_get_proto_info', - 'mysql_get_server_info' => 'Safe\mysql_get_server_info', - 'mysql_info' => 'Safe\mysql_info', - 'mysql_list_dbs' => 'Safe\mysql_list_dbs', - 'mysql_list_fields' => 'Safe\mysql_list_fields', - 'mysql_list_processes' => 'Safe\mysql_list_processes', - 'mysql_list_tables' => 'Safe\mysql_list_tables', - 'mysql_num_fields' => 'Safe\mysql_num_fields', - 'mysql_num_rows' => 'Safe\mysql_num_rows', - 'mysql_query' => 'Safe\mysql_query', - 'mysql_real_escape_string' => 'Safe\mysql_real_escape_string', - 'mysql_result' => 'Safe\mysql_result', - 'mysql_select_db' => 'Safe\mysql_select_db', - 'mysql_set_charset' => 'Safe\mysql_set_charset', - 'mysql_tablename' => 'Safe\mysql_tablename', - 'mysql_thread_id' => 'Safe\mysql_thread_id', - 'mysql_unbuffered_query' => 'Safe\mysql_unbuffered_query', - 'natcasesort' => 'Safe\natcasesort', - 'natsort' => 'Safe\natsort', - 'ob_end_clean' => 'Safe\ob_end_clean', - 'ob_end_flush' => 'Safe\ob_end_flush', - 'oci_bind_array_by_name' => 'Safe\oci_bind_array_by_name', - 'oci_bind_by_name' => 'Safe\oci_bind_by_name', - 'oci_cancel' => 'Safe\oci_cancel', - 'oci_close' => 'Safe\oci_close', - 'oci_commit' => 'Safe\oci_commit', - 'oci_connect' => 'Safe\oci_connect', - 'oci_define_by_name' => 'Safe\oci_define_by_name', - 'oci_execute' => 'Safe\oci_execute', - 'oci_fetch_all' => 'Safe\oci_fetch_all', - 'oci_field_name' => 'Safe\oci_field_name', - 'oci_field_precision' => 'Safe\oci_field_precision', - 'oci_field_scale' => 'Safe\oci_field_scale', - 'oci_field_size' => 'Safe\oci_field_size', - 'oci_field_type' => 'Safe\oci_field_type', - 'oci_field_type_raw' => 'Safe\oci_field_type_raw', - 'oci_free_descriptor' => 'Safe\oci_free_descriptor', - 'oci_free_statement' => 'Safe\oci_free_statement', - 'oci_new_collection' => 'Safe\oci_new_collection', - 'oci_new_connect' => 'Safe\oci_new_connect', - 'oci_new_cursor' => 'Safe\oci_new_cursor', - 'oci_new_descriptor' => 'Safe\oci_new_descriptor', - 'oci_num_fields' => 'Safe\oci_num_fields', - 'oci_num_rows' => 'Safe\oci_num_rows', - 'oci_parse' => 'Safe\oci_parse', - 'oci_pconnect' => 'Safe\oci_pconnect', - 'oci_result' => 'Safe\oci_result', - 'oci_rollback' => 'Safe\oci_rollback', - 'oci_server_version' => 'Safe\oci_server_version', - 'oci_set_action' => 'Safe\oci_set_action', - 'oci_set_call_timeout' => 'Safe\oci_set_call_timeout', - 'oci_set_client_identifier' => 'Safe\oci_set_client_identifier', - 'oci_set_client_info' => 'Safe\oci_set_client_info', - 'oci_set_db_operation' => 'Safe\oci_set_db_operation', - 'oci_set_edition' => 'Safe\oci_set_edition', - 'oci_set_module_name' => 'Safe\oci_set_module_name', - 'oci_set_prefetch' => 'Safe\oci_set_prefetch', - 'oci_statement_type' => 'Safe\oci_statement_type', - 'oci_unregister_taf_callback' => 'Safe\oci_unregister_taf_callback', - 'odbc_autocommit' => 'Safe\odbc_autocommit', - 'odbc_binmode' => 'Safe\odbc_binmode', - 'odbc_columnprivileges' => 'Safe\odbc_columnprivileges', - 'odbc_columns' => 'Safe\odbc_columns', - 'odbc_commit' => 'Safe\odbc_commit', - 'odbc_data_source' => 'Safe\odbc_data_source', - 'odbc_exec' => 'Safe\odbc_exec', - 'odbc_execute' => 'Safe\odbc_execute', - 'odbc_fetch_into' => 'Safe\odbc_fetch_into', - 'odbc_field_len' => 'Safe\odbc_field_len', - 'odbc_field_name' => 'Safe\odbc_field_name', - 'odbc_field_num' => 'Safe\odbc_field_num', - 'odbc_field_scale' => 'Safe\odbc_field_scale', - 'odbc_field_type' => 'Safe\odbc_field_type', - 'odbc_foreignkeys' => 'Safe\odbc_foreignkeys', - 'odbc_gettypeinfo' => 'Safe\odbc_gettypeinfo', - 'odbc_longreadlen' => 'Safe\odbc_longreadlen', - 'odbc_prepare' => 'Safe\odbc_prepare', - 'odbc_primarykeys' => 'Safe\odbc_primarykeys', - 'odbc_result' => 'Safe\odbc_result', - 'odbc_result_all' => 'Safe\odbc_result_all', - 'odbc_rollback' => 'Safe\odbc_rollback', - 'odbc_setoption' => 'Safe\odbc_setoption', - 'odbc_specialcolumns' => 'Safe\odbc_specialcolumns', - 'odbc_statistics' => 'Safe\odbc_statistics', - 'odbc_tableprivileges' => 'Safe\odbc_tableprivileges', - 'odbc_tables' => 'Safe\odbc_tables', - 'opcache_compile_file' => 'Safe\opcache_compile_file', - 'opcache_get_status' => 'Safe\opcache_get_status', - 'opendir' => 'Safe\opendir', - 'openlog' => 'Safe\openlog', - 'openssl_cipher_iv_length' => 'Safe\openssl_cipher_iv_length', - 'openssl_csr_export' => 'Safe\openssl_csr_export', - 'openssl_csr_export_to_file' => 'Safe\openssl_csr_export_to_file', - 'openssl_csr_get_subject' => 'Safe\openssl_csr_get_subject', - 'openssl_csr_new' => 'Safe\openssl_csr_new', - 'openssl_csr_sign' => 'Safe\openssl_csr_sign', - 'openssl_decrypt' => 'Safe\openssl_decrypt', - 'openssl_dh_compute_key' => 'Safe\openssl_dh_compute_key', - 'openssl_digest' => 'Safe\openssl_digest', - 'openssl_encrypt' => 'Safe\openssl_encrypt', - 'openssl_open' => 'Safe\openssl_open', - 'openssl_pbkdf2' => 'Safe\openssl_pbkdf2', - 'openssl_pkcs7_decrypt' => 'Safe\openssl_pkcs7_decrypt', - 'openssl_pkcs7_encrypt' => 'Safe\openssl_pkcs7_encrypt', - 'openssl_pkcs7_read' => 'Safe\openssl_pkcs7_read', - 'openssl_pkcs7_sign' => 'Safe\openssl_pkcs7_sign', - 'openssl_pkcs12_export' => 'Safe\openssl_pkcs12_export', - 'openssl_pkcs12_export_to_file' => 'Safe\openssl_pkcs12_export_to_file', - 'openssl_pkcs12_read' => 'Safe\openssl_pkcs12_read', - 'openssl_pkey_export' => 'Safe\openssl_pkey_export', - 'openssl_pkey_export_to_file' => 'Safe\openssl_pkey_export_to_file', - 'openssl_pkey_get_private' => 'Safe\openssl_pkey_get_private', - 'openssl_pkey_get_public' => 'Safe\openssl_pkey_get_public', - 'openssl_pkey_new' => 'Safe\openssl_pkey_new', - 'openssl_private_decrypt' => 'Safe\openssl_private_decrypt', - 'openssl_private_encrypt' => 'Safe\openssl_private_encrypt', - 'openssl_public_decrypt' => 'Safe\openssl_public_decrypt', - 'openssl_public_encrypt' => 'Safe\openssl_public_encrypt', - 'openssl_random_pseudo_bytes' => 'Safe\openssl_random_pseudo_bytes', - 'openssl_seal' => 'Safe\openssl_seal', - 'openssl_sign' => 'Safe\openssl_sign', - 'openssl_x509_export' => 'Safe\openssl_x509_export', - 'openssl_x509_export_to_file' => 'Safe\openssl_x509_export_to_file', - 'openssl_x509_fingerprint' => 'Safe\openssl_x509_fingerprint', - 'openssl_x509_read' => 'Safe\openssl_x509_read', - 'output_add_rewrite_var' => 'Safe\output_add_rewrite_var', - 'output_reset_rewrite_vars' => 'Safe\output_reset_rewrite_vars', - 'parse_ini_file' => 'Safe\parse_ini_file', - 'parse_ini_string' => 'Safe\parse_ini_string', - 'parse_url' => 'Safe\parse_url', - 'password_hash' => 'Safe\password_hash', - 'pcntl_exec' => 'Safe\pcntl_exec', - 'pcntl_getpriority' => 'Safe\pcntl_getpriority', - 'pcntl_setpriority' => 'Safe\pcntl_setpriority', - 'pcntl_signal_dispatch' => 'Safe\pcntl_signal_dispatch', - 'pcntl_sigprocmask' => 'Safe\pcntl_sigprocmask', - 'pcntl_strerror' => 'Safe\pcntl_strerror', - 'PDF_activate_item' => 'Safe\PDF_activate_item', - 'PDF_add_locallink' => 'Safe\PDF_add_locallink', - 'PDF_add_nameddest' => 'Safe\PDF_add_nameddest', - 'PDF_add_note' => 'Safe\PDF_add_note', - 'PDF_add_pdflink' => 'Safe\PDF_add_pdflink', - 'PDF_add_thumbnail' => 'Safe\PDF_add_thumbnail', - 'PDF_add_weblink' => 'Safe\PDF_add_weblink', - 'PDF_attach_file' => 'Safe\PDF_attach_file', - 'PDF_begin_layer' => 'Safe\PDF_begin_layer', - 'PDF_begin_page' => 'Safe\PDF_begin_page', - 'PDF_begin_page_ext' => 'Safe\PDF_begin_page_ext', - 'PDF_circle' => 'Safe\PDF_circle', - 'PDF_clip' => 'Safe\PDF_clip', - 'PDF_close' => 'Safe\PDF_close', - 'PDF_closepath' => 'Safe\PDF_closepath', - 'PDF_closepath_fill_stroke' => 'Safe\PDF_closepath_fill_stroke', - 'PDF_closepath_stroke' => 'Safe\PDF_closepath_stroke', - 'PDF_close_pdi' => 'Safe\PDF_close_pdi', - 'PDF_close_pdi_page' => 'Safe\PDF_close_pdi_page', - 'PDF_concat' => 'Safe\PDF_concat', - 'PDF_continue_text' => 'Safe\PDF_continue_text', - 'PDF_curveto' => 'Safe\PDF_curveto', - 'PDF_delete' => 'Safe\PDF_delete', - 'PDF_end_layer' => 'Safe\PDF_end_layer', - 'PDF_end_page' => 'Safe\PDF_end_page', - 'PDF_end_page_ext' => 'Safe\PDF_end_page_ext', - 'PDF_end_pattern' => 'Safe\PDF_end_pattern', - 'PDF_end_template' => 'Safe\PDF_end_template', - 'PDF_fill' => 'Safe\PDF_fill', - 'PDF_fill_stroke' => 'Safe\PDF_fill_stroke', - 'PDF_fit_image' => 'Safe\PDF_fit_image', - 'PDF_fit_pdi_page' => 'Safe\PDF_fit_pdi_page', - 'PDF_fit_textline' => 'Safe\PDF_fit_textline', - 'PDF_initgraphics' => 'Safe\PDF_initgraphics', - 'PDF_lineto' => 'Safe\PDF_lineto', - 'PDF_makespotcolor' => 'Safe\PDF_makespotcolor', - 'PDF_moveto' => 'Safe\PDF_moveto', - 'PDF_open_file' => 'Safe\PDF_open_file', - 'PDF_place_image' => 'Safe\PDF_place_image', - 'PDF_place_pdi_page' => 'Safe\PDF_place_pdi_page', - 'PDF_rect' => 'Safe\PDF_rect', - 'PDF_restore' => 'Safe\PDF_restore', - 'PDF_rotate' => 'Safe\PDF_rotate', - 'PDF_save' => 'Safe\PDF_save', - 'PDF_scale' => 'Safe\PDF_scale', - 'PDF_setcolor' => 'Safe\PDF_setcolor', - 'PDF_setdash' => 'Safe\PDF_setdash', - 'PDF_setdashpattern' => 'Safe\PDF_setdashpattern', - 'PDF_setflat' => 'Safe\PDF_setflat', - 'PDF_setfont' => 'Safe\PDF_setfont', - 'PDF_setgray' => 'Safe\PDF_setgray', - 'PDF_setgray_fill' => 'Safe\PDF_setgray_fill', - 'PDF_setgray_stroke' => 'Safe\PDF_setgray_stroke', - 'PDF_setlinejoin' => 'Safe\PDF_setlinejoin', - 'PDF_setlinewidth' => 'Safe\PDF_setlinewidth', - 'PDF_setmatrix' => 'Safe\PDF_setmatrix', - 'PDF_setmiterlimit' => 'Safe\PDF_setmiterlimit', - 'PDF_setrgbcolor' => 'Safe\PDF_setrgbcolor', - 'PDF_setrgbcolor_fill' => 'Safe\PDF_setrgbcolor_fill', - 'PDF_setrgbcolor_stroke' => 'Safe\PDF_setrgbcolor_stroke', - 'PDF_set_border_color' => 'Safe\PDF_set_border_color', - 'PDF_set_border_dash' => 'Safe\PDF_set_border_dash', - 'PDF_set_border_style' => 'Safe\PDF_set_border_style', - 'PDF_set_info' => 'Safe\PDF_set_info', - 'PDF_set_layer_dependency' => 'Safe\PDF_set_layer_dependency', - 'PDF_set_parameter' => 'Safe\PDF_set_parameter', - 'PDF_set_text_pos' => 'Safe\PDF_set_text_pos', - 'PDF_set_value' => 'Safe\PDF_set_value', - 'PDF_show' => 'Safe\PDF_show', - 'PDF_show_xy' => 'Safe\PDF_show_xy', - 'PDF_skew' => 'Safe\PDF_skew', - 'PDF_stroke' => 'Safe\PDF_stroke', - 'pg_cancel_query' => 'Safe\pg_cancel_query', - 'pg_client_encoding' => 'Safe\pg_client_encoding', - 'pg_close' => 'Safe\pg_close', - 'pg_connect' => 'Safe\pg_connect', - 'pg_connection_reset' => 'Safe\pg_connection_reset', - 'pg_convert' => 'Safe\pg_convert', - 'pg_copy_from' => 'Safe\pg_copy_from', - 'pg_copy_to' => 'Safe\pg_copy_to', - 'pg_dbname' => 'Safe\pg_dbname', - 'pg_delete' => 'Safe\pg_delete', - 'pg_end_copy' => 'Safe\pg_end_copy', - 'pg_execute' => 'Safe\pg_execute', - 'pg_field_name' => 'Safe\pg_field_name', - 'pg_field_table' => 'Safe\pg_field_table', - 'pg_field_type' => 'Safe\pg_field_type', - 'pg_flush' => 'Safe\pg_flush', - 'pg_free_result' => 'Safe\pg_free_result', - 'pg_host' => 'Safe\pg_host', - 'pg_insert' => 'Safe\pg_insert', - 'pg_last_error' => 'Safe\pg_last_error', - 'pg_last_notice' => 'Safe\pg_last_notice', - 'pg_last_oid' => 'Safe\pg_last_oid', - 'pg_lo_close' => 'Safe\pg_lo_close', - 'pg_lo_export' => 'Safe\pg_lo_export', - 'pg_lo_import' => 'Safe\pg_lo_import', - 'pg_lo_open' => 'Safe\pg_lo_open', - 'pg_lo_read' => 'Safe\pg_lo_read', - 'pg_lo_read_all' => 'Safe\pg_lo_read_all', - 'pg_lo_seek' => 'Safe\pg_lo_seek', - 'pg_lo_truncate' => 'Safe\pg_lo_truncate', - 'pg_lo_unlink' => 'Safe\pg_lo_unlink', - 'pg_lo_write' => 'Safe\pg_lo_write', - 'pg_meta_data' => 'Safe\pg_meta_data', - 'pg_options' => 'Safe\pg_options', - 'pg_parameter_status' => 'Safe\pg_parameter_status', - 'pg_pconnect' => 'Safe\pg_pconnect', - 'pg_ping' => 'Safe\pg_ping', - 'pg_port' => 'Safe\pg_port', - 'pg_prepare' => 'Safe\pg_prepare', - 'pg_put_line' => 'Safe\pg_put_line', - 'pg_query' => 'Safe\pg_query', - 'pg_query_params' => 'Safe\pg_query_params', - 'pg_result_error_field' => 'Safe\pg_result_error_field', - 'pg_result_seek' => 'Safe\pg_result_seek', - 'pg_select' => 'Safe\pg_select', - 'pg_send_execute' => 'Safe\pg_send_execute', - 'pg_send_prepare' => 'Safe\pg_send_prepare', - 'pg_send_query' => 'Safe\pg_send_query', - 'pg_send_query_params' => 'Safe\pg_send_query_params', - 'pg_socket' => 'Safe\pg_socket', - 'pg_trace' => 'Safe\pg_trace', - 'pg_tty' => 'Safe\pg_tty', - 'pg_update' => 'Safe\pg_update', - 'pg_version' => 'Safe\pg_version', - 'phpcredits' => 'Safe\phpcredits', - 'phpinfo' => 'Safe\phpinfo', - 'png2wbmp' => 'Safe\png2wbmp', - 'posix_access' => 'Safe\posix_access', - 'posix_getgrnam' => 'Safe\posix_getgrnam', - 'posix_getpgid' => 'Safe\posix_getpgid', - 'posix_initgroups' => 'Safe\posix_initgroups', - 'posix_kill' => 'Safe\posix_kill', - 'posix_mkfifo' => 'Safe\posix_mkfifo', - 'posix_mknod' => 'Safe\posix_mknod', - 'posix_setegid' => 'Safe\posix_setegid', - 'posix_seteuid' => 'Safe\posix_seteuid', - 'posix_setgid' => 'Safe\posix_setgid', - 'posix_setpgid' => 'Safe\posix_setpgid', - 'posix_setrlimit' => 'Safe\posix_setrlimit', - 'posix_setuid' => 'Safe\posix_setuid', - 'preg_match' => 'Safe\preg_match', - 'preg_match_all' => 'Safe\preg_match_all', - 'preg_replace' => 'Safe\preg_replace', - 'preg_split' => 'Safe\preg_split', - 'proc_get_status' => 'Safe\proc_get_status', - 'proc_nice' => 'Safe\proc_nice', - 'pspell_add_to_personal' => 'Safe\pspell_add_to_personal', - 'pspell_add_to_session' => 'Safe\pspell_add_to_session', - 'pspell_clear_session' => 'Safe\pspell_clear_session', - 'pspell_config_create' => 'Safe\pspell_config_create', - 'pspell_config_data_dir' => 'Safe\pspell_config_data_dir', - 'pspell_config_dict_dir' => 'Safe\pspell_config_dict_dir', - 'pspell_config_ignore' => 'Safe\pspell_config_ignore', - 'pspell_config_mode' => 'Safe\pspell_config_mode', - 'pspell_config_personal' => 'Safe\pspell_config_personal', - 'pspell_config_repl' => 'Safe\pspell_config_repl', - 'pspell_config_runtogether' => 'Safe\pspell_config_runtogether', - 'pspell_config_save_repl' => 'Safe\pspell_config_save_repl', - 'pspell_new' => 'Safe\pspell_new', - 'pspell_new_config' => 'Safe\pspell_new_config', - 'pspell_save_wordlist' => 'Safe\pspell_save_wordlist', - 'pspell_store_replacement' => 'Safe\pspell_store_replacement', - 'ps_add_launchlink' => 'Safe\ps_add_launchlink', - 'ps_add_locallink' => 'Safe\ps_add_locallink', - 'ps_add_note' => 'Safe\ps_add_note', - 'ps_add_pdflink' => 'Safe\ps_add_pdflink', - 'ps_add_weblink' => 'Safe\ps_add_weblink', - 'ps_arc' => 'Safe\ps_arc', - 'ps_arcn' => 'Safe\ps_arcn', - 'ps_begin_page' => 'Safe\ps_begin_page', - 'ps_begin_pattern' => 'Safe\ps_begin_pattern', - 'ps_begin_template' => 'Safe\ps_begin_template', - 'ps_circle' => 'Safe\ps_circle', - 'ps_clip' => 'Safe\ps_clip', - 'ps_close' => 'Safe\ps_close', - 'ps_closepath' => 'Safe\ps_closepath', - 'ps_closepath_stroke' => 'Safe\ps_closepath_stroke', - 'ps_close_image' => 'Safe\ps_close_image', - 'ps_continue_text' => 'Safe\ps_continue_text', - 'ps_curveto' => 'Safe\ps_curveto', - 'ps_delete' => 'Safe\ps_delete', - 'ps_end_page' => 'Safe\ps_end_page', - 'ps_end_pattern' => 'Safe\ps_end_pattern', - 'ps_end_template' => 'Safe\ps_end_template', - 'ps_fill' => 'Safe\ps_fill', - 'ps_fill_stroke' => 'Safe\ps_fill_stroke', - 'ps_get_parameter' => 'Safe\ps_get_parameter', - 'ps_hyphenate' => 'Safe\ps_hyphenate', - 'ps_include_file' => 'Safe\ps_include_file', - 'ps_lineto' => 'Safe\ps_lineto', - 'ps_moveto' => 'Safe\ps_moveto', - 'ps_new' => 'Safe\ps_new', - 'ps_open_file' => 'Safe\ps_open_file', - 'ps_place_image' => 'Safe\ps_place_image', - 'ps_rect' => 'Safe\ps_rect', - 'ps_restore' => 'Safe\ps_restore', - 'ps_rotate' => 'Safe\ps_rotate', - 'ps_save' => 'Safe\ps_save', - 'ps_scale' => 'Safe\ps_scale', - 'ps_setcolor' => 'Safe\ps_setcolor', - 'ps_setdash' => 'Safe\ps_setdash', - 'ps_setflat' => 'Safe\ps_setflat', - 'ps_setfont' => 'Safe\ps_setfont', - 'ps_setgray' => 'Safe\ps_setgray', - 'ps_setlinecap' => 'Safe\ps_setlinecap', - 'ps_setlinejoin' => 'Safe\ps_setlinejoin', - 'ps_setlinewidth' => 'Safe\ps_setlinewidth', - 'ps_setmiterlimit' => 'Safe\ps_setmiterlimit', - 'ps_setoverprintmode' => 'Safe\ps_setoverprintmode', - 'ps_setpolydash' => 'Safe\ps_setpolydash', - 'ps_set_border_color' => 'Safe\ps_set_border_color', - 'ps_set_border_dash' => 'Safe\ps_set_border_dash', - 'ps_set_border_style' => 'Safe\ps_set_border_style', - 'ps_set_info' => 'Safe\ps_set_info', - 'ps_set_parameter' => 'Safe\ps_set_parameter', - 'ps_set_text_pos' => 'Safe\ps_set_text_pos', - 'ps_set_value' => 'Safe\ps_set_value', - 'ps_shading' => 'Safe\ps_shading', - 'ps_shading_pattern' => 'Safe\ps_shading_pattern', - 'ps_shfill' => 'Safe\ps_shfill', - 'ps_show' => 'Safe\ps_show', - 'ps_show2' => 'Safe\ps_show2', - 'ps_show_xy' => 'Safe\ps_show_xy', - 'ps_show_xy2' => 'Safe\ps_show_xy2', - 'ps_stroke' => 'Safe\ps_stroke', - 'ps_symbol' => 'Safe\ps_symbol', - 'ps_translate' => 'Safe\ps_translate', - 'putenv' => 'Safe\putenv', - 'readfile' => 'Safe\readfile', - 'readgzfile' => 'Safe\readgzfile', - 'readline_add_history' => 'Safe\readline_add_history', - 'readline_callback_handler_install' => 'Safe\readline_callback_handler_install', - 'readline_clear_history' => 'Safe\readline_clear_history', - 'readline_completion_function' => 'Safe\readline_completion_function', - 'readline_read_history' => 'Safe\readline_read_history', - 'readline_write_history' => 'Safe\readline_write_history', - 'readlink' => 'Safe\readlink', - 'realpath' => 'Safe\realpath', - 'register_tick_function' => 'Safe\register_tick_function', - 'rename' => 'Safe\rename', - 'rewind' => 'Safe\rewind', - 'rewinddir' => 'Safe\rewinddir', - 'rmdir' => 'Safe\rmdir', - 'rrd_create' => 'Safe\rrd_create', - 'rsort' => 'Safe\rsort', - 'sapi_windows_cp_conv' => 'Safe\sapi_windows_cp_conv', - 'sapi_windows_cp_set' => 'Safe\sapi_windows_cp_set', - 'sapi_windows_generate_ctrl_event' => 'Safe\sapi_windows_generate_ctrl_event', - 'sapi_windows_vt100_support' => 'Safe\sapi_windows_vt100_support', - 'scandir' => 'Safe\scandir', - 'sem_acquire' => 'Safe\sem_acquire', - 'sem_get' => 'Safe\sem_get', - 'sem_release' => 'Safe\sem_release', - 'sem_remove' => 'Safe\sem_remove', - 'session_abort' => 'Safe\session_abort', - 'session_decode' => 'Safe\session_decode', - 'session_destroy' => 'Safe\session_destroy', - 'session_regenerate_id' => 'Safe\session_regenerate_id', - 'session_reset' => 'Safe\session_reset', - 'session_unset' => 'Safe\session_unset', - 'session_write_close' => 'Safe\session_write_close', - 'settype' => 'Safe\settype', - 'set_include_path' => 'Safe\set_include_path', - 'set_time_limit' => 'Safe\set_time_limit', - 'sha1_file' => 'Safe\sha1_file', - 'shmop_delete' => 'Safe\shmop_delete', - 'shmop_read' => 'Safe\shmop_read', - 'shmop_write' => 'Safe\shmop_write', - 'shm_put_var' => 'Safe\shm_put_var', - 'shm_remove' => 'Safe\shm_remove', - 'shm_remove_var' => 'Safe\shm_remove_var', - 'shuffle' => 'Safe\shuffle', - 'simplexml_import_dom' => 'Safe\simplexml_import_dom', - 'simplexml_load_file' => 'Safe\simplexml_load_file', - 'simplexml_load_string' => 'Safe\simplexml_load_string', - 'sleep' => 'Safe\sleep', - 'socket_accept' => 'Safe\socket_accept', - 'socket_addrinfo_bind' => 'Safe\socket_addrinfo_bind', - 'socket_addrinfo_connect' => 'Safe\socket_addrinfo_connect', - 'socket_bind' => 'Safe\socket_bind', - 'socket_connect' => 'Safe\socket_connect', - 'socket_create' => 'Safe\socket_create', - 'socket_create_listen' => 'Safe\socket_create_listen', - 'socket_create_pair' => 'Safe\socket_create_pair', - 'socket_export_stream' => 'Safe\socket_export_stream', - 'socket_getpeername' => 'Safe\socket_getpeername', - 'socket_getsockname' => 'Safe\socket_getsockname', - 'socket_get_option' => 'Safe\socket_get_option', - 'socket_import_stream' => 'Safe\socket_import_stream', - 'socket_listen' => 'Safe\socket_listen', - 'socket_read' => 'Safe\socket_read', - 'socket_send' => 'Safe\socket_send', - 'socket_sendmsg' => 'Safe\socket_sendmsg', - 'socket_sendto' => 'Safe\socket_sendto', - 'socket_set_block' => 'Safe\socket_set_block', - 'socket_set_nonblock' => 'Safe\socket_set_nonblock', - 'socket_set_option' => 'Safe\socket_set_option', - 'socket_shutdown' => 'Safe\socket_shutdown', - 'socket_write' => 'Safe\socket_write', - 'socket_wsaprotocol_info_export' => 'Safe\socket_wsaprotocol_info_export', - 'socket_wsaprotocol_info_import' => 'Safe\socket_wsaprotocol_info_import', - 'socket_wsaprotocol_info_release' => 'Safe\socket_wsaprotocol_info_release', - 'sodium_crypto_pwhash' => 'Safe\sodium_crypto_pwhash', - 'sodium_crypto_pwhash_str' => 'Safe\sodium_crypto_pwhash_str', - 'solr_get_version' => 'Safe\solr_get_version', - 'sort' => 'Safe\sort', - 'soundex' => 'Safe\soundex', - 'spl_autoload_register' => 'Safe\spl_autoload_register', - 'spl_autoload_unregister' => 'Safe\spl_autoload_unregister', - 'sprintf' => 'Safe\sprintf', - 'sqlsrv_begin_transaction' => 'Safe\sqlsrv_begin_transaction', - 'sqlsrv_cancel' => 'Safe\sqlsrv_cancel', - 'sqlsrv_client_info' => 'Safe\sqlsrv_client_info', - 'sqlsrv_close' => 'Safe\sqlsrv_close', - 'sqlsrv_commit' => 'Safe\sqlsrv_commit', - 'sqlsrv_configure' => 'Safe\sqlsrv_configure', - 'sqlsrv_execute' => 'Safe\sqlsrv_execute', - 'sqlsrv_free_stmt' => 'Safe\sqlsrv_free_stmt', - 'sqlsrv_get_field' => 'Safe\sqlsrv_get_field', - 'sqlsrv_next_result' => 'Safe\sqlsrv_next_result', - 'sqlsrv_num_fields' => 'Safe\sqlsrv_num_fields', - 'sqlsrv_num_rows' => 'Safe\sqlsrv_num_rows', - 'sqlsrv_prepare' => 'Safe\sqlsrv_prepare', - 'sqlsrv_query' => 'Safe\sqlsrv_query', - 'sqlsrv_rollback' => 'Safe\sqlsrv_rollback', - 'ssdeep_fuzzy_compare' => 'Safe\ssdeep_fuzzy_compare', - 'ssdeep_fuzzy_hash' => 'Safe\ssdeep_fuzzy_hash', - 'ssdeep_fuzzy_hash_filename' => 'Safe\ssdeep_fuzzy_hash_filename', - 'ssh2_auth_agent' => 'Safe\ssh2_auth_agent', - 'ssh2_auth_hostbased_file' => 'Safe\ssh2_auth_hostbased_file', - 'ssh2_auth_password' => 'Safe\ssh2_auth_password', - 'ssh2_auth_pubkey_file' => 'Safe\ssh2_auth_pubkey_file', - 'ssh2_connect' => 'Safe\ssh2_connect', - 'ssh2_disconnect' => 'Safe\ssh2_disconnect', - 'ssh2_exec' => 'Safe\ssh2_exec', - 'ssh2_publickey_add' => 'Safe\ssh2_publickey_add', - 'ssh2_publickey_init' => 'Safe\ssh2_publickey_init', - 'ssh2_publickey_remove' => 'Safe\ssh2_publickey_remove', - 'ssh2_scp_recv' => 'Safe\ssh2_scp_recv', - 'ssh2_scp_send' => 'Safe\ssh2_scp_send', - 'ssh2_sftp' => 'Safe\ssh2_sftp', - 'ssh2_sftp_chmod' => 'Safe\ssh2_sftp_chmod', - 'ssh2_sftp_mkdir' => 'Safe\ssh2_sftp_mkdir', - 'ssh2_sftp_rename' => 'Safe\ssh2_sftp_rename', - 'ssh2_sftp_rmdir' => 'Safe\ssh2_sftp_rmdir', - 'ssh2_sftp_symlink' => 'Safe\ssh2_sftp_symlink', - 'ssh2_sftp_unlink' => 'Safe\ssh2_sftp_unlink', - 'stats_covariance' => 'Safe\stats_covariance', - 'stats_standard_deviation' => 'Safe\stats_standard_deviation', - 'stats_stat_correlation' => 'Safe\stats_stat_correlation', - 'stats_stat_innerproduct' => 'Safe\stats_stat_innerproduct', - 'stats_variance' => 'Safe\stats_variance', - 'stream_context_set_params' => 'Safe\stream_context_set_params', - 'stream_copy_to_stream' => 'Safe\stream_copy_to_stream', - 'stream_filter_append' => 'Safe\stream_filter_append', - 'stream_filter_prepend' => 'Safe\stream_filter_prepend', - 'stream_filter_register' => 'Safe\stream_filter_register', - 'stream_filter_remove' => 'Safe\stream_filter_remove', - 'stream_get_contents' => 'Safe\stream_get_contents', - 'stream_isatty' => 'Safe\stream_isatty', - 'stream_resolve_include_path' => 'Safe\stream_resolve_include_path', - 'stream_set_blocking' => 'Safe\stream_set_blocking', - 'stream_set_timeout' => 'Safe\stream_set_timeout', - 'stream_socket_accept' => 'Safe\stream_socket_accept', - 'stream_socket_client' => 'Safe\stream_socket_client', - 'stream_socket_pair' => 'Safe\stream_socket_pair', - 'stream_socket_server' => 'Safe\stream_socket_server', - 'stream_socket_shutdown' => 'Safe\stream_socket_shutdown', - 'stream_supports_lock' => 'Safe\stream_supports_lock', - 'stream_wrapper_register' => 'Safe\stream_wrapper_register', - 'stream_wrapper_restore' => 'Safe\stream_wrapper_restore', - 'stream_wrapper_unregister' => 'Safe\stream_wrapper_unregister', - 'strptime' => 'Safe\strptime', - 'strtotime' => 'Safe\strtotime', - 'substr' => 'Safe\substr', - 'swoole_async_write' => 'Safe\swoole_async_write', - 'swoole_async_writefile' => 'Safe\swoole_async_writefile', - 'swoole_event_defer' => 'Safe\swoole_event_defer', - 'swoole_event_del' => 'Safe\swoole_event_del', - 'swoole_event_write' => 'Safe\swoole_event_write', - 'symlink' => 'Safe\symlink', - 'syslog' => 'Safe\syslog', - 'system' => 'Safe\system', - 'tempnam' => 'Safe\tempnam', - 'timezone_name_from_abbr' => 'Safe\timezone_name_from_abbr', - 'time_nanosleep' => 'Safe\time_nanosleep', - 'time_sleep_until' => 'Safe\time_sleep_until', - 'tmpfile' => 'Safe\tmpfile', - 'touch' => 'Safe\touch', - 'uasort' => 'Safe\uasort', - 'uksort' => 'Safe\uksort', - 'unlink' => 'Safe\unlink', - 'uopz_extend' => 'Safe\uopz_extend', - 'uopz_implement' => 'Safe\uopz_implement', - 'usort' => 'Safe\usort', - 'virtual' => 'Safe\virtual', - 'vsprintf' => 'Safe\vsprintf', - 'xdiff_file_bdiff' => 'Safe\xdiff_file_bdiff', - 'xdiff_file_bpatch' => 'Safe\xdiff_file_bpatch', - 'xdiff_file_diff' => 'Safe\xdiff_file_diff', - 'xdiff_file_diff_binary' => 'Safe\xdiff_file_diff_binary', - 'xdiff_file_patch_binary' => 'Safe\xdiff_file_patch_binary', - 'xdiff_file_rabdiff' => 'Safe\xdiff_file_rabdiff', - 'xdiff_string_bpatch' => 'Safe\xdiff_string_bpatch', - 'xdiff_string_patch' => 'Safe\xdiff_string_patch', - 'xdiff_string_patch_binary' => 'Safe\xdiff_string_patch_binary', - 'xmlrpc_set_type' => 'Safe\xmlrpc_set_type', - 'xml_parser_create' => 'Safe\xml_parser_create', - 'xml_parser_create_ns' => 'Safe\xml_parser_create_ns', - 'xml_set_object' => 'Safe\xml_set_object', - 'yaml_parse' => 'Safe\yaml_parse', - 'yaml_parse_file' => 'Safe\yaml_parse_file', - 'yaml_parse_url' => 'Safe\yaml_parse_url', - 'yaz_ccl_parse' => 'Safe\yaz_ccl_parse', - 'yaz_close' => 'Safe\yaz_close', - 'yaz_connect' => 'Safe\yaz_connect', - 'yaz_database' => 'Safe\yaz_database', - 'yaz_element' => 'Safe\yaz_element', - 'yaz_present' => 'Safe\yaz_present', - 'yaz_search' => 'Safe\yaz_search', - 'yaz_wait' => 'Safe\yaz_wait', - 'zip_entry_close' => 'Safe\zip_entry_close', - 'zip_entry_open' => 'Safe\zip_entry_open', - 'zip_entry_read' => 'Safe\zip_entry_read', - 'zlib_decode' => 'Safe\zlib_decode', - ], - ]]); -}; diff --git a/config/set/strict-booleans.php b/config/set/strict-booleans.php new file mode 100644 index 00000000000..b5be5a0db30 --- /dev/null +++ b/config/set/strict-booleans.php @@ -0,0 +1,10 @@ +rules([DisallowedEmptyRuleFixerRector::class]); +}; diff --git a/config/set/type-declaration-docblocks.php b/config/set/type-declaration-docblocks.php new file mode 100644 index 00000000000..31c410d68c9 --- /dev/null +++ b/config/set/type-declaration-docblocks.php @@ -0,0 +1,13 @@ +rules(TypeDeclarationDocblocksLevel::RULES); +}; diff --git a/config/set/type-declaration-strict.php b/config/set/type-declaration-strict.php deleted file mode 100644 index 945e951db47..00000000000 --- a/config/set/type-declaration-strict.php +++ /dev/null @@ -1,25 +0,0 @@ -services(); - $services->set(AddClosureReturnTypeRector::class); - $services->set(ReturnTypeFromStrictTypedPropertyRector::class); - $services->set(TypedPropertyFromStrictConstructorRector::class); - $services->set(ParamTypeFromStrictTypedPropertyRector::class); - $services->set(ReturnTypeFromStrictTypedCallRector::class); - $services->set(AddVoidReturnTypeWhereNoReturnRector::class); - // $services->set(AddMethodCallBasedStrictParamTypeRector::class); - $services->set(ReturnTypeFromReturnNewRector::class); -}; diff --git a/config/set/type-declaration.php b/config/set/type-declaration.php index d9f1b92b4e3..0ada67cf0b3 100644 --- a/config/set/type-declaration.php +++ b/config/set/type-declaration.php @@ -2,26 +2,11 @@ declare(strict_types=1); -use Rector\TypeDeclaration\Rector\ClassMethod\AddArrayParamDocTypeRector; -use Rector\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector; -use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromCallersRector; -use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector; -use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector; -use Rector\TypeDeclaration\Rector\Closure\AddClosureReturnTypeRector; -use Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector; -use Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector; -use Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\Level\TypeDeclarationLevel; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ParamTypeDeclarationRector::class); - $services->set(ReturnTypeDeclarationRector::class); - $services->set(PropertyTypeDeclarationRector::class); - $services->set(AddClosureReturnTypeRector::class); - $services->set(AddArrayParamDocTypeRector::class); - $services->set(AddArrayReturnDocTypeRector::class); - // $services->set(AddParamTypeFromCallersRector::class); - $services->set(ParamTypeByParentCallTypeRector::class); - $services->set(ParamTypeByMethodCallTypeRector::class); +return static function (RectorConfig $rectorConfig): void { + // the rule order matters, as its used in withTypeCoverageLevel() method + // place the safest rules first, follow by more complex ones + $rectorConfig->rules(TypeDeclarationLevel::RULES); }; diff --git a/config/set/unwrap-compat.php b/config/set/unwrap-compat.php deleted file mode 100644 index ef27e4184f9..00000000000 --- a/config/set/unwrap-compat.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(UnwrapFutureCompatibleIfFunctionExistsRector::class); -}; diff --git a/dev-docs/packages_ci_status.md b/dev-docs/packages_ci_status.md deleted file mode 100644 index 2bacfa6ab0e..00000000000 --- a/dev-docs/packages_ci_status.md +++ /dev/null @@ -1,44 +0,0 @@ -# Packages CI Status - -## Nette - -* https://github.com/rectorphp/rector-nette -* ![](https://github.com/rectorphp/rector-nette/actions/workflows/tests.yaml/badge.svg) -* ![](https://github.com/rectorphp/rector-nette/actions/workflows/code_analysis.yaml/badge.svg) - -## Symfony - -* https://github.com/rectorphp/rector-symfony -* ![](https://github.com/rectorphp/rector-symfony/actions/workflows/tests.yaml/badge.svg) -* ![](https://github.com/rectorphp/rector-symfony/actions/workflows/code_analysis.yaml/badge.svg) - -## Laravel - -* https://github.com/rectorphp/rector-laravel -* ![](https://github.com/rectorphp/rector-laravel/actions/workflows/tests.yaml/badge.svg) -* ![](https://github.com/rectorphp/rector-laravel/actions/workflows/code_analysis.yaml/badge.svg) - -## PHPUnit - -* https://github.com/rectorphp/rector-phpunit -* ![](https://github.com/rectorphp/rector-phpunit/actions/workflows/tests.yaml/badge.svg) -* ![](https://github.com/rectorphp/rector-phpunit/actions/workflows/code_analysis.yaml/badge.svg) - -## CakePHP - -* https://github.com/rectorphp/rector-cakephp -* ![](https://github.com/rectorphp/rector-cakephp/actions/workflows/tests.yaml/badge.svg) -* ![](https://github.com/rectorphp/rector-cakephp/actions/workflows/code_analysis.yaml/badge.svg) - -## Doctrine - -* https://github.com/rectorphp/rector-doctrine -* ![](https://github.com/rectorphp/rector-doctrine/actions/workflows/tests.yaml/badge.svg) -* ![](https://github.com/rectorphp/rector-doctrine/actions/workflows/code_analysis.yaml/badge.svg) - -## Typo3 - -* https://github.com/sabbelasichon/typo3-rector -* ![](https://github.com/sabbelasichon/typo3-rector/actions/workflows/tests.yaml/badge.svg) -* ![](https://github.com/sabbelasichon/typo3-rector/actions/workflows/phpstan.yaml/badge.svg) -* ![](https://github.com/sabbelasichon/typo3-rector/actions/workflows/rector.yaml/badge.svg) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000000..aa58699ac23 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +services: + php: + build: . + environment: + - COMPOSER_ROOT_VERSION=dev-main + volumes: + - .:/etc/rector diff --git a/docs/testing/multiple_files_changed.md b/docs/testing/multiple_files_changed.md deleted file mode 100644 index 05d998023d6..00000000000 --- a/docs/testing/multiple_files_changed.md +++ /dev/null @@ -1,136 +0,0 @@ -# Multiple files changed - -Sometimes Rector does changes in multiple files. How to test it? - -## Rector -Add file with content in Rector: - -```php -$addedFileWithContent = new \Rector\FileSystemRector\ValueObject\AddedFileWithContent($filePath, $content); -$this->removedAndAddedFilesCollector->addAddedFile($addedFileWithContent); -``` - -## RectorTest -In RectorTest just use -```php -$this->doTestFileInfoWithAdditionalChanges($fileInfo); -``` -instead of - -```php -$this->doTestFileInfo($fileInfo); -``` - -## Test fixtures -Fixture contains more parts separated by `-----` like in classic tests: -``` -1) original content ------ -2) expected content (keep empty if no change should happen) ------ -3) path to another changed file (relative to file processed) ------ -4) original content of another file (keep empty if file doesn't exist yet) ------ -5) expected content of another file -``` - -Parts 3-5 can be multiplied in case there are more files created / updated. - -### Fixture examples -Example #1: Rector is not changing processed PHP file, but changes corresponding template file (adds {varType} for each variable). -``` -template->title = 'My title'; - $this->template->count = 123; - } -} - -?> ------ ------ -templates/Some/default.latte ------ -

{$title}

-{$count} ------ -{varType string $title} -{varType int $count} - -

{$title}

-{$count} -``` - -Example #2: Rector creates Template class, uses it in processed PHP file, and also in corresponding template file (adds {templateType}). -``` -template->title = 'My title'; - $this->template->count = 123; - } -} - -?> ------ -template->title = 'My title'; - $this->template->count = 123; - } -} - -?> ------ -SomeTemplate.php ------ ------ -{$title} -{$count} ------ -{templateType \Rector\Nette\Tests\Rector\Class_\LatteVarTypesBasedOnPresenterTemplateParametersRector\Fixture\SomeTemplate} - -

{$title}

-{$count} -``` diff --git a/e2e/applied-auto-import/.gitignore b/e2e/applied-auto-import/.gitignore new file mode 100644 index 00000000000..61ead86667c --- /dev/null +++ b/e2e/applied-auto-import/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/e2e/applied-auto-import/composer.json b/e2e/applied-auto-import/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/applied-auto-import/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/applied-auto-import/expected-output.diff b/e2e/applied-auto-import/expected-output.diff new file mode 100644 index 00000000000..9233afa20c6 --- /dev/null +++ b/e2e/applied-auto-import/expected-output.diff @@ -0,0 +1,28 @@ +1 file with changes +=================== + +1) src/RenameDocblock.php:2 + + ---------- begin diff ---------- +@@ Line 2 @@ + + namespace App; + +-use SomeUnusedClass; ++use DateTimeInterface; + + /** +- * @param \DateTime $someOldClass ++ * @param DateTimeInterface $someOldClass + */ +-function someFunction(\DateTime $someOldClass) ++function someFunction(DateTimeInterface $someOldClass) + { + } + ----------- end diff ----------- + +Applied rules: + * RenameClassRector + + + [OK] 1 file would have been changed (dry-run) by Rector diff --git a/e2e/applied-auto-import/rector.php b/e2e/applied-auto-import/rector.php new file mode 100644 index 00000000000..e6de67b6c3e --- /dev/null +++ b/e2e/applied-auto-import/rector.php @@ -0,0 +1,19 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'DateTime' => 'DateTimeInterface' + ]); + + $rectorConfig->importNames(); + $rectorConfig->removeUnusedImports(); +}; diff --git a/e2e/applied-auto-import/src/RenameDocblock.php b/e2e/applied-auto-import/src/RenameDocblock.php new file mode 100644 index 00000000000..25c59f18bca --- /dev/null +++ b/e2e/applied-auto-import/src/RenameDocblock.php @@ -0,0 +1,12 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + ]); +}; diff --git a/e2e/applied-no-diffs-format-json/src/RemoveAlwaysElse.php b/e2e/applied-no-diffs-format-json/src/RemoveAlwaysElse.php new file mode 100644 index 00000000000..59097555196 --- /dev/null +++ b/e2e/applied-no-diffs-format-json/src/RemoveAlwaysElse.php @@ -0,0 +1,13 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->phpVersion(PhpVersion::PHP_74); + $rectorConfig->rule(StrStartsWithRector::class); +}; diff --git a/e2e/applied-polyfill-php80/src/SomeStartWith.php b/e2e/applied-polyfill-php80/src/SomeStartWith.php new file mode 100644 index 00000000000..a62aff898cd --- /dev/null +++ b/e2e/applied-polyfill-php80/src/SomeStartWith.php @@ -0,0 +1,9 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'DateTime' => 'DateTimeInterface' + ]); + $rectorConfig->ruleWithConfiguration(DowngradeAttributeToAnnotationRector::class, [ + new DowngradeAttributeToAnnotation('Symfony\Component\Routing\Annotation\Route') + ]); + + $rectorConfig->rule(RemoveUselessVarTagRector::class); +}; diff --git a/e2e/applied-rule-change-docblock/src/AlreadyChangedDocblock.php b/e2e/applied-rule-change-docblock/src/AlreadyChangedDocblock.php new file mode 100644 index 00000000000..3c839fbfa12 --- /dev/null +++ b/e2e/applied-rule-change-docblock/src/AlreadyChangedDocblock.php @@ -0,0 +1,8 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rule(RemoveEmptyClassMethodRector::class); + $rectorConfig->rule(RemoveAlwaysTrueIfConditionRector::class); +}; diff --git a/e2e/applied-rule-removed-node-no-diffs/src/AlwaysTrue.php b/e2e/applied-rule-removed-node-no-diffs/src/AlwaysTrue.php new file mode 100644 index 00000000000..7b70b9e9ccd --- /dev/null +++ b/e2e/applied-rule-removed-node-no-diffs/src/AlwaysTrue.php @@ -0,0 +1,12 @@ +cacheClass(FileCacheStorage::class); + + $rectorConfig->paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rule(RemoveEmptyClassMethodRector::class); + $rectorConfig->rule(RemoveAlwaysTrueIfConditionRector::class); +}; diff --git a/e2e/applied-rule-removed-node-with-cache/src/AlwaysTrue.php b/e2e/applied-rule-removed-node-with-cache/src/AlwaysTrue.php new file mode 100644 index 00000000000..7b70b9e9ccd --- /dev/null +++ b/e2e/applied-rule-removed-node-with-cache/src/AlwaysTrue.php @@ -0,0 +1,12 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rule(RemoveEmptyClassMethodRector::class); + $rectorConfig->rule(RemoveAlwaysTrueIfConditionRector::class); +}; diff --git a/e2e/applied-rule-removed-node/src/AlwaysTrue.php b/e2e/applied-rule-removed-node/src/AlwaysTrue.php new file mode 100644 index 00000000000..7b70b9e9ccd --- /dev/null +++ b/e2e/applied-rule-removed-node/src/AlwaysTrue.php @@ -0,0 +1,12 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; + diff --git a/e2e/applied-rule-return-array-nodes/src/MultiRules.php b/e2e/applied-rule-return-array-nodes/src/MultiRules.php new file mode 100644 index 00000000000..fc0db50a1fb --- /dev/null +++ b/e2e/applied-rule-return-array-nodes/src/MultiRules.php @@ -0,0 +1,17 @@ +create(); + +$e2eCommand = 'php '. $rectorBin .' process --dry-run --no-ansi -a '. $autoloadFile; + +// Step 1: enabled=false, clear cache → no changes +file_put_contents(__DIR__ . '/enabled.txt', "false\n"); + +$output = []; +exec($e2eCommand . ' --clear-cache', $output, $exitCode); +$outputString = trim(implode("\n", $output)); + +if (! str_contains($outputString, '[OK] Rector is done!')) { + $symfonyStyle->error('Step 1 failed: Expected no changes with enabled=false'); + $symfonyStyle->writeln($outputString); + exit(Command::FAILURE); +} + +$symfonyStyle->success('Step 1 passed: No changes with enabled=false'); + +// Step 2: enabled=true, no --clear-cache → cache meta invalidated → rule triggers +file_put_contents(__DIR__ . '/enabled.txt', "true\n"); + +$output = []; +exec($e2eCommand, $output, $exitCode); +$outputString = trim(implode("\n", $output)); +$outputString = str_replace(__DIR__, '.', $outputString); + +$expectedOutput = trim((string) file_get_contents(__DIR__ . '/expected-output.diff')); + +// Restore enabled.txt +file_put_contents(__DIR__ . '/enabled.txt', "false\n"); + +if ($outputString === $expectedOutput) { + $symfonyStyle->success('Step 2 passed: Cache invalidated, rule triggered'); + exit(Command::SUCCESS); +} + +$symfonyStyle->error('Step 2 failed: Expected cache invalidation to trigger the rule'); + +$defaultDiffer = new DefaultDiffer(); +$colorConsoleDiffFormatter = new ColorConsoleDiffFormatter(); +$diff = $colorConsoleDiffFormatter->format($defaultDiffer->diff($outputString, $expectedOutput)); +$symfonyStyle->writeln($diff); + +exit(Command::FAILURE); diff --git a/e2e/cache-meta-extension/enabled.txt b/e2e/cache-meta-extension/enabled.txt new file mode 100644 index 00000000000..c508d5366f7 --- /dev/null +++ b/e2e/cache-meta-extension/enabled.txt @@ -0,0 +1 @@ +false diff --git a/e2e/cache-meta-extension/expected-output.diff b/e2e/cache-meta-extension/expected-output.diff new file mode 100644 index 00000000000..2ab9053fa21 --- /dev/null +++ b/e2e/cache-meta-extension/expected-output.diff @@ -0,0 +1,21 @@ +1 file with changes +=================== + +1) src/DeadConstructor.php:2 + + ---------- begin diff ---------- +@@ Line 2 @@ + + final class DeadConstructor + { +- public function __construct() +- { +- } + } + ----------- end diff ----------- + +Applied rules: + * ConditionalEmptyConstructorRector + + + [OK] 1 file would have been changed (dry-run) by Rector \ No newline at end of file diff --git a/e2e/cache-meta-extension/rector.php b/e2e/cache-meta-extension/rector.php new file mode 100644 index 00000000000..cc8df07efcf --- /dev/null +++ b/e2e/cache-meta-extension/rector.php @@ -0,0 +1,21 @@ +cacheClass(FileCacheStorage::class); + + $rectorConfig->paths([ + __DIR__ . '/src/DeadConstructor.php', + ]); + + $rectorConfig->rule(ConditionalEmptyConstructorRector::class); + $rectorConfig->cacheMetaExtension(EnabledFlagCacheMetaExtension::class); +}; diff --git a/e2e/cache-meta-extension/src/ConditionalEmptyConstructorRector.php b/e2e/cache-meta-extension/src/ConditionalEmptyConstructorRector.php new file mode 100644 index 00000000000..c34195954bc --- /dev/null +++ b/e2e/cache-meta-extension/src/ConditionalEmptyConstructorRector.php @@ -0,0 +1,86 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + $enabled = trim((string) file_get_contents(__DIR__ . '/../enabled.txt')); + + if ($enabled !== 'true') { + return null; + } + + $hasChanged = false; + + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof ClassMethod) { + continue; + } + + if (! $this->isName($stmt, '__construct')) { + continue; + } + + if ($stmt->stmts !== null && $stmt->stmts !== []) { + continue; + } + + unset($node->stmts[$key]); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } +} diff --git a/e2e/cache-meta-extension/src/DeadConstructor.php b/e2e/cache-meta-extension/src/DeadConstructor.php new file mode 100644 index 00000000000..03d800c4879 --- /dev/null +++ b/e2e/cache-meta-extension/src/DeadConstructor.php @@ -0,0 +1,8 @@ +withPaths([ + __DIR__ . '/some_file.php' + ]) + ->withRules([ + StringClassNameToClassConstantRector::class, + RemovePhpVersionIdCheckRector::class, + ]); diff --git a/e2e/command-with-option/some_file.php b/e2e/command-with-option/some_file.php new file mode 100644 index 00000000000..c62bb1e85dc --- /dev/null +++ b/e2e/command-with-option/some_file.php @@ -0,0 +1,5 @@ +paths([ + __DIR__ . '/src', + ]); + + // This rule should be applied when rector.dist.php is used as fallback + $rectorConfig->rule(RemoveUselessVarTagRector::class); +}; diff --git a/e2e/config-dist-fallback/src/SomeClass.php b/e2e/config-dist-fallback/src/SomeClass.php new file mode 100644 index 00000000000..84d5c95be59 --- /dev/null +++ b/e2e/config-dist-fallback/src/SomeClass.php @@ -0,0 +1,11 @@ +paths([ + __DIR__ . '/src', + ]); + + // This rule should NOT be applied because rector.php takes priority + $rectorConfig->rule(ClosureToArrowFunctionRector::class); +}; diff --git a/e2e/config-file-priority/rector.php b/e2e/config-file-priority/rector.php new file mode 100644 index 00000000000..c69ea6e0e55 --- /dev/null +++ b/e2e/config-file-priority/rector.php @@ -0,0 +1,15 @@ +paths([ + __DIR__ . '/src', + ]); + + // This rule should be applied when rector.php is used + $rectorConfig->rule(RemoveUselessVarTagRector::class); +}; diff --git a/e2e/config-file-priority/src/SomeClass.php b/e2e/config-file-priority/src/SomeClass.php new file mode 100644 index 00000000000..84d5c95be59 --- /dev/null +++ b/e2e/config-file-priority/src/SomeClass.php @@ -0,0 +1,11 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->skip([ + RemoveEmptyClassMethodRector::class => [ + __DIR__ . '/src/controllers', + ], + ]); + + $rectorConfig->rule(RemoveEmptyClassMethodRector::class); +}; diff --git a/e2e/different-path-over-skip-config/src/controllers/DeadConstructor.php b/e2e/different-path-over-skip-config/src/controllers/DeadConstructor.php new file mode 100644 index 00000000000..5d1dd2c8144 --- /dev/null +++ b/e2e/different-path-over-skip-config/src/controllers/DeadConstructor.php @@ -0,0 +1,10 @@ +create(); + +$matchedExpectedOutput = false; +$expectedOutput = trim((string) file_get_contents($expectedDiff)); +if ($output === $expectedOutput) { + $symfonyStyle->success('End-to-end test successfully completed'); + exit(Command::SUCCESS); +} + +// print color diff, to make easy find the differences +$defaultDiffer = new DefaultDiffer(); +$colorConsoleDiffFormatter = new ColorConsoleDiffFormatter(); +$diff = $colorConsoleDiffFormatter->format($defaultDiffer->diff($output, $expectedOutput)); +$symfonyStyle->writeln($diff); + +exit(Command::FAILURE); diff --git a/e2e/e2eTestRunnerWithCache.php b/e2e/e2eTestRunnerWithCache.php new file mode 100644 index 00000000000..033b7fd2440 --- /dev/null +++ b/e2e/e2eTestRunnerWithCache.php @@ -0,0 +1,49 @@ +#!/usr/bin/env php +create(); + +$matchedExpectedOutput = false; +$expectedOutput = trim((string) file_get_contents($expectedDiff)); +if ($output === $expectedOutput) { + $symfonyStyle->success('End-to-end test successfully completed'); + exit(Command::SUCCESS); +} + +// print color diff, to make easy find the differences +$defaultDiffer = new DefaultDiffer(); +$colorConsoleDiffFormatter = new ColorConsoleDiffFormatter(); +$diff = $colorConsoleDiffFormatter->format($defaultDiffer->diff($output, $expectedOutput)); +$symfonyStyle->writeln($diff); + +exit(Command::FAILURE); diff --git a/e2e/invalid-paths/.gitignore b/e2e/invalid-paths/.gitignore new file mode 100644 index 00000000000..61ead86667c --- /dev/null +++ b/e2e/invalid-paths/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/e2e/invalid-paths/composer.json b/e2e/invalid-paths/composer.json new file mode 100644 index 00000000000..8b5ca727be7 --- /dev/null +++ b/e2e/invalid-paths/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "php": "^8.1" + } +} diff --git a/e2e/invalid-paths/expected-output.diff b/e2e/invalid-paths/expected-output.diff new file mode 100644 index 00000000000..62f75907136 --- /dev/null +++ b/e2e/invalid-paths/expected-output.diff @@ -0,0 +1,3 @@ +[ERROR] The path + "./invalid-paths/does-not-exi + st/" does not exist. \ No newline at end of file diff --git a/e2e/invalid-paths/rector.php b/e2e/invalid-paths/rector.php new file mode 100644 index 00000000000..f3d5850acf4 --- /dev/null +++ b/e2e/invalid-paths/rector.php @@ -0,0 +1,14 @@ +disableParallel(); + + $rectorConfig->paths([ + __DIR__ . '/src/', // correct path + __DIR__ . '/does-not-exist/' + ]); +}; diff --git a/e2e/invalid-paths/src/NamespacedSomeClassFound.php b/e2e/invalid-paths/src/NamespacedSomeClassFound.php new file mode 100644 index 00000000000..0fc6a46b185 --- /dev/null +++ b/e2e/invalid-paths/src/NamespacedSomeClassFound.php @@ -0,0 +1,10 @@ +disableParallel(); + + $rectorConfig->paths([ + __DIR__ . '/src/', + ]); + + $rectorConfig->rule(RemoveUnusedPrivatePropertyRector::class); +}; diff --git a/e2e/no-parallel-reflection-resolver/src/NamespacedSomeClassFound.php b/e2e/no-parallel-reflection-resolver/src/NamespacedSomeClassFound.php new file mode 100644 index 00000000000..0fc6a46b185 --- /dev/null +++ b/e2e/no-parallel-reflection-resolver/src/NamespacedSomeClassFound.php @@ -0,0 +1,10 @@ +paths([ + __DIR__ . '/../only-option/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; diff --git a/e2e/only-option-quote-single-bsdouble/cli-options.txt b/e2e/only-option-quote-single-bsdouble/cli-options.txt new file mode 100644 index 00000000000..0a20188319d --- /dev/null +++ b/e2e/only-option-quote-single-bsdouble/cli-options.txt @@ -0,0 +1 @@ +--only='Rector\\DeadCode\\Rector\\ClassMethod\\RemoveUnusedPrivateMethodRector' diff --git a/e2e/only-option-quote-single-bsdouble/composer.json b/e2e/only-option-quote-single-bsdouble/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option-quote-single-bsdouble/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option-quote-single-bsdouble/expected-output.diff b/e2e/only-option-quote-single-bsdouble/expected-output.diff new file mode 100644 index 00000000000..6c525ad985f --- /dev/null +++ b/e2e/only-option-quote-single-bsdouble/expected-output.diff @@ -0,0 +1,22 @@ +1 file with changes +=================== + +1) ../only-option/src/MultiRules.php:10 + + ---------- begin diff ---------- +@@ Line 10 @@ + echo 'a statement'; + } + } +- +- private function notUsed() +- { +- } + } + ----------- end diff ----------- + +Applied rules: + * RemoveUnusedPrivateMethodRector + + + [OK] 1 file would have been changed (dry-run) by Rector diff --git a/e2e/only-option-quote-single-bsdouble/rector.php b/e2e/only-option-quote-single-bsdouble/rector.php new file mode 100644 index 00000000000..ad19381b8b4 --- /dev/null +++ b/e2e/only-option-quote-single-bsdouble/rector.php @@ -0,0 +1,18 @@ +paths([ + __DIR__ . '/../only-option/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; diff --git a/e2e/only-option-quote-single-equalnone/cli-options.txt b/e2e/only-option-quote-single-equalnone/cli-options.txt new file mode 100644 index 00000000000..9159b9e2101 --- /dev/null +++ b/e2e/only-option-quote-single-equalnone/cli-options.txt @@ -0,0 +1 @@ +--only 'Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector' diff --git a/e2e/only-option-quote-single-equalnone/composer.json b/e2e/only-option-quote-single-equalnone/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option-quote-single-equalnone/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option-quote-single-equalnone/expected-output.diff b/e2e/only-option-quote-single-equalnone/expected-output.diff new file mode 100644 index 00000000000..6c525ad985f --- /dev/null +++ b/e2e/only-option-quote-single-equalnone/expected-output.diff @@ -0,0 +1,22 @@ +1 file with changes +=================== + +1) ../only-option/src/MultiRules.php:10 + + ---------- begin diff ---------- +@@ Line 10 @@ + echo 'a statement'; + } + } +- +- private function notUsed() +- { +- } + } + ----------- end diff ----------- + +Applied rules: + * RemoveUnusedPrivateMethodRector + + + [OK] 1 file would have been changed (dry-run) by Rector diff --git a/e2e/only-option-quote-single-equalnone/rector.php b/e2e/only-option-quote-single-equalnone/rector.php new file mode 100644 index 00000000000..ad19381b8b4 --- /dev/null +++ b/e2e/only-option-quote-single-equalnone/rector.php @@ -0,0 +1,18 @@ +paths([ + __DIR__ . '/../only-option/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; diff --git a/e2e/only-option-quote-single/cli-options.txt b/e2e/only-option-quote-single/cli-options.txt new file mode 100644 index 00000000000..34057c5eb98 --- /dev/null +++ b/e2e/only-option-quote-single/cli-options.txt @@ -0,0 +1 @@ +--only='Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector' diff --git a/e2e/only-option-quote-single/composer.json b/e2e/only-option-quote-single/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option-quote-single/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option-quote-single/expected-output.diff b/e2e/only-option-quote-single/expected-output.diff new file mode 100644 index 00000000000..6c525ad985f --- /dev/null +++ b/e2e/only-option-quote-single/expected-output.diff @@ -0,0 +1,22 @@ +1 file with changes +=================== + +1) ../only-option/src/MultiRules.php:10 + + ---------- begin diff ---------- +@@ Line 10 @@ + echo 'a statement'; + } + } +- +- private function notUsed() +- { +- } + } + ----------- end diff ----------- + +Applied rules: + * RemoveUnusedPrivateMethodRector + + + [OK] 1 file would have been changed (dry-run) by Rector diff --git a/e2e/only-option-quote-single/rector.php b/e2e/only-option-quote-single/rector.php new file mode 100644 index 00000000000..ad19381b8b4 --- /dev/null +++ b/e2e/only-option-quote-single/rector.php @@ -0,0 +1,18 @@ +paths([ + __DIR__ . '/../only-option/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; diff --git a/e2e/only-option/cli-options.txt b/e2e/only-option/cli-options.txt new file mode 100644 index 00000000000..4109051ee1d --- /dev/null +++ b/e2e/only-option/cli-options.txt @@ -0,0 +1 @@ +--only="Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector" diff --git a/e2e/only-option/composer.json b/e2e/only-option/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option/expected-output.diff b/e2e/only-option/expected-output.diff new file mode 100644 index 00000000000..7bb469c4f6d --- /dev/null +++ b/e2e/only-option/expected-output.diff @@ -0,0 +1,22 @@ +1 file with changes +=================== + +1) src/MultiRules.php:10 + + ---------- begin diff ---------- +@@ Line 10 @@ + echo 'a statement'; + } + } +- +- private function notUsed() +- { +- } + } + ----------- end diff ----------- + +Applied rules: + * RemoveUnusedPrivateMethodRector + + + [OK] 1 file would have been changed (dry-run) by Rector diff --git a/e2e/only-option/rector.php b/e2e/only-option/rector.php new file mode 100644 index 00000000000..407016c715e --- /dev/null +++ b/e2e/only-option/rector.php @@ -0,0 +1,19 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; + diff --git a/e2e/only-option/src/MultiRules.php b/e2e/only-option/src/MultiRules.php new file mode 100644 index 00000000000..fc0db50a1fb --- /dev/null +++ b/e2e/only-option/src/MultiRules.php @@ -0,0 +1,17 @@ +paths([ + __DIR__ . '/src', + ]); + $rectorConfig->parallel(); + + $rectorConfig->sets([\Rector\Set\ValueObject\SetList::CODE_QUALITY]); +}; diff --git a/e2e/parallel with space/src/Test.php b/e2e/parallel with space/src/Test.php new file mode 100644 index 00000000000..58dd6ff4c24 --- /dev/null +++ b/e2e/parallel with space/src/Test.php @@ -0,0 +1,7 @@ +parallel(); + + $rectorConfig->paths([ + __DIR__.'/../../src/', + ]); + + $rectorConfig->rule(DowngradeReadonlyPropertyRector::class); +}; diff --git a/e2e/parallel-custom-config/expected-output.diff b/e2e/parallel-custom-config/expected-output.diff new file mode 100644 index 00000000000..b29db192fee --- /dev/null +++ b/e2e/parallel-custom-config/expected-output.diff @@ -0,0 +1,25 @@ +1 file with changes +=================== + +1) src/SomeClass.php:7 + + ---------- begin diff ---------- +@@ Line 7 @@ + class SomeClass + { + public function __construct( +- private readonly \stdClass $stdClass ++ /** ++ * @readonly ++ */ ++ private \stdClass $stdClass + ) + { + } + ----------- end diff ----------- + +Applied rules: + * DowngradeReadonlyPropertyRector + + + [OK] 1 file would have been changed (dry-run) by Rector diff --git a/e2e/parallel-custom-config/src/SomeClass.php b/e2e/parallel-custom-config/src/SomeClass.php new file mode 100644 index 00000000000..784cf43c9f0 --- /dev/null +++ b/e2e/parallel-custom-config/src/SomeClass.php @@ -0,0 +1,14 @@ +parallel(); + + $rectorConfig->paths([ + __DIR__ . '/src/', + ]); + + $rectorConfig->rule(RemoveUnusedPrivatePropertyRector::class); +}; + diff --git a/e2e/parallel-reflection-resolver/src/NamespacedSomeClassFound.php b/e2e/parallel-reflection-resolver/src/NamespacedSomeClassFound.php new file mode 100644 index 00000000000..0fc6a46b185 --- /dev/null +++ b/e2e/parallel-reflection-resolver/src/NamespacedSomeClassFound.php @@ -0,0 +1,10 @@ +paths([ + __DIR__ . '/src/TestClass.php', + __DIR__ . '/src/ExtendingTestClass.php', + ]); + + $rectorConfig->rule(AddParamBasedOnParentClassMethodRector::class); +}; \ No newline at end of file diff --git a/e2e/print-new-node/src/ExtendingTestClass.php b/e2e/print-new-node/src/ExtendingTestClass.php new file mode 100644 index 00000000000..cef467fb61f --- /dev/null +++ b/e2e/print-new-node/src/ExtendingTestClass.php @@ -0,0 +1,7 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rule(RemoveEmptyClassMethodRector::class); + $rectorConfig->rule(RemoveAlwaysTrueIfConditionRector::class); +}; diff --git a/e2e/rules-summary-option/src/AlwaysTrue.php b/e2e/rules-summary-option/src/AlwaysTrue.php new file mode 100644 index 00000000000..c7fc2ce126c --- /dev/null +++ b/e2e/rules-summary-option/src/AlwaysTrue.php @@ -0,0 +1,15 @@ +cacheClass(FileCacheStorage::class); + $rectorConfig->parallel(0); + + $rectorConfig->paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->sets([LevelSetList::UP_TO_PHP_82]); +}; diff --git a/e2e/timeout-file-not-cached/src/SomeFixturePrinter.php b/e2e/timeout-file-not-cached/src/SomeFixturePrinter.php new file mode 100644 index 00000000000..69ed3a47227 --- /dev/null +++ b/e2e/timeout-file-not-cached/src/SomeFixturePrinter.php @@ -0,0 +1,9 @@ +services(); - $services->set(GeneralPhpdocAnnotationRemoveFixer::class) - ->call('configure', [[ - 'annotations' => [ - 'throws', - 'author', - 'package', - 'group', - 'required', - 'phpstan-ignore-line', - 'phpstan-ignore-next-line', - ], - ]]); - - $services->set(NoSuperfluousPhpdocTagsFixer::class) - ->call('configure', [[ - 'allow_mixed' => true, - ]]); - - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::PATHS, [ +return ECSConfig::configure() + ->withPreparedSets(symplify: true, common: true, psr12: true) + ->withPaths([ __DIR__ . '/bin', __DIR__ . '/src', - __DIR__ . '/packages', - __DIR__ . '/packages-tests', __DIR__ . '/rules', __DIR__ . '/rules-tests', __DIR__ . '/tests', __DIR__ . '/utils', __DIR__ . '/config', - __DIR__ . '/ecs.php', - __DIR__ . '/rector.php', - __DIR__ . '/scoper.php', - ]); - - $parameters->set(Option::SKIP, [ + __DIR__ . '/scripts', + __DIR__ . '/build/build-preload.php', + ]) + ->withSkip([ '*/Source/*', '*/Fixture/*', '*/Expected/*', - // fixed in master - ParamReturnAndVarTagMalformsFixer::class, + // avoid re-running on build + __DIR__ . '/preload.php', + __DIR__ . '/preload-split-package.php', - GeneralPhpdocAnnotationRemoveFixer::class => [ - __DIR__ . '/src/Rector/AbstractRector.php', - '*TypeInferer*', - '*TypeResolver*', - '*NameResolver*', - '*Mapper*', - // allowed @required - __DIR__ . '/packages/StaticTypeMapper/Naming/NameScopeFactory.php', - __DIR__ . '/packages/NodeTypeResolver/NodeTypeResolver.php', - __DIR__ . '/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/PlainValueParser.php', + PhpdocTypesFixer::class => [ + // double to Double false positive + __DIR__ . '/rules/Php74/Rector/Double/RealToFloatTypeCastRector.php', + // Scalar to scalar false positive + __DIR__ . '/src/NodeTypeResolver/NodeTypeResolver/ScalarTypeResolver.php', ], - UnaryOperatorSpacesFixer::class, - - // buggy - @todo fix on Symplify master - RemoveCommentedCodeFixer::class, - DocBlockLineLengthFixer::class, - - // breaks on-purpose annotated variables - ReturnAssignmentFixer::class, - - PhpdocTypesFixer::class => [__DIR__ . '/rules/Php74/Rector/Double/RealToFloatTypeCastRector.php'], - - // buggy on "Float" class - PhpUnitStrictFixer::class => [ - __DIR__ . '/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/PhpDocInfoTest.php', - __DIR__ . '/tests/PhpParser/Node/NodeFactoryTest.php', - __DIR__ . '/packages-tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/StaticDoctrineAnnotationParserTest.php', - '*TypeResolverTest.php', + GeneralPhpdocAnnotationRemoveFixer::class => [ + // bug remove @author annotation + __DIR__ . '/src/Util/ArrayParametersMerger.php', ], - ]); - - // import SetList here on purpose to avoid overridden by existing Skip Option in current config - $containerConfigurator->import(SetList::PSR_12); - $containerConfigurator->import(SetList::SYMPLIFY); - $containerConfigurator->import(SetList::COMMON); - $containerConfigurator->import(SetList::CLEAN_CODE); - $parameters->set(Option::LINE_ENDING, "\n"); -}; + LowercaseKeywordsFixer::class => [__DIR__ . '/src/ValueObject/Visibility.php'], + ]) + ->withRootFiles(); diff --git a/full_build.sh b/full_build.sh index ca1abb1c678..21697762173 100644 --- a/full_build.sh +++ b/full_build.sh @@ -1,5 +1,9 @@ #!/usr/bin/env bash +# usage: +# +# export PHP74_BIN_PATH=/opt/local/bin/php74 && sh ./full_build.sh + # see https://stackoverflow.com/questions/66644233/how-to-propagate-colors-from-bash-script-to-github-action?noredirect=1#comment117811853_66644233 export TERM=xterm-color @@ -9,13 +13,45 @@ set -e # script fails if trying to access to an undefined variable set -u +# clean up +rm -rf rector-prefixed-downgraded +rm -rf composer.lock +rm -rf vendor +composer clear-cache +composer install --ansi +# ensure remove cache directory +php -r 'shell_exec("rm -rf " . sys_get_temp_dir() . "/rector_cached_files");'; composer install --no-dev --ansi +# early downgrade individual functions +bin/rector process src/functions/node_helper.php -c build/config/config-downgrade.php --ansi + rsync --exclude rector-build -av * rector-build --quiet -rm -rf rector-build/packages-tests rector-build/rules-tests rector-build/tests -sh build/downgrade-rector.sh rector-build +rm -rf rector-build/rules-tests rector-build/templates rector-build/tests rector-build/scripts/validate-phpstan-version.php rector-build/vendor/tracy/tracy/examples rector-build/vendor/symfony/console/Tester rector-build/vendor/symfony/console/Event rector-build/vendor/symfony/console/EventListener rector-build/vendor/tracy/tracy/examples rector-build/vendor/tracy/tracy/src/Bridges rector-build/vendor/tracy/tracy/src/Tracy/Bar rector-build/vendor/tracy/tracy/src/Tracy/Session rector-build/vendor/symfony/service-contracts/Test + +php -d memory_limit=-1 bin/rector process rector-build/bin rector-build/config rector-build/src rector-build/rules rector-build/vendor --config build/config/config-downgrade.php --ansi --no-diffs + sh build/build-rector-scoped.sh rector-build rector-prefixed-downgraded + +# verify syntax valid in php 7.4 +composer global require php-parallel-lint/php-parallel-lint + +if test -z ${PHP74_BIN_PATH+y}; then + ~/.config/composer/vendor/bin/parallel-lint rector-prefixed-downgraded --exclude rector-prefixed-downgraded/stubs --exclude rector-prefixed-downgraded/vendor/tracy/tracy/examples --exclude rector-prefixed-downgraded/vendor/rector/rector-generator/templates --exclude rector-prefixed-downgraded/vendor/symfony/console/Debug/CliRequest.php +else + echo "verify syntax valid in php 7.4 with specify PHP74_BIN_PATH env"; + $PHP74_BIN_PATH ~/.composer/vendor/bin/parallel-lint rector-prefixed-downgraded --exclude rector-prefixed-downgraded/stubs --exclude rector-prefixed-downgraded/vendor/tracy/tracy/examples --exclude rector-prefixed-downgraded/vendor/rector/rector-generator/templates --exclude rector-prefixed-downgraded/vendor/symfony/console/Debug/CliRequest.php +fi + +# rollback, done testing succeed +rm -rf rector-prefixed-downgraded/ +git checkout src + +composer up + +# the bin/rector cannot work, as depends on external phpstan/phpstan dependency +# this package cannot be installed here, as it would override scoped autoload diff --git a/monorepo-builder.php b/monorepo-builder.php deleted file mode 100644 index f31de864fe4..00000000000 --- a/monorepo-builder.php +++ /dev/null @@ -1,15 +0,0 @@ -services(); - - // @see https://github.com/symplify/monorepo-builder#6-release-flow - $services->set(TagVersionReleaseWorker::class); - $services->set(PushTagReleaseWorker::class); -}; diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/PhpDocInfoTest.php b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/PhpDocInfoTest.php deleted file mode 100644 index 79ee59e57cc..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/PhpDocInfoTest.php +++ /dev/null @@ -1,75 +0,0 @@ -boot(); - - $this->phpDocInfoPrinter = $this->getService(PhpDocInfoPrinter::class); - $this->smartFileSystem = $this->getService(SmartFileSystem::class); - $this->docBlockTagReplacer = $this->getService(DocBlockTagReplacer::class); - - $this->phpDocInfo = $this->createPhpDocInfoFromFile(__DIR__ . '/Source/doc.txt'); - } - - public function testGetTagsByName(): void - { - $paramTags = $this->phpDocInfo->getTagsByName('param'); - $this->assertCount(2, $paramTags); - } - - public function testGetVarType(): void - { - $expectedObjectType = new ObjectType('SomeType'); - $this->assertEquals($expectedObjectType, $this->phpDocInfo->getVarType()); - } - - public function testGetReturnType(): void - { - $expectedObjectType = new ObjectType('SomeType'); - $this->assertEquals($expectedObjectType, $this->phpDocInfo->getReturnType()); - } - - public function testReplaceTagByAnother(): void - { - $phpDocInfo = $this->createPhpDocInfoFromFile(__DIR__ . '/Source/test-tag.txt'); - $this->docBlockTagReplacer->replaceTagByAnother($phpDocInfo, 'test', 'flow'); - - $printedPhpDocInfo = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); - $this->assertStringEqualsFile(__DIR__ . '/Source/expected-replaced-tag.txt', $printedPhpDocInfo); - } - - private function createPhpDocInfoFromFile(string $path): ?PhpDocInfo - { - $phpDocInfoFactory = $this->getService(PhpDocInfoFactory::class); - $phpDocContent = $this->smartFileSystem->readFile($path); - - $nop = new Nop(); - $nop->setDocComment(new Doc($phpDocContent)); - - return $phpDocInfoFactory->createFromNode($nop); - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/AbstractPhpDocInfoPrinterTest.php b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/AbstractPhpDocInfoPrinterTest.php deleted file mode 100644 index 72f8f456ccd..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/AbstractPhpDocInfoPrinterTest.php +++ /dev/null @@ -1,44 +0,0 @@ -boot(); - - $this->phpDocInfoFactory = $this->getService(PhpDocInfoFactory::class); - $this->phpDocInfoPrinter = $this->getService(PhpDocInfoPrinter::class); - $this->smartFileSystem = $this->getService(SmartFileSystem::class); - } - - protected function createPhpDocInfoFromDocCommentAndNode(string $docComment, Node $node): PhpDocInfo - { - $node->setDocComment(new Doc($docComment)); - return $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - } - - protected function yieldFilesFromDirectory(string $directory, string $suffix = '*.php'): Iterator - { - return StaticFixtureFinder::yieldDirectory($directory, $suffix); - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/DoctrineTest.php b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/DoctrineTest.php deleted file mode 100644 index 866a01eae95..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/DoctrineTest.php +++ /dev/null @@ -1,38 +0,0 @@ -smartFileSystem->readFile($docFilePath); - $phpDocInfo = $this->createPhpDocInfoFromDocCommentAndNode($docComment, $node); - - $fileInfo = new SmartFileInfo($docFilePath); - $relativeFilePathFromCwd = $fileInfo->getRelativeFilePathFromCwd(); - - $printedPhpDocInfo = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); - $this->assertSame($docComment, $printedPhpDocInfo, $relativeFilePathFromCwd); - } - - public function provideDataClass(): Iterator - { - yield [__DIR__ . '/Source/Doctrine/index_in_table.txt', new Class_(IndexInTable::class)]; - yield [__DIR__ . '/Source/Doctrine/case_sensitive.txt', new Class_(CaseSensitive::class)]; - yield [__DIR__ . '/Source/Doctrine/short.txt', new Class_(Short::class)]; - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/minimal.txt b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/minimal.txt deleted file mode 100644 index 9b0829d0fcd..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/minimal.txt +++ /dev/null @@ -1,3 +0,0 @@ -/** - * @foor a, - */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/MultilineTest.php b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/MultilineTest.php deleted file mode 100644 index 8502e9c28d1..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/MultilineTest.php +++ /dev/null @@ -1,104 +0,0 @@ -smartFileSystem->readFile($docFilePath); - $phpDocInfo = $this->createPhpDocInfoFromDocCommentAndNode($docComment, $node); - - $fileInfo = new SmartFileInfo($docFilePath); - $relativeFilePathFromCwd = $fileInfo->getRelativeFilePathFromCwd(); - - $printedPhpDocInfo = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); - $this->assertSame($docComment, $printedPhpDocInfo, $relativeFilePathFromCwd); - } - - public function provideData(): Iterator - { - yield [__DIR__ . '/Source/Multiline/multiline1.txt', new Nop()]; - yield [__DIR__ . '/Source/Multiline/multiline2.txt', new Nop()]; - yield [__DIR__ . '/Source/Multiline/multiline3.txt', new Nop()]; - yield [__DIR__ . '/Source/Multiline/multiline4.txt', new Nop()]; - yield [__DIR__ . '/Source/Multiline/multiline5.txt', new Nop()]; - } - - /** - * @return Iterator - */ - public function provideDataClass(): Iterator - { - yield [__DIR__ . '/Source/Class_/some_entity_class.txt', new Class_(SomeEntityClass::class)]; - yield [__DIR__ . '/Source/Multiline/table.txt', new Class_(TableClass::class)]; - } - - public function provideDataForProperty(): Iterator - { - $property = $this->createPublicPropertyUnderClass('manyTo', ManyToPropertyClass::class); - yield [__DIR__ . '/Source/Multiline/many_to.txt', $property]; - - $property = $this->createPublicPropertyUnderClass('anotherProperty', AnotherPropertyClass::class); - yield [__DIR__ . '/Source/Multiline/assert_serialize.txt', $property]; - - $property = $this->createPublicPropertyUnderClass('anotherSerializeSingleLine', SinglePropertyClass::class); - yield [__DIR__ . '/Source/Multiline/assert_serialize_single_line.txt', $property]; - - $property = $this->createPublicPropertyUnderClass('someProperty', DoctrinePropertyClass::class); - yield [__DIR__ . '/Source/Multiline/multiline6.txt', $property]; - - $property = $this->createMethodUnderClass('someMethod', RoutePropertyClass::class); - yield [__DIR__ . '/Source/Multiline/route_property.txt', $property]; - } - - private function createPublicPropertyUnderClass(string $name, string $class): Property - { - $builderFactory = new BuilderFactory(); - - $propertyBuilder = $builderFactory->property($name); - $propertyBuilder->makePublic(); - - $property = $propertyBuilder->getNode(); - $property->setAttribute(AttributeKey::CLASS_NAME, $class); - - return $property; - } - - private function createMethodUnderClass(string $name, string $class): ClassMethod - { - $builderFactory = new BuilderFactory(); - - $methodBuilder = $builderFactory->method($name); - $methodBuilder->makePublic(); - - $classMethod = $methodBuilder->getNode(); - $classMethod->setAttribute(AttributeKey::CLASS_NAME, $class); - - return $classMethod; - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php deleted file mode 100644 index 9fbfb560a8b..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php +++ /dev/null @@ -1,65 +0,0 @@ -doComparePrintedFileEquals($docFileInfo, $docFileInfo); - } - - public function testRemoveSpace(): void - { - $this->doComparePrintedFileEquals( - new SmartFileInfo(__DIR__ . '/FixtureChanged/with_space.txt'), - new SmartFileInfo(__DIR__ . '/FixtureChangedExpected/with_space_expected.txt') - ); - } - - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureBasic', '*.txt'); - } - - public function provideDataCallable(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureCallable', '*.txt'); - } - - /** - * @dataProvider provideDataEmpty() - */ - public function testEmpty(SmartFileInfo $fileInfo): void - { - $phpDocInfo = $this->createPhpDocInfoFromDocCommentAndNode($fileInfo->getContents(), new Nop()); - $this->assertEmpty($this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo)); - } - - public function provideDataEmpty(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureEmpty', '*.txt'); - } - - private function doComparePrintedFileEquals(SmartFileInfo $inputFileInfo, SmartFileInfo $expectedFileInfo): void - { - $phpDocInfo = $this->createPhpDocInfoFromDocCommentAndNode($inputFileInfo->getContents(), new Nop()); - $printedDocComment = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); - - $this->assertSame( - $expectedFileInfo->getContents(), - $printedDocComment, - $inputFileInfo->getRelativeFilePathFromCwd() - ); - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_single_line.txt b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_single_line.txt deleted file mode 100644 index 3912c81150b..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_single_line.txt +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @Assert\Type( - * "bool" - * ) - */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/many_to.txt b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/many_to.txt deleted file mode 100644 index cf246d44df4..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/many_to.txt +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @var Collection - * @ORM\OneToMany(targetEntity="Spaceflow\Api\Reservation\Entity\Reservation", mappedBy="amenity", cascade={"persist", "merge"}) - * @Serializer\Type("int") - * @Assert\Range( - * min = 0, - * max = 2629744 - * ) - * @Assert\Url( - * protocols = {"https"} - * ) - */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline1.txt b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline1.txt deleted file mode 100644 index a43cae180bc..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline1.txt +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This is just text - * why is this missing asterisk? - */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline4.txt b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline4.txt deleted file mode 100644 index aeacd733009..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline4.txt +++ /dev/null @@ -1,4 +0,0 @@ -/** - * @return string|null Also if we do the check in the self::execute method, - * allow for null to make PHPStan pass - */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline5.txt b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline5.txt deleted file mode 100644 index b8472cd2093..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline5.txt +++ /dev/null @@ -1,4 +0,0 @@ -/** - * @deprecated Also if we do the check in the self::execute method, - * allow for null to make PHPStan pass - */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/route_property.txt b/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/route_property.txt deleted file mode 100644 index b75c793c5c9..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/route_property.txt +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @Route( - * "/{arg1}/{arg2}", - * defaults={"arg1"=null, "arg2"=""}, - * requirements={"arg1"="\d+", "arg2"=".*"} - * ) - */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/Fixture/fixture.php.inc b/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/Fixture/fixture.php.inc deleted file mode 100644 index b5a3ecae86e..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/Fixture/fixture.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -

- - ------ - -

- - diff --git a/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/Source/InlineHtmlRector.php b/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/Source/InlineHtmlRector.php deleted file mode 100644 index 28e46ac58c6..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/Source/InlineHtmlRector.php +++ /dev/null @@ -1,47 +0,0 @@ -value, '

')) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - $currentVarTagValueNode = $phpDocInfo->getVarTagValueNode(); - if ($currentVarTagValueNode !== null) { - return null; - } - - $varTagValueNode = new VarTagValueNode(new IdentifierTypeNode('string'), '$hello1', ''); - $phpDocInfo->addTagValueNode($varTagValueNode); - - return $node; - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/TestInlineHtmlTest.php b/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/TestInlineHtmlTest.php deleted file mode 100644 index 3b272ed934a..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/TestInlineHtmlTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/config/configured_rule.php b/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/config/configured_rule.php deleted file mode 100644 index 128333e1726..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocInlineHtml/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(InlineHtmlRector::class); -}; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParserTest.php b/packages-tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParserTest.php deleted file mode 100644 index 18ef02cdb1a..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParserTest.php +++ /dev/null @@ -1,50 +0,0 @@ -boot(); - - $this->arrayParser = $this->getService(ArrayParser::class); - $this->tokenIteratorFactory = $this->getService(TokenIteratorFactory::class); - } - - /** - * @dataProvider provideData() - * @param array|string[] $expectedArray - */ - public function test(string $docContent, array $expectedArray): void - { - $betterTokenIterator = $this->tokenIteratorFactory->create($docContent); - - $array = $this->arrayParser->parseCurlyArray($betterTokenIterator); - $this->assertSame($expectedArray, $array); - } - - public function provideData(): Iterator - { - yield ['{key: "value"}', [ - 'key' => '"value"', - ]]; - - yield ['{"key": "value"}', [ - '"key"' => '"value"', - ]]; - - yield ['{"value", "value2"}', ['"value"', '"value2"']]; - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/StaticDoctrineAnnotationParserTest.php b/packages-tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/StaticDoctrineAnnotationParserTest.php deleted file mode 100644 index 2991e47155d..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/StaticDoctrineAnnotationParserTest.php +++ /dev/null @@ -1,52 +0,0 @@ -boot(); - - $this->tokenIteratorFactory = $this->getService(TokenIteratorFactory::class); - $this->staticDoctrineAnnotationParser = $this->getService(StaticDoctrineAnnotationParser::class); - } - - /** - * @dataProvider provideData() - * @param CurlyListNode|array $expectedValue - */ - public function test(string $docContent, CurlyListNode | array $expectedValue): void - { - $betterTokenIterator = $this->tokenIteratorFactory->create($docContent); - - $value = $this->staticDoctrineAnnotationParser->resolveAnnotationValue($betterTokenIterator); - - // "equals" on purpose to compare 2 object with same content - $this->assertEquals($expectedValue, $value); - } - - public function provideData(): Iterator - { - $curlyListNode = new CurlyListNode(['"chalet"', '"apartment"']); - yield ['{"chalet", "apartment"}', $curlyListNode]; - - yield [ - 'key={"chalet", "apartment"}', [ - 'key' => $curlyListNode, - ], - ]; - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TagValueNodeReprintTest.php b/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TagValueNodeReprintTest.php deleted file mode 100644 index 636ad8331fc..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TagValueNodeReprintTest.php +++ /dev/null @@ -1,159 +0,0 @@ -boot(); - - $this->fileInfoParser = $this->getService(FileInfoParser::class); - - $this->betterNodeFinder = $this->getService(BetterNodeFinder::class); - $this->phpDocInfoPrinter = $this->getService(PhpDocInfoPrinter::class); - $this->phpDocInfoFactory = $this->getService(PhpDocInfoFactory::class); - } - - /** - * @dataProvider provideData() - * @dataProvider provideDataNested() - */ - public function test(SmartFileInfo $fixtureFileInfo): void - { - $trioFixtureSplitter = new TrioFixtureSplitter(); - $trioContent = $trioFixtureSplitter->splitFileInfo($fixtureFileInfo); - - $nodeClass = trim($trioContent->getSecondValue()); - $tagValueNodeClasses = $this->splitListByEOL($trioContent->getExpectedResult()); - - $fixtureFileInfo = $this->createFixtureFileInfo($trioContent, $fixtureFileInfo); - foreach ($tagValueNodeClasses as $tagValueNodeClass) { - $this->doTestPrintedPhpDocInfo($fixtureFileInfo, $tagValueNodeClass, $nodeClass); - } - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture'); - } - - /** - * @return Iterator - */ - public function provideDataNested(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/FixtureNested'); - } - - /** - * @param class-string $annotationClass - * @param class-string $nodeClass - */ - private function doTestPrintedPhpDocInfo( - SmartFileInfo $smartFileInfo, - string $annotationClass, - string $nodeClass - ): void { - $nodeWithPhpDocInfo = $this->parseFileAndGetFirstNodeOfType($smartFileInfo, $nodeClass); - - $docComment = $nodeWithPhpDocInfo->getDocComment(); - if (! $docComment instanceof Doc) { - throw new ShouldNotHappenException(sprintf( - 'Doc comments for "%s" file cannot not be empty', - $smartFileInfo - )); - } - - $originalDocCommentText = $docComment->getText(); - $printedPhpDocInfo = $this->printNodePhpDocInfoToString($nodeWithPhpDocInfo); - - $this->assertSame($originalDocCommentText, $printedPhpDocInfo); - $this->doTestContainsTagValueNodeType($nodeWithPhpDocInfo, $annotationClass, $smartFileInfo); - } - - /** - * @return string[] - */ - private function splitListByEOL(string $content): array - { - $trimmedContent = trim($content); - return explode(PHP_EOL, $trimmedContent); - } - - private function createFixtureFileInfo(TrioContent $trioContent, SmartFileInfo $fixturefileInfo): SmartFileInfo - { - $temporaryFileName = sys_get_temp_dir() . '/rector/tests/' . $fixturefileInfo->getRelativePathname(); - $firstValue = $trioContent->getFirstValue(); - - $smartFileSystem = new SmartFileSystem(); - $smartFileSystem->dumpFile($temporaryFileName, $firstValue); - - return new SmartFileInfo($temporaryFileName); - } - - /** - * @template T as Node - * @param class-string $nodeType - * @return T - */ - private function parseFileAndGetFirstNodeOfType(SmartFileInfo $smartFileInfo, string $nodeType): Node - { - $nodes = $this->fileInfoParser->parseFileInfoToNodesAndDecorate($smartFileInfo); - - $node = $this->betterNodeFinder->findFirstInstanceOf($nodes, $nodeType); - if (! $node instanceof Node) { - throw new ShouldNotHappenException($smartFileInfo->getRealPath()); - } - - return $node; - } - - private function printNodePhpDocInfoToString(Node $node): string - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - return $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); - } - - /** - * @param class-string $annotationClass - */ - private function doTestContainsTagValueNodeType( - Node $node, - string $annotationClass, - SmartFileInfo $smartFileInfo - ): void { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $hasByAnnotationClass = $phpDocInfo->hasByAnnotationClass($annotationClass); - - $this->assertTrue($hasByAnnotationClass, $smartFileInfo->getRelativeFilePathFromCwd()); - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TestModifyReprintTest.php b/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TestModifyReprintTest.php deleted file mode 100644 index 38d0b669586..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TestModifyReprintTest.php +++ /dev/null @@ -1,91 +0,0 @@ -boot(); - - $this->fileInfoParser = $this->getService(FileInfoParser::class); - - $this->betterNodeFinder = $this->getService(BetterNodeFinder::class); - $this->phpDocInfoPrinter = $this->getService(PhpDocInfoPrinter::class); - $this->phpDocInfoFactory = $this->getService(PhpDocInfoFactory::class); - $this->currentFileProvider = $this->getService(CurrentFileProvider::class); - } - - public function test(): void - { - $fixtureFileInfo = new SmartFileInfo(__DIR__ . '/FixtureModify/route_with_extra_methods.php.inc'); - - $inputFileInfoAndExpected = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpected($fixtureFileInfo); - $inputFileInfo = $inputFileInfoAndExpected->getInputFileInfo(); - - $this->currentFileProvider->setFile(new File($inputFileInfo, $inputFileInfo->getContents())); - $phpDocInfo = $this->parseFileAndGetFirstNodeOfType($inputFileInfo, ClassMethod::class); - - /** @var DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode */ - $doctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass( - 'Symfony\Component\Routing\Annotation\Route' - ); - // this will extended tokens of first node - $doctrineAnnotationTagValueNode->changeValue('methods', new CurlyListNode(['"GET"', '"HEAD"'])); - - $expectedDocContent = trim($inputFileInfoAndExpected->getExpected()); - - $printedPhpDocInfo = $this->printPhpDocInfoToString($phpDocInfo); - $this->assertSame($expectedDocContent, $printedPhpDocInfo); - } - - /** - * @param class-string $nodeType - */ - private function parseFileAndGetFirstNodeOfType(SmartFileInfo $smartFileInfo, string $nodeType): PhpDocInfo - { - $nodes = $this->fileInfoParser->parseFileInfoToNodesAndDecorate($smartFileInfo); - - $node = $this->betterNodeFinder->findFirstInstanceOf($nodes, $nodeType); - if (! $node instanceof Node) { - throw new ShouldNotHappenException($smartFileInfo->getRealPath()); - } - - return $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - } - - private function printPhpDocInfoToString(PhpDocInfo $phpDocInfo): string - { - // invoke re-print - $phpDocInfo->markAsChanged(); - return $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); - } -} diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TypeNodeAnalyzerTest.php b/packages-tests/BetterPhpDocParser/PhpDocParser/TypeNodeAnalyzerTest.php deleted file mode 100644 index f668891dbbc..00000000000 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TypeNodeAnalyzerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -boot(); - $this->typeNodeAnalyzer = $this->getService(TypeNodeAnalyzer::class); - } - - /** - * @dataProvider provideDataForIntersectionAndNotNullable() - */ - public function testIsIntersectionAndNotNullable(TypeNode $typeNode, bool $expectedIs): void - { - $isIntersection = $this->typeNodeAnalyzer->isIntersectionAndNotNullable($typeNode); - $this->assertSame($expectedIs, $isIntersection); - } - - /** - * @return Iterator - */ - public function provideDataForIntersectionAndNotNullable(): Iterator - { - yield [new IntersectionTypeNode([new IdentifierTypeNode(self::INT)]), true]; - yield [new IntersectionTypeNode([new NullableTypeNode(new IdentifierTypeNode(self::INT))]), false]; - } -} diff --git a/packages-tests/Caching/Detector/ChangedFilesDetectorTest.php b/packages-tests/Caching/Detector/ChangedFilesDetectorTest.php deleted file mode 100644 index 4b5feb0bd8a..00000000000 --- a/packages-tests/Caching/Detector/ChangedFilesDetectorTest.php +++ /dev/null @@ -1,75 +0,0 @@ -changedFilesDetector = $this->getService(ChangedFilesDetector::class); - } - - protected function tearDown(): void - { - $this->changedFilesDetector->clear(); - } - - public function testHasFileChanged(): void - { - $smartFileInfo = new SmartFileInfo(__DIR__ . '/Source/file.php'); - - $this->assertTrue($this->changedFilesDetector->hasFileChanged($smartFileInfo)); - $this->changedFilesDetector->addFileWithDependencies($smartFileInfo, []); - - $this->assertFalse($this->changedFilesDetector->hasFileChanged($smartFileInfo)); - $this->changedFilesDetector->invalidateFile($smartFileInfo); - - $this->assertTrue($this->changedFilesDetector->hasFileChanged($smartFileInfo)); - } - - /** - * @param mixed[]|string[] $dependantFiles - * @dataProvider provideData() - */ - public function testGetDependentFileInfos(string $filePathName, array $dependantFiles): void - { - $smartFileInfo = new SmartFileInfo($filePathName); - - $this->changedFilesDetector->addFileWithDependencies($smartFileInfo, $dependantFiles); - $dependantSmartFileInfos = $this->changedFilesDetector->getDependentFileInfos($smartFileInfo); - - $dependantFilesCount = count($dependantFiles); - - $this->assertCount($dependantFilesCount, $dependantSmartFileInfos); - - foreach ($dependantFiles as $key => $dependantFile) { - $this->assertSame($dependantFile, $dependantSmartFileInfos[$key]->getPathname()); - } - } - - public function provideData(): Iterator - { - yield [__DIR__ . '/Source/file.php', []]; - yield [__DIR__ . '/Source/file.php', [__DIR__ . '/Source/file.php']]; - yield [ - __DIR__ . '/Source/file.php', - [__DIR__ . '/Source/file.php', __DIR__ . '/Source/file2.php', __DIR__ . '/Source/file3.php'], - ]; - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config.php'; - } -} diff --git a/packages-tests/Caching/Detector/config.php b/packages-tests/Caching/Detector/config.php deleted file mode 100644 index ab92831c447..00000000000 --- a/packages-tests/Caching/Detector/config.php +++ /dev/null @@ -1,13 +0,0 @@ -parameters(); - $parameters->set(Option::CACHE_DIR, sys_get_temp_dir() . '/_rector_cached_files_test'); - $parameters->set(Option::CACHE_CLASS, MemoryCacheStorage::class); -}; diff --git a/packages-tests/ChangesReporting/Annotation/AnnotationExtractorTest.php b/packages-tests/ChangesReporting/Annotation/AnnotationExtractorTest.php deleted file mode 100644 index 6a057a5433d..00000000000 --- a/packages-tests/ChangesReporting/Annotation/AnnotationExtractorTest.php +++ /dev/null @@ -1,41 +0,0 @@ -annotationExtractor = new AnnotationExtractor(); - } - - /** - * @dataProvider extractAnnotationProvider() - */ - public function testExtractAnnotationFromClass(string $className, string $annotation, ?string $expected): void - { - $value = $this->annotationExtractor->extractAnnotationFromClass($className, $annotation); - $this->assertSame($expected, $value); - } - - public function extractAnnotationProvider(): Iterator - { - yield 'Class with changelog annotation' => [ - RectorWithChangelog::class, - '@changelog', - 'https://github.com/rectorphp/rector/blob/master/docs/rector_rules_overview.md', - ]; - - yield 'Class without changelog annotation' => [RectorWithOutChangelog::class, '@changelog', null]; - } -} diff --git a/packages-tests/ChangesReporting/Annotation/AppliedRectorsChangelogResolver/RectorsChangelogResolverTest.php b/packages-tests/ChangesReporting/Annotation/AppliedRectorsChangelogResolver/RectorsChangelogResolverTest.php deleted file mode 100644 index aad28249026..00000000000 --- a/packages-tests/ChangesReporting/Annotation/AppliedRectorsChangelogResolver/RectorsChangelogResolverTest.php +++ /dev/null @@ -1,49 +0,0 @@ -boot(); - $this->rectorsChangelogResolver = $this->getService(RectorsChangelogResolver::class); - - $this->fileDiff = $this->createFileDiff(); - } - - public function test(): void - { - $rectorsChangelogs = $this->rectorsChangelogResolver->resolve($this->fileDiff->getRectorClasses()); - - $expectedRectorsChangelogs = [ - RectorWithChangelog::class => 'https://github.com/rectorphp/rector/blob/master/docs/rector_rules_overview.md', - ]; - $this->assertSame($expectedRectorsChangelogs, $rectorsChangelogs); - } - - private function createFileDiff(): FileDiff - { - // This is by intention to test the array_unique functionality - $rectorWithLineChanges = []; - $rectorWithLineChanges[] = new RectorWithLineChange(new RectorWithChangelog(), 1); - $rectorWithLineChanges[] = new RectorWithLineChange(new RectorWithChangelog(), 1); - $rectorWithLineChanges[] = new RectorWithLineChange(new RectorWithOutChangelog(), 1); - - return new FileDiff(new SmartFileInfo(__FILE__), 'foo', 'foo', $rectorWithLineChanges); - } -} diff --git a/packages-tests/ChangesReporting/Annotation/AppliedRectorsChangelogResolver/Source/RectorWithChangelog.php b/packages-tests/ChangesReporting/Annotation/AppliedRectorsChangelogResolver/Source/RectorWithChangelog.php deleted file mode 100644 index 4d551321878..00000000000 --- a/packages-tests/ChangesReporting/Annotation/AppliedRectorsChangelogResolver/Source/RectorWithChangelog.php +++ /dev/null @@ -1,20 +0,0 @@ -boot(); - $this->commentRemover = $this->getService(CommentRemover::class); - $this->fileInfoParser = $this->getService(FileInfoParser::class); - $this->betterStandardPrinter = $this->getService(BetterStandardPrinter::class); - } - - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $smartFileInfo): void - { - $fileInfoToLocalInputAndExpected = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpected($smartFileInfo); - - $nodes = $this->fileInfoParser->parseFileInfoToNodesAndDecorate( - $fileInfoToLocalInputAndExpected->getInputFileInfo() - ); - - $nodesWithoutComments = $this->commentRemover->removeFromNode($nodes); - - $fileContent = $this->betterStandardPrinter->print($nodesWithoutComments); - $fileContent = trim($fileContent); - - $expectedContent = trim($fileInfoToLocalInputAndExpected->getExpected()); - - $this->assertSame($fileContent, $expectedContent, $smartFileInfo->getRelativeFilePathFromCwd()); - - // original nodes are not touched - $originalContent = $this->betterStandardPrinter->print($nodes); - $this->assertNotSame($expectedContent, $originalContent); - } - - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.php.inc'); - } -} diff --git a/packages-tests/Comments/CommentRemover/Fixture/another_comment.php.inc b/packages-tests/Comments/CommentRemover/Fixture/another_comment.php.inc deleted file mode 100644 index 612470840dd..00000000000 --- a/packages-tests/Comments/CommentRemover/Fixture/another_comment.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ -namespace Rector\Comments\Tests\CommentRemover\Fixture; - -$values = new class -{ - public function run($value) - { - switch ($value) { - case 'key': - return 'https://some_very_long_link.cz'; - break; - } - } -}; diff --git a/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/.editorconfig b/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/.editorconfig deleted file mode 100644 index d8a51d6e3bb..00000000000 --- a/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -[composer.json] -indent_size = 1 -indent_style = tab diff --git a/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/EditorConfigParserTest.php b/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/EditorConfigParserTest.php deleted file mode 100644 index 1df7c35d749..00000000000 --- a/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/EditorConfigParserTest.php +++ /dev/null @@ -1,41 +0,0 @@ -boot(); - $this->editorConfigParser = $this->getService(EditorConfigParser::class); - } - - public function testComposerJsonFile(): void - { - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - $editorConfigConfigurationBuilder->withIndent(Indent::createSpaceWithSize(20)); - - $composerJsonFile = new SmartFileInfo(__DIR__ . '/Fixture/composer.json'); - - $file = new File($composerJsonFile, $composerJsonFile->getContents()); - - $editorConfigConfiguration = $this->editorConfigParser->extractConfigurationForFile( - $file, - $editorConfigConfigurationBuilder - ); - - $this->assertSame('tab', $editorConfigConfiguration->getIndentStyle()); - $this->assertSame(1, $editorConfigConfiguration->getIndentSize()); - } -} diff --git a/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/Fixture/composer.json b/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/Fixture/composer.json deleted file mode 100644 index fe051e7e968..00000000000 --- a/packages-tests/FileFormatter/EditorConfig/EditorConfigParser/Fixture/composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "foo/bar", - "description": "A foo bar baz extension", - "license": "GPL-2.0-or-later" -} diff --git a/packages-tests/FileFormatter/Formatter/JsonFileFormatter/Fixture/composer_change_indent_style_to_tab.json b/packages-tests/FileFormatter/Formatter/JsonFileFormatter/Fixture/composer_change_indent_style_to_tab.json deleted file mode 100644 index 07b8c998133..00000000000 --- a/packages-tests/FileFormatter/Formatter/JsonFileFormatter/Fixture/composer_change_indent_style_to_tab.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "foo/bar", - "description": "A foo bar baz extension", - "license": "GPL-2.0-or-later" -} ------ -{ - "name": "foo/bar", - "description": "A foo bar baz extension", - "license": "GPL-2.0-or-later" -} diff --git a/packages-tests/FileFormatter/Formatter/JsonFileFormatter/JsonFileFormatterTest.php b/packages-tests/FileFormatter/Formatter/JsonFileFormatter/JsonFileFormatterTest.php deleted file mode 100644 index fa0d27725bd..00000000000 --- a/packages-tests/FileFormatter/Formatter/JsonFileFormatter/JsonFileFormatterTest.php +++ /dev/null @@ -1,57 +0,0 @@ -boot(); - $this->jsonFileFormatter = $this->getService(JsonFileFormatter::class); - } - - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void - { - $this->doTestFileInfo($fileInfo); - } - - /** - * @return Iterator> - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.json'); - } - - private function doTestFileInfo(SmartFileInfo $smartFileInfo): void - { - $inputFileInfoAndExpected = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpected($smartFileInfo); - - $inputFileInfo = $inputFileInfoAndExpected->getInputFileInfo(); - $file = new File($inputFileInfo, $inputFileInfo->getContents()); - - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - $editorConfigConfigurationBuilder->withIndent(Indent::createTab()); - - $this->jsonFileFormatter->format($file, $editorConfigConfigurationBuilder->build()); - - $this->assertSame($inputFileInfoAndExpected->getExpected(), $file->getFileContent()); - } -} diff --git a/packages-tests/FileFormatter/Formatter/XmlFileFormatter/Fixture/change_from_spaces_to_tabs.xml b/packages-tests/FileFormatter/Formatter/XmlFileFormatter/Fixture/change_from_spaces_to_tabs.xml deleted file mode 100644 index 548304f240d..00000000000 --- a/packages-tests/FileFormatter/Formatter/XmlFileFormatter/Fixture/change_from_spaces_to_tabs.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - -Gambardella, Matthew - XML Developer's Guide - Computer - 44.95 - 2000-10-01 - An in-depth look at creating applications - with XML. - - ------ - - - - Gambardella, Matthew - XML Developer's Guide - Computer - 44.95 - 2000-10-01 - An in-depth look at creating applications - with XML. - - diff --git a/packages-tests/FileFormatter/Formatter/XmlFileFormatter/XmlFileFormatterTest.php b/packages-tests/FileFormatter/Formatter/XmlFileFormatter/XmlFileFormatterTest.php deleted file mode 100644 index b1b549c310e..00000000000 --- a/packages-tests/FileFormatter/Formatter/XmlFileFormatter/XmlFileFormatterTest.php +++ /dev/null @@ -1,57 +0,0 @@ -boot(); - $this->xmlFileFormatter = $this->getService(XmlFileFormatter::class); - } - - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void - { - $this->doTestFileInfo($fileInfo); - } - - /** - * @return Iterator> - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.xml'); - } - - private function doTestFileInfo(SmartFileInfo $smartFileInfo): void - { - $inputFileInfoAndExpected = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpected($smartFileInfo); - - $inputFileInfo = $inputFileInfoAndExpected->getInputFileInfo(); - $file = new File($inputFileInfo, $inputFileInfo->getContents()); - - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - $editorConfigConfigurationBuilder->withIndent(Indent::createTab()); - - $this->xmlFileFormatter->format($file, $editorConfigConfigurationBuilder->build()); - - $this->assertSame($inputFileInfoAndExpected->getExpected(), $file->getFileContent()); - } -} diff --git a/packages-tests/FileFormatter/Formatter/YamlFileFormatter/Fixture/yaml_change_indent_size_from_two_to_four.yaml b/packages-tests/FileFormatter/Formatter/YamlFileFormatter/Fixture/yaml_change_indent_size_from_two_to_four.yaml deleted file mode 100644 index aa5d6d4a791..00000000000 --- a/packages-tests/FileFormatter/Formatter/YamlFileFormatter/Fixture/yaml_change_indent_size_from_two_to_four.yaml +++ /dev/null @@ -1,15 +0,0 @@ -martin: - name: Martin - job: Developer - skills: - - python - - perl - - pascal ------ -martin: - name: Martin - job: Developer - skills: - - python - - perl - - pascal diff --git a/packages-tests/FileFormatter/Formatter/YamlFileFormatter/YamlFileFormatterTest.php b/packages-tests/FileFormatter/Formatter/YamlFileFormatter/YamlFileFormatterTest.php deleted file mode 100644 index 559718063f2..00000000000 --- a/packages-tests/FileFormatter/Formatter/YamlFileFormatter/YamlFileFormatterTest.php +++ /dev/null @@ -1,58 +0,0 @@ -boot(); - $this->yamlFileFormatter = $this->getService(YamlFileFormatter::class); - } - - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void - { - $this->doTestFileInfo($fileInfo); - } - - /** - * @return Iterator> - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.yaml'); - } - - private function doTestFileInfo(SmartFileInfo $smartFileInfo): void - { - $inputFileInfoAndExpected = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpected($smartFileInfo); - - $inputFileInfo = $inputFileInfoAndExpected->getInputFileInfo(); - $file = new File($inputFileInfo, $inputFileInfo->getContents()); - - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - $editorConfigConfigurationBuilder->withIndent(Indent::createSpaceWithSize(4)); - $editorConfigConfigurationBuilder->withInsertFinalNewline(false); - - $this->yamlFileFormatter->format($file, $editorConfigConfigurationBuilder->build()); - - $this->assertSame($inputFileInfoAndExpected->getExpected(), $file->getFileContent()); - } -} diff --git a/packages-tests/FileFormatter/ValueObject/EditorConfigConfigurationTest.php b/packages-tests/FileFormatter/ValueObject/EditorConfigConfigurationTest.php deleted file mode 100644 index e0d0d5ae0ed..00000000000 --- a/packages-tests/FileFormatter/ValueObject/EditorConfigConfigurationTest.php +++ /dev/null @@ -1,51 +0,0 @@ -build(); - - $this->assertSame(StaticEolConfiguration::getEolChar(), $editorConfigConfiguration->getFinalNewline()); - } - - public function testWithoutFinalNewline(): void - { - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - $editorConfigConfigurationBuilder->withInsertFinalNewline(false); - - $editorConfigConfiguration = $editorConfigConfigurationBuilder->build(); - - $this->assertSame('', $editorConfigConfiguration->getFinalNewline()); - } - - public function testIndentForTab(): void - { - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - $editorConfigConfigurationBuilder->withIndent(Indent::createTab()); - - $editorConfigConfiguration = $editorConfigConfigurationBuilder->build(); - - $this->assertSame(' ', $editorConfigConfiguration->getIndent()); - } - - public function testIndentForSpace(): void - { - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - $editorConfigConfigurationBuilder->withIndent(Indent::createSpaceWithSize(10)); - - $editorConfigConfiguration = $editorConfigConfigurationBuilder->build(); - - $this->assertSame(' ', $editorConfigConfiguration->getIndent()); - } -} diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/composer_carriage_return.json b/packages-tests/FileFormatter/ValueObject/Fixture/composer_carriage_return.json deleted file mode 100644 index e1357a8bc0d..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/composer_carriage_return.json +++ /dev/null @@ -1 +0,0 @@ -{ "name": "foo/bar", "description": "A foo bar baz extension", "license": "GPL-2.0-or-later" } \ No newline at end of file diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/composer_carriage_return_line_feed.json b/packages-tests/FileFormatter/ValueObject/Fixture/composer_carriage_return_line_feed.json deleted file mode 100644 index fe051e7e968..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/composer_carriage_return_line_feed.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "foo/bar", - "description": "A foo bar baz extension", - "license": "GPL-2.0-or-later" -} diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/composer_indentation_space_six.json b/packages-tests/FileFormatter/ValueObject/Fixture/composer_indentation_space_six.json deleted file mode 100644 index 8bb73ebece1..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/composer_indentation_space_six.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "foo/bar", - "description": "A foo bar baz extension", - "license": "GPL-2.0-or-later" -} diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/composer_indentation_tab_two.json b/packages-tests/FileFormatter/ValueObject/Fixture/composer_indentation_tab_two.json deleted file mode 100644 index 3f208c13a1c..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/composer_indentation_tab_two.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "foo/bar", - "description": "A foo bar baz extension", - "license": "GPL-2.0-or-later" -} diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/composer_line_feed.json b/packages-tests/FileFormatter/ValueObject/Fixture/composer_line_feed.json deleted file mode 100644 index fe051e7e968..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/composer_line_feed.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "foo/bar", - "description": "A foo bar baz extension", - "license": "GPL-2.0-or-later" -} diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/xml_line_feed.xml b/packages-tests/FileFormatter/ValueObject/Fixture/xml_line_feed.xml deleted file mode 100644 index 3f413a56a9b..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/xml_line_feed.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - -Gambardella, Matthew - XML Developer's Guide - Computer - 44.95 - 2000-10-01 - An in-depth look at creating applications - with XML. - - diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/yaml_carriage_return.yaml b/packages-tests/FileFormatter/ValueObject/Fixture/yaml_carriage_return.yaml deleted file mode 100644 index 690ff612785..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/yaml_carriage_return.yaml +++ /dev/null @@ -1 +0,0 @@ -martin: name: Martin job: Developer skills: - python - perl - pascal \ No newline at end of file diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/yaml_indentation_space_four.yaml b/packages-tests/FileFormatter/ValueObject/Fixture/yaml_indentation_space_four.yaml deleted file mode 100644 index 7b2a03d244f..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/yaml_indentation_space_four.yaml +++ /dev/null @@ -1,7 +0,0 @@ -martin: - name: Martin - job: Developer - skills: - - python - - perl - - pascal diff --git a/packages-tests/FileFormatter/ValueObject/Fixture/yaml_indentation_space_two.yaml b/packages-tests/FileFormatter/ValueObject/Fixture/yaml_indentation_space_two.yaml deleted file mode 100644 index eb68623178f..00000000000 --- a/packages-tests/FileFormatter/ValueObject/Fixture/yaml_indentation_space_two.yaml +++ /dev/null @@ -1,7 +0,0 @@ -martin: - name: Martin - job: Developer - skills: - - python - - perl - - pascal diff --git a/packages-tests/FileFormatter/ValueObject/IndentTest.php b/packages-tests/FileFormatter/ValueObject/IndentTest.php deleted file mode 100644 index 48a9bdf2d3c..00000000000 --- a/packages-tests/FileFormatter/ValueObject/IndentTest.php +++ /dev/null @@ -1,107 +0,0 @@ -getContents()); - $this->assertSame($expectedIndent, $indent->__toString()); - } - - /** - * @dataProvider provideSizeStyleAndIndentString - */ - public function testFromSizeAndStyle(int $size, string $style, string $string): void - { - $indent = Indent::fromSizeAndStyle($size, $style); - - $this->assertSame($string, $indent->__toString()); - $this->assertSame($size, $indent->getIndentSize()); - $this->assertSame($style, $indent->getIndentStyle()); - } - - public function testFromSizeAndStyleWithInvalidSizeThrowsException(): void - { - $this->expectException(InvalidIndentSizeException::class); - Indent::fromSizeAndStyle(0, 'invalid'); - } - - public function testFromSizeAndStyleWithInvalidStyleThrowsException(): void - { - $this->expectException(InvalidIndentStyleException::class); - Indent::fromSizeAndStyle(1, 'invalid'); - } - - public function testFromInvalidContentThrowsException(): void - { - $this->expectException(ParseIndentException::class); - Indent::fromContent('This is invalid content'); - } - - /** - * @return Iterator> - */ - public function extractFromFiles(): Iterator - { - yield 'Yaml file with space indentation of size 4' => [ - new SmartFileInfo(__DIR__ . '/Fixture/yaml_indentation_space_four.yaml'), - ' ', - ]; - - yield 'Yaml file with space indentation of size 2' => [ - new SmartFileInfo(__DIR__ . '/Fixture/yaml_indentation_space_two.yaml'), - ' ', - ]; - - yield 'Json file with tab indentation of size 2' => [ - new SmartFileInfo(__DIR__ . '/Fixture/composer_indentation_tab_two.json'), - ' ', - ]; - - yield 'Json file with space indentation of size 6' => [ - new SmartFileInfo(__DIR__ . '/Fixture/composer_indentation_space_six.json'), - ' ', - ]; - } - - /** - * @return Generator - */ - public function provideSizeStyleAndIndentString(): Iterator - { - foreach ($this->sizes() as $size) { - foreach (Indent::CHARACTERS as $style => $character) { - $string = str_repeat($character, $size); - - yield [$size, $style, $string]; - } - } - } - - /** - * @return int[] - */ - private static function sizes(): array - { - return [ - 'int-one' => 1, - 'int-greater-than-one' => 5, - ]; - } -} diff --git a/packages-tests/FileFormatter/ValueObject/NewLineTest.php b/packages-tests/FileFormatter/ValueObject/NewLineTest.php deleted file mode 100644 index 1ff4d35f997..00000000000 --- a/packages-tests/FileFormatter/ValueObject/NewLineTest.php +++ /dev/null @@ -1,112 +0,0 @@ -getContents()); - $this->assertSame($expectedNewLine, $newLine->__toString()); - } - - /** - * @dataProvider provideInvalidNewLineString - */ - public function testFromStringRejectsInvalidNewLineString(string $string): void - { - $this->expectException(InvalidNewLineStringException::class); - - NewLine::fromSingleCharacter($string); - } - - /** - * @dataProvider provideValidNewLineString - */ - public function testFromStringReturnsNewLine(string $string): void - { - $newLine = NewLine::fromSingleCharacter($string); - - $this->assertSame($string, $newLine->__toString()); - } - - /** - * @dataProvider provideValidNewLineStringFromEditorConfig - */ - public function testFromEditorConfigReturnsNewLine(string $string, string $expected): void - { - $newLine = NewLine::fromEditorConfig($string); - - $this->assertSame($expected, $newLine->__toString()); - } - - /** - * @return Iterator> - */ - public function provideValidNewLineString(): Iterator - { - foreach (["\n", "\r", "\r\n"] as $string) { - yield [$string]; - } - } - - /** - * @return Iterator> - */ - public function provideInvalidNewLineString(): Iterator - { - foreach (["\t", " \r ", " \r\n ", " \n ", ' ', "\f", "\x0b", "\x85"] as $string) { - yield [$string]; - } - } - - /** - * @return Iterator> - */ - public function extractFromFiles(): Iterator - { - yield 'Yaml file with carriage return' => [ - new SmartFileInfo(__DIR__ . '/Fixture/yaml_carriage_return.yaml'), - "\r", - ]; - - yield 'Xml file with line feed' => [new SmartFileInfo(__DIR__ . '/Fixture/xml_line_feed.xml'), "\n"]; - - yield 'Json file with line feed' => [new SmartFileInfo(__DIR__ . '/Fixture/composer_line_feed.json'), "\n"]; - - yield 'Json file with carriage return' => [ - new SmartFileInfo(__DIR__ . '/Fixture/composer_carriage_return.json'), - "\r", - ]; - - yield 'Json file with carriage return and line feed' => [ - new SmartFileInfo(__DIR__ . '/Fixture/composer_carriage_return_line_feed.json'), - "\r\n", - ]; - } - - /** - * @return Iterator> - */ - public function provideValidNewLineStringFromEditorConfig(): Iterator - { - foreach ([ - 'lf' => "\n", - 'cr' => "\r", - 'crlf' => "\r\n", - ] as $editorConfig => $string) { - yield [$editorConfig, $string]; - } - } -} diff --git a/packages-tests/NodeTypeResolver/NodeVisitor/FunctionMethodAndClassNodeVisitor/Fixture/anonymous_class.php.inc b/packages-tests/NodeTypeResolver/NodeVisitor/FunctionMethodAndClassNodeVisitor/Fixture/anonymous_class.php.inc deleted file mode 100644 index d34ce44a780..00000000000 --- a/packages-tests/NodeTypeResolver/NodeVisitor/FunctionMethodAndClassNodeVisitor/Fixture/anonymous_class.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -boot(); - - $this->betterNodeFinder = $this->getService(BetterNodeFinder::class); - $this->testingParser = $this->getService(TestingParser::class); - $this->nodeTypeResolver = $this->getService(NodeTypeResolver::class); - } - - /** - * @template T as Node - * @param class-string $type - * @return T[] - */ - protected function getNodesForFileOfType(string $file, string $type): array - { - $nodes = $this->testingParser->parseFileToDecoratedNodes($file); - return $this->betterNodeFinder->findInstanceOf($nodes, $type); - } -} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentTrait.php b/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentTrait.php deleted file mode 100644 index 6f270403353..00000000000 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentTrait.php +++ /dev/null @@ -1,9 +0,0 @@ -getNodesForFileOfType($file, Name::class); - - $resolvedType = $this->nodeTypeResolver->resolve($nameNodes[$nodePosition]); - $this->assertEquals($expectedType, $resolvedType); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $expectedObjectType = new ObjectType(AnotherClass::class); - - # test new - yield [__DIR__ . '/Source/ParentCall.php', 2, $expectedObjectType]; - } -} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/ParamTypeResolverTest.php b/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/ParamTypeResolverTest.php deleted file mode 100644 index b6a93a9ed94..00000000000 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/ParamTypeResolverTest.php +++ /dev/null @@ -1,41 +0,0 @@ -getNodesForFileOfType($file, Param::class); - - $resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]); - - $this->assertInstanceOf(TypeWithClassName::class, $resolvedType); - - /** @var TypeWithClassName $resolvedType */ - $this->assertSame($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName()); - } - - public function provideData(): Iterator - { - $objectType = new ObjectType(Html::class); - - yield [__DIR__ . '/Source/MethodParamTypeHint.php', 0, $objectType]; - yield [__DIR__ . '/Source/MethodParamDocBlock.php', 0, $objectType]; - } -} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/AbstractPropertyFetchTypeResolverTest.php b/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/AbstractPropertyFetchTypeResolverTest.php deleted file mode 100644 index b648ea06616..00000000000 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/AbstractPropertyFetchTypeResolverTest.php +++ /dev/null @@ -1,39 +0,0 @@ -getInputFileInfo(); - $expectedFileInfo = $inputFileInfoAndExpectedFileInfo->getExpectedFileInfo(); - - $propertyFetchNodes = $this->getNodesForFileOfType($inputFileInfo->getRealPath(), PropertyFetch::class); - $resolvedType = $this->nodeTypeResolver->resolve($propertyFetchNodes[0]); - - $expectedType = include $expectedFileInfo->getRealPath(); - - $expectedTypeAsString = $this->getStringFromType($expectedType); - $resolvedTypeAsString = $this->getStringFromType($resolvedType); - - $this->assertSame($expectedTypeAsString, $resolvedTypeAsString); - } - - private function getStringFromType(Type $type): string - { - return $type->describe(VerbosityLevel::precise()); - } -} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/implicit_mixed.php.inc b/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/implicit_mixed.php.inc deleted file mode 100644 index 8a1cf0a35a3..00000000000 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/implicit_mixed.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -implicitMixed->xxx(); - } -} - -?> ------ -nonexistent->xxx(); - } -} - -?> ------ -textNullable->xxx(); - } -} - -?> ------ -doTestFileInfo($smartFileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectoryExclusively(__DIR__ . '/FixturePhp74'); - } -} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Php80Test.php b/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Php80Test.php deleted file mode 100644 index 1888ed79774..00000000000 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Php80Test.php +++ /dev/null @@ -1,32 +0,0 @@ -doTestFileInfo($smartFileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectoryExclusively(__DIR__ . '/FixturePhp80'); - } -} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/PropertyFetchTypeResolverTest.php b/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/PropertyFetchTypeResolverTest.php deleted file mode 100644 index b694f85a1c1..00000000000 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/PropertyFetchTypeResolverTest.php +++ /dev/null @@ -1,28 +0,0 @@ -doTestFileInfo($smartFileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectoryExclusively(__DIR__ . '/Fixture'); - } -} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/PropertyTypeResolverTest.php b/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/PropertyTypeResolverTest.php deleted file mode 100644 index c6de2298d01..00000000000 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/PropertyTypeResolverTest.php +++ /dev/null @@ -1,60 +0,0 @@ -getNodesForFileOfType($file, Property::class); - - $resolvedType = $this->nodeTypeResolver->resolve($propertyNodes[$nodePosition]); - - // type is as expected - $expectedTypeClass = $expectedType::class; - $this->assertInstanceOf($expectedTypeClass, $resolvedType); - - $expectedTypeAsString = $this->getStringFromType($expectedType); - $resolvedTypeAsString = $this->getStringFromType($resolvedType); - - $this->assertEquals($expectedTypeAsString, $resolvedTypeAsString); - } - - public function provideData(): Iterator - { - $unionTypeFactory = new UnionTypeFactory(); - - yield [__DIR__ . '/Source/MethodParamDocBlock.php', 0, new ObjectType(Html::class)]; - - yield [__DIR__ . '/Source/MethodParamDocBlock.php', 1, new ObjectType(ClassThatExtendsHtml::class)]; - - // mimics failing test from DomainDrivenDesign set - $unionType = $unionTypeFactory->createUnionObjectType([SomeChild::class, new NullType()]); - yield [__DIR__ . '/Source/ActionClass.php', 0, $unionType]; - } - - private function getStringFromType(Type $type): string - { - return $type->describe(VerbosityLevel::precise()); - } -} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ClassThatExtendsHtml.php b/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ClassThatExtendsHtml.php deleted file mode 100644 index 666455c6fd4..00000000000 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ClassThatExtendsHtml.php +++ /dev/null @@ -1,10 +0,0 @@ -getNodesForFileOfType($file, Trait_::class); - - $resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]); - $this->assertEquals($expectedType, $resolvedType); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $unionTypeFactory = new UnionTypeFactory(); - - yield [ - __DIR__ . '/Source/TraitWithTrait.php', - 0, - $unionTypeFactory->createUnionObjectType([AnotherTrait::class, TraitWithTrait::class]), - ]; - } -} diff --git a/packages-tests/NodeTypeResolver/StaticTypeMapper/StaticTypeMapperTest.php b/packages-tests/NodeTypeResolver/StaticTypeMapper/StaticTypeMapperTest.php deleted file mode 100644 index 17d89306c3e..00000000000 --- a/packages-tests/NodeTypeResolver/StaticTypeMapper/StaticTypeMapperTest.php +++ /dev/null @@ -1,101 +0,0 @@ -boot(); - - $this->staticTypeMapper = $this->getService(StaticTypeMapper::class); - } - - /** - * @dataProvider provideDataForMapPHPStanPhpDocTypeNodeToPHPStanType() - */ - public function testMapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, string $expectedType): void - { - $string = new String_('hey'); - - $phpStanType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, $string); - - $this->assertInstanceOf($expectedType, $phpStanType); - } - - public function provideDataForMapPHPStanPhpDocTypeNodeToPHPStanType(): Iterator - { - $genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('Traversable'), []); - yield [$genericTypeNode, GenericObjectType::class]; - - $genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('iterable'), [ - new IdentifierTypeNode('string'), - ]); - - yield [$genericTypeNode, IterableType::class]; - - yield [new IdentifierTypeNode('mixed'), MixedType::class]; - } - - public function testMapPHPStanTypeToPHPStanPhpDocTypeNode(): void - { - $iterableType = new IterableType(new MixedType(), new ClassStringType()); - - $phpStanDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( - $iterableType, - TypeKind::ANY() - ); - $this->assertInstanceOf(ArrayTypeNode::class, $phpStanDocTypeNode); - - /** @var ArrayTypeNode $phpStanDocTypeNode */ - $this->assertInstanceOf(IdentifierTypeNode::class, $phpStanDocTypeNode->type); - } - - public function testMixed(): void - { - $mixedType = new MixedType(); - - $phpStanDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( - $mixedType, - TypeKind::ANY() - ); - $this->assertInstanceOf(IdentifierTypeNode::class, $phpStanDocTypeNode); - } - - /** - * @dataProvider provideDataForMapPhpParserNodePHPStanType() - */ - public function testMapPhpParserNodePHPStanType(Node $node, string $expectedType): void - { - $phpStanType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($node); - $this->assertInstanceOf($expectedType, $phpStanType); - } - - /** - * @return Iterator[]|Identifier[]> - */ - public function provideDataForMapPhpParserNodePHPStanType(): Iterator - { - yield [new Identifier('iterable'), IterableType::class]; - } -} diff --git a/packages-tests/NodeTypeResolver/TypeComparator/ArrayTypeComparatorTest.php b/packages-tests/NodeTypeResolver/TypeComparator/ArrayTypeComparatorTest.php deleted file mode 100644 index aa24c976f98..00000000000 --- a/packages-tests/NodeTypeResolver/TypeComparator/ArrayTypeComparatorTest.php +++ /dev/null @@ -1,59 +0,0 @@ -boot(); - $this->arrayTypeComparator = $this->getService(ArrayTypeComparator::class); - } - - /** - * @dataProvider provideData() - */ - public function test(ArrayType $firstArrayType, ArrayType $secondArrayType, bool $areExpectedEqual): void - { - $areEqual = $this->arrayTypeComparator->isSubtype($firstArrayType, $secondArrayType); - $this->assertSame($areExpectedEqual, $areEqual); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $unionTypeFactory = new UnionTypeFactory(); - - $classStringKeysArrayType = new ArrayType(new StringType(), new ClassStringType()); - $stringArrayType = new ArrayType(new StringType(), new MixedType()); - yield [$stringArrayType, $classStringKeysArrayType, false]; - - $genericClassStringType = new GenericClassStringType(new ObjectType(SomeGenericTypeObject::class)); - $constantArrayType = new ConstantArrayType( - [new ConstantIntegerType(0)], - [$unionTypeFactory->createUnionObjectType([$genericClassStringType, $genericClassStringType])] - ); - - yield [$constantArrayType, $stringArrayType, false]; - } -} diff --git a/packages-tests/NodeTypeResolver/TypeComparator/ScalarTypeComparatorTest.php b/packages-tests/NodeTypeResolver/TypeComparator/ScalarTypeComparatorTest.php deleted file mode 100644 index 4bdf8ddbc7e..00000000000 --- a/packages-tests/NodeTypeResolver/TypeComparator/ScalarTypeComparatorTest.php +++ /dev/null @@ -1,40 +0,0 @@ -boot(); - $this->scalarTypeComparator = $this->getService(ScalarTypeComparator::class); - } - - /** - * @dataProvider provideData() - */ - public function test(Type $firstType, Type $secondType, bool $areExpectedEqual): void - { - $areEqual = $this->scalarTypeComparator->areEqualScalar($firstType, $secondType); - $this->assertSame($areExpectedEqual, $areEqual); - } - - public function provideData(): Iterator - { - yield [new StringType(), new BooleanType(), false]; - yield [new StringType(), new StringType(), true]; - yield [new StringType(), new ClassStringType(), false]; - } -} diff --git a/packages-tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php b/packages-tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php deleted file mode 100644 index fdb119f265f..00000000000 --- a/packages-tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php +++ /dev/null @@ -1,88 +0,0 @@ -boot(); - - $this->arrayTypeMapper = $this->getService(ArrayTypeMapper::class); - } - - /** - * @dataProvider provideDataWithoutKeys() - * @dataProvider provideDataUnionedWithoutKeys() - */ - public function testWithoutKeys(ArrayType $arrayType, string $expectedResult): void - { - $actualTypeNode = $this->arrayTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType, TypeKind::ANY()); - $this->assertSame($expectedResult, (string) $actualTypeNode); - } - - /** - * @dataProvider provideDataWithKeys() - */ - public function testWithKeys(ArrayType $arrayType, string $expectedResult): void - { - $actualTypeNode = $this->arrayTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType, TypeKind::ANY()); - $this->assertSame($expectedResult, (string) $actualTypeNode); - } - - /** - * @return Iterator - */ - public function provideDataWithoutKeys(): Iterator - { - $arrayType = new ArrayType(new MixedType(), new StringType()); - yield [$arrayType, 'string[]']; - - $stringStringUnionType = new UnionType([new StringType(), new StringType()]); - $arrayType = new ArrayType(new MixedType(), $stringStringUnionType); - yield [$arrayType, 'string[]']; - } - - public function provideDataUnionedWithoutKeys(): Iterator - { - $stringAndIntegerUnionType = new UnionType([new StringType(), new IntegerType()]); - $unionArrayType = new ArrayType(new MixedType(), $stringAndIntegerUnionType); - yield [$unionArrayType, 'int[]|string[]']; - - $moreNestedUnionArrayType = new ArrayType(new MixedType(), $unionArrayType); - yield [$moreNestedUnionArrayType, 'int[][]|string[][]']; - - $evenMoreNestedUnionArrayType = new ArrayType(new MixedType(), $moreNestedUnionArrayType); - yield [$evenMoreNestedUnionArrayType, 'int[][][]|string[][][]']; - } - - public function provideDataWithKeys(): Iterator - { - $arrayMixedToStringType = new ArrayType(new MixedType(), new StringType()); - $arrayType = new ArrayType(new StringType(), $arrayMixedToStringType); - yield [$arrayType, 'array']; - - $stringAndIntegerUnionType = new UnionType([new StringType(), new IntegerType()]); - - $stringAndIntegerUnionArrayType = new ArrayType(new MixedType(), $stringAndIntegerUnionType); - $arrayType = new ArrayType(new StringType(), $stringAndIntegerUnionArrayType); - yield [$arrayType, 'array>']; - - $arrayType = new ArrayType(new StringType(), new IntegerType()); - yield [$arrayType, 'array']; - } -} diff --git a/packages-tests/PhpAttribute/Printer/PhpAttributeGroupFactoryTest.php b/packages-tests/PhpAttribute/Printer/PhpAttributeGroupFactoryTest.php deleted file mode 100644 index edde527391d..00000000000 --- a/packages-tests/PhpAttribute/Printer/PhpAttributeGroupFactoryTest.php +++ /dev/null @@ -1,46 +0,0 @@ -boot(); - - $this->phpAttributeGroupFactory = $this->getService(PhpAttributeGroupFactory::class); - } - - public function testCreateFromClassWithItems(): void - { - $attributeGroup = $this->phpAttributeGroupFactory->createFromClassWithItems( - 'Symfony\Component\Routing\Annotation\Route', - [ - 'path' => '/path', - 'name' => 'action', - ] - ); - - $this->assertInstanceOf(AttributeGroup::class, $attributeGroup); - } - - public function testCreateArgsFromItems(): void - { - $args = $this->phpAttributeGroupFactory->createArgsFromItems([ - 'path' => '/path', - 'name' => 'action', - ]); - - $this->assertCount(2, $args); - $this->assertContainsOnlyInstancesOf(Arg::class, $args); - } -} diff --git a/packages-tests/StaticTypeMapper/PhpDoc/PhpDocTypeMapperTest.php b/packages-tests/StaticTypeMapper/PhpDoc/PhpDocTypeMapperTest.php deleted file mode 100644 index 2002a98e176..00000000000 --- a/packages-tests/StaticTypeMapper/PhpDoc/PhpDocTypeMapperTest.php +++ /dev/null @@ -1,53 +0,0 @@ -boot(); - $this->phpDocTypeMapper = $this->getService(PhpDocTypeMapper::class); - $this->nameScopeFactory = $this->getService(NameScopeFactory::class); - } - - /** - * @dataProvider provideData() - */ - public function test(TypeNode $typeNode, string $expectedPHPStanType): void - { - $nop = new Nop(); - $nameScope = $this->nameScopeFactory->createNameScopeFromNode($nop); - - $phpStanType = $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $nop, $nameScope); - - $this->assertInstanceOf($expectedPHPStanType, $phpStanType); - } - - /** - * @return Iterator[]|ArrayShapeNode[]> - */ - public function provideData(): Iterator - { - $arrayShapeNode = new ArrayShapeNode([new ArrayShapeItemNode(null, true, new IdentifierTypeNode('string'))]); - - yield [$arrayShapeNode, ArrayType::class]; - } -} diff --git a/packages/BetterPhpDocParser/Comment/CommentsMerger.php b/packages/BetterPhpDocParser/Comment/CommentsMerger.php deleted file mode 100644 index acc40af4bda..00000000000 --- a/packages/BetterPhpDocParser/Comment/CommentsMerger.php +++ /dev/null @@ -1,93 +0,0 @@ -getComments(); - - foreach ($mergedNodes as $mergedNode) { - $comments = array_merge($comments, $mergedNode->getComments()); - } - - if ($comments === []) { - return; - } - - $newNode->setAttribute(AttributeKey::COMMENTS, $comments); - - // remove so comments "win" - $newNode->setAttribute(AttributeKey::PHP_DOC_INFO, null); - } - - public function keepParent(Node $newNode, Node $oldNode): void - { - $parent = $oldNode->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { - return; - } - - $phpDocInfo = $parent->getAttribute(AttributeKey::PHP_DOC_INFO); - $comments = $parent->getComments(); - - if ($phpDocInfo === null && $comments === []) { - return; - } - - $newNode->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); - $newNode->setAttribute(AttributeKey::COMMENTS, $comments); - } - - public function keepChildren(Node $newNode, Node $oldNode): void - { - $childrenComments = $this->collectChildrenComments($oldNode); - - if ($childrenComments === []) { - return; - } - - $commentContent = ''; - foreach ($childrenComments as $childComment) { - $commentContent .= $childComment->getText() . PHP_EOL; - } - - $newNode->setAttribute(AttributeKey::COMMENTS, [new Comment($commentContent)]); - } - - /** - * @return Comment[] - */ - private function collectChildrenComments(Node $node): array - { - $childrenComments = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($node, function (Node $node) use ( - &$childrenComments - ): void { - $comments = $node->getComments(); - - if ($comments !== []) { - $childrenComments = array_merge($childrenComments, $comments); - } - }); - - return $childrenComments; - } -} diff --git a/packages/BetterPhpDocParser/Contract/BasePhpDocNodeVisitorInterface.php b/packages/BetterPhpDocParser/Contract/BasePhpDocNodeVisitorInterface.php deleted file mode 100644 index 8430713d8cc..00000000000 --- a/packages/BetterPhpDocParser/Contract/BasePhpDocNodeVisitorInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -, string> - */ - private const TAGS_TYPES_TO_NAMES = [ - ReturnTagValueNode::class => '@return', - ParamTagValueNode::class => '@param', - VarTagValueNode::class => '@var', - MethodTagValueNode::class => '@method', - PropertyTagValueNode::class => '@property', - ]; - - private bool $isSingleLine = false; - - private PhpDocNode $originalPhpDocNode; - - private bool $hasChanged = false; - - public function __construct( - private PhpDocNode $phpDocNode, - private BetterTokenIterator $betterTokenIterator, - private StaticTypeMapper $staticTypeMapper, - private \PhpParser\Node $node, - private AnnotationNaming $annotationNaming, - private CurrentNodeProvider $currentNodeProvider, - private RectorChangeCollector $rectorChangeCollector, - private PhpDocNodeByTypeFinder $phpDocNodeByTypeFinder - ) { - $this->originalPhpDocNode = clone $phpDocNode; - - if (! $betterTokenIterator->containsTokenType(Lexer::TOKEN_PHPDOC_EOL)) { - $this->isSingleLine = true; - } - } - - public function addPhpDocTagNode(PhpDocChildNode $phpDocChildNode): void - { - $this->phpDocNode->children[] = $phpDocChildNode; - // to give node more space - $this->makeMultiLined(); - - $this->markAsChanged(); - } - - public function getPhpDocNode(): PhpDocNode - { - return $this->phpDocNode; - } - - public function getOriginalPhpDocNode(): PhpDocNode - { - return $this->originalPhpDocNode; - } - - /** - * @return mixed[] - */ - public function getTokens(): array - { - return $this->betterTokenIterator->getTokens(); - } - - public function getTokenCount(): int - { - return $this->betterTokenIterator->count(); - } - - public function getVarTagValueNode(string $tagName = '@var'): ?VarTagValueNode - { - return $this->phpDocNode->getVarTagValues($tagName)[0] ?? null; - } - - /** - * @return array - */ - public function getTagsByName(string $name): array - { - // for simple tag names only - if (str_contains($name, '\\')) { - return []; - } - - $tags = $this->phpDocNode->getTags(); - $name = $this->annotationNaming->normalizeName($name); - - $tags = array_filter($tags, fn (PhpDocTagNode $tag): bool => $tag->name === $name); - - $tags = array_values($tags); - return array_values($tags); - } - - public function getParamType(string $name): Type - { - $paramTagValueNodes = $this->getParamTagValueByName($name); - - return $this->getTypeOrMixed($paramTagValueNodes); - } - - /** - * @return ParamTagValueNode[] - */ - public function getParamTagValueNodes(): array - { - return $this->phpDocNode->getParamTagValues(); - } - - public function getParamTagValueNodeByName(string $parameterName): ?ParamTagValueNode - { - foreach ($this->phpDocNode->getParamTagValues() as $paramTagValueNode) { - if ($paramTagValueNode->parameterName !== '$' . $parameterName) { - continue; - } - - return $paramTagValueNode; - } - - return null; - } - - public function getVarType(string $tagName = '@var'): Type - { - return $this->getTypeOrMixed($this->getVarTagValueNode($tagName)); - } - - public function getReturnType(): Type - { - return $this->getTypeOrMixed($this->getReturnTagValue()); - } - - /** - * @param class-string $type - * @return TNode[] - */ - public function getByType(string $type): array - { - return $this->phpDocNodeByTypeFinder->findByType($this->phpDocNode, $type); - } - - /** - * @param class-string $type - */ - public function hasByType(string $type): bool - { - return $this->phpDocNodeByTypeFinder->findByType($this->phpDocNode, $type) !== []; - } - - /** - * @param array> $types - */ - public function hasByTypes(array $types): bool - { - foreach ($types as $type) { - if ($this->hasByType($type)) { - return true; - } - } - - return false; - } - - /** - * @param string[] $names - */ - public function hasByNames(array $names): bool - { - foreach ($names as $name) { - if ($this->hasByName($name)) { - return true; - } - } - - return false; - } - - public function hasByName(string $name): bool - { - return (bool) $this->getTagsByName($name); - } - - public function getByName(string $name): ?Node - { - return $this->getTagsByName($name)[0] ?? null; - } - - /** - * @param class-string[] $classes - */ - public function getByAnnotationClasses(array $classes): ?DoctrineAnnotationTagValueNode - { - $doctrineAnnotationTagValueNodes = $this->phpDocNodeByTypeFinder->findDoctrineAnnotationsByClasses( - $this->phpDocNode, - $classes - ); - - return $doctrineAnnotationTagValueNodes[0] ?? null; - } - - /** - * @param class-string $class - */ - public function getByAnnotationClass(string $class): ?DoctrineAnnotationTagValueNode - { - $doctrineAnnotationTagValueNodes = $this->phpDocNodeByTypeFinder->findDoctrineAnnotationsByClass( - $this->phpDocNode, - $class - ); - return $doctrineAnnotationTagValueNodes[0] ?? null; - } - - /** - * @param class-string $class - */ - public function hasByAnnotationClass(string $class): bool - { - return $this->findByAnnotationClass($class) !== []; - } - - /** - * @param string[] $annotationsClasses - */ - public function hasByAnnotationClasses(array $annotationsClasses): bool - { - return $this->getByAnnotationClasses($annotationsClasses) !== null; - } - - /** - * @param class-string $desiredClass - */ - public function findOneByAnnotationClass(string $desiredClass): ?DoctrineAnnotationTagValueNode - { - $foundTagValueNodes = $this->findByAnnotationClass($desiredClass); - return $foundTagValueNodes[0] ?? null; - } - - /** - * @param class-string $desiredClass - * @return DoctrineAnnotationTagValueNode[] - */ - public function findByAnnotationClass(string $desiredClass): array - { - return $this->phpDocNodeByTypeFinder->findDoctrineAnnotationsByClass($this->phpDocNode, $desiredClass); - } - - /** - * @template T of \PHPStan\PhpDocParser\Ast\Node - * @param class-string $typeToRemove - */ - public function removeByType(string $typeToRemove): void - { - $phpDocNodeTraverser = new PhpDocNodeTraverser(); - $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', function (Node $node) use ( - $typeToRemove - ): ?int { - if ($node instanceof PhpDocTagNode && is_a($node->value, $typeToRemove, true)) { - $this->markAsChanged(); - return PhpDocNodeTraverser::NODE_REMOVE; - } - - if (! is_a($node, $typeToRemove, true)) { - return null; - } - - $this->markAsChanged(); - return PhpDocNodeTraverser::NODE_REMOVE; - }); - } - - /** - * @return array - */ - public function getParamTypesByName(): array - { - $paramTypesByName = []; - - foreach ($this->phpDocNode->getParamTagValues() as $paramTagValueNode) { - $parameterName = $paramTagValueNode->parameterName; - $parameterType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType( - $paramTagValueNode, - $this->node - ); - - $paramTypesByName[$parameterName] = $parameterType; - } - - return $paramTypesByName; - } - - public function addTagValueNode(PhpDocTagValueNode $phpDocTagValueNode): void - { - if ($phpDocTagValueNode instanceof DoctrineAnnotationTagValueNode) { - $spacelessPhpDocTagNode = new SpacelessPhpDocTagNode( - '@\\' . $phpDocTagValueNode->identifierTypeNode, - $phpDocTagValueNode - ); - $this->addPhpDocTagNode($spacelessPhpDocTagNode); - return; - } - - $name = $this->resolveNameForPhpDocTagValueNode($phpDocTagValueNode); - - $phpDocTagNode = new PhpDocTagNode($name, $phpDocTagValueNode); - $this->addPhpDocTagNode($phpDocTagNode); - } - - public function isNewNode(): bool - { - if ($this->phpDocNode->children === []) { - return false; - } - - return $this->betterTokenIterator->count() === 0; - } - - public function makeSingleLined(): void - { - $this->isSingleLine = true; - } - - public function isSingleLine(): bool - { - return $this->isSingleLine; - } - - public function hasInvalidTag(string $name): bool - { - // fallback for invalid tag value node - foreach ($this->phpDocNode->children as $phpDocChildNode) { - if (! $phpDocChildNode instanceof PhpDocTagNode) { - continue; - } - - if ($phpDocChildNode->name !== $name) { - continue; - } - - if (! $phpDocChildNode->value instanceof InvalidTagValueNode) { - continue; - } - - return true; - } - - return false; - } - - public function getReturnTagValue(): ?ReturnTagValueNode - { - $returnTagValueNodes = $this->phpDocNode->getReturnTagValues(); - return $returnTagValueNodes[0] ?? null; - } - - public function getParamTagValueByName(string $name): ?ParamTagValueNode - { - $desiredParamNameWithDollar = '$' . ltrim($name, '$'); - - foreach ($this->getParamTagValueNodes() as $paramTagValueNode) { - if ($paramTagValueNode->parameterName !== $desiredParamNameWithDollar) { - continue; - } - - return $paramTagValueNode; - } - - return null; - } - - /** - * @return TemplateTagValueNode[] - */ - public function getTemplateTagValueNodes(): array - { - return $this->phpDocNode->getTemplateTagValues(); - } - - public function hasInheritDoc(): bool - { - return $this->hasByNames(['inheritdoc', 'inheritDoc']); - } - - /** - * @deprecated - * Should be handled by attributes of phpdoc node - if stard_and_end is missing in one of nodes, it has been changed - * Similar to missing original node in php-aprser - */ - public function markAsChanged(): void - { - $this->hasChanged = true; - - $node = $this->currentNodeProvider->getNode(); - if ($node !== null) { - $this->rectorChangeCollector->notifyNodeFileInfo($node); - } - } - - public function hasChanged(): bool - { - if ($this->isNewNode()) { - return true; - } - - if ($this->hasChanged) { - return true; - } - - // has a single node with missing start_end - $phpDocNodeTraverser = new PhpDocNodeTraverser(); - $changedPhpDocNodeVisitor = new ChangedPhpDocNodeVisitor(); - $phpDocNodeTraverser->addPhpDocNodeVisitor($changedPhpDocNodeVisitor); - $phpDocNodeTraverser->traverse($this->phpDocNode); - - return $changedPhpDocNodeVisitor->hasChanged(); - } - - /** - * @return string[] - */ - public function getMethodTagNames(): array - { - $methodTagNames = []; - foreach ($this->phpDocNode->getMethodTagValues() as $methodTagValueNode) { - $methodTagNames[] = $methodTagValueNode->methodName; - } - - return $methodTagNames; - } - - public function makeMultiLined(): void - { - $this->isSingleLine = false; - } - - public function getNode(): \PhpParser\Node - { - return $this->node; - } - - private function getTypeOrMixed(?PhpDocTagValueNode $phpDocTagValueNode): MixedType | Type - { - if ($phpDocTagValueNode === null) { - return new MixedType(); - } - - return $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($phpDocTagValueNode, $this->node); - } - - private function resolveNameForPhpDocTagValueNode(PhpDocTagValueNode $phpDocTagValueNode): string - { - foreach (self::TAGS_TYPES_TO_NAMES as $tagValueNodeType => $name) { - /** @var class-string $tagValueNodeType */ - if (is_a($phpDocTagValueNode, $tagValueNodeType, true)) { - return $name; - } - } - - throw new NotImplementedYetException($phpDocTagValueNode::class); - } -} diff --git a/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfoFactory.php b/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfoFactory.php deleted file mode 100644 index b1ffdd9e28a..00000000000 --- a/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfoFactory.php +++ /dev/null @@ -1,147 +0,0 @@ - - */ - private array $phpDocInfosByObjectHash = []; - - public function __construct( - private PhpDocNodeMapper $phpDocNodeMapper, - private CurrentNodeProvider $currentNodeProvider, - private Lexer $lexer, - private BetterPhpDocParser $betterPhpDocParser, - private StaticTypeMapper $staticTypeMapper, - private AnnotationNaming $annotationNaming, - private RectorChangeCollector $rectorChangeCollector, - private PhpDocNodeByTypeFinder $phpDocNodeByTypeFinder - ) { - } - - public function createFromNodeOrEmpty(Node $node): PhpDocInfo - { - // already added - $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); - if ($phpDocInfo instanceof PhpDocInfo) { - return $phpDocInfo; - } - - $phpDocInfo = $this->createFromNode($node); - if ($phpDocInfo instanceof PhpDocInfo) { - return $phpDocInfo; - } - - return $this->createEmpty($node); - } - - public function createFromNode(Node $node): ?PhpDocInfo - { - $objectHash = spl_object_hash($node); - if (isset($this->phpDocInfosByObjectHash[$objectHash])) { - return $this->phpDocInfosByObjectHash[$objectHash]; - } - - /** @see \Rector\BetterPhpDocParser\PhpDocParser\DoctrineAnnotationDecorator::decorate() */ - $this->currentNodeProvider->setNode($node); - - $docComment = $node->getDocComment(); - if (! $docComment instanceof Doc) { - if ($node->getComments() !== []) { - return null; - } - - // create empty node - $tokenIterator = new BetterTokenIterator([]); - $phpDocNode = new PhpDocNode([]); - } else { - $text = $docComment->getText(); - $tokens = $this->lexer->tokenize($text); - $tokenIterator = new BetterTokenIterator($tokens); - - $phpDocNode = $this->betterPhpDocParser->parse($tokenIterator); - $this->setPositionOfLastToken($phpDocNode); - } - - $phpDocInfo = $this->createFromPhpDocNode($phpDocNode, $tokenIterator, $node); - $this->phpDocInfosByObjectHash[$objectHash] = $phpDocInfo; - - return $phpDocInfo; - } - - public function createEmpty(Node $node): PhpDocInfo - { - /** @see \Rector\BetterPhpDocParser\PhpDocParser\DoctrineAnnotationDecorator::decorate() */ - $this->currentNodeProvider->setNode($node); - - $phpDocNode = new PhpDocNode([]); - $phpDocInfo = $this->createFromPhpDocNode($phpDocNode, new BetterTokenIterator([]), $node); - - // multiline by default - $phpDocInfo->makeMultiLined(); - - return $phpDocInfo; - } - - /** - * Needed for printing - */ - private function setPositionOfLastToken(PhpDocNode $phpDocNode): void - { - if ($phpDocNode->children === []) { - return; - } - - $phpDocChildNodes = $phpDocNode->children; - $lastChildNode = array_pop($phpDocChildNodes); - - $startAndEnd = $lastChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); - - if ($startAndEnd instanceof StartAndEnd) { - $phpDocNode->setAttribute(PhpDocAttributeKey::LAST_PHP_DOC_TOKEN_POSITION, $startAndEnd->getEnd()); - } - } - - private function createFromPhpDocNode( - PhpDocNode $phpDocNode, - BetterTokenIterator $betterTokenIterator, - Node $node - ): PhpDocInfo { - $this->phpDocNodeMapper->transform($phpDocNode, $betterTokenIterator); - - $phpDocInfo = new PhpDocInfo( - $phpDocNode, - $betterTokenIterator, - $this->staticTypeMapper, - $node, - $this->annotationNaming, - $this->currentNodeProvider, - $this->rectorChangeCollector, - $this->phpDocNodeByTypeFinder - ); - - $node->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); - - return $phpDocInfo; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocInfo/TokenIteratorFactory.php b/packages/BetterPhpDocParser/PhpDocInfo/TokenIteratorFactory.php deleted file mode 100644 index b3360d12f58..00000000000 --- a/packages/BetterPhpDocParser/PhpDocInfo/TokenIteratorFactory.php +++ /dev/null @@ -1,46 +0,0 @@ -lexer->tokenize($content); - return new BetterTokenIterator($tokens); - } - - public function createFromTokenIterator(TokenIterator $tokenIterator): BetterTokenIterator - { - if ($tokenIterator instanceof BetterTokenIterator) { - return $tokenIterator; - } - - $tokens = $this->privatesAccessor->getPrivateProperty($tokenIterator, 'tokens'); - $betterTokenIterator = new BetterTokenIterator($tokens); - - // keep original position - $currentIndex = $this->privatesAccessor->getPrivateProperty($tokenIterator, 'index'); - $this->privatesAccessor->setPrivateProperty($betterTokenIterator, self::INDEX, $currentIndex); - - return $betterTokenIterator; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php deleted file mode 100644 index 0af3d992228..00000000000 --- a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php +++ /dev/null @@ -1,150 +0,0 @@ -processAssertChoiceTagValueNode($oldToNewClasses, $phpDocInfo); - $this->processDoctrineRelationTagValueNode($node, $oldToNewClasses, $phpDocInfo); - $this->processSerializerTypeTagValueNode($oldToNewClasses, $phpDocInfo); - } - - /** - * @param array $oldToNewClasses - */ - private function processAssertChoiceTagValueNode(array $oldToNewClasses, PhpDocInfo $phpDocInfo): void - { - $assertChoiceTagValueNode = $phpDocInfo->findOneByAnnotationClass( - 'Symfony\Component\Validator\Constraints\Choice' - ); - if (! $assertChoiceTagValueNode instanceof DoctrineAnnotationTagValueNode) { - return; - } - - $callback = $assertChoiceTagValueNode->getValueWithoutQuotes('callback'); - if (! $callback instanceof CurlyListNode) { - return; - } - - $callbackClass = $callback->getValueWithoutQuotes(0); - - foreach ($oldToNewClasses as $oldClass => $newClass) { - if ($callbackClass !== $oldClass) { - continue; - } - - $callback->changeValue('0', $newClass); - - $assertChoiceTagValueNode->changeValue('callback', $callback); - break; - } - } - - /** - * @param array $oldToNewClasses - */ - private function processDoctrineRelationTagValueNode( - Node $node, - array $oldToNewClasses, - PhpDocInfo $phpDocInfo - ): void { - $doctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClasses([ - 'Doctrine\ORM\Mapping\OneToMany', - 'Doctrine\ORM\Mapping\ManyToMany', - 'Doctrine\ORM\Mapping\Embedded', - ]); - - if (! $doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { - return; - } - - $this->processDoctrineToMany($doctrineAnnotationTagValueNode, $node, $oldToNewClasses); - } - - /** - * @param array $oldToNewClasses - */ - private function processSerializerTypeTagValueNode(array $oldToNewClasses, PhpDocInfo $phpDocInfo): void - { - $doctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass('JMS\Serializer\Annotation\Type'); - if (! $doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { - return; - } - - foreach ($oldToNewClasses as $oldClass => $newClass) { - $className = $doctrineAnnotationTagValueNode->getSilentValue(); - - if ($className) { - if ($className === $oldClass) { - $doctrineAnnotationTagValueNode->changeSilentValue($newClass); - continue; - } - - $newContent = Strings::replace($className, '#\b' . preg_quote($oldClass, '#') . '\b#', $newClass); - if ($newContent === $className) { - continue; - } - - $doctrineAnnotationTagValueNode->changeSilentValue($newContent); - continue; - } - - $currentType = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('type'); - if ($currentType === $oldClass) { - $doctrineAnnotationTagValueNode->changeValue('type', $newClass); - continue; - } - } - } - - /** - * @param array $oldToNewClasses - */ - private function processDoctrineToMany( - DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, - Node $node, - array $oldToNewClasses - ): void { - $classKey = $doctrineAnnotationTagValueNode->hasClassName( - 'Doctrine\ORM\Mapping\Embedded' - ) ? 'class' : 'targetEntity'; - - $targetEntity = $doctrineAnnotationTagValueNode->getValueWithoutQuotes($classKey); - if ($targetEntity === null) { - return; - } - - // resolve to FQN - $tagFullyQualifiedName = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($targetEntity, $node); - - foreach ($oldToNewClasses as $oldClass => $newClass) { - if ($tagFullyQualifiedName !== $oldClass) { - continue; - } - - $doctrineAnnotationTagValueNode->changeValue($classKey, $newClass); - } - } -} diff --git a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTagRemover.php b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTagRemover.php deleted file mode 100644 index 8d4abb62be7..00000000000 --- a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTagRemover.php +++ /dev/null @@ -1,71 +0,0 @@ -getPhpDocNode(); - - foreach ($phpDocNode->children as $key => $phpDocChildNode) { - if (! $phpDocChildNode instanceof PhpDocTagNode) { - continue; - } - - if ($this->areAnnotationNamesEqual($name, $phpDocChildNode->name)) { - unset($phpDocNode->children[$key]); - $phpDocInfo->markAsChanged(); - } - - if ($phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) { - $doctrineAnnotationTagValueNode = $phpDocChildNode->value; - - if ($doctrineAnnotationTagValueNode->hasClassName($name)) { - unset($phpDocNode->children[$key]); - $phpDocInfo->markAsChanged(); - } - } - } - } - - public function removeTagValueFromNode(PhpDocInfo $phpDocInfo, Node $desiredNode): void - { - $phpDocNode = $phpDocInfo->getPhpDocNode(); - - $phpDocNodeTraverser = new PhpDocNodeTraverser(); - $phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', function ($node) use ( - $desiredNode, - $phpDocInfo - ): ?int { - if ($node instanceof PhpDocTagNode && $node->value === $desiredNode) { - $phpDocInfo->markAsChanged(); - return PhpDocNodeTraverser::NODE_REMOVE; - } - - if ($node !== $desiredNode) { - return null; - } - - $phpDocInfo->markAsChanged(); - - return PhpDocNodeTraverser::NODE_REMOVE; - }); - } - - private function areAnnotationNamesEqual(string $firstAnnotationName, string $secondAnnotationName): bool - { - $firstAnnotationName = trim($firstAnnotationName, '@'); - $secondAnnotationName = trim($secondAnnotationName, '@'); - - return $firstAnnotationName === $secondAnnotationName; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php deleted file mode 100644 index 4b256f8a614..00000000000 --- a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php +++ /dev/null @@ -1,148 +0,0 @@ -hasInvalidTag('@var')) { - return; - } - - // make sure the tags are not identical, e.g imported class vs FQN class - if ($this->typeComparator->areTypesEqual($phpDocInfo->getVarType(), $newType)) { - return; - } - - // prevent existing type override by mixed - if (! $phpDocInfo->getVarType() instanceof MixedType && $newType instanceof ConstantArrayType && $newType->getItemType() instanceof NeverType) { - return; - } - - // override existing type - $newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( - $newType, - TypeKind::PROPERTY() - ); - - $currentVarTagValueNode = $phpDocInfo->getVarTagValueNode(); - if ($currentVarTagValueNode !== null) { - // only change type - $currentVarTagValueNode->type = $newPHPStanPhpDocType; - } else { - // add completely new one - $varTagValueNode = new VarTagValueNode($newPHPStanPhpDocType, '', ''); - $phpDocInfo->addTagValueNode($varTagValueNode); - } - } - - public function changeReturnType(PhpDocInfo $phpDocInfo, Type $newType): void - { - // better not touch this, can crash - if ($phpDocInfo->hasInvalidTag('@return')) { - return; - } - - // make sure the tags are not identical, e.g imported class vs FQN class - if ($this->typeComparator->areTypesEqual($phpDocInfo->getReturnType(), $newType)) { - return; - } - - // override existing type - $newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( - $newType, - TypeKind::RETURN() - ); - - $currentReturnTagValueNode = $phpDocInfo->getReturnTagValue(); - - if ($currentReturnTagValueNode !== null) { - // only change type - $currentReturnTagValueNode->type = $newPHPStanPhpDocType; - } else { - // add completely new one - $returnTagValueNode = new ReturnTagValueNode($newPHPStanPhpDocType, ''); - $phpDocInfo->addTagValueNode($returnTagValueNode); - } - } - - public function changeParamType(PhpDocInfo $phpDocInfo, Type $newType, Param $param, string $paramName): void - { - // better skip, could crash hard - if ($phpDocInfo->hasInvalidTag('@param')) { - return; - } - - $phpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType, TypeKind::PARAM()); - $paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName); - - // override existing type - if ($paramTagValueNode !== null) { - // already set - $currentType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( - $paramTagValueNode->type, - $param - ); - - // avoid overriding better type - if ($this->typeComparator->isSubtype($currentType, $newType)) { - return; - } - - if ($this->typeComparator->areTypesEqual($currentType, $newType)) { - return; - } - - $paramTagValueNode->type = $phpDocType; - } else { - $paramTagValueNode = $this->paramPhpDocNodeFactory->create($phpDocType, $param); - $phpDocInfo->addTagValueNode($paramTagValueNode); - } - } - - public function copyPropertyDocToParam(Property $property, Param $param): void - { - $phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO); - if (! $phpDocInfo) { - return; - } - - $varTag = $phpDocInfo->getVarTagValueNode(); - if (! $varTag instanceof VarTagValueNode) { - return; - } - - if ($varTag->description !== '') { - return; - } - - $phpDocInfo->removeByType(VarTagValueNode::class); - $param->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); - } -} diff --git a/packages/BetterPhpDocParser/PhpDocManipulator/PropertyDocBlockManipulator.php b/packages/BetterPhpDocParser/PhpDocManipulator/PropertyDocBlockManipulator.php deleted file mode 100644 index b27681a63a9..00000000000 --- a/packages/BetterPhpDocParser/PhpDocManipulator/PropertyDocBlockManipulator.php +++ /dev/null @@ -1,36 +0,0 @@ -getFunctionLike(); - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - $paramTagValueNode = $phpDocInfo->getParamTagValueNodeByName($renameValueObject->getCurrentName()); - if (! $paramTagValueNode instanceof ParamTagValueNode) { - return; - } - - $paramTagValueNode->parameterName = '$' . $renameValueObject->getExpectedName(); - $paramTagValueNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); - } -} diff --git a/packages/BetterPhpDocParser/PhpDocManipulator/VarAnnotationManipulator.php b/packages/BetterPhpDocParser/PhpDocManipulator/VarAnnotationManipulator.php deleted file mode 100644 index a0996f004cf..00000000000 --- a/packages/BetterPhpDocParser/PhpDocManipulator/VarAnnotationManipulator.php +++ /dev/null @@ -1,68 +0,0 @@ -resolvePhpDocInfo($node); - - // already done - if ($phpDocInfo->getVarTagValueNode() !== null) { - return; - } - - $fullyQualifiedIdentifierTypeNode = new FullyQualifiedIdentifierTypeNode($typeWithClassName->getClassName()); - - $varTagValueNode = new VarTagValueNode($fullyQualifiedIdentifierTypeNode, '$' . $variableName, ''); - $phpDocInfo->addTagValueNode($varTagValueNode); - $phpDocInfo->makeSingleLined(); - } - - public function decorateNodeWithType(Node $node, Type $staticType): void - { - if ($staticType instanceof MixedType) { - return; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $staticType); - } - - private function resolvePhpDocInfo(Node $node): PhpDocInfo - { - $currentStmt = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - if ($currentStmt instanceof Expression) { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($currentStmt); - } else { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - } - - $phpDocInfo->makeSingleLined(); - - return $phpDocInfo; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocNodeMapper.php b/packages/BetterPhpDocParser/PhpDocNodeMapper.php deleted file mode 100644 index 538586e9160..00000000000 --- a/packages/BetterPhpDocParser/PhpDocNodeMapper.php +++ /dev/null @@ -1,50 +0,0 @@ -currentTokenIteratorProvider->setBetterTokenIterator($betterTokenIterator); - - $parentPhpDocNodeTraverser = new PhpDocNodeTraverser(); - $parentPhpDocNodeTraverser->addPhpDocNodeVisitor($this->parentConnectingPhpDocNodeVisitor); - $parentPhpDocNodeTraverser->traverse($phpDocNode); - - $cloningPhpDocNodeTraverser = new PhpDocNodeTraverser(); - $cloningPhpDocNodeTraverser->addPhpDocNodeVisitor($this->cloningPhpDocNodeVisitor); - $cloningPhpDocNodeTraverser->traverse($phpDocNode); - - $phpDocNodeTraverser = new PhpDocNodeTraverser(); - foreach ($this->phpDocNodeVisitors as $phpDocNodeVisitor) { - $phpDocNodeTraverser->addPhpDocNodeVisitor($phpDocNodeVisitor); - } - - $phpDocNodeTraverser->traverse($phpDocNode); - } -} diff --git a/packages/BetterPhpDocParser/PhpDocNodeTraverser/ChangedPhpDocNodeTraverserFactory.php b/packages/BetterPhpDocParser/PhpDocNodeTraverser/ChangedPhpDocNodeTraverserFactory.php deleted file mode 100644 index a8fa2c1ca11..00000000000 --- a/packages/BetterPhpDocParser/PhpDocNodeTraverser/ChangedPhpDocNodeTraverserFactory.php +++ /dev/null @@ -1,24 +0,0 @@ -addPhpDocNodeVisitor($this->changedPhpDocNodeVisitor); - - return $changedPhpDocNodeTraverser; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocNodeVisitor/ParamPhpDocNodeVisitor.php b/packages/BetterPhpDocParser/PhpDocNodeVisitor/ParamPhpDocNodeVisitor.php deleted file mode 100644 index 20ce7a4693b..00000000000 --- a/packages/BetterPhpDocParser/PhpDocNodeVisitor/ParamPhpDocNodeVisitor.php +++ /dev/null @@ -1,42 +0,0 @@ -type, - $node->isVariadic, - $node->parameterName, - $node->description - ); - - $this->attributeMirrorer->mirror($node, $variadicAwareParamTagValueNode); - - return $variadicAwareParamTagValueNode; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocNodeVisitor/TemplatePhpDocNodeVisitor.php b/packages/BetterPhpDocParser/PhpDocNodeVisitor/TemplatePhpDocNodeVisitor.php deleted file mode 100644 index d4f9b740bc2..00000000000 --- a/packages/BetterPhpDocParser/PhpDocNodeVisitor/TemplatePhpDocNodeVisitor.php +++ /dev/null @@ -1,76 +0,0 @@ -currentTokenIteratorProvider->provide(); - - $startAndEnd = $node->getAttribute(PhpDocAttributeKey::START_AND_END); - if (! $startAndEnd instanceof StartAndEnd) { - throw new ShouldNotHappenException(); - } - - $prepositions = $this->resolvePreposition($betterTokenIterator, $startAndEnd); - $spacingAwareTemplateTagValueNode = new SpacingAwareTemplateTagValueNode( - $node->name, - $node->bound, - $node->description, - $prepositions - ); - - $this->attributeMirrorer->mirror($node, $spacingAwareTemplateTagValueNode); - - return $spacingAwareTemplateTagValueNode; - } - - private function resolvePreposition(BetterTokenIterator $betterTokenIterator, StartAndEnd $startAndEnd): string - { - $partialTokens = $betterTokenIterator->partialTokens($startAndEnd->getStart(), $startAndEnd->getEnd()); - - foreach ($partialTokens as $partialToken) { - if ($partialToken[1] !== Lexer::TOKEN_IDENTIFIER) { - continue; - } - - if (! in_array($partialToken[0], ['as', 'of'], true)) { - continue; - } - - return $partialToken[0]; - } - - return 'of'; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocNodeVisitor/UnionTypeNodePhpDocNodeVisitor.php b/packages/BetterPhpDocParser/PhpDocNodeVisitor/UnionTypeNodePhpDocNodeVisitor.php deleted file mode 100644 index 3064b1b29a5..00000000000 --- a/packages/BetterPhpDocParser/PhpDocNodeVisitor/UnionTypeNodePhpDocNodeVisitor.php +++ /dev/null @@ -1,65 +0,0 @@ -getAttribute(PhpDocAttributeKey::START_AND_END); - if (! $startAndEnd instanceof StartAndEnd) { - throw new ShouldNotHappenException(); - } - - $betterTokenProvider = $this->currentTokenIteratorProvider->provide(); - - $isWrappedInCurlyBrackets = $this->isWrappedInCurlyBrackets($betterTokenProvider, $startAndEnd); - $bracketsAwareUnionTypeNode = new BracketsAwareUnionTypeNode($node->types, $isWrappedInCurlyBrackets); - - $this->attributeMirrorer->mirror($node, $bracketsAwareUnionTypeNode); - - return $bracketsAwareUnionTypeNode; - } - - private function isWrappedInCurlyBrackets(BetterTokenIterator $betterTokenProvider, StartAndEnd $startAndEnd): bool - { - $previousPosition = $startAndEnd->getStart() - 1; - - if ($betterTokenProvider->isTokenTypeOnPosition(Lexer::TOKEN_OPEN_PARENTHESES, $previousPosition)) { - return true; - } - - // there is no + 1, as end is right at the next token - return $betterTokenProvider->isTokenTypeOnPosition(Lexer::TOKEN_CLOSE_PARENTHESES, $startAndEnd->getEnd()); - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php b/packages/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php deleted file mode 100644 index f9b9d7c8428..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php +++ /dev/null @@ -1,134 +0,0 @@ -privatesCaller = new PrivatesCaller(); - } - - public function parse(TokenIterator $tokenIterator): PhpDocNode - { - $tokenIterator->consumeTokenType(Lexer::TOKEN_OPEN_PHPDOC); - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - - $children = []; - if (! $tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { - $children[] = $this->parseChildAndStoreItsPositions($tokenIterator); - - while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && ! $tokenIterator->isCurrentTokenType( - Lexer::TOKEN_CLOSE_PHPDOC - )) { - $children[] = $this->parseChildAndStoreItsPositions($tokenIterator); - } - } - - // might be in the middle of annotations - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC); - - $phpDocNode = new PhpDocNode($children); - // replace generic nodes with DoctrineAnnotations - $this->doctrineAnnotationDecorator->decorate($phpDocNode); - - return $phpDocNode; - } - - /** - * @param BetterTokenIterator $tokenIterator - */ - public function parseTag(TokenIterator $tokenIterator): PhpDocTagNode - { - $tag = $this->resolveTag($tokenIterator); - - $phpDocTagValueNode = $this->parseTagValue($tokenIterator, $tag); - return new PhpDocTagNode($tag, $phpDocTagValueNode); - } - - /** - * @param BetterTokenIterator $tokenIterator - */ - public function parseTagValue(TokenIterator $tokenIterator, string $tag): PhpDocTagValueNode - { - $startPosition = $tokenIterator->currentPosition(); - $tagValueNode = parent::parseTagValue($tokenIterator, $tag); - $endPosition = $tokenIterator->currentPosition(); - - $startAndEnd = new StartAndEnd($startPosition, $endPosition); - $tagValueNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); - - return $tagValueNode; - } - - /** - * @return PhpDocTextNode|PhpDocTagNode - */ - private function parseChildAndStoreItsPositions(TokenIterator $tokenIterator): PhpDocChildNode - { - $betterTokenIterator = $this->tokenIteratorFactory->createFromTokenIterator($tokenIterator); - - $startPosition = $betterTokenIterator->currentPosition(); - - /** @var PhpDocChildNode $phpDocNode */ - $phpDocNode = $this->privatesCaller->callPrivateMethod($this, 'parseChild', [$betterTokenIterator]); - $endPosition = $betterTokenIterator->currentPosition(); - - $startAndEnd = new StartAndEnd($startPosition, $endPosition); - $phpDocNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); - - return $phpDocNode; - } - - private function resolveTag(BetterTokenIterator $tokenIterator): string - { - $tag = $tokenIterator->currentTokenValue(); - $tokenIterator->next(); - - // there is a space → stop - if ($tokenIterator->isPrecededByHorizontalWhitespace()) { - return $tag; - } - - // is not e.g "@var " - // join tags like "@ORM\Column" etc. - if (! $tokenIterator->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { - return $tag; - } - - // @todo use joinUntil("(")? - $tag .= $tokenIterator->currentTokenValue(); - $tokenIterator->next(); - - return $tag; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/BetterTypeParser.php b/packages/BetterPhpDocParser/PhpDocParser/BetterTypeParser.php deleted file mode 100644 index 2f45c0039b8..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/BetterTypeParser.php +++ /dev/null @@ -1,37 +0,0 @@ -tokenIteratorFactory->createFromTokenIterator($tokenIterator); - - $startPosition = $betterTokenIterator->currentPosition(); - $typeNode = parent::parse($betterTokenIterator); - $endPosition = $betterTokenIterator->currentPosition(); - - $startAndEnd = new StartAndEnd($startPosition, $endPosition); - $typeNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); - - return $typeNode; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher.php b/packages/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher.php deleted file mode 100644 index c9f2b9a57b3..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ - private array $fullyQualifiedNameByHash = []; - - public function __construct( - private UseImportNameMatcher $useImportNameMatcher, - private ReflectionProvider $reflectionProvider - ) { - } - - public function resolveTagFullyQualifiedName(string $tag, Node $node): string - { - $uniqueHash = $tag . spl_object_hash($node); - if (isset($this->fullyQualifiedNameByHash[$uniqueHash])) { - return $this->fullyQualifiedNameByHash[$uniqueHash]; - } - - $tag = ltrim($tag, '@'); - - /** @var Use_[] $uses */ - $uses = (array) $node->getAttribute(AttributeKey::USE_NODES); - $fullyQualifiedClass = $this->resolveFullyQualifiedClass($uses, $node, $tag); - - $this->fullyQualifiedNameByHash[$uniqueHash] = $fullyQualifiedClass; - - return $fullyQualifiedClass; - } - - /** - * @param Use_[] $uses - */ - private function resolveFullyQualifiedClass(array $uses, Node $node, string $tag): string - { - $scope = $node->getAttribute(AttributeKey::SCOPE); - if ($scope instanceof Scope) { - $namespace = $scope->getNamespace(); - if ($namespace !== null) { - $namespacedTag = $namespace . '\\' . $tag; - if ($this->reflectionProvider->hasClass($namespacedTag)) { - return $namespacedTag; - } - } - } - - return $this->useImportNameMatcher->matchNameWithUses($tag, $uses) ?? $tag; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/DoctrineAnnotationDecorator.php b/packages/BetterPhpDocParser/PhpDocParser/DoctrineAnnotationDecorator.php deleted file mode 100644 index 2d7d18282e9..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/DoctrineAnnotationDecorator.php +++ /dev/null @@ -1,216 +0,0 @@ -currentNodeProvider->getNode(); - if (! $currentPhpNode instanceof Node) { - throw new ShouldNotHappenException(); - } - - // merge split doctrine nested tags - $this->mergeNestedDoctrineAnnotations($phpDocNode); - - $this->transformGenericTagValueNodesToDoctrineAnnotationTagValueNodes($phpDocNode, $currentPhpNode); - } - - /** - * Join token iterator with all the following nodes if nested - */ - private function mergeNestedDoctrineAnnotations(PhpDocNode $phpDocNode): void - { - $removedKeys = []; - - foreach ($phpDocNode->children as $key => $phpDocChildNode) { - if (in_array($key, $removedKeys, true)) { - continue; - } - - if (! $phpDocChildNode instanceof PhpDocTagNode) { - continue; - } - - if (! $phpDocChildNode->value instanceof GenericTagValueNode) { - continue; - } - - $genericTagValueNode = $phpDocChildNode->value; - - while (isset($phpDocNode->children[$key])) { - ++$key; - - // no more next nodes - if (! isset($phpDocNode->children[$key])) { - break; - } - - $nextPhpDocChildNode = $phpDocNode->children[$key]; - if (! $nextPhpDocChildNode instanceof PhpDocTagNode) { - continue; - } - - if (! $nextPhpDocChildNode->value instanceof GenericTagValueNode) { - continue; - } - - if ($this->isClosedContent($genericTagValueNode->value)) { - break; - } - - $composedContent = $genericTagValueNode->value . PHP_EOL . $nextPhpDocChildNode->name . $nextPhpDocChildNode->value; - $genericTagValueNode->value = $composedContent; - - /** @var StartAndEnd $currentStartAndEnd */ - $currentStartAndEnd = $phpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); - - /** @var StartAndEnd $nextStartAndEnd */ - $nextStartAndEnd = $nextPhpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); - - $startAndEnd = new StartAndEnd($currentStartAndEnd->getStart(), $nextStartAndEnd->getEnd()); - $phpDocChildNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); - - $currentChildValueNode = $phpDocNode->children[$key]; - if (! $currentChildValueNode instanceof PhpDocTagNode) { - continue; - } - - $currentGenericTagValueNode = $currentChildValueNode->value; - if (! $currentGenericTagValueNode instanceof GenericTagValueNode) { - continue; - } - - $removedKeys[] = $key; - } - } - - foreach (array_keys($phpDocNode->children) as $key) { - if (! in_array($key, $removedKeys, true)) { - continue; - } - - unset($phpDocNode->children[$key]); - } - } - - private function transformGenericTagValueNodesToDoctrineAnnotationTagValueNodes( - PhpDocNode $phpDocNode, - Node $currentPhpNode - ): void { - foreach ($phpDocNode->children as $key => $phpDocChildNode) { - if (! $phpDocChildNode instanceof PhpDocTagNode) { - continue; - } - - if (! $phpDocChildNode->value instanceof GenericTagValueNode) { - continue; - } - - // known doc tag to annotation class - $fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName( - $phpDocChildNode->name, - $currentPhpNode - ); - - // not an annotations class - if (! \str_contains($fullyQualifiedAnnotationClass, '\\')) { - continue; - } - - $genericTagValueNode = $phpDocChildNode->value; - $nestedTokenIterator = $this->tokenIteratorFactory->create($genericTagValueNode->value); - - // mimics doctrine behavior just in phpdoc-parser syntax :) - // https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L742 - $values = $this->staticDoctrineAnnotationParser->resolveAnnotationMethodCall($nestedTokenIterator); - - $formerStartEnd = $genericTagValueNode->getAttribute(PhpDocAttributeKey::START_AND_END); - - $identifierTypeNode = new IdentifierTypeNode($phpDocChildNode->name); - $identifierTypeNode->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $fullyQualifiedAnnotationClass); - - $doctrineAnnotationTagValueNode = new DoctrineAnnotationTagValueNode( - $identifierTypeNode, - $genericTagValueNode->value, - $values, - SilentKeyMap::CLASS_NAMES_TO_SILENT_KEYS[$fullyQualifiedAnnotationClass] ?? null - ); - - $doctrineAnnotationTagValueNode->setAttribute(PhpDocAttributeKey::START_AND_END, $formerStartEnd); - - $spacelessPhpDocTagNode = new SpacelessPhpDocTagNode( - $phpDocChildNode->name, - $doctrineAnnotationTagValueNode - ); - - $this->attributeMirrorer->mirror($phpDocChildNode, $spacelessPhpDocTagNode); - $phpDocNode->children[$key] = $spacelessPhpDocTagNode; - } - } - - /** - * This is closed block, e.g. {( ... )}, - * false on: {( ... ) - */ - private function isClosedContent(string $composedContent): bool - { - $composedTokenIterator = $this->tokenIteratorFactory->create($composedContent); - $tokenCount = $composedTokenIterator->count(); - - $openBracketCount = 0; - $closeBracketCount = 0; - if ($composedContent === '') { - return true; - } - - do { - if ($composedTokenIterator->isCurrentTokenTypes([ - Lexer::TOKEN_OPEN_CURLY_BRACKET, - Lexer::TOKEN_OPEN_PARENTHESES, - ]) || \str_contains($composedTokenIterator->currentTokenValue(), '(')) { - ++$openBracketCount; - } - - if ($composedTokenIterator->isCurrentTokenTypes([ - Lexer::TOKEN_CLOSE_CURLY_BRACKET, - Lexer::TOKEN_CLOSE_PARENTHESES, - // sometimes it gets mixed int ") - ]) || \str_contains($composedTokenIterator->currentTokenValue(), ')')) { - ++$closeBracketCount; - } - - $composedTokenIterator->next(); - } while ($composedTokenIterator->currentPosition() < ($tokenCount - 1)); - - return $openBracketCount === $closeBracketCount; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/PhpDocFromTypeDeclarationDecorator.php b/packages/BetterPhpDocParser/PhpDocParser/PhpDocFromTypeDeclarationDecorator.php deleted file mode 100644 index 4dcfc82add6..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/PhpDocFromTypeDeclarationDecorator.php +++ /dev/null @@ -1,132 +0,0 @@ -returnType === null) { - return; - } - - $type = $this->staticTypeMapper->mapPhpParserNodePHPStanType($functionLike->returnType); - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - $this->phpDocTypeChanger->changeReturnType($phpDocInfo, $type); - - $functionLike->returnType = null; - } - - /** - * @param array> $requiredTypes - */ - public function decorateParam( - Param $param, - ClassMethod | Function_ | Closure $functionLike, - array $requiredTypes - ): void { - if ($param->type === null) { - return; - } - - $type = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - - if (! $this->isMatchingType($type, $requiredTypes)) { - return; - } - - $this->moveParamTypeToParamDoc($functionLike, $param, $type); - } - - public function decorateParamWithSpecificType( - Param $param, - ClassMethod | Function_ | Closure $functionLike, - Type $requireType - ): void { - if ($param->type === null) { - return; - } - - if (! $this->isTypeMatch($param->type, $requireType)) { - return; - } - - $type = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - $this->moveParamTypeToParamDoc($functionLike, $param, $type); - } - - /** - * @return bool True if node was changed - */ - public function decorateReturnWithSpecificType( - ClassMethod | Function_ | Closure $functionLike, - Type $requireType - ): bool { - if ($functionLike->returnType === null) { - return false; - } - - if (! $this->isTypeMatch($functionLike->returnType, $requireType)) { - return false; - } - - $this->decorate($functionLike); - return true; - } - - private function isTypeMatch(Node $typeNode, Type $requireType): bool - { - $returnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($typeNode); - - // cover nullable union types - if ($returnType instanceof UnionType) { - $returnType = $this->typeUnwrapper->unwrapNullableType($returnType); - } - - return $returnType::class === $requireType::class; - } - - private function moveParamTypeToParamDoc( - ClassMethod | Function_ | Closure $functionLike, - Param $param, - Type $type - ): void { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - $paramName = $this->nodeNameResolver->getName($param); - $this->phpDocTypeChanger->changeParamType($phpDocInfo, $type, $param, $paramName); - - $param->type = null; - } - - /** - * @param array> $requiredTypes - */ - private function isMatchingType(Type $type, array $requiredTypes): bool - { - return in_array($type::class, $requiredTypes, true); - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php b/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php deleted file mode 100644 index d5639ed66e2..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php +++ /dev/null @@ -1,125 +0,0 @@ - - */ - public function resolveAnnotationMethodCall(BetterTokenIterator $tokenIterator): array - { - if (! $tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - return []; - } - - $tokenIterator->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); - - // empty () - if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { - return []; - } - - return $this->resolveAnnotationValues($tokenIterator); - } - - /** - * @see https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1215-L1224 - * @return array - */ - public function resolveAnnotationValue( - BetterTokenIterator $tokenIterator - ): CurlyListNode | string | array | ConstExprNode | DoctrineAnnotationTagValueNode { - // skips dummy tokens like newlines - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - - // no assign - if (! $tokenIterator->isNextTokenType(Lexer::TOKEN_EQUAL)) { - // 1. plain value - mimics https://github.com/doctrine/annotations/blob/0cb0cd2950a5c6cdbf22adbe2bfd5fd1ea68588f/lib/Doctrine/Common/Annotations/DocParser.php#L1234-L1282 - return $this->parseValue($tokenIterator); - } - - // 2. assign key = value - mimics FieldAssignment() https://github.com/doctrine/annotations/blob/0cb0cd2950a5c6cdbf22adbe2bfd5fd1ea68588f/lib/Doctrine/Common/Annotations/DocParser.php#L1291-L1303 - /** @var int $key */ - $key = $this->parseValue($tokenIterator); - $tokenIterator->consumeTokenType(Lexer::TOKEN_EQUAL); - - // mimics https://github.com/doctrine/annotations/blob/1.13.x/lib/Doctrine/Common/Annotations/DocParser.php#L1236-L1238 - $value = $this->parseValue($tokenIterator); - - return [ - // plain token value - $key => $value, - ]; - } - - /** - * @see https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1051-L1079 - * @return array - */ - private function resolveAnnotationValues(BetterTokenIterator $tokenIterator): array - { - $values = []; - $resolvedValue = $this->resolveAnnotationValue($tokenIterator); - - if (is_array($resolvedValue)) { - $values = array_merge($values, $resolvedValue); - } else { - $values[] = $resolvedValue; - } - - while ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_COMMA)) { - $tokenIterator->next(); - - // if is next item just closing brackets - if ($tokenIterator->isNextTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { - continue; - } - - $nestedValues = $this->resolveAnnotationValue($tokenIterator); - - if (is_array($nestedValues)) { - $values = array_merge($values, $nestedValues); - } else { - $values[] = $nestedValues; - } - } - - return $values; - } - - /** - * @return array - */ - private function parseValue( - BetterTokenIterator $tokenIterator - ): CurlyListNode | string | array | ConstExprNode | DoctrineAnnotationTagValueNode { - if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET)) { - $items = $this->arrayParser->parseCurlyArray($tokenIterator); - return new CurlyListNode($items); - } - - return $this->plainValueParser->parseValue($tokenIterator); - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParser.php b/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParser.php deleted file mode 100644 index 86e01a7e9e9..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParser.php +++ /dev/null @@ -1,122 +0,0 @@ -isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { - return []; - } - - $tokenIterator->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); - - // If the array is empty, stop parsing and return. - if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { - $tokenIterator->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); - return []; - } - - // first item - $values[] = $this->resolveArrayItem($tokenIterator); - - // 2nd+ item - while ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_COMMA)) { - // optional trailing comma - $tokenIterator->consumeTokenType(Lexer::TOKEN_COMMA); - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - - if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { - break; - } - - $values[] = $this->resolveArrayItem($tokenIterator); - if ($tokenIterator->isNextTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { - break; - } - - // skip newlines - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - } - - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - - // special case for nested doctrine annotations - if (! $tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); - } - - return $this->createArrayFromValues($values); - } - - /** - * Mimics https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1354-L1385 - * @return array - */ - private function resolveArrayItem(BetterTokenIterator $tokenIterator): array - { - // skip newlines - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - - if ($tokenIterator->isNextTokenTypes([Lexer::TOKEN_EQUAL, Lexer::TOKEN_COLON])) { - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_EQUAL); - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_COLON); - - if ($tokenIterator->isNextTokenType(Lexer::TOKEN_IDENTIFIER)) { - $key = $this->plainValueParser->parseValue($tokenIterator); - } else { - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_COMMA); - $key = $this->plainValueParser->parseValue($tokenIterator); - } - - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_EQUAL); - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_COLON); - - return [$key, $this->plainValueParser->parseValue($tokenIterator)]; - } - - return [null, $this->plainValueParser->parseValue($tokenIterator)]; - } - - /** - * @param mixed[] $values - * @return mixed[] - */ - private function createArrayFromValues(array $values): array - { - $array = []; - - foreach ($values as $value) { - [$key, $val] = $value; - - if ($key !== null) { - $array[$key] = $val; - } else { - $array[] = $val; - } - } - - return $array; - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/PlainValueParser.php b/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/PlainValueParser.php deleted file mode 100644 index f636366e982..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/PlainValueParser.php +++ /dev/null @@ -1,146 +0,0 @@ -staticDoctrineAnnotationParser = $staticDoctrineAnnotationParser; - $this->arrayParser = $arrayParser; - } - - /** - * @return mixed[] - */ - public function parseValue( - BetterTokenIterator $tokenIterator - ): string | array | ConstExprNode | DoctrineAnnotationTagValueNode { - $currentTokenValue = $tokenIterator->currentTokenValue(); - - // temporary hackaround multi-line doctrine annotations - if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_END)) { - return $currentTokenValue; - } - - // consume the token - $isOpenCurlyArray = $tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); - if ($isOpenCurlyArray) { - return $this->arrayParser->parseCurlyArray($tokenIterator); - } - - $tokenIterator->next(); - - // normalize value - $constantValue = $this->matchConstantValue($currentTokenValue); - if ($constantValue !== null) { - return $constantValue; - } - - while ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON) || - $tokenIterator->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER) - ) { - $currentTokenValue .= $tokenIterator->currentTokenValue(); - $tokenIterator->next(); - } - - // nested entity! - if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - return $this->parseNestedDoctrineAnnotationTagValueNode($currentTokenValue, $tokenIterator); - } - - $start = $tokenIterator->currentPosition(); - - // from "quote to quote" - if ($currentTokenValue === '"') { - do { - $tokenIterator->next(); - } while (! str_contains($tokenIterator->currentTokenValue(), '"')); - } - - $end = $tokenIterator->currentPosition(); - if ($start + 1 < $end) { - return $tokenIterator->printFromTo($start, $end); - } - - return $currentTokenValue; - } - - private function parseNestedDoctrineAnnotationTagValueNode( - string $currentTokenValue, - BetterTokenIterator $tokenIterator - ): DoctrineAnnotationTagValueNode { - // @todo - $annotationShortName = $currentTokenValue; - $values = $this->staticDoctrineAnnotationParser->resolveAnnotationMethodCall($tokenIterator); - - $currentNode = $this->currentNodeProvider->getNode(); - if (! $currentNode instanceof Node) { - throw new ShouldNotHappenException(); - } - - $fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName( - $annotationShortName, - $currentNode - ); - - // keep the last ")" - $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokenIterator->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); - - // keep original name to differentiate between short and FQN class - $identifierTypeNode = new IdentifierTypeNode($annotationShortName); - $identifierTypeNode->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $fullyQualifiedAnnotationClass); - - return new DoctrineAnnotationTagValueNode($identifierTypeNode, $annotationShortName, $values); - } - - private function matchConstantValue(string $currentTokenValue): ConstExprNode | null - { - if (strtolower($currentTokenValue) === 'false') { - return new ConstExprFalseNode(); - } - - if (strtolower($currentTokenValue) === 'true') { - return new ConstExprTrueNode(); - } - if (! is_numeric($currentTokenValue)) { - return null; - } - if ((string) (int) $currentTokenValue !== $currentTokenValue) { - return null; - } - return new ConstExprIntegerNode($currentTokenValue); - } -} diff --git a/packages/BetterPhpDocParser/PhpDocParser/TypeNodeAnalyzer.php b/packages/BetterPhpDocParser/PhpDocParser/TypeNodeAnalyzer.php deleted file mode 100644 index 513b305ad3e..00000000000 --- a/packages/BetterPhpDocParser/PhpDocParser/TypeNodeAnalyzer.php +++ /dev/null @@ -1,30 +0,0 @@ -types as $subType) { - if ($subType instanceof NullableTypeNode) { - return false; - } - } - - return true; - } -} diff --git a/packages/BetterPhpDocParser/Printer/DocBlockInliner.php b/packages/BetterPhpDocParser/Printer/DocBlockInliner.php deleted file mode 100644 index bc5e82ecc18..00000000000 --- a/packages/BetterPhpDocParser/Printer/DocBlockInliner.php +++ /dev/null @@ -1,28 +0,0 @@ -changedPhpDocNodeTraverser = $changedPhpDocNodeTraverserFactory->create(); - } - - public function printNew(PhpDocInfo $phpDocInfo): string - { - $docContent = (string) $phpDocInfo->getPhpDocNode(); - if ($phpDocInfo->isSingleLine()) { - return $this->docBlockInliner->inline($docContent); - } - - if ($phpDocInfo->getNode() instanceof InlineHTML) { - return ''; - } - - return $docContent; - } - - /** - * As in php-parser - * - * ref: https://github.com/nikic/PHP-Parser/issues/487#issuecomment-375986259 - * - Tokens[node.startPos .. subnode1.startPos] - * - Print(subnode1) - * - Tokens[subnode1.endPos .. subnode2.startPos] - * - Print(subnode2) - * - Tokens[subnode2.endPos .. node.endPos] - */ - public function printFormatPreserving(PhpDocInfo $phpDocInfo): string - { - if ($phpDocInfo->getTokens() === []) { - // completely new one, just print string version of it - if ($phpDocInfo->getPhpDocNode()->children === []) { - return ''; - } - - if ($phpDocInfo->getNode() instanceof InlineHTML) { - return 'getPhpDocNode() . PHP_EOL . '?>'; - } - - return (string) $phpDocInfo->getPhpDocNode(); - } - - $phpDocNode = $phpDocInfo->getPhpDocNode(); - $this->tokens = $phpDocInfo->getTokens(); - - $this->tokenCount = $phpDocInfo->getTokenCount(); - $this->phpDocInfo = $phpDocInfo; - - $this->currentTokenPosition = 0; - - $phpDocString = $this->printPhpDocNode($phpDocNode); - - // hotfix of extra space with callable () - return Strings::replace($phpDocString, self::CALLABLE_REGEX, 'callable('); - } - - public function getCurrentPhpDocInfo(): PhpDocInfo - { - if ($this->phpDocInfo === null) { - throw new ShouldNotHappenException(); - } - - return $this->phpDocInfo; - } - - private function printPhpDocNode(PhpDocNode $phpDocNode): string - { - // no nodes were, so empty doc - if ($this->emptyPhpDocDetector->isPhpDocNodeEmpty($phpDocNode)) { - return ''; - } - - $output = ''; - - // node output - $nodeCount = count($phpDocNode->children); - - foreach ($phpDocNode->children as $key => $phpDocChildNode) { - $output .= $this->printDocChildNode($phpDocChildNode, $key + 1, $nodeCount); - } - - $output = $this->printEnd($output); - - // fix missing start - if (! Strings::match($output, self::DOCBLOCK_START_REGEX) && $output) { - $output = '/**' . $output; - } - - // fix missing end - if (Strings::match($output, self::OPENING_DOCBLOCK_REGEX) && $output && ! Strings::match( - $output, - self::CLOSING_DOCBLOCK_REGEX - )) { - $output .= ' */'; - } - - return $output; - } - - private function printDocChildNode( - PhpDocChildNode $phpDocChildNode, - int $key = 0, - int $nodeCount = 0 - ): string { - $output = ''; - - $shouldReprintChildNode = $this->shouldReprint($phpDocChildNode); - - if ($phpDocChildNode instanceof PhpDocTagNode) { - if ($shouldReprintChildNode && ($phpDocChildNode->value instanceof ParamTagValueNode || $phpDocChildNode->value instanceof ThrowsTagValueNode || $phpDocChildNode->value instanceof VarTagValueNode || $phpDocChildNode->value instanceof ReturnTagValueNode || $phpDocChildNode->value instanceof PropertyTagValueNode)) { - // the type has changed → reprint - $phpDocChildNodeStartEnd = $phpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); - // bump the last position of token after just printed node - if ($phpDocChildNodeStartEnd instanceof StartAndEnd) { - $this->currentTokenPosition = $phpDocChildNodeStartEnd->getEnd(); - } - - return $this->standardPrintPhpDocChildNode($phpDocChildNode); - } - - if ($phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode && $shouldReprintChildNode) { - $printedNode = (string) $phpDocChildNode; - - // remove extra space between tags - $printedNode = Strings::replace($printedNode, self::TAG_AND_SPACE_REGEX, '$1('); - return self::NEWLINE_WITH_ASTERISK . $printedNode; - } - } - - /** @var StartAndEnd|null $startAndEnd */ - $startAndEnd = $phpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); - - if ($startAndEnd instanceof StartAndEnd && ! $shouldReprintChildNode) { - $isLastToken = $nodeCount === $key; - - // correct previously changed node - $this->correctPreviouslyReprintedFirstNode($key, $startAndEnd); - - $output = $this->addTokensFromTo( - $output, - $this->currentTokenPosition, - $startAndEnd->getEnd(), - $isLastToken - ); - - $this->currentTokenPosition = $startAndEnd->getEnd(); - - return rtrim($output); - } - - if ($startAndEnd instanceof StartAndEnd) { - $this->currentTokenPosition = $startAndEnd->getEnd(); - } - - $standardPrintedPhpDocChildNode = $this->standardPrintPhpDocChildNode($phpDocChildNode); - return $output . $standardPrintedPhpDocChildNode; - } - - private function printEnd(string $output): string - { - $lastTokenPosition = $this->getCurrentPhpDocInfo() - ->getPhpDocNode() - ->getAttribute(PhpDocAttributeKey::LAST_PHP_DOC_TOKEN_POSITION) ?: $this->currentTokenPosition; - if ($lastTokenPosition === 0) { - $lastTokenPosition = 1; - } - - return $this->addTokensFromTo($output, $lastTokenPosition, $this->tokenCount, true); - } - - private function addTokensFromTo(string $output, int $from, int $to, bool $shouldSkipEmptyLinesAbove): string - { - // skip removed nodes - $positionJumpSet = []; - - $removedStartAndEnds = $this->removeNodesStartAndEndResolver->resolve( - $this->getCurrentPhpDocInfo() - ->getOriginalPhpDocNode(), - $this->getCurrentPhpDocInfo() - ->getPhpDocNode(), - $this->tokens - ); - - foreach ($removedStartAndEnds as $removedStartAndEnd) { - $positionJumpSet[$removedStartAndEnd->getStart()] = $removedStartAndEnd->getEnd(); - } - - // include also space before, in case of inlined docs - if (isset($this->tokens[$from - 1]) && $this->tokens[$from - 1][1] === Lexer::TOKEN_HORIZONTAL_WS) { - --$from; - } - - // skip extra empty lines above if this is the last one - if ($shouldSkipEmptyLinesAbove && - \str_contains($this->tokens[$from][0], PHP_EOL) && - \str_contains($this->tokens[$from + 1][0], PHP_EOL) - ) { - ++$from; - } - - return $this->appendToOutput($output, $from, $to, $positionJumpSet); - } - - /** - * @param array $positionJumpSet - */ - private function appendToOutput(string $output, int $from, int $to, array $positionJumpSet): string - { - for ($i = $from; $i < $to; ++$i) { - while (isset($positionJumpSet[$i])) { - $i = $positionJumpSet[$i]; - continue; - } - - $output .= $this->tokens[$i][0] ?? ''; - } - - return $output; - } - - private function correctPreviouslyReprintedFirstNode(int $key, StartAndEnd $startAndEnd): void - { - if ($this->currentTokenPosition !== 0) { - return; - } - - if ($key === 1) { - return; - } - - $startTokenPosition = $startAndEnd->getStart(); - - $tokens = $this->getCurrentPhpDocInfo() - ->getTokens(); - if (! isset($tokens[$startTokenPosition - 1])) { - return; - } - - $previousToken = $tokens[$startTokenPosition - 1]; - if ($previousToken[1] === Lexer::TOKEN_PHPDOC_EOL) { - --$startTokenPosition; - } - - $this->currentTokenPosition = $startTokenPosition; - } - - private function shouldReprint(PhpDocChildNode $phpDocChildNode): bool - { - $this->changedPhpDocNodeTraverser->traverse($phpDocChildNode); - return $this->changedPhpDocNodeVisitor->hasChanged(); - } - - private function standardPrintPhpDocChildNode(PhpDocChildNode $phpDocChildNode): string - { - if ($this->getCurrentPhpDocInfo()->isSingleLine()) { - return ' ' . $phpDocChildNode; - } - - return self::NEWLINE_WITH_ASTERISK . $phpDocChildNode; - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/AroundSpaces.php b/packages/BetterPhpDocParser/ValueObject/AroundSpaces.php deleted file mode 100644 index a2fe99460aa..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/AroundSpaces.php +++ /dev/null @@ -1,24 +0,0 @@ -closingSpace; - } - - public function getOpeningSpace(): string - { - return $this->openingSpace; - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php b/packages/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php deleted file mode 100644 index 2f0d5db6a15..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php +++ /dev/null @@ -1,185 +0,0 @@ - $tokens - */ - public function __construct(array $tokens, int $index = 0) - { - $this->privatesAccessor = new PrivatesAccessor(); - - if ($tokens === []) { - $this->privatesAccessor->setPrivateProperty($this, self::TOKENS, []); - $this->privatesAccessor->setPrivateProperty($this, self::INDEX, 0); - } else { - parent::__construct($tokens, $index); - } - } - - /** - * @param int[] $types - */ - public function isNextTokenTypes(array $types): bool - { - foreach ($types as $type) { - if ($this->isNextTokenType($type)) { - return true; - } - } - - return false; - } - - /** - * @param int[] $tokenTypes - */ - public function isCurrentTokenTypes(array $tokenTypes): bool - { - foreach ($tokenTypes as $tokenType) { - if ($this->isCurrentTokenType($tokenType)) { - return true; - } - } - - return false; - } - - public function isTokenTypeOnPosition(int $tokenType, int $position): bool - { - $tokens = $this->getTokens(); - $token = $tokens[$position] ?? null; - - if ($token === null) { - return false; - } - - return $token[1] === $tokenType; - } - - public function isNextTokenType(int $tokenType): bool - { - if ($this->nextTokenType() === null) { - return false; - } - - return $this->nextTokenType() === $tokenType; - } - - public function printFromTo(int $from, int $to): string - { - if ($to < $from) { - throw new ShouldNotHappenException('Arguments are flipped'); - } - - $tokens = $this->getTokens(); - - $content = ''; - - foreach ($tokens as $key => $token) { - if ($key < $from) { - continue; - } - - if ($key >= $to) { - continue; - } - - $content .= $token[0]; - } - - return $content; - } - - public function print(): string - { - $content = ''; - foreach ($this->getTokens() as $token) { - $content .= $token[0]; - } - - return $content; - } - - public function nextTokenType(): ?int - { - $tokens = $this->getTokens(); - - // does next token exist? - $nextIndex = $this->currentPosition() + 1; - if (! isset($tokens[$nextIndex])) { - return null; - } - - $this->pushSavePoint(); - $this->next(); - $nextTokenType = $this->currentTokenType(); - $this->rollback(); - - return $nextTokenType; - } - - public function currentPosition(): int - { - return $this->privatesAccessor->getPrivateProperty($this, self::INDEX); - } - - /** - * @return mixed[] - */ - public function getTokens(): array - { - return $this->privatesAccessor->getPrivateProperty($this, self::TOKENS); - } - - public function count(): int - { - return count($this->getTokens()); - } - - /** - * @return mixed[] - */ - public function partialTokens(int $start, int $end): array - { - $tokens = $this->getTokens(); - - $chunkTokens = []; - for ($i = $start; $i <= $end; ++$i) { - $chunkTokens[$i] = $tokens[$i]; - } - - return $chunkTokens; - } - - public function containsTokenType(int $type): bool - { - foreach ($this->getTokens() as $token) { - if ($token[1] === $type) { - return true; - } - } - - return false; - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php b/packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php deleted file mode 100644 index 01ca76e7cc9..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php +++ /dev/null @@ -1,264 +0,0 @@ -.*?)("|\')$#'; - - protected bool $hasChanged = false; - - /** - * @var mixed[] - */ - private array $originalValues = []; - - /** - * @param mixed[] $values Must be public so node traverser can go through them - */ - public function __construct( - public array $values = [], - protected ?string $originalContent = null, - protected ?string $silentKey = null - ) { - $this->originalValues = $values; - } - - public function removeValue(string $key): void - { - $quotedKey = '"' . $key . '"'; - - // isset? - if (! isset($this->values[$key]) && ! isset($this->values[$quotedKey])) { - return; - } - - unset($this->values[$key]); - unset($this->values[$quotedKey]); - - // invoke reprint - $this->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); - } - - /** - * @return mixed[] - */ - public function getValues(): array - { - return $this->values; - } - - /** - * @return mixed|Node|null - */ - public function getValue(string | int $key) - { - // to allow false as default - if (! array_key_exists($key, $this->values)) { - return null; - } - - return $this->values[$key]; - } - - /** - * @param mixed $value - */ - public function changeValue(string $key, $value): void - { - // is quoted? - if (isset($this->values[$key]) && is_string($this->values[$key])) { - $isQuoted = (bool) Strings::match($this->values[$key], self::UNQUOTED_VALUE_REGEX); - if ($isQuoted) { - $value = '"' . $value . '"'; - } - } - - $this->values[$key] = $value; - - // invoke reprint - $this->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); - } - - /** - * @return mixed|null - */ - public function getValueWithoutQuotes(string | int $key) - { - $value = $this->getValue($key); - if ($value === null) { - return null; - } - - return $this->removeQuotes($value); - } - - /** - * @param mixed $value - */ - public function changeSilentValue($value): void - { - // is quoted? - $isQuoted = (bool) Strings::match($this->values[0], self::UNQUOTED_VALUE_REGEX); - if ($isQuoted) { - $value = '"' . $value . '"'; - } - - $this->values[0] = $value; - $this->hasChanged = true; - - // invoke reprint - $this->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); - } - - /** - * @return mixed|null - */ - public function getSilentValue() - { - $value = $this->values[0] ?? null; - if ($value === null) { - return null; - } - - return $this->removeQuotes($value); - } - - /** - * Useful for attributes - * @return array - */ - public function getValuesWithExplicitSilentAndWithoutQuotes(): array - { - $explicitKeysValues = []; - - foreach (array_keys($this->values) as $key) { - $valueWithoutQuotes = $this->getValueWithoutQuotes($key); - - if (is_int($key) && $this->silentKey !== null) { - $explicitKeysValues[$this->silentKey] = $valueWithoutQuotes; - } else { - $explicitKeysValues[$this->removeQuotes($key)] = $valueWithoutQuotes; - } - } - - return $explicitKeysValues; - } - - public function markAsChanged(): void - { - $this->hasChanged = true; - } - - /** - * @return mixed[] - */ - public function getOriginalValues(): array - { - return $this->originalValues; - } - - /** - * @param mixed|string $value - * @return mixed|string - */ - protected function removeQuotes($value) - { - if (\is_array($value)) { - return $this->removeQuotesFromArray($value); - } - - if (! is_string($value)) { - return $value; - } - - $matches = Strings::match($value, self::UNQUOTED_VALUE_REGEX); - if ($matches === null) { - return $value; - } - - return $matches['content']; - } - - /** - * @param mixed[] $values - * @return array - */ - protected function removeQuotesFromArray(array $values): array - { - $unquotedArray = []; - foreach ($values as $key => $value) { - $unquotedKey = $this->removeQuotes($key); - $unquotedValue = $this->removeQuotes($value); - $unquotedArray[$unquotedKey] = $unquotedValue; - } - - return $unquotedArray; - } - - /** - * @param mixed[] $values - */ - protected function printValuesContent(array $values): string - { - $itemContents = ''; - $lastItemKey = array_key_last($values); - - foreach ($values as $key => $value) { - if (is_int($key)) { - $itemContents .= $this->stringifyValue($value); - } else { - $itemContents .= $key . '=' . $this->stringifyValue($value); - } - - if ($lastItemKey !== $key) { - $itemContents .= ', '; - } - } - - return $itemContents; - } - - /** - * @param mixed $value - */ - private function stringifyValue($value): string - { - // @todo resolve original casing - if ($value === false) { - return 'false'; - } - - if ($value === true) { - return 'true'; - } - - if (is_int($value)) { - return (string) $value; - } - - if (is_float($value)) { - return (string) $value; - } - - if (is_array($value)) { - return $this->printValuesContent($value); - } - - return (string) $value; - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/CurlyListNode.php b/packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/CurlyListNode.php deleted file mode 100644 index 521a817fd59..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/CurlyListNode.php +++ /dev/null @@ -1,58 +0,0 @@ -implode($this->values); - } - - /** - * @param mixed $value - */ - private function stringifyValue($value): string - { - if ($value === false) { - return 'false'; - } - - if ($value === true) { - return 'true'; - } - - if (is_array($value)) { - return $this->implode($value); - } - - return (string) $value; - } - - /** - * @param mixed[] $array - */ - private function implode(array $array): string - { - $itemContents = ''; - $lastItemKey = array_key_last($array); - - foreach ($array as $key => $value) { - if (is_int($key)) { - $itemContents .= $this->stringifyValue($value); - } else { - $itemContents .= $key . '=' . $this->stringifyValue($value); - } - - if ($lastItemKey !== $key) { - $itemContents .= ', '; - } - } - - return '{' . $itemContents . '}'; - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/PhpDoc/VariadicAwareParamTagValueNode.php b/packages/BetterPhpDocParser/ValueObject/PhpDoc/VariadicAwareParamTagValueNode.php deleted file mode 100644 index 48f4fdbc22e..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/PhpDoc/VariadicAwareParamTagValueNode.php +++ /dev/null @@ -1,20 +0,0 @@ -isVariadic ? '...' : ''; - - $content = sprintf('%s %s%s %s', $this->type, $variadic, $this->parameterName, $this->description); - - return trim($content); - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/PhpDocAttributeKey.php b/packages/BetterPhpDocParser/ValueObject/PhpDocAttributeKey.php deleted file mode 100644 index 15a46320b89..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/PhpDocAttributeKey.php +++ /dev/null @@ -1,36 +0,0 @@ -start; - } - - public function getEnd(): int - { - return $this->end; - } - - public function contains(int $position): bool - { - if ($position < $this->start) { - return false; - } - - return $position < $this->end; - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/Type/BracketsAwareUnionTypeNode.php b/packages/BetterPhpDocParser/ValueObject/Type/BracketsAwareUnionTypeNode.php deleted file mode 100644 index c7bd81fe4db..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/Type/BracketsAwareUnionTypeNode.php +++ /dev/null @@ -1,39 +0,0 @@ -isWrappedInBrackets) { - return implode('|', $this->types); - } - - return '(' . implode('|', $this->types) . ')'; - } - - public function isWrappedInBrackets(): bool - { - return $this->isWrappedInBrackets; - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/Type/EmptyGenericTypeNode.php b/packages/BetterPhpDocParser/ValueObject/Type/EmptyGenericTypeNode.php deleted file mode 100644 index 45c3d582afa..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/Type/EmptyGenericTypeNode.php +++ /dev/null @@ -1,22 +0,0 @@ -type; - } -} diff --git a/packages/BetterPhpDocParser/ValueObject/Type/SpacingAwareArrayTypeNode.php b/packages/BetterPhpDocParser/ValueObject/Type/SpacingAwareArrayTypeNode.php deleted file mode 100644 index 71e4ddc8697..00000000000 --- a/packages/BetterPhpDocParser/ValueObject/Type/SpacingAwareArrayTypeNode.php +++ /dev/null @@ -1,76 +0,0 @@ -type instanceof CallableTypeNode) { - return sprintf('(%s)[]', (string) $this->type); - } - - $typeAsString = (string) $this->type; - - if ($this->isGenericArrayCandidate($this->type)) { - return sprintf('array<%s>', $typeAsString); - } - - if ($this->type instanceof ArrayTypeNode) { - return $this->printArrayType($this->type); - } - - if ($this->type instanceof BracketsAwareUnionTypeNode) { - return $this->printUnionType($this->type); - } - - return $typeAsString . '[]'; - } - - private function isGenericArrayCandidate(TypeNode $typeNode): bool - { - $hasGenericTypeParent = (bool) $this->getAttribute(ArrayTypeMapper::HAS_GENERIC_TYPE_PARENT); - if (! $hasGenericTypeParent) { - return false; - } - - return $typeNode instanceof UnionTypeNode || $typeNode instanceof ArrayTypeNode; - } - - private function printArrayType(ArrayTypeNode $arrayTypeNode): string - { - $typeAsString = (string) $arrayTypeNode; - - $singleTypesAsString = explode('|', $typeAsString); - foreach ($singleTypesAsString as $key => $singleTypeAsString) { - $singleTypesAsString[$key] = $singleTypeAsString . '[]'; - } - - return implode('|', $singleTypesAsString); - } - - private function printUnionType(BracketsAwareUnionTypeNode $bracketsAwareUnionTypeNode): string - { - $unionedTypes = []; - - if ($bracketsAwareUnionTypeNode->isWrappedInBrackets()) { - return $bracketsAwareUnionTypeNode . '[]'; - } - - foreach ($bracketsAwareUnionTypeNode->types as $unionedType) { - $unionedTypes[] = $unionedType . '[]'; - } - - return implode('|', $unionedTypes); - } -} diff --git a/packages/BetterPhpDocParser/ValueObjectFactory/PhpDocNode/Symfony/SymfonyRouteTagValueNodeFactory.php b/packages/BetterPhpDocParser/ValueObjectFactory/PhpDocNode/Symfony/SymfonyRouteTagValueNodeFactory.php deleted file mode 100644 index 61ecba8c968..00000000000 --- a/packages/BetterPhpDocParser/ValueObjectFactory/PhpDocNode/Symfony/SymfonyRouteTagValueNodeFactory.php +++ /dev/null @@ -1,24 +0,0 @@ - $items - */ - public function createFromItems(array $items): DoctrineAnnotationTagValueNode - { - return new DoctrineAnnotationTagValueNode( - new IdentifierTypeNode('Symfony\Component\Routing\Annotation\Route'), - null, - $items, - 'path' - ); - } -} diff --git a/packages/Caching/CacheFactory.php b/packages/Caching/CacheFactory.php deleted file mode 100644 index 339eedd0893..00000000000 --- a/packages/Caching/CacheFactory.php +++ /dev/null @@ -1,42 +0,0 @@ -parameterProvider->provideStringParameter(Option::CACHE_DIR); - - $cacheClass = FileCacheStorage::class; - if ($this->parameterProvider->hasParameter(Option::CACHE_CLASS)) { - $cacheClass = $this->parameterProvider->provideStringParameter(Option::CACHE_CLASS); - } - - if ($cacheClass === FileCacheStorage::class) { - // ensure cache directory exists - if (! $this->smartFileSystem->exists($cacheDirectory)) { - $this->smartFileSystem->mkdir($cacheDirectory); - } - - $fileCacheStorage = new FileCacheStorage($cacheDirectory, $this->smartFileSystem); - return new Cache($fileCacheStorage); - } - - return new Cache(new MemoryCacheStorage()); - } -} diff --git a/packages/Caching/Config/FileHashComputer.php b/packages/Caching/Config/FileHashComputer.php deleted file mode 100644 index 8c5a0dac6d5..00000000000 --- a/packages/Caching/Config/FileHashComputer.php +++ /dev/null @@ -1,74 +0,0 @@ -ensureIsPhp($fileInfo); - - $containerBuilder = new ContainerBuilder(); - $fileLoader = $this->createFileLoader($fileInfo, $containerBuilder); - - $fileLoader->load($fileInfo->getRealPath()); - - $parameterBag = $containerBuilder->getParameterBag(); - - return $this->arrayToHash($containerBuilder->getDefinitions()) . $this->arrayToHash($parameterBag->all()); - } - - private function ensureIsPhp(SmartFileInfo $fileInfo): void - { - if ($fileInfo->hasSuffixes(['php'])) { - return; - } - - throw new ShouldNotHappenException(sprintf( - // getRealPath() cannot be used, as it breaks in phar - 'Provide only PHP file, ready for Symfony Dependency Injection. "%s" given', - $fileInfo->getRelativeFilePath() - )); - } - - private function createFileLoader(SmartFileInfo $fileInfo, ContainerBuilder $containerBuilder): LoaderInterface - { - $fileLocator = new FileLocator([$fileInfo->getPath()]); - - $fileLoaders = [ - new GlobFileLoader($containerBuilder, $fileLocator), - new PhpFileLoader($containerBuilder, $fileLocator), - ]; - - $loaderResolver = new LoaderResolver($fileLoaders); - $loader = $loaderResolver->resolve($fileInfo->getRealPath()); - if (! $loader) { - throw new ShouldNotHappenException(); - } - - return $loader; - } - - /** - * @param mixed[] $array - */ - private function arrayToHash(array $array): string - { - $serializedArray = serialize($array); - return md5($serializedArray); - } -} diff --git a/packages/Caching/Detector/ChangedFilesDetector.php b/packages/Caching/Detector/ChangedFilesDetector.php deleted file mode 100644 index 6c1f61965ca..00000000000 --- a/packages/Caching/Detector/ChangedFilesDetector.php +++ /dev/null @@ -1,123 +0,0 @@ -getFileInfoCacheKey($smartFileInfo); - $hash = $this->hashFile($smartFileInfo); - - $this->cache->save($fileInfoCacheKey, CacheKey::FILE_HASH_KEY, $hash); - $this->cache->save($fileInfoCacheKey . '_files', CacheKey::DEPENDENT_FILES_KEY, $dependentFiles); - } - - public function hasFileChanged(SmartFileInfo $smartFileInfo): bool - { - $currentFileHash = $this->hashFile($smartFileInfo); - - $fileInfoCacheKey = $this->getFileInfoCacheKey($smartFileInfo); - - $cachedValue = $this->cache->load($fileInfoCacheKey, CacheKey::FILE_HASH_KEY); - return $currentFileHash !== $cachedValue; - } - - public function invalidateFile(SmartFileInfo $smartFileInfo): void - { - $fileInfoCacheKey = $this->getFileInfoCacheKey($smartFileInfo); - $this->cache->clean($fileInfoCacheKey); - } - - public function clear(): void - { - $this->cache->clear(); - } - - /** - * @return SmartFileInfo[] - */ - public function getDependentFileInfos(SmartFileInfo $fileInfo): array - { - $fileInfoCacheKey = $this->getFileInfoCacheKey($fileInfo); - - $cacheValue = $this->cache->load($fileInfoCacheKey . '_files', CacheKey::DEPENDENT_FILES_KEY); - if ($cacheValue === null) { - return []; - } - - $dependentFileInfos = []; - - $dependentFiles = $cacheValue; - foreach ($dependentFiles as $dependentFile) { - if (! file_exists($dependentFile)) { - continue; - } - - $dependentFileInfos[] = new SmartFileInfo($dependentFile); - } - - return $dependentFileInfos; - } - - /** - * @api - */ - public function setFirstResolvedConfigFileInfo(SmartFileInfo $fileInfo): void - { - // the first config is core to all → if it was changed, just invalidate it - $configHash = $this->fileHashComputer->compute($fileInfo); - $this->storeConfigurationDataHash($fileInfo, $configHash); - } - - private function getFileInfoCacheKey(SmartFileInfo $smartFileInfo): string - { - return sha1($smartFileInfo->getRealPath()); - } - - private function hashFile(SmartFileInfo $smartFileInfo): string - { - return (string) sha1_file($smartFileInfo->getRealPath()); - } - - private function storeConfigurationDataHash(SmartFileInfo $fileInfo, string $configurationHash): void - { - $key = CacheKey::CONFIGURATION_HASH_KEY . '_' . Strings::webalize($fileInfo->getRealPath()); - $this->invalidateCacheIfConfigurationChanged($key, $configurationHash); - - $this->cache->save($key, CacheKey::CONFIGURATION_HASH_KEY, $configurationHash); - } - - private function invalidateCacheIfConfigurationChanged(string $key, string $configurationHash): void - { - $oldCachedValue = $this->cache->load($key, CacheKey::CONFIGURATION_HASH_KEY); - if ($oldCachedValue === $configurationHash) { - return; - } - - // should be unique per getcwd() - $this->clear(); - } -} diff --git a/packages/Caching/Enum/CacheKey.php b/packages/Caching/Enum/CacheKey.php deleted file mode 100644 index df2194bb1d7..00000000000 --- a/packages/Caching/Enum/CacheKey.php +++ /dev/null @@ -1,26 +0,0 @@ -privatesAccessor->getPrivateProperty( - $this->nodeScopeResolver, - 'analysedFiles' - ); - - $dependencyFiles = []; - - $nodeDependencies = $this->phpStanDependencyResolver->resolveDependencies($node, $mutatingScope); - foreach ($nodeDependencies as $nodeDependency) { - $dependencyFile = $nodeDependency->getFileName(); - if (! $dependencyFile) { - continue; - } - - $dependencyFile = $this->fileHelper->normalizePath($dependencyFile); - if ($mutatingScope->getFile() === $dependencyFile) { - continue; - } - - // only work with files that we've analysed - if (! in_array($dependencyFile, $analysedFileAbsolutesPaths, true)) { - continue; - } - - $dependencyFiles[] = $dependencyFile; - } - - $dependencyFiles = array_unique($dependencyFiles, SORT_STRING); - - return array_values($dependencyFiles); - } -} diff --git a/packages/Caching/UnchangedFilesFilter.php b/packages/Caching/UnchangedFilesFilter.php deleted file mode 100644 index 04344997581..00000000000 --- a/packages/Caching/UnchangedFilesFilter.php +++ /dev/null @@ -1,45 +0,0 @@ -changedFilesDetector->hasFileChanged($fileInfo)) { - continue; - } - - $changedFileInfos[] = $fileInfo; - $this->changedFilesDetector->invalidateFile($fileInfo); - - $dependentFileInfos = array_merge( - $dependentFileInfos, - $this->changedFilesDetector->getDependentFileInfos($fileInfo) - ); - } - - // add dependent files - $dependentFileInfos = array_merge($dependentFileInfos, $changedFileInfos); - - return array_unique($dependentFileInfos); - } -} diff --git a/packages/Caching/ValueObject/Storage/FileCacheStorage.php b/packages/Caching/ValueObject/Storage/FileCacheStorage.php deleted file mode 100644 index 7eee2eec171..00000000000 --- a/packages/Caching/ValueObject/Storage/FileCacheStorage.php +++ /dev/null @@ -1,102 +0,0 @@ -getCacheFilePaths($key); - - $filePath = $cacheFilePaths->getFilePath(); - if (! \is_file($filePath)) { - return null; - } - $cacheItem = (require $filePath); - if (! $cacheItem instanceof CacheItem) { - return null; - } - if (! $cacheItem->isVariableKeyValid($variableKey)) { - return null; - } - return $cacheItem->getData(); - })($key, $variableKey); - } - - public function save(string $key, string $variableKey, $data): void - { - $cacheFilePaths = $this->getCacheFilePaths($key); - $this->smartFileSystem->mkdir($cacheFilePaths->getFirstDirectory()); - $this->smartFileSystem->mkdir($cacheFilePaths->getSecondDirectory()); - $path = $cacheFilePaths->getFilePath(); - - $tmpPath = \sprintf('%s/%s.tmp', $this->directory, Random::generate()); - $errorBefore = \error_get_last(); - $exported = @\var_export(new CacheItem($variableKey, $data), true); - $errorAfter = \error_get_last(); - if ($errorAfter !== null && $errorBefore !== $errorAfter) { - throw new CachingException(\sprintf( - 'Error occurred while saving item %s (%s) to cache: %s', - $key, - $variableKey, - $errorAfter['message'] - )); - } - // for performance reasons we don't use SmartFileSystem - FileWriter::write($tmpPath, \sprintf("getCacheFilePaths($key); - - $this->smartFileSystem->remove([ - $cacheFilePaths->getFirstDirectory(), - $cacheFilePaths->getSecondDirectory(), - $cacheFilePaths->getFilePath(), - ]); - } - - public function clear(): void - { - $this->smartFileSystem->remove($this->directory); - } - - private function getCacheFilePaths(string $key): CacheFilePaths - { - $keyHash = sha1($key); - $firstDirectory = sprintf('%s/%s', $this->directory, substr($keyHash, 0, 2)); - $secondDirectory = sprintf('%s/%s', $firstDirectory, substr($keyHash, 2, 2)); - $filePath = sprintf('%s/%s.php', $secondDirectory, $keyHash); - - return new CacheFilePaths($firstDirectory, $secondDirectory, $filePath); - } -} diff --git a/packages/ChangesReporting/Annotation/AnnotationExtractor.php b/packages/ChangesReporting/Annotation/AnnotationExtractor.php deleted file mode 100644 index 013e32c82c8..00000000000 --- a/packages/ChangesReporting/Annotation/AnnotationExtractor.php +++ /dev/null @@ -1,35 +0,0 @@ - $className - */ - public function extractAnnotationFromClass(string $className, string $annotation): ?string - { - $reflectionClass = new ReflectionClass($className); - - $docComment = $reflectionClass->getDocComment(); - if (! is_string($docComment)) { - return null; - } - - // @see https://3v4l.org/ouYfB - // uses '\r?\n' instead of '$' because windows compat - $pattern = '#' . preg_quote($annotation, '#') . '\s+(?.*?)\r?\n#m'; - $matches = Strings::match($docComment, $pattern); - - return $matches['content'] ?? null; - } -} diff --git a/packages/ChangesReporting/Annotation/RectorsChangelogResolver.php b/packages/ChangesReporting/Annotation/RectorsChangelogResolver.php deleted file mode 100644 index eb3bc3e1f60..00000000000 --- a/packages/ChangesReporting/Annotation/RectorsChangelogResolver.php +++ /dev/null @@ -1,40 +0,0 @@ -> $rectorClasses - * @return array - */ - public function resolve(array $rectorClasses): array - { - $rectorClassesToChangelogUrls = $this->resolveIncludingMissing($rectorClasses); - return array_filter($rectorClassesToChangelogUrls); - } - - /** - * @param array> $rectorClasses - * @return array - */ - public function resolveIncludingMissing(array $rectorClasses): array - { - $rectorClassesToChangelogUrls = []; - foreach ($rectorClasses as $rectorClass) { - $changelogUrl = $this->annotationExtractor->extractAnnotationFromClass($rectorClass, '@changelog'); - $rectorClassesToChangelogUrls[$rectorClass] = $changelogUrl; - } - - return $rectorClassesToChangelogUrls; - } -} diff --git a/packages/ChangesReporting/Collector/AffectedFilesCollector.php b/packages/ChangesReporting/Collector/AffectedFilesCollector.php deleted file mode 100644 index 880afc398a4..00000000000 --- a/packages/ChangesReporting/Collector/AffectedFilesCollector.php +++ /dev/null @@ -1,35 +0,0 @@ -getSmartFileInfo(); - $this->affectedFiles[$smartFileInfo->getRealPath()] = $file; - } - - public function getNext(): ?File - { - if ($this->affectedFiles !== []) { - return current($this->affectedFiles); - } - return null; - } - - public function removeFromList(File $file): void - { - $smartFileInfo = $file->getSmartFileInfo(); - unset($this->affectedFiles[$smartFileInfo->getRealPath()]); - } -} diff --git a/packages/ChangesReporting/Collector/RectorChangeCollector.php b/packages/ChangesReporting/Collector/RectorChangeCollector.php deleted file mode 100644 index 9802a8f225e..00000000000 --- a/packages/ChangesReporting/Collector/RectorChangeCollector.php +++ /dev/null @@ -1,42 +0,0 @@ - method instead - */ - public function notifyNodeFileInfo(Node $node): void - { - $file = $this->currentFileProvider->getFile(); - if (! $file instanceof File) { - // this file was changed before and this is a sub-new node - // array Traverse to all new nodes would have to be used, but it's not worth the performance - return; - } - - $currentRector = $this->currentRectorProvider->getCurrentRector(); - if (! $currentRector instanceof RectorInterface) { - return; - } - - $rectorWithLineChange = new RectorWithLineChange($currentRector, $node->getLine()); - $file->addRectorClassWithLine($rectorWithLineChange); - } -} diff --git a/packages/ChangesReporting/Output/ConsoleOutputFormatter.php b/packages/ChangesReporting/Output/ConsoleOutputFormatter.php deleted file mode 100644 index 178cd80ef68..00000000000 --- a/packages/ChangesReporting/Output/ConsoleOutputFormatter.php +++ /dev/null @@ -1,186 +0,0 @@ -shouldShowDiffs()) { - $this->reportFileDiffs($processResult->getFileDiffs()); - } - - $this->reportErrors($processResult->getErrors()); - $this->reportRemovedFilesAndNodes($processResult); - - if ($processResult->getErrors() !== []) { - return; - } - - $message = $this->createSuccessMessage($processResult, $configuration); - $this->outputStyle->success($message); - } - - public function getName(): string - { - return self::NAME; - } - - /** - * @param FileDiff[] $fileDiffs - */ - private function reportFileDiffs(array $fileDiffs): void - { - if (count($fileDiffs) <= 0) { - return; - } - - // normalize - ksort($fileDiffs); - $message = sprintf('%d file%s with changes', count($fileDiffs), count($fileDiffs) === 1 ? '' : 's'); - - $this->outputStyle->title($message); - - $i = 0; - foreach ($fileDiffs as $fileDiff) { - $relativeFilePath = $fileDiff->getRelativeFilePath(); - - // append line number for faster file jump in diff - $firstLineNumber = $fileDiff->getFirstLineNumber(); - if ($firstLineNumber !== null) { - $relativeFilePath .= ':' . $firstLineNumber; - } - - $message = sprintf('%d) %s', ++$i, $relativeFilePath); - - $this->outputStyle->writeln($message); - $this->outputStyle->newLine(); - $this->outputStyle->writeln($fileDiff->getDiffConsoleFormatted()); - - $rectorsChangelogsLines = $this->createRectorChangelogLines($fileDiff); - - if ($fileDiff->getRectorChanges() !== []) { - $this->outputStyle->writeln('Applied rules:'); - $this->outputStyle->listing($rectorsChangelogsLines); - $this->outputStyle->newLine(); - } - } - } - - /** - * @param RectorError[] $errors - */ - private function reportErrors(array $errors): void - { - foreach ($errors as $error) { - $errorMessage = $error->getMessage(); - $errorMessage = $this->normalizePathsToRelativeWithLine($errorMessage); - - $message = sprintf( - 'Could not process "%s" file%s, due to: %s"%s".', - $error->getRelativeFilePath(), - $error->getRectorClass() ? ' by "' . $error->getRectorClass() . '"' : '', - PHP_EOL, - $errorMessage - ); - - if ($error->getLine()) { - $message .= ' On line: ' . $error->getLine(); - } - - $this->outputStyle->error($message); - } - } - - private function reportRemovedFilesAndNodes(ProcessResult $processResult): void - { - if ($processResult->getAddedFilesCount() !== 0) { - $message = sprintf('%d files were added', $processResult->getAddedFilesCount()); - $this->outputStyle->note($message); - } - - if ($processResult->getRemovedFilesCount() !== 0) { - $message = sprintf('%d files were removed', $processResult->getRemovedFilesCount()); - $this->outputStyle->note($message); - } - - $this->reportRemovedNodes($processResult); - } - - private function normalizePathsToRelativeWithLine(string $errorMessage): string - { - $regex = '#' . preg_quote(getcwd(), '#') . '/#'; - $errorMessage = Strings::replace($errorMessage, $regex, ''); - return Strings::replace($errorMessage, self::ON_LINE_REGEX, ':'); - } - - private function reportRemovedNodes(ProcessResult $processResult): void - { - if ($processResult->getRemovedNodeCount() === 0) { - return; - } - - $message = sprintf('%d nodes were removed', $processResult->getRemovedNodeCount()); - $this->outputStyle->warning($message); - } - - private function createSuccessMessage(ProcessResult $processResult, Configuration $configuration): string - { - $changeCount = count($processResult->getFileDiffs()) + $processResult->getRemovedAndAddedFilesCount(); - - if ($changeCount === 0) { - return 'Rector is done!'; - } - - return sprintf( - '%d file%s %s by Rector', - $changeCount, - $changeCount > 1 ? 's' : '', - $configuration->isDryRun() ? 'would have changed (dry-run)' : ($changeCount === 1 ? 'has' : 'have') . ' been changed' - ); - } - - /** - * @return string[] - */ - private function createRectorChangelogLines(FileDiff $fileDiff): array - { - $rectorsChangelogs = $this->rectorsChangelogResolver->resolveIncludingMissing($fileDiff->getRectorClasses()); - - $rectorsChangelogsLines = []; - foreach ($rectorsChangelogs as $rectorClass => $changelog) { - $rectorShortClass = (string) Strings::after($rectorClass, '\\', -1); - $rectorsChangelogsLines[] = $changelog === null ? $rectorShortClass : $rectorShortClass . ' (' . $changelog . ')'; - } - - return $rectorsChangelogsLines; - } -} diff --git a/packages/ChangesReporting/Output/JsonOutputFormatter.php b/packages/ChangesReporting/Output/JsonOutputFormatter.php deleted file mode 100644 index 6203d66f17e..00000000000 --- a/packages/ChangesReporting/Output/JsonOutputFormatter.php +++ /dev/null @@ -1,100 +0,0 @@ - [ - 'config' => $configuration->getMainConfigFilePath(), - ], - 'totals' => [ - 'changed_files' => count($processResult->getFileDiffs()), - 'removed_and_added_files_count' => $processResult->getRemovedAndAddedFilesCount(), - 'removed_node_count' => $processResult->getRemovedNodeCount(), - ], - ]; - - $fileDiffs = $processResult->getFileDiffs(); - ksort($fileDiffs); - foreach ($fileDiffs as $fileDiff) { - $relativeFilePath = $fileDiff->getRelativeFilePath(); - - $appliedRectorsWithChangelog = $this->rectorsChangelogResolver->resolve($fileDiff->getRectorClasses()); - - $errorsArray['file_diffs'][] = [ - 'file' => $relativeFilePath, - 'diff' => $fileDiff->getDiff(), - 'applied_rectors' => $fileDiff->getRectorClasses(), - 'applied_rectors_with_changelog' => $appliedRectorsWithChangelog, - ]; - - // for Rector CI - $errorsArray['changed_files'][] = $relativeFilePath; - } - - $errors = $processResult->getErrors(); - $errorsArray['totals']['errors'] = count($errors); - - $errorsData = $this->createErrorsData($errors); - if ($errorsData !== []) { - $errorsArray['errors'] = $errorsData; - } - - $json = Json::encode($errorsArray, Json::PRETTY); - echo $json . PHP_EOL; - } - - /** - * @param mixed[] $errors - * @return mixed[] - */ - private function createErrorsData(array $errors): array - { - $errorsData = []; - - foreach ($errors as $error) { - $errorData = [ - 'message' => $error->getMessage(), - 'file' => $error->getRelativeFilePath(), - ]; - - if ($error->getRectorClass()) { - $errorData['caused_by'] = $error->getRectorClass(); - } - - if ($error->getLine() !== null) { - $errorData['line'] = $error->getLine(); - } - - $errorsData[] = $errorData; - } - - return $errorsData; - } -} diff --git a/packages/ChangesReporting/ValueObject/RectorWithLineChange.php b/packages/ChangesReporting/ValueObject/RectorWithLineChange.php deleted file mode 100644 index cde1822d633..00000000000 --- a/packages/ChangesReporting/ValueObject/RectorWithLineChange.php +++ /dev/null @@ -1,29 +0,0 @@ - - */ - public function getRectorClass(): string - { - return $this->rector::class; - } - - public function getLine(): int - { - return $this->line; - } -} diff --git a/packages/ChangesReporting/ValueObjectFactory/ErrorFactory.php b/packages/ChangesReporting/ValueObjectFactory/ErrorFactory.php deleted file mode 100644 index a80a20b4254..00000000000 --- a/packages/ChangesReporting/ValueObjectFactory/ErrorFactory.php +++ /dev/null @@ -1,26 +0,0 @@ -exceptionCorrector->getAutoloadExceptionMessageAndAddLocation($analysedCodeException); - return new RectorError($message, $smartFileInfo); - } -} diff --git a/packages/ChangesReporting/ValueObjectFactory/FileDiffFactory.php b/packages/ChangesReporting/ValueObjectFactory/FileDiffFactory.php deleted file mode 100644 index a458ad9312e..00000000000 --- a/packages/ChangesReporting/ValueObjectFactory/FileDiffFactory.php +++ /dev/null @@ -1,30 +0,0 @@ -getSmartFileInfo(), - $this->defaultDiffer->diff($oldContent, $newContent), - $this->consoleDiffer->diff($oldContent, $newContent), - $file->getRectorWithLineChanges() - ); - } -} diff --git a/packages/Comments/CommentRemover.php b/packages/Comments/CommentRemover.php deleted file mode 100644 index 235502c5913..00000000000 --- a/packages/Comments/CommentRemover.php +++ /dev/null @@ -1,44 +0,0 @@ -commentRemovingNodeTraverser->traverse($nodes); - } - - public function rollbackComments(Node $node, Comment $comment): void - { - $node->setAttribute(AttributeKey::COMMENTS, null); - $node->setDocComment(new Doc($comment->getText())); - $node->setAttribute(AttributeKey::PHP_DOC_INFO, null); - } -} diff --git a/packages/Comments/NodeDocBlock/DocBlockUpdater.php b/packages/Comments/NodeDocBlock/DocBlockUpdater.php deleted file mode 100644 index 7acf85b6e32..00000000000 --- a/packages/Comments/NodeDocBlock/DocBlockUpdater.php +++ /dev/null @@ -1,68 +0,0 @@ -getAttribute(AttributeKey::PHP_DOC_INFO); - if (! $phpDocInfo instanceof PhpDocInfo) { - return; - } - - if (! $phpDocInfo->hasChanged()) { - return; - } - - $phpDoc = $this->printPhpDocInfoToString($phpDocInfo); - - // make sure, that many separated comments are not removed - if ($phpDoc === '') { - if (count($node->getComments()) > 1) { - foreach ($node->getComments() as $comment) { - $phpDoc .= $comment->getText() . PHP_EOL; - } - } - - if ($phpDocInfo->getOriginalPhpDocNode()->children !== []) { - // all comments were removed → null - $node->setAttribute(AttributeKey::COMMENTS, null); - } - - return; - } - - // this is needed to remove duplicated // commentsAsText - $node->setDocComment(new Doc($phpDoc)); - } - - private function printPhpDocInfoToString(PhpDocInfo $phpDocInfo): string - { - if ($phpDocInfo->isNewNode()) { - return $this->phpDocInfoPrinter->printNew($phpDocInfo); - } - - return $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); - } -} diff --git a/packages/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php b/packages/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php deleted file mode 100644 index c2f58500f39..00000000000 --- a/packages/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php +++ /dev/null @@ -1,61 +0,0 @@ -familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); - - foreach ($childrenClassReflections as $childClassReflection) { - if (! $childClassReflection->hasMethod($methodName)) { - continue; - } - - $constructorReflectionMethod = $childClassReflection->getNativeMethod($methodName); - if (! $constructorReflectionMethod instanceof PhpMethodReflection) { - continue; - } - - $methodDeclaringClassReflection = $constructorReflectionMethod->getDeclaringClass(); - if ($methodDeclaringClassReflection->getName() === $childClassReflection->getName()) { - return true; - } - } - - return false; - } - - public function hasParentClassMethod(ClassReflection $classReflection, string $methodName): bool - { - foreach ($classReflection->getParents() as $parentClassReflections) { - if (! $parentClassReflections->hasMethod($methodName)) { - continue; - } - - $constructMethodReflection = $parentClassReflections->getNativeMethod($methodName); - if (! $constructMethodReflection instanceof PhpMethodReflection) { - continue; - } - - $methodDeclaringMethodClass = $constructMethodReflection->getDeclaringClass(); - if ($methodDeclaringMethodClass->getName() === $parentClassReflections->getName()) { - return true; - } - } - - return false; - } -} diff --git a/packages/FamilyTree/NodeAnalyzer/PropertyUsageAnalyzer.php b/packages/FamilyTree/NodeAnalyzer/PropertyUsageAnalyzer.php deleted file mode 100644 index 4219b22a3fe..00000000000 --- a/packages/FamilyTree/NodeAnalyzer/PropertyUsageAnalyzer.php +++ /dev/null @@ -1,72 +0,0 @@ -getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return false; - } - - $scope = $property->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - throw new ShouldNotHappenException(); - } - - if ($classReflection->isClass() && $classReflection->isFinal()) { - return false; - } - - $propertyName = $this->nodeNameResolver->getName($property); - - $childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); - - foreach ($childrenClassReflections as $childClassReflection) { - $childClass = $this->astResolver->resolveClassFromName($childClassReflection->getName()); - if (! $childClass instanceof Class_) { - continue; - } - - $isPropertyFetched = (bool) $this->betterNodeFinder->findFirst( - $childClass->stmts, - fn (Node $node): bool => $this->nodeNameResolver->isLocalPropertyFetchNamed($node, $propertyName) - ); - - if ($isPropertyFetched) { - return true; - } - } - - return false; - } -} diff --git a/packages/FamilyTree/Reflection/FamilyRelationsAnalyzer.php b/packages/FamilyTree/Reflection/FamilyRelationsAnalyzer.php deleted file mode 100644 index 3928ca5793e..00000000000 --- a/packages/FamilyTree/Reflection/FamilyRelationsAnalyzer.php +++ /dev/null @@ -1,216 +0,0 @@ -privatesAccessor->getPrivateProperty($this->reflectionProvider, 'classes'); - - $childrenClassReflections = []; - - foreach ($classReflections as $classReflection) { - if (! $classReflection->isSubclassOf($desiredClassReflection->getName())) { - continue; - } - - $childrenClassReflections[] = $classReflection; - } - - return $childrenClassReflections; - } - - public function getPossibleUnionPropertyType( - Property $property, - Type $varType, - ?Scope $scope, - Name | NullableType | PhpParserUnionType | null $propertyTypeNode - ): PropertyType { - if ($varType instanceof UnionType) { - return new PropertyType($varType, $propertyTypeNode); - } - - if (! $scope instanceof Scope) { - return new PropertyType($varType, $propertyTypeNode); - } - - /** @var ClassReflection $classReflection */ - $classReflection = $scope->getClassReflection(); - $ancestors = $classReflection->getAncestors(); - $propertyName = $this->nodeNameResolver->getName($property); - $kindPropertyFetch = $this->getKindPropertyFetch($property); - - $className = $property->getAttribute(AttributeKey::CLASS_NAME); - foreach ($ancestors as $ancestor) { - $ancestorName = $ancestor->getName(); - if ($ancestorName === $className) { - continue; - } - - $fileName = $ancestor->getFileName(); - if ($fileName === false) { - continue; - } - - $fileContent = $this->smartFileSystem->readFile($fileName); - $nodes = $this->parser->parse($fileContent); - if ($ancestor->isSubclassOf('PHPUnit\Framework\TestCase')) { - continue; - } - if ($nodes === null) { - continue; - } - if (! $this->isPropertyWritten($nodes, $propertyName, $kindPropertyFetch)) { - continue; - } - - $varType = new UnionType([$varType, new NullType()]); - $propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $varType, - TypeKind::PROPERTY() - ); - - return new PropertyType($varType, $propertyTypeNode); - } - - return new PropertyType($varType, $propertyTypeNode); - } - - /** - * @return string[] - */ - public function getClassLikeAncestorNames(Class_ | Interface_ | Name $classOrName): array - { - $ancestorNames = []; - - if ($classOrName instanceof Name) { - $fullName = $this->nodeNameResolver->getName($classOrName); - $classLike = $this->astResolver->resolveClassFromName($fullName); - } else { - $classLike = $classOrName; - } - - if ($classLike instanceof Interface_) { - foreach ($classLike->extends as $extendInterfaceName) { - $ancestorNames[] = $this->nodeNameResolver->getName($extendInterfaceName); - $ancestorNames = array_merge($ancestorNames, $this->getClassLikeAncestorNames($extendInterfaceName)); - } - } - - if ($classLike instanceof Class_) { - if ($classLike->extends instanceof Name) { - $extendName = $classLike->extends; - - $ancestorNames[] = $this->nodeNameResolver->getName($extendName); - $ancestorNames = array_merge($ancestorNames, $this->getClassLikeAncestorNames($extendName)); - } - - foreach ($classLike->implements as $implement) { - $ancestorNames[] = $this->nodeNameResolver->getName($implement); - $ancestorNames = array_merge($ancestorNames, $this->getClassLikeAncestorNames($implement)); - } - } - - return $ancestorNames; - } - - private function getKindPropertyFetch(Property $property): string - { - return $property->isStatic() - ? StaticPropertyFetch::class - : PropertyFetch::class; - } - - /** - * @param Stmt[] $nodes - */ - private function isPropertyWritten(array $nodes, string $propertyName, string $kindPropertyFetch): bool - { - return (bool) $this->betterNodeFinder->findFirst($nodes, function (Node $node) use ( - $propertyName, - $kindPropertyFetch - ): bool { - if (! $node instanceof ClassMethod) { - return false; - } - - if ($this->nodeNameResolver->isName($node->name, 'autowire')) { - return false; - } - - return $this->isPropertyAssignedInClassMethod($node, $propertyName, $kindPropertyFetch); - }); - } - - private function isPropertyAssignedInClassMethod( - ClassMethod $classMethod, - string $propertyName, - string $kindPropertyFetch - ): bool { - if ($classMethod->stmts === null) { - return false; - } - - return (bool) $this->betterNodeFinder->findFirst($classMethod->stmts, function (Node $node) use ( - $propertyName, - $kindPropertyFetch - ): bool { - if (! $node instanceof Assign) { - return false; - } - - return $kindPropertyFetch === $node->var::class && $this->nodeNameResolver->isName( - $node->var, - $propertyName - ); - }); - } -} diff --git a/packages/FamilyTree/ValueObject/PropertyType.php b/packages/FamilyTree/ValueObject/PropertyType.php deleted file mode 100644 index aaa188dd996..00000000000 --- a/packages/FamilyTree/ValueObject/PropertyType.php +++ /dev/null @@ -1,36 +0,0 @@ -varType; - } - - /** - * @return Name|NullableType|PhpParserUnionType|null - */ - public function getPropertyTypeNode(): ?Node - { - return $this->propertyTypeNode; - } -} diff --git a/packages/FileFormatter/Contract/Formatter/FileFormatterInterface.php b/packages/FileFormatter/Contract/Formatter/FileFormatterInterface.php deleted file mode 100644 index f477b4cf052..00000000000 --- a/packages/FileFormatter/Contract/Formatter/FileFormatterInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -getSmartFileInfo(); - $configuration = $this->editorConfig->getConfigForPath($smartFileInfo->getRealPath()); - - if (array_key_exists(EditorConfigOption::INDENT_STYLE, $configuration)) { - $indentStyle = (string) $configuration[EditorConfigOption::INDENT_STYLE]->getValue(); - - $editorConfigConfigurationBuilder->withIndentStyle($indentStyle); - } - - if (array_key_exists(EditorConfigOption::INDENT_SIZE, $configuration)) { - $indentSize = (int) $configuration[EditorConfigOption::INDENT_SIZE]->getValue(); - - $editorConfigConfigurationBuilder->withIndentSize($indentSize); - } - - if (array_key_exists(EditorConfigOption::END_OF_LINE, $configuration)) { - $endOfLine = (string) $configuration[EditorConfigOption::END_OF_LINE]->getValue(); - - $editorConfigConfigurationBuilder->withEndOfLineFromEditorConfig($endOfLine); - } - - if (array_key_exists(EditorConfigOption::INSERT_FINAL_NEWLINE, $configuration)) { - $insertFinalNewline = (bool) $configuration[EditorConfigOption::INSERT_FINAL_NEWLINE]->getValue(); - - $editorConfigConfigurationBuilder->withInsertFinalNewline($insertFinalNewline); - } - - return $editorConfigConfigurationBuilder->build(); - } -} diff --git a/packages/FileFormatter/Exception/InvalidIndentSizeException.php b/packages/FileFormatter/Exception/InvalidIndentSizeException.php deleted file mode 100644 index d99e9d4d3a1..00000000000 --- a/packages/FileFormatter/Exception/InvalidIndentSizeException.php +++ /dev/null @@ -1,17 +0,0 @@ - $allowedStyles - */ - public static function fromStyleAndAllowedStyles(string $style, array $allowedStyles): self - { - $message = sprintf('Given style "%s" is not allowed. Allowed are "%s"', $style, implode(' ', $allowedStyles)); - - return new self($message); - } -} diff --git a/packages/FileFormatter/Exception/InvalidNewLineStringException.php b/packages/FileFormatter/Exception/InvalidNewLineStringException.php deleted file mode 100644 index 9936e7645f4..00000000000 --- a/packages/FileFormatter/Exception/InvalidNewLineStringException.php +++ /dev/null @@ -1,20 +0,0 @@ -hasChanged()) { - continue; - } - - foreach ($this->fileFormatters as $fileFormatter) { - if (! $fileFormatter->supports($file)) { - continue; - } - - $editorConfigConfigurationBuilder = $fileFormatter->createDefaultEditorConfigConfigurationBuilder(); - $this->sniffOriginalFileContent($file, $editorConfigConfigurationBuilder); - - $editorConfiguration = $this->createEditorConfiguration($file, $editorConfigConfigurationBuilder); - $fileFormatter->format($file, $editorConfiguration); - } - } - } - - private function sniffOriginalFileContent( - File $file, - EditorConfigConfigurationBuilder $editorConfigConfigurationBuilder - ): void { - // Try to sniff into the original content to get the indentation and new line - try { - $indent = Indent::fromContent($file->getOriginalFileContent()); - $editorConfigConfigurationBuilder->withIndent($indent); - } catch (ParseIndentException) { - } - - try { - $newLine = NewLine::fromContent($file->getOriginalFileContent()); - $editorConfigConfigurationBuilder->withNewLine($newLine); - } catch (InvalidNewLineStringException) { - } - } - - private function createEditorConfiguration( - File $file, - EditorConfigConfigurationBuilder $editorConfigConfigurationBuilder - ): EditorConfigConfiguration { - if (! $this->parameterProvider->provideBoolParameter(Option::ENABLE_EDITORCONFIG)) { - return $editorConfigConfigurationBuilder->build(); - } - - return $this->editorConfigParser->extractConfigurationForFile($file, $editorConfigConfigurationBuilder); - } -} diff --git a/packages/FileFormatter/Formatter/JsonFileFormatter.php b/packages/FileFormatter/Formatter/JsonFileFormatter.php deleted file mode 100644 index ad4df2d244f..00000000000 --- a/packages/FileFormatter/Formatter/JsonFileFormatter.php +++ /dev/null @@ -1,52 +0,0 @@ -getSmartFileInfo(); - - return $smartFileInfo->getExtension() === 'json'; - } - - public function format(File $file, EditorConfigConfiguration $editorConfigConfiguration): void - { - $newFileContent = $this->jsonPrinter->print( - $file->getFileContent(), - $editorConfigConfiguration->getIndent(), - $editorConfigConfiguration->getNewLine() - ); - - $newFileContent .= $editorConfigConfiguration->getFinalNewline(); - - $file->changeFileContent($newFileContent); - } - - public function createDefaultEditorConfigConfigurationBuilder(): EditorConfigConfigurationBuilder - { - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - - $editorConfigConfigurationBuilder->withIndent(Indent::createSpaceWithSize(4)); - - return $editorConfigConfigurationBuilder; - } -} diff --git a/packages/FileFormatter/Formatter/XmlFileFormatter.php b/packages/FileFormatter/Formatter/XmlFileFormatter.php deleted file mode 100644 index e921ace941e..00000000000 --- a/packages/FileFormatter/Formatter/XmlFileFormatter.php +++ /dev/null @@ -1,161 +0,0 @@ -)(<)(\/*)#'; - - /** - * @see https://regex101.com/r/hSG1JT/1 - * @var string - */ - private const IS_OPENING_TAG_REGEX = '#^<[^\/]*>$#'; - - /** - * @see https://regex101.com/r/ywS62K/1 - * @var string - */ - private const IS_CLOSING_TAG_REGEX = '#^\s*<\/#'; - - private ?int $depth = null; - - private int $indent = 4; - - private string $padChar = ' '; - - private bool $preserveWhitespace = false; - - public function supports(File $file): bool - { - $smartFileInfo = $file->getSmartFileInfo(); - - return $smartFileInfo->getExtension() === 'xml'; - } - - public function format(File $file, EditorConfigConfiguration $editorConfigConfiguration): void - { - $this->padChar = $editorConfigConfiguration->getIndentStyleCharacter(); - $this->indent = $editorConfigConfiguration->getIndentSize(); - - $newFileContent = $this->formatXml($file->getFileContent(), $editorConfigConfiguration); - - $newFileContent .= $editorConfigConfiguration->getFinalNewline(); - - $file->changeFileContent($newFileContent); - } - - public function createDefaultEditorConfigConfigurationBuilder(): EditorConfigConfigurationBuilder - { - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - - $editorConfigConfigurationBuilder->withIndent(Indent::createTab()); - - return $editorConfigConfigurationBuilder; - } - - private function formatXml(string $xml, EditorConfigConfiguration $editorConfigConfiguration): string - { - $output = ''; - $this->depth = 0; - - $parts = $this->getXmlParts($xml); - - if (str_starts_with($parts[0], 'getNewline(); - } - - foreach ($parts as $part) { - $output .= $this->getOutputForPart($part, $editorConfigConfiguration); - } - - return trim($output); - } - - /** - * @return string[] - */ - private function getXmlParts(string $xml): array - { - $withNewLines = Strings::replace(trim($xml), self::XML_PARTS_REGEX, "$1\n$2$3"); - return explode("\n", $withNewLines); - } - - private function getOutputForPart(string $part, EditorConfigConfiguration $editorConfigConfiguration): string - { - $output = ''; - $this->runPre($part); - - if ($this->preserveWhitespace) { - $output .= $part . $editorConfigConfiguration->getNewline(); - } else { - $part = trim($part); - $output .= $this->getPaddedString($part) . $editorConfigConfiguration->getNewline(); - } - - $this->runPost($part); - - return $output; - } - - private function runPre(string $part): void - { - if ($this->isClosingTag($part)) { - --$this->depth; - } - } - - private function runPost(string $part): void - { - if ($this->isOpeningTag($part)) { - ++$this->depth; - } - if ($this->isClosingCdataTag($part)) { - $this->preserveWhitespace = false; - } - if ($this->isOpeningCdataTag($part)) { - $this->preserveWhitespace = true; - } - } - - private function getPaddedString(string $part): string - { - return str_pad($part, strlen($part) + ($this->depth * $this->indent), $this->padChar, STR_PAD_LEFT); - } - - private function isOpeningTag(string $part): bool - { - return (bool) Strings::match($part, self::IS_OPENING_TAG_REGEX); - } - - private function isClosingTag(string $part): bool - { - return (bool) Strings::match($part, self::IS_CLOSING_TAG_REGEX); - } - - private function isOpeningCdataTag(string $part): bool - { - return \str_contains($part, ''); - } -} diff --git a/packages/FileFormatter/Formatter/YamlFileFormatter.php b/packages/FileFormatter/Formatter/YamlFileFormatter.php deleted file mode 100644 index 474fd5c24dc..00000000000 --- a/packages/FileFormatter/Formatter/YamlFileFormatter.php +++ /dev/null @@ -1,43 +0,0 @@ -getSmartFileInfo(); - - return in_array($smartFileInfo->getExtension(), ['yaml', 'yml'], true); - } - - public function format(File $file, EditorConfigConfiguration $editorConfigConfiguration): void - { - $yaml = Yaml::parse($file->getFileContent()); - - $newFileContent = Yaml::dump($yaml, 99, $editorConfigConfiguration->getIndentSize()); - - $file->changeFileContent($newFileContent); - } - - public function createDefaultEditorConfigConfigurationBuilder(): EditorConfigConfigurationBuilder - { - $editorConfigConfigurationBuilder = new EditorConfigConfigurationBuilder(); - - $editorConfigConfigurationBuilder->withIndent(Indent::createSpaceWithSize(2)); - - return $editorConfigConfigurationBuilder; - } -} diff --git a/packages/FileFormatter/ValueObject/EditorConfigConfiguration.php b/packages/FileFormatter/ValueObject/EditorConfigConfiguration.php deleted file mode 100644 index 5c470b2b967..00000000000 --- a/packages/FileFormatter/ValueObject/EditorConfigConfiguration.php +++ /dev/null @@ -1,48 +0,0 @@ -newLine->__toString(); - } - - public function getFinalNewline(): string - { - return $this->insertFinalNewline ? $this->getNewLine() : ''; - } - - public function getIndent(): string - { - return $this->indent->__toString(); - } - - public function getIndentStyleCharacter(): string - { - return $this->indent->getIndentStyleCharacter(); - } - - public function getIndentStyle(): string - { - return $this->indent->getIndentStyle(); - } - - public function getIndentSize(): int - { - return $this->indent->getIndentSize(); - } -} diff --git a/packages/FileFormatter/ValueObject/EditorConfigOption.php b/packages/FileFormatter/ValueObject/EditorConfigOption.php deleted file mode 100644 index 907e134c431..00000000000 --- a/packages/FileFormatter/ValueObject/EditorConfigOption.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ - public const CHARACTERS = [ - self::SPACE => ' ', - self::TAB => "\t", - ]; - - /** - * @var string - */ - private const SPACE = 'space'; - - /** - * @var string - */ - private const TAB = 'tab'; - - /** - * @see https://regex101.com/r/A2XiaF/1 - * @var string - */ - private const VALID_INDENT_REGEX = '#^( *|\t+)$#'; - - /** - * @var int - */ - private const MINIMUM_SIZE = 1; - - /** - * @see https://regex101.com/r/3HFEjX/1 - * @var string - */ - private const PARSE_INDENT_REGEX = '/^(?P( +|\t+)).*/m'; - - private function __construct( - private string $string - ) { - } - - public function __toString(): string - { - return $this->string; - } - - public static function fromString(string $content): self - { - $match = Strings::match($content, self::VALID_INDENT_REGEX); - if ($match === null) { - throw InvalidIndentStringException::fromString($content); - } - - return new self($content); - } - - public static function createSpaceWithSize(int $size): self - { - return self::fromSizeAndStyle($size, self::SPACE); - } - - public static function createTab(): self - { - return self::fromSizeAndStyle(1, self::TAB); - } - - public static function fromSizeAndStyle(int $size, string $style): self - { - if ($size < self::MINIMUM_SIZE) { - throw InvalidIndentSizeException::fromSizeAndMinimumSize($size, self::MINIMUM_SIZE); - } - - if (! array_key_exists($style, self::CHARACTERS)) { - throw InvalidIndentStyleException::fromStyleAndAllowedStyles($style, array_keys(self::CHARACTERS)); - } - - $value = str_repeat(self::CHARACTERS[$style], $size); - - return new self($value); - } - - public static function fromContent(string $content): self - { - $match = Strings::match($content, self::PARSE_INDENT_REGEX); - if (isset($match['indent'])) { - return self::fromString($match['indent']); - } - - throw ParseIndentException::fromString($content); - } - - public function getIndentSize(): int - { - return strlen($this->string); - } - - public function getIndentStyle(): string - { - return $this->startsWithSpace() ? self::SPACE : self::TAB; - } - - public function getIndentStyleCharacter(): string - { - return $this->startsWithSpace() ? self::CHARACTERS[self::SPACE] : self::CHARACTERS[self::TAB]; - } - - private function startsWithSpace(): bool - { - return \str_starts_with($this->string, ' '); - } -} diff --git a/packages/FileFormatter/ValueObject/NewLine.php b/packages/FileFormatter/ValueObject/NewLine.php deleted file mode 100644 index a8d4bd21673..00000000000 --- a/packages/FileFormatter/ValueObject/NewLine.php +++ /dev/null @@ -1,97 +0,0 @@ - - */ - private const ALLOWED_END_OF_LINE = [ - self::LINE_FEED => "\n", - self::CARRIAGE_RETURN => "\r", - self::CARRIAGE_RETURN_LINE_FEED => "\r\n", - ]; - - /** - * @see https://regex101.com/r/icaBBp/1 - * @var string - */ - private const NEWLINE_REGEX = '#(?P\r\n|\n|\r)#'; - - /** - * @see https://regex101.com/r/WrY9ZW/1/ - * @var string - */ - private const VALID_NEWLINE_REGEX = '#^(?>\r\n|\n|\r)$#'; - - private function __construct( - private string $string - ) { - } - - public function __toString(): string - { - return $this->string; - } - - public static function fromSingleCharacter(string $content): self - { - $matches = Strings::match($content, self::VALID_NEWLINE_REGEX); - if ($matches === null) { - throw InvalidNewLineStringException::fromString($content); - } - - return new self($content); - } - - public static function fromContent(string $content): self - { - $match = Strings::match($content, self::NEWLINE_REGEX); - if (isset($match['newLine'])) { - return self::fromSingleCharacter($match['newLine']); - } - - return self::fromSingleCharacter(PHP_EOL); - } - - public static function fromEditorConfig(string $endOfLine): self - { - if (! array_key_exists($endOfLine, self::ALLOWED_END_OF_LINE)) { - $allowedEndOfLineValues = array_keys(self::ALLOWED_END_OF_LINE); - $message = sprintf( - 'The endOfLine "%s" is not allowed. Allowed are "%s"', - $endOfLine, - implode(',', $allowedEndOfLineValues) - ); - throw InvalidNewLineStringException::create($message); - } - - return self::fromSingleCharacter(self::ALLOWED_END_OF_LINE[$endOfLine]); - } -} diff --git a/packages/FileFormatter/ValueObjectFactory/EditorConfigConfigurationBuilder.php b/packages/FileFormatter/ValueObjectFactory/EditorConfigConfigurationBuilder.php deleted file mode 100644 index 12aced2aa94..00000000000 --- a/packages/FileFormatter/ValueObjectFactory/EditorConfigConfigurationBuilder.php +++ /dev/null @@ -1,81 +0,0 @@ -newLine = NewLine::fromEditorConfig('lf'); - } - - public static function create(): self - { - return new self(); - } - - public function withNewLine(NewLine $newLine): self - { - $this->newLine = $newLine; - - return $this; - } - - public function withIndent(Indent $indent): self - { - $this->indentSize = $indent->getIndentSize(); - $this->indentStyle = $indent->getIndentStyle(); - - return $this; - } - - public function withIndentStyle(string $indentStyle): self - { - $this->indentStyle = $indentStyle; - - return $this; - } - - public function withIndentSize(int $indentSize): self - { - $this->indentSize = $indentSize; - - return $this; - } - - public function withInsertFinalNewline(bool $insertFinalNewline): self - { - $this->insertFinalNewline = $insertFinalNewline; - - return $this; - } - - public function withEndOfLineFromEditorConfig(string $endOfLine): self - { - $this->newLine = NewLine::fromEditorConfig($endOfLine); - - return $this; - } - - public function build(): EditorConfigConfiguration - { - $newLine = $this->newLine; - - return new EditorConfigConfiguration( - Indent::fromSizeAndStyle($this->indentSize, $this->indentStyle), - $newLine, - $this->insertFinalNewline - ); - } -} diff --git a/packages/FileSystemRector/Contract/AddedFileInterface.php b/packages/FileSystemRector/Contract/AddedFileInterface.php deleted file mode 100644 index 01f7ed5f174..00000000000 --- a/packages/FileSystemRector/Contract/AddedFileInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -parser->parseFileInfo($smartFileInfo); - $file = new File($smartFileInfo, $smartFileInfo->getContents()); - - return $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($file, $oldStmts); - } -} diff --git a/packages/FileSystemRector/ValueObject/AddedFileWithContent.php b/packages/FileSystemRector/ValueObject/AddedFileWithContent.php deleted file mode 100644 index 4c4dc91776a..00000000000 --- a/packages/FileSystemRector/ValueObject/AddedFileWithContent.php +++ /dev/null @@ -1,40 +0,0 @@ -filePath); - if ($realpath === false) { - throw new ShouldNotHappenException(); - } - - return $realpath; - } - - public function getFilePath(): string - { - return $this->filePath; - } - - public function getFileContent(): string - { - return $this->fileContent; - } -} diff --git a/packages/FileSystemRector/ValueObject/AddedFileWithNodes.php b/packages/FileSystemRector/ValueObject/AddedFileWithNodes.php deleted file mode 100644 index cb9df83a8b0..00000000000 --- a/packages/FileSystemRector/ValueObject/AddedFileWithNodes.php +++ /dev/null @@ -1,34 +0,0 @@ -filePath; - } - - /** - * @return Node[] - */ - public function getNodes(): array - { - return $this->nodes; - } -} diff --git a/packages/FileSystemRector/ValueObjectFactory/AddedFileWithNodesFactory.php b/packages/FileSystemRector/ValueObjectFactory/AddedFileWithNodesFactory.php deleted file mode 100644 index 3968340d121..00000000000 --- a/packages/FileSystemRector/ValueObjectFactory/AddedFileWithNodesFactory.php +++ /dev/null @@ -1,129 +0,0 @@ -getNewStmts(); - - $currentNamespace = $this->betterNodeFinder->findFirstInstanceOf($fileNodes, Namespace_::class); - - // file without namespace → skip - if (! $currentNamespace instanceof Namespace_) { - return null; - } - - if ($currentNamespace->name === null) { - return null; - } - - // is already in the right group - $currentNamespaceName = $currentNamespace->name->toString(); - if (\str_ends_with($currentNamespaceName, '\\' . $desiredGroupName)) { - return null; - } - - $oldClassName = $currentNamespaceName . '\\' . $this->fileInfoDeletionAnalyzer->clearNameFromTestingPrefix( - $oldFileInfo->getBasenameWithoutSuffix() - ); - - // change namespace to new one - $newNamespaceName = $this->createNewNamespaceName($desiredGroupName, $currentNamespace); - $newClassName = $this->createNewClassName($oldFileInfo, $newNamespaceName); - - // classes are identical, no rename - if ($oldClassName === $newClassName) { - return null; - } - - if (Strings::match($oldClassName, '#\b' . $desiredGroupName . '\b#')) { - return null; - } - - // 1. rename namespace - $this->renameNamespace($file->getNewStmts(), $newNamespaceName); - - // 2. return changed nodes and new file destination - $newFileDestination = $this->fileRelocationResolver->createNewFileDestination( - $oldFileInfo, - $desiredGroupName, - $this->categoryNamespaceProvider->provide() - ); - - // 3. update fully qualifed name of the class like - will be used further - $classLike = $this->betterNodeFinder->findFirstInstanceOf($fileNodes, ClassLike::class); - if (! $classLike instanceof ClassLike) { - return null; - } - - // clone to prevent deep override - $classLike = clone $classLike; - $classLike->namespacedName = new FullyQualified($newClassName); - - $this->renamedClassesDataCollector->addOldToNewClass($oldClassName, $newClassName); - - return new AddedFileWithNodes($newFileDestination, $fileNodes); - } - - private function createNewNamespaceName(string $desiredGroupName, Namespace_ $currentNamespace): string - { - return $this->fileRelocationResolver->resolveNewNamespaceName( - $currentNamespace, - $desiredGroupName, - $this->categoryNamespaceProvider->provide() - ); - } - - private function createNewClassName(SmartFileInfo $smartFileInfo, string $newNamespaceName): string - { - $basename = $this->fileInfoDeletionAnalyzer->clearNameFromTestingPrefix( - $smartFileInfo->getBasenameWithoutSuffix() - ); - return $newNamespaceName . '\\' . $basename; - } - - /** - * @param Node[] $nodes - */ - private function renameNamespace(array $nodes, string $newNamespaceName): void - { - foreach ($nodes as $node) { - if (! $node instanceof Namespace_) { - continue; - } - - $node->name = new Name($newNamespaceName); - } - } -} diff --git a/packages/ListReporting/Contract/Output/ShowOutputFormatterInterface.php b/packages/ListReporting/Contract/Output/ShowOutputFormatterInterface.php deleted file mode 100644 index e7d799bc5c5..00000000000 --- a/packages/ListReporting/Contract/Output/ShowOutputFormatterInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -outputStyle->title('Loaded Rector rules'); - - foreach ($rectors as $rector) { - $this->outputStyle->writeln(' * ' . $rector::class); - } - - $message = sprintf('%d loaded Rectors', $rectorCount); - $this->outputStyle->success($message); - } - - public function getName(): string - { - return self::NAME; - } -} diff --git a/packages/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php b/packages/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php deleted file mode 100644 index 3d1c4fb370e..00000000000 --- a/packages/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php +++ /dev/null @@ -1,132 +0,0 @@ -items; - if (count($arrayItems) !== 2) { - return null; - } - - if ($array->items[0] === null) { - return null; - } - - if ($array->items[1] === null) { - return null; - } - - $secondItemValue = $array->items[1]->value; - - if (! $secondItemValue instanceof String_) { - return null; - } - - // $this, self, static, FQN - $firstItemValue = $array->items[0]->value; - - // static ::class reference? - if ($firstItemValue instanceof ClassConstFetch) { - $calleeType = $this->resolveClassConstFetchType($firstItemValue); - } else { - $calleeType = $this->nodeTypeResolver->resolve($firstItemValue); - } - - if (! $calleeType instanceof TypeWithClassName) { - return null; - } - - if ($this->isCallbackAtFunctionNames($array, ['register_shutdown_function', 'forward_static_call'])) { - return null; - } - - $className = $calleeType->getClassName(); - $methodName = $secondItemValue->value; - return new ArrayCallable($firstItemValue, $className, $methodName); - } - - /** - * @param string[] $functionNames - */ - private function isCallbackAtFunctionNames(Array_ $array, array $functionNames): bool - { - $parentNode = $array->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Arg) { - return false; - } - - $parentParentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentParentNode instanceof FuncCall) { - return false; - } - - return $this->nodeNameResolver->isNames($parentParentNode, $functionNames); - } - - private function resolveClassConstFetchType(ClassConstFetch $classConstFetch): MixedType | ObjectType - { - $classConstantReference = $this->valueResolver->getValue($classConstFetch); - if ($classConstantReference === 'static') { - $classConstantReference = $classConstFetch->getAttribute(AttributeKey::CLASS_NAME); - } - - // non-class value - if (! is_string($classConstantReference)) { - return new MixedType(); - } - - if (! $this->reflectionProvider->hasClass($classConstantReference)) { - return new MixedType(); - } - - $classReflection = $this->reflectionProvider->getClass($classConstantReference); - $scope = $classConstFetch->getAttribute(AttributeKey::SCOPE); - $hasConstruct = $classReflection->hasMethod(MethodName::CONSTRUCT); - - if ($hasConstruct) { - $methodReflection = $classReflection->getMethod(MethodName::CONSTRUCT, $scope); - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); - - foreach ($parametersAcceptor->getParameters() as $parameterReflection) { - if ($parameterReflection->getDefaultValue() === null) { - return new MixedType(); - } - } - } - - return new ObjectType($classConstantReference, null, $classReflection); - } -} diff --git a/packages/NodeCollector/NodeAnalyzer/BooleanAndAnalyzer.php b/packages/NodeCollector/NodeAnalyzer/BooleanAndAnalyzer.php deleted file mode 100644 index f86d1d83aa4..00000000000 --- a/packages/NodeCollector/NodeAnalyzer/BooleanAndAnalyzer.php +++ /dev/null @@ -1,32 +0,0 @@ -right; - $booleanAnd = $booleanAnd->left; - - if (! $booleanAnd instanceof BooleanAnd) { - $conditions[] = $booleanAnd; - break; - } - } - - krsort($conditions); - return $conditions; - } -} diff --git a/packages/NodeCollector/ScopeResolver/ParentClassScopeResolver.php b/packages/NodeCollector/ScopeResolver/ParentClassScopeResolver.php deleted file mode 100644 index f95c9d9c4c7..00000000000 --- a/packages/NodeCollector/ScopeResolver/ParentClassScopeResolver.php +++ /dev/null @@ -1,33 +0,0 @@ -getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $parentClassReflection = $classReflection->getParentClass(); - if ($parentClassReflection === false) { - return null; - } - - return $parentClassReflection->getName(); - } -} diff --git a/packages/NodeCollector/StaticAnalyzer.php b/packages/NodeCollector/StaticAnalyzer.php deleted file mode 100644 index f51a875ab5c..00000000000 --- a/packages/NodeCollector/StaticAnalyzer.php +++ /dev/null @@ -1,52 +0,0 @@ -reflectionProvider->hasClass($className)) { - return false; - } - - $classReflection = $this->reflectionProvider->getClass($className); - - if ($classReflection->hasNativeMethod($methodName)) { - $methodReflection = $classReflection->getNativeMethod($methodName); - if ($methodReflection->isStatic()) { - return true; - } - } - - // could be static in doc type magic - // @see https://regex101.com/r/tlvfTB/1 - return $this->hasStaticAnnotation($methodName, $classReflection); - } - - private function hasStaticAnnotation(string $methodName, ClassReflection $classReflection): bool - { - $resolvedPhpDocBlock = $classReflection->getResolvedPhpDoc(); - if (! $resolvedPhpDocBlock instanceof ResolvedPhpDocBlock) { - return false; - } - - // @see https://regex101.com/r/7Zkej2/1 - return (bool) Strings::match( - $resolvedPhpDocBlock->getPhpDocString(), - '#@method\s*static\s*((([\w\|\\\\]+)|\$this)*+(\[\])*)*\s+\b' . $methodName . '\b#' - ); - } -} diff --git a/packages/NodeCollector/ValueObject/ArrayCallable.php b/packages/NodeCollector/ValueObject/ArrayCallable.php deleted file mode 100644 index 7410c3306cf..00000000000 --- a/packages/NodeCollector/ValueObject/ArrayCallable.php +++ /dev/null @@ -1,32 +0,0 @@ -class; - } - - public function getMethod(): string - { - return $this->method; - } - - public function getCallerExpr(): Expr - { - return $this->callerExpr; - } -} diff --git a/packages/NodeNameResolver/Contract/NodeNameResolverInterface.php b/packages/NodeNameResolver/Contract/NodeNameResolverInterface.php deleted file mode 100644 index cb02e3e864f..00000000000 --- a/packages/NodeNameResolver/Contract/NodeNameResolverInterface.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ - public function getNode(): string; - - public function resolve(Node $node): ?string; -} diff --git a/packages/NodeNameResolver/Error/InvalidNameNodeReporter.php b/packages/NodeNameResolver/Error/InvalidNameNodeReporter.php deleted file mode 100644 index 2b6cf7f64f9..00000000000 --- a/packages/NodeNameResolver/Error/InvalidNameNodeReporter.php +++ /dev/null @@ -1,86 +0,0 @@ -name"', $node::class); - - $file = $this->currentFileProvider->getFile(); - - if ($file instanceof File) { - $smartFileInfo = $file->getSmartFileInfo(); - $message .= PHP_EOL . PHP_EOL; - $message .= sprintf( - 'Caused in "%s" file on line %d on code "%s"', - $smartFileInfo->getRelativeFilePathFromCwd(), - $node->getStartLine(), - $this->betterStandardPrinter->print($node) - ); - } - - $backtrace = debug_backtrace(); - $rectorBacktrace = $this->matchRectorBacktraceCall($backtrace); - - if ($rectorBacktrace) { - // issues to find the file in prefixed - if (file_exists($rectorBacktrace[self::FILE])) { - $smartFileInfo = new SmartFileInfo($rectorBacktrace[self::FILE]); - $fileAndLine = $smartFileInfo->getRelativeFilePathFromCwd() . ':' . $rectorBacktrace['line']; - } else { - $fileAndLine = $rectorBacktrace[self::FILE] . ':' . $rectorBacktrace['line']; - } - - $message .= PHP_EOL . PHP_EOL; - $message .= sprintf('Look at "%s"', $fileAndLine); - } - - throw new ShouldNotHappenException($message); - } - - /** - * @param mixed[] $backtrace - * @return string[]|null - */ - private function matchRectorBacktraceCall(array $backtrace): ?array - { - foreach ($backtrace as $singleBacktrace) { - if (! isset($singleBacktrace['object'])) { - continue; - } - - // match a Rector class - if (! is_a($singleBacktrace['object'], RectorInterface::class)) { - continue; - } - - return $singleBacktrace; - } - - return $backtrace[1] ?? null; - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver.php b/packages/NodeNameResolver/NodeNameResolver.php deleted file mode 100644 index e5e7a8d605d..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver.php +++ /dev/null @@ -1,252 +0,0 @@ -isName($node, $name)) { - return true; - } - } - - return false; - } - - /** - * @param Node|Node[] $node - */ - public function isName(Node | array $node, string $name): bool - { - if ($node instanceof MethodCall) { - return false; - } - if ($node instanceof StaticCall) { - return false; - } - $nodes = is_array($node) ? $node : [$node]; - - foreach ($nodes as $node) { - if ($this->isSingleName($node, $name)) { - return true; - } - } - - return false; - } - - public function isCaseSensitiveName(Node $node, string $name): bool - { - if ($name === '') { - return false; - } - - if ($node instanceof MethodCall) { - return false; - } - if ($node instanceof StaticCall) { - return false; - } - - $resolvedName = $this->getName($node); - if ($resolvedName === null) { - return false; - } - - return $name === $resolvedName; - } - - public function getName(Node | string $node): ?string - { - if (is_string($node)) { - return $node; - } - - // useful for looped imported names - $namespacedName = $node->getAttribute(AttributeKey::NAMESPACED_NAME); - if (is_string($namespacedName)) { - return $namespacedName; - } - - if ($node instanceof MethodCall || $node instanceof StaticCall) { - if ($this->isCallOrIdentifier($node->name)) { - return null; - } - - $this->invalidNameNodeReporter->reportInvalidNodeForName($node); - } - - foreach ($this->nodeNameResolvers as $nodeNameResolver) { - if (! is_a($node, $nodeNameResolver->getNode(), true)) { - continue; - } - - return $nodeNameResolver->resolve($node); - } - - // more complex - if (! property_exists($node, 'name')) { - return null; - } - - // unable to resolve - if ($node->name instanceof Expr) { - return null; - } - - return (string) $node->name; - } - - public function areNamesEqual(Node $firstNode, Node $secondNode): bool - { - $secondResolvedName = $this->getName($secondNode); - if ($secondResolvedName === null) { - return false; - } - - return $this->isName($firstNode, $secondResolvedName); - } - - /** - * @param Name[]|Node[] $nodes - * @return string[] - */ - public function getNames(array $nodes): array - { - $names = []; - foreach ($nodes as $node) { - $name = $this->getName($node); - if (! is_string($name)) { - throw new ShouldNotHappenException(); - } - - $names[] = $name; - } - - return $names; - } - - public function isLocalPropertyFetchNamed(Node $node, string $name): bool - { - if (! $node instanceof PropertyFetch) { - return false; - } - - if ($node->var instanceof MethodCall) { - return false; - } - - if (! $this->isName($node->var, 'this')) { - return false; - } - - if ($node->name instanceof Expr) { - return false; - } - - return $this->isName($node->name, $name); - } - - /** - * Ends with ucname - * Starts with adjective, e.g. (Post $firstPost, Post $secondPost) - */ - public function endsWith(string $currentName, string $expectedName): bool - { - $suffixNamePattern = '#\w+' . ucfirst($expectedName) . '#'; - return (bool) Strings::match($currentName, $suffixNamePattern); - } - - public function getShortName(string | Name | Identifier | ClassLike $name): string - { - return $this->classNaming->getShortName($name); - } - - /** - * @param array $renameMap - */ - public function matchNameFromMap(Node $node, array $renameMap): ?string - { - $name = $this->getName($node); - return $renameMap[$name] ?? null; - } - - private function isCallOrIdentifier(Node $node): bool - { - if (! $node instanceof Expr) { - return $node instanceof Identifier; - } - - return $this->callAnalyzer->isObjectCall($node); - } - - private function isSingleName(Node $node, string $name): bool - { - if ($node instanceof MethodCall) { - // method call cannot have a name, only the variable or method name - return false; - } - - $resolvedName = $this->getName($node); - if ($resolvedName === null) { - return false; - } - - if ($name === '') { - return false; - } - - // is probably regex pattern - if ($this->regexPatternDetector->isRegexPattern($name)) { - return (bool) Strings::match($resolvedName, $name); - } - - // is probably fnmatch - if (\str_contains($name, '*')) { - return fnmatch($name, $resolvedName, FNM_NOESCAPE); - } - - // special case - if ($name === 'Object') { - return $name === $resolvedName; - } - - return strtolower($resolvedName) === strtolower($name); - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/ClassConstFetchNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/ClassConstFetchNameResolver.php deleted file mode 100644 index e934c8fccd2..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/ClassConstFetchNameResolver.php +++ /dev/null @@ -1,47 +0,0 @@ -nodeNameResolver = $nodeNameResolver; - } - - /** - * @return class-string - */ - public function getNode(): string - { - return ClassConstFetch::class; - } - - /** - * @param ClassConstFetch $node - */ - public function resolve(Node $node): ?string - { - $class = $this->nodeNameResolver->getName($node->class); - $name = $this->nodeNameResolver->getName($node->name); - if ($class === null) { - return null; - } - if ($name === null) { - return null; - } - - return $class . '::' . $name; - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/ClassConstNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/ClassConstNameResolver.php deleted file mode 100644 index 3bc107ab916..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/ClassConstNameResolver.php +++ /dev/null @@ -1,44 +0,0 @@ -nodeNameResolver = $nodeNameResolver; - } - - /** - * @return class-string - */ - public function getNode(): string - { - return ClassConst::class; - } - - /** - * @param ClassConst $node - */ - public function resolve(Node $node): ?string - { - if ($node->consts === []) { - return null; - } - - $onlyConstant = $node->consts[0]; - - return $this->nodeNameResolver->getName($onlyConstant); - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/ClassNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/ClassNameResolver.php deleted file mode 100644 index e6e1f59e4c2..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/ClassNameResolver.php +++ /dev/null @@ -1,47 +0,0 @@ -nodeNameResolver = $nodeNameResolver; - } - - /** - * @return class-string - */ - public function getNode(): string - { - return ClassLike::class; - } - - /** - * @param Class_ $node - */ - public function resolve(Node $node): ?string - { - if (property_exists($node, 'namespacedName')) { - return $node->namespacedName->toString(); - } - - if ($node->name === null) { - return null; - } - - return $this->nodeNameResolver->getName($node->name); - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/EmptyNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/EmptyNameResolver.php deleted file mode 100644 index cabc52ff45e..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/EmptyNameResolver.php +++ /dev/null @@ -1,28 +0,0 @@ - - */ - public function getNode(): string - { - return Empty_::class; - } - - /** - * @param Empty_ $node - */ - public function resolve(Node $node): ?string - { - return 'empty'; - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/FuncCallNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/FuncCallNameResolver.php deleted file mode 100644 index d00ed5b3e0e..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/FuncCallNameResolver.php +++ /dev/null @@ -1,55 +0,0 @@ - - */ - public function getNode(): string - { - return FuncCall::class; - } - - /** - * If some function is namespaced, it will be used over global one. - * But only if it really exists. - * - * @param FuncCall $node - */ - public function resolve(Node $node): ?string - { - if ($node->name instanceof Expr) { - return null; - } - - $functionName = $node->name; - - $namespaceName = $functionName->getAttribute(AttributeKey::NAMESPACED_NAME); - if ($namespaceName instanceof FullyQualified) { - $functionFqnName = $namespaceName->toString(); - - if ($this->reflectionProvider->hasFunction($namespaceName, null)) { - return $functionFqnName; - } - } - - return (string) $functionName; - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/NameNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/NameNameResolver.php deleted file mode 100644 index 8f8e221ca84..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/NameNameResolver.php +++ /dev/null @@ -1,47 +0,0 @@ - - */ - public function getNode(): string - { - return Name::class; - } - - /** - * @param Name $node - */ - public function resolve(Node $node): ?string - { - // possible function parent - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof FuncCall) { - return $this->funcCallNameResolver->resolve($parent); - } - - $resolvedName = $node->getAttribute(AttributeKey::RESOLVED_NAME); - if ($resolvedName instanceof FullyQualified) { - return $resolvedName->toString(); - } - - return $node->toString(); - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/ParamNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/ParamNameResolver.php deleted file mode 100644 index dbd30dc93d1..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/ParamNameResolver.php +++ /dev/null @@ -1,38 +0,0 @@ -nodeNameResolver = $nodeNameResolver; - } - - /** - * @return class-string - */ - public function getNode(): string - { - return Param::class; - } - - /** - * @param Param $node - */ - public function resolve(Node $node): ?string - { - return $this->nodeNameResolver->getName($node->var); - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/PropertyNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/PropertyNameResolver.php deleted file mode 100644 index 83c148c36cc..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/PropertyNameResolver.php +++ /dev/null @@ -1,44 +0,0 @@ -nodeNameResolver = $nodeNameResolver; - } - - /** - * @return class-string - */ - public function getNode(): string - { - return Property::class; - } - - /** - * @param Property $node - */ - public function resolve(Node $node): ?string - { - if ($node->props === []) { - return null; - } - - $onlyProperty = $node->props[0]; - - return $this->nodeNameResolver->getName($onlyProperty); - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/UseNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/UseNameResolver.php deleted file mode 100644 index 6e534fcc3af..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/UseNameResolver.php +++ /dev/null @@ -1,44 +0,0 @@ -nodeNameResolver = $nodeNameResolver; - } - - /** - * @return class-string - */ - public function getNode(): string - { - return Use_::class; - } - - /** - * @param Use_ $node - */ - public function resolve(Node $node): ?string - { - if ($node->uses === []) { - return null; - } - - $onlyUse = $node->uses[0]; - - return $this->nodeNameResolver->getName($onlyUse); - } -} diff --git a/packages/NodeNameResolver/NodeNameResolver/VariableNameResolver.php b/packages/NodeNameResolver/NodeNameResolver/VariableNameResolver.php deleted file mode 100644 index 0ccf9102cd8..00000000000 --- a/packages/NodeNameResolver/NodeNameResolver/VariableNameResolver.php +++ /dev/null @@ -1,48 +0,0 @@ - - */ - public function getNode(): string - { - return Variable::class; - } - - /** - * @param Variable $node - */ - public function resolve(Node $node): ?string - { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - - // skip $some->$dynamicMethodName() - if ($parentNode instanceof MethodCall && $node === $parentNode->name) { - return null; - } - - // skip $some->$dynamicPropertyName - if ($parentNode instanceof PropertyFetch && $node === $parentNode->name) { - return null; - } - - if ($node->name instanceof Expr) { - return null; - } - - return $node->name; - } -} diff --git a/packages/NodeNameResolver/Regex/RegexPatternDetector.php b/packages/NodeNameResolver/Regex/RegexPatternDetector.php deleted file mode 100644 index 165c9a37788..00000000000 --- a/packages/NodeNameResolver/Regex/RegexPatternDetector.php +++ /dev/null @@ -1,32 +0,0 @@ -> - */ - private const BREAK_NODES = [FunctionLike::class, ClassMethod::class]; - - /** - * @var array> - */ - private const LOOP_NODES = [For_::class, Foreach_::class, While_::class, Do_::class]; - - public function __construct( - private BetterNodeFinder $betterNodeFinder, - private NodeTypeResolver $nodeTypeResolver, - private ParentFinder $parentFinder - ) { - } - - public function isInLoop(Node $node): bool - { - $stopNodes = array_merge(self::LOOP_NODES, self::BREAK_NODES); - - $firstParent = $this->parentFinder->findByTypes($node, $stopNodes); - if (! $firstParent instanceof Node) { - return false; - } - - foreach (self::LOOP_NODES as $type) { - if (is_a($firstParent, $type, true)) { - return true; - } - } - - return false; - } - - public function isInSwitch(Node $node): bool - { - return (bool) $this->betterNodeFinder->findParentType($node, Switch_::class); - } - - public function isInIf(Node $node): bool - { - $breakNodes = array_merge([If_::class], self::BREAK_NODES); - - $previousNode = $this->parentFinder->findByTypes($node, $breakNodes); - - if (! $previousNode instanceof Node) { - return false; - } - - return $previousNode instanceof If_; - } - - public function isHasAssignWithIndirectReturn(Node $node, If_ $if): bool - { - $loopNodes = self::LOOP_NODES; - - foreach ($loopNodes as $loopNode) { - $loopObjectType = new ObjectType($loopNode); - $parentType = $this->nodeTypeResolver->resolve($node); - $superType = $parentType->isSuperTypeOf($loopObjectType); - $isLoopType = $superType->yes(); - - if (! $isLoopType) { - continue; - } - - $next = $node->getAttribute(AttributeKey::NEXT_NODE); - if ($next instanceof Node) { - if ($next instanceof Return_ && $next->expr === null) { - continue; - } - - $hasAssign = (bool) $this->betterNodeFinder->findInstanceOf($if->stmts, Assign::class); - if (! $hasAssign) { - continue; - } - - return true; - } - } - - return false; - } -} diff --git a/packages/NodeNestingScope/FlowOfControlLocator.php b/packages/NodeNestingScope/FlowOfControlLocator.php deleted file mode 100644 index 5480a0b26c2..00000000000 --- a/packages/NodeNestingScope/FlowOfControlLocator.php +++ /dev/null @@ -1,62 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE)) { - if ($currentNode instanceof Expression) { - continue; - } - - if (! $currentNode instanceof Node) { - continue; - } - - if ($functionLike === $currentNode) { - // to high - break; - } - - $nestingHash .= $this->resolveBinaryOpNestingHash($currentNode, $previous); - - $nestingHash .= spl_object_hash($currentNode); - - $previous = $currentNode; - } - - return $nestingHash; - } - - private function resolveBinaryOpNestingHash(Node $currentNode, Node $previous): string - { - if (! $currentNode instanceof BinaryOp) { - return ''; - } - - // left && right have differnt nesting - if ($currentNode->left === $previous) { - return 'binary_left__'; - } - - if ($currentNode->right === $previous) { - return 'binary_right__'; - } - - return ''; - } -} diff --git a/packages/NodeNestingScope/NodeFinder/ScopeAwareNodeFinder.php b/packages/NodeNestingScope/NodeFinder/ScopeAwareNodeFinder.php deleted file mode 100644 index e3b9558c25e..00000000000 --- a/packages/NodeNestingScope/NodeFinder/ScopeAwareNodeFinder.php +++ /dev/null @@ -1,78 +0,0 @@ -> $allowedTypes - */ - public function findParentType(Node $node, array $allowedTypes): ?Node - { - $callable = function (Node $node) use ($allowedTypes): bool { - foreach ($allowedTypes as $allowedType) { - if (! is_a($node, $allowedType)) { - continue; - } - - return true; - } - - return false; - }; - - return $this->findParent($node, $callable, $allowedTypes); - } - - /** - * Find node based on $callable or null, when the nesting scope is broken - * @param array> $allowedTypes - */ - public function findParent(Node $node, callable $callable, array $allowedTypes): ?Node - { - /** @var array> $parentNestingBreakTypes */ - $parentNestingBreakTypes = array_diff(ControlStructure::BREAKING_SCOPE_NODE_TYPES, $allowedTypes); - - $this->isBreakingNodeFoundFirst = false; - - $foundNode = $this->betterNodeFinder->findFirstPrevious($node, function (Node $node) use ( - $callable, - $parentNestingBreakTypes - ): bool { - if ($callable($node)) { - return true; - } - - foreach ($parentNestingBreakTypes as $parentNestingBreakType) { - if (! is_a($node, $parentNestingBreakType, true)) { - continue; - } - - $this->isBreakingNodeFoundFirst = true; - return true; - } - - return false; - }); - - if ($this->isBreakingNodeFoundFirst) { - return null; - } - - return $foundNode; - } -} diff --git a/packages/NodeNestingScope/ParentFinder.php b/packages/NodeNestingScope/ParentFinder.php deleted file mode 100644 index e6917c52164..00000000000 --- a/packages/NodeNestingScope/ParentFinder.php +++ /dev/null @@ -1,41 +0,0 @@ -> $types - * @return T|null - */ - public function findByTypes(Node $node, array $types): ?Node - { - Assert::allIsAOf($types, Node::class); - - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { - return null; - } - - do { - foreach ($types as $type) { - if (is_a($parent, $type, true)) { - return $parent; - } - } - - if ($parent === null) { - return null; - } - } while ($parent = $parent->getAttribute(AttributeKey::PARENT_NODE)); - - return null; - } -} diff --git a/packages/NodeNestingScope/ParentScopeFinder.php b/packages/NodeNestingScope/ParentScopeFinder.php deleted file mode 100644 index 1e1700c56d1..00000000000 --- a/packages/NodeNestingScope/ParentScopeFinder.php +++ /dev/null @@ -1,31 +0,0 @@ -parentFinder->findByTypes($node, [ - Closure::class, - Function_::class, - ClassMethod::class, - Class_::class, - Namespace_::class, - ]); - } -} diff --git a/packages/NodeNestingScope/ScopeNestingComparator.php b/packages/NodeNestingScope/ScopeNestingComparator.php deleted file mode 100644 index 75024a967e8..00000000000 --- a/packages/NodeNestingScope/ScopeNestingComparator.php +++ /dev/null @@ -1,109 +0,0 @@ -parentFinder->findByTypes( - $return, - ControlStructure::RETURN_ISOLATING_SCOPE_NODE_TYPES - ); - - return $this->nodeComparator->areNodesEqual($firstNodeScopeNode, $secondNodeScopeNode); - } - - public function areScopeNestingEqual(Node $firstNode, Node $secondNode): bool - { - $firstNodeScopeNode = $this->findParentControlStructure($firstNode); - $secondNodeScopeNode = $this->findParentControlStructure($secondNode); - - return $this->nodeComparator->areNodesEqual($firstNodeScopeNode, $secondNodeScopeNode); - } - - public function isNodeConditionallyScoped(Expr $expr): bool - { - $foundParent = $this->parentFinder->findByTypes( - $expr, - ControlStructure::CONDITIONAL_NODE_SCOPE_TYPES + [FunctionLike::class] - ); - - if (! $foundParent instanceof Node) { - return false; - } - - // is in both if/else branches - if ($this->isInBothIfElseBranch($foundParent, $expr)) { - return false; - } - if (! $foundParent instanceof Else_) { - return ! $foundParent instanceof FunctionLike; - } - if (! $this->nodeComparator->areNodesEqual($expr, $this->doubleIfBranchExprs)) { - return ! $foundParent instanceof FunctionLike; - } - return false; - } - - public function isInBothIfElseBranch(Node $foundParentNode, Expr $seekedExpr): bool - { - if ($foundParentNode instanceof Else_) { - return $this->nodeComparator->isNodeEqual($seekedExpr, $this->doubleIfBranchExprs); - } - - if (! $foundParentNode instanceof If_) { - return false; - } - - $foundIfNode = $this->betterNodeFinder->find( - $foundParentNode->stmts, - fn ($node): bool => $this->nodeComparator->areNodesEqual($node, $seekedExpr) - ); - - if ($foundParentNode->else === null) { - return false; - } - - $foundElseNode = $this->betterNodeFinder->find( - $foundParentNode->else, - fn ($node): bool => $this->nodeComparator->areNodesEqual($node, $seekedExpr) - ); - - if ($foundIfNode && $foundElseNode) { - $this->doubleIfBranchExprs[] = $seekedExpr; - return true; - } - - return false; - } - - private function findParentControlStructure(Node $node): ?Node - { - return $this->parentFinder->findByTypes($node, ControlStructure::BREAKING_SCOPE_NODE_TYPES); - } -} diff --git a/packages/NodeNestingScope/ValueObject/ControlStructure.php b/packages/NodeNestingScope/ValueObject/ControlStructure.php deleted file mode 100644 index a4a7db114fe..00000000000 --- a/packages/NodeNestingScope/ValueObject/ControlStructure.php +++ /dev/null @@ -1,69 +0,0 @@ -> - */ - public const RETURN_ISOLATING_SCOPE_NODE_TYPES = [ - Function_::class, - ClassMethod::class, - Closure::class, - ArrowFunction::class, - ]; - - /** - * @var array> - */ - public const BREAKING_SCOPE_NODE_TYPES = [ - For_::class, - Foreach_::class, - If_::class, - While_::class, - Do_::class, - Else_::class, - ElseIf_::class, - Catch_::class, - Case_::class, - FunctionLike::class, - ]; - - /** - * These situations happens only if condition is met - * @var array> - */ - public const CONDITIONAL_NODE_SCOPE_TYPES = [ - If_::class, - While_::class, - Do_::class, - Else_::class, - ElseIf_::class, - Catch_::class, - Case_::class, - Match_::class, - Switch_::class, - Foreach_::class, - ]; -} diff --git a/packages/NodeRemoval/AssignRemover.php b/packages/NodeRemoval/AssignRemover.php deleted file mode 100644 index 5a96a06978b..00000000000 --- a/packages/NodeRemoval/AssignRemover.php +++ /dev/null @@ -1,38 +0,0 @@ -getAttribute(AttributeKey::CURRENT_STATEMENT); - $this->livingCodeManipulator->addLivingCodeBeforeNode($assign->var, $currentStatement); - - /** @var Assign $assign */ - $parent = $assign->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Expression) { - $this->nodeRemover->removeNode($assign); - } else { - $this->nodesToReplaceCollector->addReplaceNodeWithAnotherNode($assign, $assign->expr); - $this->rectorChangeCollector->notifyNodeFileInfo($assign->expr); - } - } -} diff --git a/packages/NodeRemoval/BreakingRemovalGuard.php b/packages/NodeRemoval/BreakingRemovalGuard.php deleted file mode 100644 index 72fbe56316c..00000000000 --- a/packages/NodeRemoval/BreakingRemovalGuard.php +++ /dev/null @@ -1,75 +0,0 @@ -isLegalNodeRemoval($node)) { - return; - } - - // validate the node can be removed - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - throw new ShouldNotHappenException(); - } - - throw new ShouldNotHappenException(sprintf( - 'Node "%s" on line %d is child of "%s", so it cannot be removed as it would break PHP code. Change or remove the parent node instead.', - $node::class, - $node->getLine(), - $parentNode::class - )); - } - - public function isLegalNodeRemoval(Node $node): bool - { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof If_ && $parent->cond === $node) { - return false; - } - - if ($parent instanceof BooleanNot) { - $parent = $parent->getAttribute(AttributeKey::PARENT_NODE); - } - if ($parent instanceof Assign) { - return false; - } - if ($this->isIfCondition($node)) { - return false; - } - return ! $this->isWhileCondition($node); - } - - private function isIfCondition(Node $node): bool - { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof If_) { - return false; - } - - return $parentNode->cond === $node; - } - - private function isWhileCondition(Node $node): bool - { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof While_) { - return false; - } - - return $parentNode->cond === $node; - } -} diff --git a/packages/NodeRemoval/NodeRemover.php b/packages/NodeRemoval/NodeRemover.php deleted file mode 100644 index 5eaa409f6b7..00000000000 --- a/packages/NodeRemoval/NodeRemover.php +++ /dev/null @@ -1,125 +0,0 @@ -getAttribute(AttributeKey::ORIGINAL_NODE); - if ($isJustAddedNode) { - return; - } - - $this->nodesToRemoveCollector->addNodeToRemove($node); - $this->rectorChangeCollector->notifyNodeFileInfo($node); - } - - public function removeNodeFromStatements( - Class_ | ClassMethod | Function_ $nodeWithStatements, - Node $toBeRemovedNode - ): void { - foreach ((array) $nodeWithStatements->stmts as $key => $stmt) { - if ($toBeRemovedNode !== $stmt) { - continue; - } - - unset($nodeWithStatements->stmts[$key]); - break; - } - } - - /** - * @param Node[] $nodes - */ - public function removeNodes(array $nodes): void - { - foreach ($nodes as $node) { - $this->removeNode($node); - } - } - - public function removeStmt(Closure | ClassMethod | Function_ $functionLike, int $key): void - { - if ($functionLike->stmts === null) { - throw new ShouldNotHappenException(); - } - - // notify about remove node - $this->rectorChangeCollector->notifyNodeFileInfo($functionLike->stmts[$key]); - - unset($functionLike->stmts[$key]); - } - - public function removeParam(ClassMethod $classMethod, int | Param $keyOrParam): void - { - $key = $keyOrParam instanceof Param ? $keyOrParam->getAttribute(AttributeKey::PARAMETER_POSITION) : $keyOrParam; - - if ($classMethod->params === null) { - throw new ShouldNotHappenException(); - } - - // already removed - if (! isset($classMethod->params[$key])) { - return; - } - - // notify about remove node - $this->rectorChangeCollector->notifyNodeFileInfo($classMethod->params[$key]); - - unset($classMethod->params[$key]); - } - - public function removeArg(FuncCall | MethodCall | StaticCall $node, int $key): void - { - if ($node->args === null) { - throw new ShouldNotHappenException(); - } - - // already removed - if (! isset($node->args[$key])) { - return; - } - - // notify about remove node - $this->rectorChangeCollector->notifyNodeFileInfo($node->args[$key]); - - unset($node->args[$key]); - } - - public function removeImplements(Class_ $class, int $key): void - { - if ($class->implements === null) { - throw new ShouldNotHappenException(); - } - - // notify about remove node - $this->rectorChangeCollector->notifyNodeFileInfo($class->implements[$key]); - - unset($class->implements[$key]); - } -} diff --git a/packages/NodeTypeResolver/Contract/SourceLocatorProviderInterface.php b/packages/NodeTypeResolver/Contract/SourceLocatorProviderInterface.php deleted file mode 100644 index 5e1368f10f8..00000000000 --- a/packages/NodeTypeResolver/Contract/SourceLocatorProviderInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -provideStringParameter(Option::PHPSTAN_FOR_RECTOR_PATH); - $additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/static-reflection.neon'; - $additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/better-infer.neon'; - - $existingAdditionalConfigFiles = array_filter($additionalConfigFiles, 'file_exists'); - - $this->container = $containerFactory->create(sys_get_temp_dir(), $existingAdditionalConfigFiles, []); - } - - /** - * @api - */ - public function createReflectionProvider(): ReflectionProvider - { - return $this->container->getByType(ReflectionProvider::class); - } - - /** - * @api - */ - public function createNodeScopeResolver(): NodeScopeResolver - { - return $this->container->getByType(NodeScopeResolver::class); - } - - /** - * @api - */ - public function createTypeSpecifier(): TypeSpecifier - { - return $this->container->getByType(TypeSpecifier::class); - } - - /** - * @api - */ - public function createScopeFactory(): ScopeFactory - { - return $this->container->getByType(ScopeFactory::class); - } - - /** - * @api - */ - public function createDependencyResolver(): DependencyResolver - { - return $this->container->getByType(DependencyResolver::class); - } - - /** - * @api - */ - public function createFileHelper(): FileHelper - { - return $this->container->getByType(FileHelper::class); - } - - /** - * @api - */ - public function createOperatorTypeSpecifyingExtensionRegistryProvider(): OperatorTypeSpecifyingExtensionRegistryProvider - { - return $this->container->getByType(OperatorTypeSpecifyingExtensionRegistryProvider::class); - } - - /** - * @api - */ - public function createTypeNodeResolver(): TypeNodeResolver - { - return $this->container->getByType(TypeNodeResolver::class); - } - - /** - * @api - */ - public function createDynamicSourceLocatorProvider(): DynamicSourceLocatorProvider - { - return $this->container->getByType(DynamicSourceLocatorProvider::class); - } -} diff --git a/packages/NodeTypeResolver/Exception/MissingTagException.php b/packages/NodeTypeResolver/Exception/MissingTagException.php deleted file mode 100644 index c2e2a958e10..00000000000 --- a/packages/NodeTypeResolver/Exception/MissingTagException.php +++ /dev/null @@ -1,11 +0,0 @@ -reflectionResolver->resolveMethodReflectionFromStaticCall($staticCall); - if (! $methodReflection instanceof MethodReflection) { - return []; - } - - return $this->provideParameterTypesFromMethodReflection($methodReflection); - } - - /** - * @return Type[] - */ - public function provideParameterTypesByClassMethod(ClassMethod $classMethod): array - { - $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromClassMethod($classMethod); - if (! $methodReflection instanceof MethodReflection) { - return []; - } - - return $this->provideParameterTypesFromMethodReflection($methodReflection); - } - - /** - * @return Type[] - */ - private function provideParameterTypesFromMethodReflection(MethodReflection $methodReflection): array - { - if ($methodReflection instanceof NativeMethodReflection) { - // method "getParameters()" does not exist there - return []; - } - - $parameterTypes = []; - - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); - foreach ($parametersAcceptor->getParameters() as $parameterReflection) { - $parameterTypes[] = $parameterReflection->getType(); - } - - return $parameterTypes; - } -} diff --git a/packages/NodeTypeResolver/Node/AttributeKey.php b/packages/NodeTypeResolver/Node/AttributeKey.php deleted file mode 100644 index 206d0295d46..00000000000 --- a/packages/NodeTypeResolver/Node/AttributeKey.php +++ /dev/null @@ -1,200 +0,0 @@ -addVisitor(new NameResolver(null, [ - self::OPTION_PRESERVE_ORIGINAL_NAMES => true, - // required by PHPStan - self::OPTION_REPLACE_NODES => true, - ])); - - /** @var Stmt[] $nodes */ - $nodes = $nodeTraverser->traverse($nodes); - - $smartFileInfo = $file->getSmartFileInfo(); - $nodes = $this->phpStanNodeScopeResolver->processNodes($nodes, $smartFileInfo); - - $nodeTraverser = new NodeTraverser(); - - $preservingNameResolver = new NameResolver(null, [ - self::OPTION_PRESERVE_ORIGINAL_NAMES => true, - // this option would override old non-fqn-namespaced nodes otherwise, so it needs to be disabled - self::OPTION_REPLACE_NODES => false, - ]); - - $nodeTraverser->addVisitor($preservingNameResolver); - $nodes = $nodeTraverser->traverse($nodes); - - $nodeTraverser = new NodeTraverser(); - // needed also for format preserving printing - $nodeTraverser->addVisitor($this->cloningVisitor); - $nodeTraverser->addVisitor($this->nodeConnectingVisitor); - $nodeTraverser->addVisitor($this->functionMethodAndClassNodeVisitor); - $nodeTraverser->addVisitor($this->namespaceNodeVisitor); - $nodeTraverser->addVisitor($this->functionLikeParamArgPositionNodeVisitor); - - $fileNodeVisitor = new FileNodeVisitor($file); - $nodeTraverser->addVisitor($fileNodeVisitor); - - $nodes = $nodeTraverser->traverse($nodes); - - // this split is needed, so nodes have names, classes and namespaces - $nodeTraverser = new NodeTraverser(); - $nodeTraverser->addVisitor($this->statementNodeVisitor); - - return $nodeTraverser->traverse($nodes); - } - - /** - * @param Stmt[] $nodes - * @return Stmt[] - */ - public function decorateNodesFromString(array $nodes): array - { - $nodeTraverser = new NodeTraverser(); - $nodeTraverser->addVisitor($this->nodeConnectingVisitor); - $nodeTraverser->addVisitor($this->functionMethodAndClassNodeVisitor); - $nodeTraverser->addVisitor($this->statementNodeVisitor); - - return $nodeTraverser->traverse($nodes); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyStringTypeCorrector.php b/packages/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyStringTypeCorrector.php deleted file mode 100644 index 556cbed7f15..00000000000 --- a/packages/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyStringTypeCorrector.php +++ /dev/null @@ -1,38 +0,0 @@ -isSubTypeOf(new AccessoryNonEmptyStringType())->yes()) { - return $mainType; - } - - $clearIntersectionedTypes = []; - foreach ($mainType->getTypes() as $intersectionedType) { - if ($intersectionedType instanceof AccessoryNonEmptyStringType) { - continue; - } - - $clearIntersectionedTypes[] = $intersectionedType; - } - - if (count($clearIntersectionedTypes) === 1) { - return $clearIntersectionedTypes[0]; - } - - return new IntersectionType($clearIntersectionedTypes); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeCorrector/GenericClassStringTypeCorrector.php b/packages/NodeTypeResolver/NodeTypeCorrector/GenericClassStringTypeCorrector.php deleted file mode 100644 index 0732871aaac..00000000000 --- a/packages/NodeTypeResolver/NodeTypeCorrector/GenericClassStringTypeCorrector.php +++ /dev/null @@ -1,36 +0,0 @@ -reflectionProvider->hasClass($traversedType->getValue())) { - return $traverseCallback($traversedType); - } - - return new GenericClassStringType(new ObjectType($traversedType->getValue())); - }); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeCorrector/HasOffsetTypeCorrector.php b/packages/NodeTypeResolver/NodeTypeCorrector/HasOffsetTypeCorrector.php deleted file mode 100644 index 62019aa1fa1..00000000000 --- a/packages/NodeTypeResolver/NodeTypeCorrector/HasOffsetTypeCorrector.php +++ /dev/null @@ -1,42 +0,0 @@ -getTypes() as $intersectionedType) { - if ($intersectionedType instanceof HasOffsetType) { - continue; - } - - if ($intersectionedType instanceof NonEmptyArrayType) { - continue; - } - - $clearTypes[] = $intersectionedType; - } - - if (count($clearTypes) === 1) { - return $clearTypes[0]; - } - - return new IntersectionType($clearTypes); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeCorrector/PregMatchTypeCorrector.php b/packages/NodeTypeResolver/NodeTypeCorrector/PregMatchTypeCorrector.php deleted file mode 100644 index 198ad2592d5..00000000000 --- a/packages/NodeTypeResolver/NodeTypeCorrector/PregMatchTypeCorrector.php +++ /dev/null @@ -1,93 +0,0 @@ -getVariableUsages($node); - foreach ($variableUsages as $variableUsage) { - $possiblyArg = $variableUsage->getAttribute(AttributeKey::PARENT_NODE); - if (! $possiblyArg instanceof Arg) { - continue; - } - - $funcCallNode = $possiblyArg->getAttribute(AttributeKey::PARENT_NODE); - - if (! $funcCallNode instanceof FuncCall) { - continue; - } - - if (! $this->nodeNameResolver->isNames($funcCallNode, ['preg_match', 'preg_match_all'])) { - continue; - } - - if (! isset($funcCallNode->args[2])) { - continue; - } - - // are the same variables - if (! $this->nodeComparator->areNodesEqual($funcCallNode->args[2]->value, $node)) { - continue; - } - - return new ArrayType(new MixedType(), new MixedType()); - } - - return $originalType; - } - - /** - * @return Node[] - */ - private function getVariableUsages(Variable $variable): array - { - $scope = $this->parentScopeFinder->find($variable); - if ($scope === null) { - return []; - } - - return $this->betterNodeFinder->find((array) $scope->stmts, function (Node $node) use ($variable): bool { - if (! $node instanceof Variable) { - return false; - } - return $node->name === $variable->name; - }); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver.php deleted file mode 100644 index 6e4190bfa16..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver.php +++ /dev/null @@ -1,503 +0,0 @@ -, NodeTypeResolverInterface> - */ - private array $nodeTypeResolvers = []; - - private ArrayTypeAnalyzer $arrayTypeAnalyzer; - - /** - * @param NodeTypeResolverInterface[] $nodeTypeResolvers - */ - public function __construct( - private ObjectTypeSpecifier $objectTypeSpecifier, - private ClassAnalyzer $classAnalyzer, - private GenericClassStringTypeCorrector $genericClassStringTypeCorrector, - private ReflectionProvider $reflectionProvider, - private HasOffsetTypeCorrector $hasOffsetTypeCorrector, - private AccessoryNonEmptyStringTypeCorrector $accessoryNonEmptyStringTypeCorrector, - private IdentifierTypeResolver $identifierTypeResolver, - private RenamedClassesDataCollector $renamedClassesDataCollector, - array $nodeTypeResolvers - ) { - foreach ($nodeTypeResolvers as $nodeTypeResolver) { - $this->addNodeTypeResolver($nodeTypeResolver); - } - } - - // Prevents circular dependency - - #[Required] - public function autowireNodeTypeResolver(ArrayTypeAnalyzer $arrayTypeAnalyzer): void - { - $this->arrayTypeAnalyzer = $arrayTypeAnalyzer; - } - - /** - * @param ObjectType[] $requiredTypes - */ - public function isObjectTypes(Node $node, array $requiredTypes): bool - { - foreach ($requiredTypes as $requiredType) { - if ($this->isObjectType($node, $requiredType)) { - return true; - } - } - - return false; - } - - public function isObjectType(Node $node, ObjectType $requiredObjectType): bool - { - if ($node instanceof ClassConstFetch) { - return false; - } - - $resolvedType = $this->resolve($node); - if ($resolvedType instanceof MixedType) { - return false; - } - - if ($resolvedType instanceof ThisType) { - $resolvedType = $resolvedType->getStaticObjectType(); - } - - if ($resolvedType instanceof ObjectType) { - return $this->resolveObjectType($resolvedType, $requiredObjectType); - } - - return $this->isMatchingUnionType($resolvedType, $requiredObjectType); - } - - public function resolve(Node $node): Type - { - if ($node instanceof Ternary) { - if ($node->if !== null) { - $first = $this->resolve($node->if); - $second = $this->resolve($node->else); - - if ($this->isUnionTypeable($first, $second)) { - return new UnionType([$first, $second]); - } - } - - $condType = $this->resolve($node->cond); - if ($this->isNullableType($node->cond) && $condType instanceof UnionType) { - $first = $condType->getTypes()[0]; - $second = $this->resolve($node->else); - - if ($this->isUnionTypeable($first, $second)) { - return new UnionType([$first, $second]); - } - } - } - - if ($node instanceof Coalesce) { - $first = $this->resolve($node->left); - $second = $this->resolve($node->right); - - if ($this->isUnionTypeable($first, $second)) { - return new UnionType([$first, $second]); - } - } - - $type = $this->resolveByNodeTypeResolvers($node); - if ($type !== null) { - $type = $this->accessoryNonEmptyStringTypeCorrector->correct($type); - return $this->hasOffsetTypeCorrector->correct($type); - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - - if (! $scope instanceof Scope) { - if ($node instanceof ConstFetch && $node->name instanceof Name) { - $name = (string) $node->name; - if (strtolower($name) === 'null') { - return new NullType(); - } - } - - return new MixedType(); - } - - if (! $node instanceof Expr) { - // scalar type, e.g. from param type name - if ($node instanceof Identifier) { - return $this->identifierTypeResolver->resolve($node); - } - - return new MixedType(); - } - - // skip anonymous classes, ref https://github.com/rectorphp/rector/issues/1574 - if ($node instanceof New_ && $this->classAnalyzer->isAnonymousClass($node->class)) { - return new ObjectWithoutClassType(); - } - - $type = $scope->getType($node); - $type = $this->accessoryNonEmptyStringTypeCorrector->correct($type); - - // hot fix for phpstan not resolving chain method calls - if (! $node instanceof MethodCall) { - return $type; - } - - if (! $type instanceof MixedType) { - return $type; - } - - return $this->resolve($node->var); - } - - /** - * e.g. string|null, ObjectNull|null - */ - public function isNullableType(Node $node): bool - { - $nodeType = $this->resolve($node); - return TypeCombinator::containsNull($nodeType); - } - - public function getNativeType(Expr $expr): Type - { - $scope = $expr->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return new MixedType(); - } - - return $scope->getNativeType($expr); - } - - public function getStaticType(Node $node): Type - { - if ($node instanceof Param) { - return $this->resolve($node); - } - - if ($node instanceof New_) { - return $this->resolve($node); - } - - if ($node instanceof Return_) { - return $this->resolve($node); - } - - if (! $node instanceof Expr) { - return new MixedType(); - } - - if ($this->arrayTypeAnalyzer->isArrayType($node)) { - return $this->resolveArrayType($node); - } - - if ($node instanceof Scalar) { - return $this->resolve($node); - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return new MixedType(); - } - - $staticType = $scope->getType($node); - if ($staticType instanceof GenericObjectType) { - return $staticType; - } - - if ($staticType instanceof ObjectType) { - return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType($node, $staticType); - } - - return $this->accessoryNonEmptyStringTypeCorrector->correct($staticType); - } - - public function isNumberType(Node $node): bool - { - if ($this->isStaticType($node, IntegerType::class)) { - return true; - } - - return $this->isStaticType($node, FloatType::class); - } - - /** - * @param class-string $staticTypeClass - */ - public function isStaticType(Node $node, string $staticTypeClass): bool - { - if (! is_a($staticTypeClass, Type::class, true)) { - throw new ShouldNotHappenException(sprintf( - '"%s" in "%s()" must be type of "%s"', - $staticTypeClass, - __METHOD__, - Type::class - )); - } - - return is_a($this->resolve($node), $staticTypeClass); - } - - /** - * @param class-string $desiredType - */ - public function isNullableTypeOfSpecificType(Node $node, string $desiredType): bool - { - $nodeType = $this->resolve($node); - if (! $nodeType instanceof UnionType) { - return false; - } - - if (! TypeCombinator::containsNull($nodeType)) { - return false; - } - - if (count($nodeType->getTypes()) !== 2) { - return false; - } - - foreach ($nodeType->getTypes() as $type) { - if (is_a($type, $desiredType, true)) { - return true; - } - } - - return false; - } - - /** - * @return class-string - */ - public function getFullyQualifiedClassName(TypeWithClassName $typeWithClassName): string - { - if ($typeWithClassName instanceof ShortenedObjectType) { - return $typeWithClassName->getFullyQualifiedName(); - } - - return $typeWithClassName->getClassName(); - } - - /** - * @param Type[] $desiredTypes - */ - public function isSameObjectTypes(ObjectType $objectType, array $desiredTypes): bool - { - foreach ($desiredTypes as $desiredType) { - $desiredTypeEquals = $desiredType->equals($objectType); - if ($desiredTypeEquals) { - return true; - } - } - - return false; - } - - public function isMethodStaticCallOrClassMethodObjectType(Node $node, ObjectType $objectType): bool - { - if ($node instanceof MethodCall) { - // method call is variable return - return $this->isObjectType($node->var, $objectType); - } - - if ($node instanceof StaticCall) { - return $this->isObjectType($node->class, $objectType); - } - - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return false; - } - - return $this->isObjectType($classLike, $objectType); - } - - public function resolveObjectTypeFromScope(Scope $scope): ?ObjectType - { - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $className = $classReflection->getName(); - if (! $this->reflectionProvider->hasClass($className)) { - return null; - } - - return new ObjectType($className, null, $classReflection); - } - - private function isUnionTypeable(Type $first, Type $second): bool - { - return ! $first instanceof UnionType && ! $second instanceof UnionType && ! $second instanceof NullType; - } - - private function addNodeTypeResolver(NodeTypeResolverInterface $nodeTypeResolver): void - { - foreach ($nodeTypeResolver->getNodeClasses() as $nodeClass) { - $this->nodeTypeResolvers[$nodeClass] = $nodeTypeResolver; - } - } - - private function isMatchingUnionType(Type $resolvedType, ObjectType $requiredObjectType): bool - { - $type = TypeCombinator::removeNull($resolvedType); - // for falsy nullables - $type = TypeCombinator::remove($type, new ConstantBooleanType(false)); - - if (! $type instanceof ObjectType) { - return false; - } - - return $type->isInstanceOf($requiredObjectType->getClassName()) - ->yes(); - } - - private function resolveArrayType(Expr $expr): Type - { - /** @var Scope|null $scope */ - $scope = $expr->getAttribute(AttributeKey::SCOPE); - - if ($scope instanceof Scope) { - $arrayType = $scope->getType($expr); - $arrayType = $this->genericClassStringTypeCorrector->correct($arrayType); - return $this->removeNonEmptyArrayFromIntersectionWithArrayType($arrayType); - } - - return new ArrayType(new MixedType(), new MixedType()); - } - - private function resolveByNodeTypeResolvers(Node $node): ?Type - { - foreach ($this->nodeTypeResolvers as $nodeClass => $nodeTypeResolver) { - if (! is_a($node, $nodeClass, true)) { - continue; - } - - return $nodeTypeResolver->resolve($node); - } - - return null; - } - - private function removeNonEmptyArrayFromIntersectionWithArrayType(Type $type): Type - { - if (! $type instanceof IntersectionType) { - return $type; - } - - if (count($type->getTypes()) !== 2) { - return $type; - } - - if (! $type->isSubTypeOf(new NonEmptyArrayType())->yes()) { - return $type; - } - - $otherType = null; - foreach ($type->getTypes() as $intersectionedType) { - if ($intersectionedType instanceof NonEmptyArrayType) { - continue; - } - - $otherType = $intersectionedType; - break; - } - - if ($otherType === null) { - return $type; - } - - return $otherType; - } - - private function isObjectTypeOfObjectType(ObjectType $resolvedObjectType, ObjectType $requiredObjectType): bool - { - if ($resolvedObjectType->isInstanceOf($requiredObjectType->getClassName())->yes()) { - return true; - } - - if ($resolvedObjectType->getClassName() === $requiredObjectType->getClassName()) { - return true; - } - - if (! $this->reflectionProvider->hasClass($resolvedObjectType->getClassName())) { - return false; - } - - $classReflection = $this->reflectionProvider->getClass($resolvedObjectType->getClassName()); - foreach ($classReflection->getAncestors() as $ancestorClassReflection) { - if ($ancestorClassReflection->hasTraitUse($requiredObjectType->getClassName())) { - return true; - } - } - - return $classReflection->isSubclassOf($requiredObjectType->getClassName()); - } - - private function resolveObjectType(ObjectType $resolvedObjectType, ObjectType $requiredObjectType): bool - { - $renamedObjectType = $this->renamedClassesDataCollector->matchClassName($resolvedObjectType); - if (! $renamedObjectType instanceof ObjectType) { - return $this->isObjectTypeOfObjectType($resolvedObjectType, $requiredObjectType); - } - - if (! $this->isObjectTypeOfObjectType($renamedObjectType, $requiredObjectType)) { - return $this->isObjectTypeOfObjectType($resolvedObjectType, $requiredObjectType); - } - return true; - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/ClassMethodOrClassConstTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/ClassMethodOrClassConstTypeResolver.php deleted file mode 100644 index fa4740d6de5..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/ClassMethodOrClassConstTypeResolver.php +++ /dev/null @@ -1,50 +0,0 @@ -nodeTypeResolver = $nodeTypeResolver; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ClassMethod::class, ClassConst::class]; - } - - /** - * @param ClassMethod|ClassConst $node - */ - public function resolve(Node $node): Type - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof ClassLike) { - // anonymous class - return new ObjectWithoutClassType(); - } - - return $this->nodeTypeResolver->resolve($classLike); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php deleted file mode 100644 index acc78e362b0..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php +++ /dev/null @@ -1,36 +0,0 @@ -toLowerString() === 'string') { - return new StringType(); - } - - if ($identifier->toLowerString() === 'bool') { - return new BooleanType(); - } - - if ($identifier->toLowerString() === 'int') { - return new IntegerType(); - } - - if ($identifier->toLowerString() === 'float') { - return new FloatType(); - } - - return new MixedType(); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/NameTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/NameTypeResolver.php deleted file mode 100644 index cd966111f46..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/NameTypeResolver.php +++ /dev/null @@ -1,106 +0,0 @@ -> - */ - public function getNodeClasses(): array - { - return [Name::class, FullyQualified::class]; - } - - /** - * @param Name $node - */ - public function resolve(Node $node): Type - { - if ($node->toString() === 'parent') { - return $this->resolveParent($node); - } - - $fullyQualifiedName = $this->resolveFullyQualifiedName($node); - - if ($node->toString() === 'array') { - return new ArrayType(new MixedType(), new MixedType()); - } - - return new ObjectType($fullyQualifiedName); - } - - private function resolveParent(Name $name): MixedType | ObjectType | UnionType - { - $className = $name->getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return new MixedType(); - } - - if (! $this->reflectionProvider->hasClass($className)) { - return new MixedType(); - } - - $classReflection = $this->reflectionProvider->getClass($className); - - $parentClassObjectTypes = []; - foreach ($classReflection->getParents() as $parentClassReflection) { - $parentClassObjectTypes[] = new ObjectType($parentClassReflection->getName()); - } - - if ($parentClassObjectTypes === []) { - return new MixedType(); - } - - if (count($parentClassObjectTypes) === 1) { - return $parentClassObjectTypes[0]; - } - - return new UnionType($parentClassObjectTypes); - } - - private function resolveFullyQualifiedName(Name $name): string - { - $nameValue = $name->toString(); - if (in_array($nameValue, ['self', 'static', 'this'], true)) { - /** @var string|null $class */ - $class = $name->getAttribute(AttributeKey::CLASS_NAME); - if ($class === null) { - // anonymous class probably - return 'Anonymous'; - } - - return $class; - } - - /** @var Name|null $resolvedNameNode */ - $resolvedNameNode = $name->getAttribute(AttributeKey::RESOLVED_NAME); - if ($resolvedNameNode instanceof Name) { - return $resolvedNameNode->toString(); - } - - return $nameValue; - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php deleted file mode 100644 index 61f84452435..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php +++ /dev/null @@ -1,97 +0,0 @@ -> - */ - public function getNodeClasses(): array - { - return [New_::class]; - } - - /** - * @param New_ $node - */ - public function resolve(Node $node): Type - { - if ($node->class instanceof Name) { - $className = $this->nodeNameResolver->getName($node->class); - if (! in_array($className, ['self', 'parent'], true)) { - return new ObjectType($className); - } - } - - $isAnonymousClass = $this->classAnalyzer->isAnonymousClass($node->class); - if ($isAnonymousClass) { - return $this->resolveAnonymousClassType($node); - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - // new node probably - return new MixedType(); - } - - return $scope->getType($node); - } - - private function resolveAnonymousClassType(New_ $new): ObjectWithoutClassType - { - if (! $new->class instanceof Class_) { - return new ObjectWithoutClassType(); - } - - $types = []; - - /** @var Class_ $class */ - $class = $new->class; - if ($class->extends !== null) { - $parentClass = (string) $class->extends; - $types[] = new FullyQualifiedObjectType($parentClass); - } - - foreach ($class->implements as $implement) { - $parentClass = (string) $implement; - $types[] = new FullyQualifiedObjectType($parentClass); - } - - if (count($types) > 1) { - $unionType = $this->unionTypeFactory->createUnionObjectType($types); - return new ObjectWithoutClassType($unionType); - } - - if (count($types) === 1) { - return new ObjectWithoutClassType($types[0]); - } - - return new ObjectWithoutClassType(); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/ParamTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/ParamTypeResolver.php deleted file mode 100644 index d51fcdc5e9e..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/ParamTypeResolver.php +++ /dev/null @@ -1,138 +0,0 @@ -nodeTypeResolver = $nodeTypeResolver; - $this->staticTypeMapper = $staticTypeMapper; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [Param::class]; - } - - /** - * @param Param $node - */ - public function resolve(Node $node): Type - { - $paramType = $this->resolveFromParamType($node); - if (! $paramType instanceof MixedType) { - return $paramType; - } - - $firstVariableUseType = $this->resolveFromFirstVariableUse($node); - if (! $firstVariableUseType instanceof MixedType) { - return $firstVariableUseType; - } - - return $this->resolveFromFunctionDocBlock($node); - } - - private function resolveFromParamType(Param $param): Type - { - if ($param->type === null) { - return new MixedType(); - } - - if ($param->type instanceof Identifier) { - return new MixedType(); - } - - return $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - } - - private function resolveFromFirstVariableUse(Param $param): Type - { - $classMethod = $param->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return new MixedType(); - } - - $paramName = $this->nodeNameResolver->getName($param); - $paramStaticType = new MixedType(); - - // special case for param inside method/function - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $classMethod->stmts, - function (Node $node) use ($paramName, &$paramStaticType): ?int { - if (! $node instanceof Variable) { - return null; - } - - if (! $this->nodeNameResolver->isName($node, $paramName)) { - return null; - } - - $paramStaticType = $this->nodeTypeResolver->resolve($node); - - return NodeTraverser::STOP_TRAVERSAL; - } - ); - - return $paramStaticType; - } - - private function resolveFromFunctionDocBlock(Param $param): Type - { - $phpDocInfo = $this->getFunctionLikePhpDocInfo($param); - $paramName = $this->nodeNameResolver->getName($param); - return $phpDocInfo->getParamType($paramName); - } - - private function getFunctionLikePhpDocInfo(Param $param): PhpDocInfo - { - $parentNode = $param->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof FunctionLike) { - throw new ShouldNotHappenException(); - } - - return $this->phpDocInfoFactory->createFromNodeOrEmpty($parentNode); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/PropertyFetchTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/PropertyFetchTypeResolver.php deleted file mode 100644 index e47e657a4fe..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/PropertyFetchTypeResolver.php +++ /dev/null @@ -1,120 +0,0 @@ -nodeTypeResolver = $nodeTypeResolver; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [PropertyFetch::class]; - } - - /** - * @param PropertyFetch $node - */ - public function resolve(Node $node): Type - { - // compensate 3rd party non-analysed property reflection - $vendorPropertyType = $this->getVendorPropertyFetchType($node); - if (! $vendorPropertyType instanceof MixedType) { - return $vendorPropertyType; - } - - /** @var Scope|null $scope */ - $scope = $node->getAttribute(AttributeKey::SCOPE); - - if (! $scope instanceof Scope) { - $classNode = $node->getAttribute(AttributeKey::CLASS_NODE); - if ($classNode instanceof Trait_) { - /** @var string $traitName */ - $traitName = $classNode->getAttribute(AttributeKey::CLASS_NAME); - $scope = $this->traitNodeScopeCollector->getScopeForTrait($traitName); - } - } - - if (! $scope instanceof Scope) { - $classNode = $node->getAttribute(AttributeKey::CLASS_NODE); - // fallback to class, since property fetches are not scoped by PHPStan - if ($classNode instanceof ClassLike) { - $scope = $classNode->getAttribute(AttributeKey::SCOPE); - } - - if (! $scope instanceof Scope) { - return new MixedType(); - } - } - - return $scope->getType($node); - } - - private function getVendorPropertyFetchType(PropertyFetch $propertyFetch): Type - { - // 3rd party code - $propertyName = $this->nodeNameResolver->getName($propertyFetch->name); - if ($propertyName === null) { - return new MixedType(); - } - - $varType = $this->nodeTypeResolver->resolve($propertyFetch->var); - if (! $varType instanceof ObjectType) { - return new MixedType(); - } - - if (! $this->reflectionProvider->hasClass($varType->getClassName())) { - return new MixedType(); - } - - $classReflection = $this->reflectionProvider->getClass($varType->getClassName()); - if (! $classReflection->hasProperty($propertyName)) { - return new MixedType(); - } - - $propertyFetchScope = $propertyFetch->getAttribute(AttributeKey::SCOPE); - if ($propertyFetchScope === null) { - return new MixedType(); - } - - $propertyReflection = $classReflection->getProperty($propertyName, $propertyFetchScope); - - return $propertyReflection->getReadableType(); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/PropertyTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/PropertyTypeResolver.php deleted file mode 100644 index 27c00f733e6..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/PropertyTypeResolver.php +++ /dev/null @@ -1,49 +0,0 @@ -nodeTypeResolver = $nodeTypeResolver; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [Property::class]; - } - - /** - * @param Property $node - */ - public function resolve(Node $node): Type - { - // fake property to local PropertyFetch → PHPStan understands that - $propertyFetch = new PropertyFetch(new Variable('this'), (string) $node->props[0]->name); - $propertyFetch->setAttribute(AttributeKey::SCOPE, $node->getAttribute(AttributeKey::SCOPE)); - - return $this->nodeTypeResolver->resolve($propertyFetch); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/ReturnTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/ReturnTypeResolver.php deleted file mode 100644 index d4907cbea8f..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/ReturnTypeResolver.php +++ /dev/null @@ -1,44 +0,0 @@ -nodeTypeResolver = $nodeTypeResolver; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [Return_::class]; - } - - /** - * @param Return_ $node - */ - public function resolve(Node $node): Type - { - if ($node->expr === null) { - return new VoidType(); - } - - return $this->nodeTypeResolver->resolve($node->expr); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/ScalarTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/ScalarTypeResolver.php deleted file mode 100644 index f2295ec0fc6..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/ScalarTypeResolver.php +++ /dev/null @@ -1,56 +0,0 @@ -> - */ - public function getNodeClasses(): array - { - return [Scalar::class]; - } - - public function resolve(Node $node): Type - { - if ($node instanceof DNumber) { - return new ConstantFloatType($node->value); - } - - if ($node instanceof String_) { - return new ConstantStringType($node->value); - } - - if ($node instanceof LNumber) { - return new ConstantIntegerType($node->value); - } - - if ($node instanceof MagicConst) { - return new ConstantStringType($node->getName()); - } - - if ($node instanceof Encapsed) { - return new MixedType(); - } - - throw new NotImplementedYetException(); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php deleted file mode 100644 index 847b6f6ef3e..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php +++ /dev/null @@ -1,108 +0,0 @@ -nodeTypeResolver = $nodeTypeResolver; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [StaticCall::class, MethodCall::class]; - } - - /** - * @param StaticCall|MethodCall $node - */ - public function resolve(Node $node): Type - { - $methodName = $this->nodeNameResolver->getName($node->name); - - // no specific method found, return class types, e.g. ::$method() - if (! is_string($methodName)) { - return new MixedType(); - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return new MixedType(); - } - - $nodeReturnType = $scope->getType($node); - if (! $nodeReturnType instanceof MixedType) { - return $nodeReturnType; - } - - if ($node instanceof MethodCall) { - $callerType = $this->nodeTypeResolver->resolve($node->var); - } else { - $callerType = $this->nodeTypeResolver->resolve($node->class); - } - - foreach ($callerType->getReferencedClasses() as $referencedClass) { - $classMethodReturnType = $this->resolveClassMethodReturnType($referencedClass, $methodName, $scope); - if (! $classMethodReturnType instanceof MixedType) { - return $classMethodReturnType; - } - } - - return new MixedType(); - } - - private function resolveClassMethodReturnType(string $referencedClass, string $methodName, Scope $scope): Type - { - if (! $this->reflectionProvider->hasClass($referencedClass)) { - return new MixedType(); - } - - $classReflection = $this->reflectionProvider->getClass($referencedClass); - - foreach ($classReflection->getAncestors() as $ancestorClassReflection) { - if (! $ancestorClassReflection->hasMethod($methodName)) { - continue; - } - - $methodReflection = $ancestorClassReflection->getMethod($methodName, $scope); - if ($methodReflection instanceof PhpMethodReflection) { - $parametersAcceptorWithPhpDocs = ParametersAcceptorSelector::selectSingle( - $methodReflection->getVariants() - ); - return $parametersAcceptorWithPhpDocs->getReturnType(); - } - } - - return new MixedType(); - } -} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/VariableTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/VariableTypeResolver.php deleted file mode 100644 index 29af25bedee..00000000000 --- a/packages/NodeTypeResolver/NodeTypeResolver/VariableTypeResolver.php +++ /dev/null @@ -1,120 +0,0 @@ -> - */ - public function getNodeClasses(): array - { - return [Variable::class]; - } - - /** - * @param Variable $node - */ - public function resolve(Node $node): Type - { - $variableName = $this->nodeNameResolver->getName($node); - if ($variableName === null) { - return new MixedType(); - } - - $scopeType = $this->resolveTypesFromScope($node, $variableName); - if (! $scopeType instanceof MixedType) { - return $scopeType; - } - - // get from annotation - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - return $phpDocInfo->getVarType(); - } - - private function resolveTypesFromScope(Variable $variable, string $variableName): Type - { - $scope = $this->resolveNodeScope($variable); - if (! $scope instanceof Scope) { - return new MixedType(); - } - - if (! $scope->hasVariableType($variableName)->yes()) { - return new MixedType(); - } - - // this → object type is easier to work with and consistent with the rest of the code - return $scope->getVariableType($variableName); - } - - private function resolveNodeScope(Variable $variable): ?Scope - { - /** @var Scope|null $nodeScope */ - $nodeScope = $variable->getAttribute(AttributeKey::SCOPE); - if ($nodeScope !== null) { - return $nodeScope; - } - - // is node in trait - $classLike = $variable->getAttribute(AttributeKey::CLASS_NODE); - if ($classLike instanceof Trait_) { - /** @var string $traitName */ - $traitName = $variable->getAttribute(AttributeKey::CLASS_NAME); - $traitNodeScope = $this->traitNodeScopeCollector->getScopeForTrait($traitName); - - if ($traitNodeScope !== null) { - return $traitNodeScope; - } - } - - return $this->resolveFromParentNodes($variable); - } - - private function resolveFromParentNodes(Variable $variable): ?Scope - { - foreach (self::PARENT_NODE_ATTRIBUTES as $parentNodeAttribute) { - $parentNode = $variable->getAttribute($parentNodeAttribute); - if (! $parentNode instanceof Node) { - continue; - } - - $parentNodeScope = $parentNode->getAttribute(AttributeKey::SCOPE); - if (! $parentNodeScope instanceof Scope) { - continue; - } - - return $parentNodeScope; - } - return null; - } -} diff --git a/packages/NodeTypeResolver/NodeVisitor/FileNodeVisitor.php b/packages/NodeTypeResolver/NodeVisitor/FileNodeVisitor.php deleted file mode 100644 index a6b2d95063e..00000000000 --- a/packages/NodeTypeResolver/NodeVisitor/FileNodeVisitor.php +++ /dev/null @@ -1,30 +0,0 @@ -setAttribute(AttributeKey::FILE, $this->file); - return $node; - } -} diff --git a/packages/NodeTypeResolver/NodeVisitor/FunctionLikeParamArgPositionNodeVisitor.php b/packages/NodeTypeResolver/NodeVisitor/FunctionLikeParamArgPositionNodeVisitor.php deleted file mode 100644 index 0dff20a547a..00000000000 --- a/packages/NodeTypeResolver/NodeVisitor/FunctionLikeParamArgPositionNodeVisitor.php +++ /dev/null @@ -1,37 +0,0 @@ -getParams() as $position => $param) { - $param->setAttribute(AttributeKey::PARAMETER_POSITION, $position); - } - } - - if ($node instanceof MethodCall || $node instanceof StaticCall || $node instanceof FuncCall || $node instanceof New_) { - foreach ($node->args as $position => $arg) { - $arg->setAttribute(AttributeKey::ARGUMENT_POSITION, $position); - } - } - - return $node; - } -} diff --git a/packages/NodeTypeResolver/NodeVisitor/FunctionMethodAndClassNodeVisitor.php b/packages/NodeTypeResolver/NodeVisitor/FunctionMethodAndClassNodeVisitor.php deleted file mode 100644 index b1806d37a3a..00000000000 --- a/packages/NodeTypeResolver/NodeVisitor/FunctionMethodAndClassNodeVisitor.php +++ /dev/null @@ -1,99 +0,0 @@ -classLike = null; - $this->className = null; - $this->classMethod = null; - - return null; - } - - public function enterNode(Node $node): ?Node - { - $this->processClass($node); - $this->processMethod($node); - - return $node; - } - - public function leaveNode(Node $node) - { - if ($node instanceof ClassLike) { - $classLike = array_pop($this->classStack); - $this->setClassNodeAndName($classLike); - } - - if ($node instanceof ClassMethod) { - $this->classMethod = array_pop($this->methodStack); - } - - return null; - } - - private function processClass(Node $node): void - { - if ($node instanceof ClassLike) { - $this->classStack[] = $this->classLike; - $this->setClassNodeAndName($node); - } - - $node->setAttribute(AttributeKey::CLASS_NODE, $this->classLike); - $node->setAttribute(AttributeKey::CLASS_NAME, $this->className); - } - - private function processMethod(Node $node): void - { - if ($node instanceof ClassMethod) { - $this->methodStack[] = $this->classMethod; - - $this->classMethod = $node; - } - - $node->setAttribute(AttributeKey::METHOD_NODE, $this->classMethod); - } - - private function setClassNodeAndName(?ClassLike $classLike): void - { - $this->classLike = $classLike; - if (! $classLike instanceof ClassLike || $classLike->name === null) { - $this->className = null; - } elseif (property_exists($classLike, 'namespacedName')) { - $this->className = $classLike->namespacedName->toString(); - } else { - $this->className = (string) $classLike->name; - } - } -} diff --git a/packages/NodeTypeResolver/NodeVisitor/NamespaceNodeVisitor.php b/packages/NodeTypeResolver/NodeVisitor/NamespaceNodeVisitor.php deleted file mode 100644 index 199d4a39fe3..00000000000 --- a/packages/NodeTypeResolver/NodeVisitor/NamespaceNodeVisitor.php +++ /dev/null @@ -1,52 +0,0 @@ -betterNodeFinder->findInstanceOf($nodes, Use_::class); - $this->useNodes = $uses; - - return null; - } - - public function enterNode(Node $node): ?Node - { - if ($node instanceof Namespace_) { - /** @var Use_[] $uses */ - $uses = $this->betterNodeFinder->findInstanceOf($node, Use_::class); - $this->useNodes = $uses; - } - - $node->setAttribute(AttributeKey::USE_NODES, $this->useNodes); - - return $node; - } -} diff --git a/packages/NodeTypeResolver/NodeVisitor/StatementNodeVisitor.php b/packages/NodeTypeResolver/NodeVisitor/StatementNodeVisitor.php deleted file mode 100644 index e8593df1968..00000000000 --- a/packages/NodeTypeResolver/NodeVisitor/StatementNodeVisitor.php +++ /dev/null @@ -1,65 +0,0 @@ -previousStmt = null; - - return null; - } - - public function enterNode(Node $node): ?Node - { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { - if (! $node instanceof Stmt) { - return null; - } - - $node->setAttribute(AttributeKey::PREVIOUS_STATEMENT, $this->previousStmt); - $node->setAttribute(AttributeKey::CURRENT_STATEMENT, $node); - $this->previousStmt = $node; - } - - if (property_exists($node, 'stmts')) { - $previous = $node; - foreach ((array) $node->stmts as $stmt) { - $stmt->setAttribute(AttributeKey::PREVIOUS_STATEMENT, $previous); - $stmt->setAttribute(AttributeKey::CURRENT_STATEMENT, $stmt); - $previous = $stmt; - } - } - - $currentStmt = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - - if ($parent && ! $currentStmt) { - $node->setAttribute( - AttributeKey::PREVIOUS_STATEMENT, - $parent->getAttribute(AttributeKey::PREVIOUS_STATEMENT) - ); - - $node->setAttribute( - AttributeKey::CURRENT_STATEMENT, - $parent->getAttribute(AttributeKey::CURRENT_STATEMENT) - ); - } - - return null; - } -} diff --git a/packages/NodeTypeResolver/PHPStan/Collector/TraitNodeScopeCollector.php b/packages/NodeTypeResolver/PHPStan/Collector/TraitNodeScopeCollector.php deleted file mode 100644 index 38133f82aa6..00000000000 --- a/packages/NodeTypeResolver/PHPStan/Collector/TraitNodeScopeCollector.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ - private array $scopeByTraitNodeHash = []; - - public function addForTrait(string $traitName, Scope $scope): void - { - // probably set from another class - if (isset($this->scopeByTraitNodeHash[$traitName])) { - return; - } - - $this->scopeByTraitNodeHash[$traitName] = $scope; - } - - public function getScopeForTrait(string $traitName): ?Scope - { - return $this->scopeByTraitNodeHash[$traitName] ?? null; - } -} diff --git a/packages/NodeTypeResolver/PHPStan/Scope/NodeVisitor/RemoveDeepChainMethodCallNodeVisitor.php b/packages/NodeTypeResolver/PHPStan/Scope/NodeVisitor/RemoveDeepChainMethodCallNodeVisitor.php deleted file mode 100644 index 2bd5b4a647a..00000000000 --- a/packages/NodeTypeResolver/PHPStan/Scope/NodeVisitor/RemoveDeepChainMethodCallNodeVisitor.php +++ /dev/null @@ -1,64 +0,0 @@ -nestedChainMethodCallLimit = (int) $parameterProvider->provideParameter( - Option::NESTED_CHAIN_METHOD_CALL_LIMIT - ); - } - - public function enterNode(Node $node): ?int - { - if (! $node instanceof Expression) { - return null; - } - - if ($node->expr instanceof MethodCall && $node->expr->var instanceof MethodCall) { - $nestedChainMethodCalls = $this->betterNodeFinder->findInstanceOf([$node->expr], MethodCall::class); - if (count($nestedChainMethodCalls) > $this->nestedChainMethodCallLimit) { - $this->removingExpression = $node; - - return NodeTraverser::DONT_TRAVERSE_CHILDREN; - } - } - - return null; - } - - public function leaveNode(Node $node): Nop | Node - { - if ($node === $this->removingExpression) { - // keep any node, so we don't remove it permanently - $nop = new Nop(); - $nop->setAttributes($node->getAttributes()); - return $nop; - } - - return $node; - } -} diff --git a/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php b/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php deleted file mode 100644 index e4292bb225b..00000000000 --- a/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php +++ /dev/null @@ -1,278 +0,0 @@ -removeDeepChainMethodCallNodes($nodes); - - $scope = $this->scopeFactory->createFromFile($smartFileInfo); - - // skip chain method calls, performance issue: https://github.com/phpstan/phpstan/issues/254 - $nodeCallback = function (Node $node, Scope $scope): void { - // traversing trait inside class that is using it scope (from referenced) - the trait traversed by Rector is different (directly from parsed file) - if ($scope->isInTrait()) { - // has just entereted trait, to avoid adding it for ever ynode - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Trait_) { - /** @var ClassReflection $classReflection */ - $classReflection = $scope->getTraitReflection(); - $traitName = $classReflection->getName(); - - $this->traitNodeScopeCollector->addForTrait($traitName, $scope); - } - - return; - } - - // the class reflection is resolved AFTER entering to class node - // so we need to get it from the first after this one - if ($node instanceof Class_ || $node instanceof Interface_) { - /** @var Scope $scope */ - $scope = $this->resolveClassOrInterfaceScope($node, $scope); - } - - // special case for unreachable nodes - if ($node instanceof UnreachableStatementNode) { - $originalNode = $node->getOriginalStatement(); - $originalNode->setAttribute(AttributeKey::IS_UNREACHABLE, true); - $originalNode->setAttribute(AttributeKey::SCOPE, $scope); - } else { - $node->setAttribute(AttributeKey::SCOPE, $scope); - } - }; - - $this->decoratePHPStanNodeScopeResolverWithRenamedClassSourceLocator($this->nodeScopeResolver); - - return $this->processNodesWithMixinHandling($smartFileInfo, $nodes, $scope, $nodeCallback); - } - - /** - * @param Stmt[] $nodes - * @return Stmt[] - */ - private function processNodesWithMixinHandling( - SmartFileInfo $smartFileInfo, - array $nodes, - MutatingScope $mutatingScope, - callable $nodeCallback - ): array { - if ($this->isMixinInSource($nodes)) { - return $nodes; - } - - $this->nodeScopeResolver->processNodes($nodes, $mutatingScope, $nodeCallback); - - $this->resolveAndSaveDependentFiles($nodes, $mutatingScope, $smartFileInfo); - - return $nodes; - } - - /** - * @param Node[] $nodes - */ - private function isMixinInSource(array $nodes): bool - { - return (bool) $this->betterNodeFinder->findFirst($nodes, function (Node $node): bool { - if (! $node instanceof FullyQualified && ! $node instanceof Class_) { - return false; - } - - if ($node instanceof Class_ && $node->isAnonymous()) { - return false; - } - - $className = $node instanceof FullyQualified ? $node->toString() : $node->namespacedName->toString(); - - return $this->isCircularMixin($className); - }); - } - - private function isCircularMixin(string $className): bool - { - // fix error in parallel test - // use function_exists on purpose as using reflectionProvider broke the test in parallel - if (function_exists($className)) { - return false; - } - - $hasClass = $this->reflectionProvider->hasClass($className); - - if (! $hasClass) { - return false; - } - - $classReflection = $this->reflectionProvider->getClass($className); - if ($classReflection->isBuiltIn()) { - return false; - } - - foreach ($classReflection->getMixinTags() as $mixinTag) { - $type = $mixinTag->getType(); - if (! $type instanceof ObjectType) { - return false; - } - - if ($type->getClassName() === $className) { - return true; - } - - if ($this->isCircularMixin($type->getClassName())) { - return true; - } - } - - return false; - } - - /** - * @param Node[] $nodes - */ - private function removeDeepChainMethodCallNodes(array $nodes): void - { - $nodeTraverser = new NodeTraverser(); - $nodeTraverser->addVisitor($this->removeDeepChainMethodCallNodeVisitor); - $nodeTraverser->traverse($nodes); - } - - private function resolveClassOrInterfaceScope(Class_ | Interface_ $classLike, Scope $scope): Scope - { - $className = $this->resolveClassName($classLike); - - // is anonymous class? - not possible to enter it since PHPStan 0.12.33, see https://github.com/phpstan/phpstan-src/commit/e87fb0ec26f9c8552bbeef26a868b1e5d8185e91 - if ($classLike instanceof Class_ && Strings::match($className, self::ANONYMOUS_CLASS_START_REGEX)) { - $classReflection = $this->reflectionProvider->getAnonymousClassReflection($classLike, $scope); - } elseif (! $this->reflectionProvider->hasClass($className)) { - return $scope; - } else { - $classReflection = $this->reflectionProvider->getClass($className); - } - - /** @var MutatingScope $scope */ - return $scope->enterClass($classReflection); - } - - private function resolveClassName(Class_ | Interface_ $classLike): string - { - if (property_exists($classLike, 'namespacedName')) { - return (string) $classLike->namespacedName; - } - - if ($classLike->name === null) { - throw new ShouldNotHappenException(); - } - - return $classLike->name->toString(); - } - - /** - * @param Stmt[] $stmts - */ - private function resolveAndSaveDependentFiles( - array $stmts, - MutatingScope $mutatingScope, - SmartFileInfo $smartFileInfo - ): void { - $dependentFiles = []; - foreach ($stmts as $stmt) { - try { - $nodeDependentFiles = $this->dependencyResolver->resolveDependencies($stmt, $mutatingScope); - $dependentFiles = array_merge($dependentFiles, $nodeDependentFiles); - } catch (AnalysedCodeException) { - // @ignoreException - } - } - - $this->changedFilesDetector->addFileWithDependencies($smartFileInfo, $dependentFiles); - } - - /** - * In case PHPStan tried to parse a file with missing class, it fails. - * But sometimes we want to rename old class that is missing with Rector.. - * - * That's why we have to skip fatal errors of PHPStan caused by missing class, - * so Rector can fix it first. Then run Rector again to refactor code with new classes. - */ - private function decoratePHPStanNodeScopeResolverWithRenamedClassSourceLocator( - NodeScopeResolver $nodeScopeResolver - ): void { - // 1. get PHPStan locator - /** @var ClassReflector $classReflector */ - $classReflector = $this->privatesAccessor->getPrivateProperty($nodeScopeResolver, 'classReflector'); - - /** @var SourceLocator $sourceLocator */ - $sourceLocator = $this->privatesAccessor->getPrivateProperty($classReflector, 'sourceLocator'); - - // 2. get Rector locator - $aggregateSourceLocator = new AggregateSourceLocator([ - $sourceLocator, - $this->renamedClassesSourceLocator, - $this->parentAttributeSourceLocator, - ]); - $this->privatesAccessor->setPrivateProperty($classReflector, 'sourceLocator', $aggregateSourceLocator); - } -} diff --git a/packages/NodeTypeResolver/PHPStan/Scope/ScopeFactory.php b/packages/NodeTypeResolver/PHPStan/Scope/ScopeFactory.php deleted file mode 100644 index a5243b78168..00000000000 --- a/packages/NodeTypeResolver/PHPStan/Scope/ScopeFactory.php +++ /dev/null @@ -1,24 +0,0 @@ -getRealPath()); - return $this->phpStanScopeFactory->create($scopeContext); - } -} diff --git a/packages/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php b/packages/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php deleted file mode 100644 index 321fef3553c..00000000000 --- a/packages/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php +++ /dev/null @@ -1,100 +0,0 @@ -isAlwaysTruableArrayType($type); - } - - if ($this->isNullable($type)) { - return false; - } - - // always trueish - if ($type instanceof ObjectType) { - return true; - } - - if ($type instanceof ConstantScalarType && ! $type instanceof NullType) { - return (bool) $type->getValue(); - } - - if ($this->isScalarType($type)) { - return false; - } - - return $this->isAlwaysTruableUnionType($type); - } - - private function isNullable(Type $type): bool - { - if (! $type instanceof UnionType) { - return false; - } - - foreach ($type->getTypes() as $unionedType) { - if ($unionedType instanceof NullType) { - return true; - } - } - - return false; - } - - private function isScalarType(Type $type): bool - { - if ($type instanceof NullType) { - return true; - } - - return $type instanceof BooleanType || $type instanceof StringType || $type instanceof IntegerType || $type instanceof FloatType; - } - - private function isAlwaysTruableUnionType(Type $type): bool - { - if (! $type instanceof UnionType) { - return false; - } - - foreach ($type->getTypes() as $unionedType) { - if (! $this->isAlwaysTruableType($unionedType)) { - return false; - } - } - - return true; - } - - private function isAlwaysTruableArrayType(ArrayType $arrayType): bool - { - $itemType = $arrayType->getItemType(); - return $itemType instanceof ConstantScalarType && $itemType->getValue(); - } -} diff --git a/packages/NodeTypeResolver/PHPStan/Type/TypeFactory.php b/packages/NodeTypeResolver/PHPStan/Type/TypeFactory.php deleted file mode 100644 index cb8b7e3e6bb..00000000000 --- a/packages/NodeTypeResolver/PHPStan/Type/TypeFactory.php +++ /dev/null @@ -1,210 +0,0 @@ -unwrapUnionedTypes($types); - $types = $this->uniquateTypes($types, true); - - return $this->createUnionOrSingleType($types); - } - - /** - * @param Type[] $types - */ - public function createMixedPassedOrUnionType(array $types): Type - { - $types = $this->unwrapUnionedTypes($types); - $types = $this->uniquateTypes($types); - - return $this->createUnionOrSingleType($types); - } - - /** - * @template TType as Type - * @param array $types - * @return array - */ - public function uniquateTypes(array $types, bool $keepConstant = false): array - { - $uniqueTypes = []; - foreach ($types as $type) { - if (! $keepConstant) { - $type = $this->removeValueFromConstantType($type); - } - - $type = $this->normalizeObjectTypes($type); - - $typeHash = $type->describe(VerbosityLevel::cache()); - $uniqueTypes[$typeHash] = $type; - } - - // re-index - return array_values($uniqueTypes); - } - - /** - * @param Type[] $types - * @return Type[] - */ - private function unwrapUnionedTypes(array $types): array - { - // unwrap union types - $unwrappedTypes = []; - foreach ($types as $type) { - $flattenTypes = TypeUtils::flattenTypes($type); - - foreach ($flattenTypes as $flattenType) { - if ($flattenType instanceof ConstantArrayType) { - $unwrappedTypes = array_merge($unwrappedTypes, $this->unwrapConstantArrayTypes($flattenType)); - } else { - $unwrappedTypes[] = $flattenType; - } - } - } - - return $unwrappedTypes; - } - - /** - * @param Type[] $types - */ - private function createUnionOrSingleType(array $types): Type - { - if ($types === []) { - return new MixedType(); - } - - if (count($types) === 1) { - return $types[0]; - } - - return $this->unionTypeFactory->createUnionObjectType($types); - } - - private function removeValueFromConstantType(Type $type): Type - { - // remove values from constant types - if ($type instanceof ConstantFloatType) { - return new FloatType(); - } - - if ($type instanceof ConstantStringType) { - return new StringType(); - } - - if ($type instanceof ConstantIntegerType) { - return new IntegerType(); - } - - if ($type instanceof ConstantBooleanType) { - return new BooleanType(); - } - - return $type; - } - - /** - * @return Type[] - */ - private function unwrapConstantArrayTypes(ConstantArrayType $constantArrayType): array - { - $unwrappedTypes = []; - - $flattenKeyTypes = TypeUtils::flattenTypes($constantArrayType->getKeyType()); - $flattenItemTypes = TypeUtils::flattenTypes($constantArrayType->getItemType()); - - foreach ($flattenItemTypes as $position => $nestedFlattenItemType) { - /** @var Type|null $nestedFlattenKeyType */ - $nestedFlattenKeyType = $flattenKeyTypes[$position] ?? null; - if ($nestedFlattenKeyType === null) { - $nestedFlattenKeyType = new MixedType(); - } - - $unwrappedTypes[] = new ArrayType($nestedFlattenKeyType, $nestedFlattenItemType); - } - - return $unwrappedTypes; - } - - private function normalizeObjectTypes(Type $type): Type - { - return TypeTraverser::map($type, function (Type $traversedType, callable $traverseCallback): Type { - if ($this->isStatic($traversedType) && $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersionFeature::STATIC_RETURN_TYPE - )) { - /** @var ObjectType $traversedType */ - return new ThisType($traversedType->getClassName()); - } - - if ($this->isStatic($traversedType)) { - return new MixedType(); - } - - if ($this->isSelf($traversedType)) { - /** @var ObjectType $traversedType */ - return new SelfObjectType($traversedType->getClassName()); - } - - if ($traversedType instanceof ShortenedObjectType) { - return new FullyQualifiedObjectType($traversedType->getFullyQualifiedName()); - } - - if ($traversedType instanceof ObjectType && ! $traversedType instanceof GenericObjectType && ! $traversedType instanceof AliasedObjectType && $traversedType->getClassName() !== 'Iterator') { - return new FullyQualifiedObjectType($traversedType->getClassName()); - } - - return $traverseCallback($traversedType); - }); - } - - private function isStatic(Type $type): bool - { - return $type instanceof ObjectType && $type->getClassName() === 'static'; - } - - private function isSelf(Type $type): bool - { - return $type instanceof ObjectType && $type->getClassName() === 'self'; - } -} diff --git a/packages/NodeTypeResolver/PHPStan/TypeHasher.php b/packages/NodeTypeResolver/PHPStan/TypeHasher.php deleted file mode 100644 index ee2b6fc1c30..00000000000 --- a/packages/NodeTypeResolver/PHPStan/TypeHasher.php +++ /dev/null @@ -1,97 +0,0 @@ -createTypeHash($firstType) === $this->createTypeHash($secondType); - } - - private function createTypeHash(Type $type): string - { - if ($type instanceof MixedType) { - return serialize($type) . $type->isExplicitMixed(); - } - - if ($type instanceof ArrayType) { - return $this->createTypeHash($type->getItemType()) . $this->createTypeHash($type->getKeyType()) . '[]'; - } - - if ($type instanceof GenericObjectType) { - return $type->describe(VerbosityLevel::precise()); - } - - if ($type instanceof TypeWithClassName) { - return $this->resolveUniqueTypeWithClassNameHash($type); - } - - if ($type instanceof ConstantType) { - return $type::class . $type->getValue(); - } - - if ($type instanceof UnionType) { - return $this->createUnionTypeHash($type); - } - - return $type->describe(VerbosityLevel::value()); - } - - private function resolveUniqueTypeWithClassNameHash(TypeWithClassName $typeWithClassName): string - { - if ($typeWithClassName instanceof ShortenedObjectType) { - return $typeWithClassName->getFullyQualifiedName(); - } - - if ($typeWithClassName instanceof AliasedObjectType) { - return $typeWithClassName->getFullyQualifiedClass(); - } - - return $typeWithClassName->getClassName(); - } - - private function createUnionTypeHash(UnionType $unionType): string - { - $sortedTypes = UnionTypeHelper::sortTypes($unionType->getTypes()); - $sortedUnionType = new UnionType($sortedTypes); - - $booleanType = new BooleanType(); - if ($booleanType->isSuperTypeOf($unionType)->yes()) { - return $booleanType->describe(VerbosityLevel::precise()); - } - - $normalizedUnionType = clone $sortedUnionType; - - // change alias to non-alias - $normalizedUnionType = TypeTraverser::map( - $normalizedUnionType, - function (Type $type, callable $callable): Type { - if (! $type instanceof AliasedObjectType) { - return $callable($type); - } - - return new FullyQualifiedObjectType($type->getFullyQualifiedClass()); - } - ); - - return $normalizedUnionType->describe(VerbosityLevel::cache()); - } -} diff --git a/packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockClassRenamer.php b/packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockClassRenamer.php deleted file mode 100644 index 10bb8218953..00000000000 --- a/packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockClassRenamer.php +++ /dev/null @@ -1,34 +0,0 @@ -renamingPhpDocNodeVisitorFactory->create(); - $this->classRenamePhpDocNodeVisitor->setOldToNewTypes($oldToNewTypes); - - $phpDocNodeTraverser->traverse($phpDocInfo->getPhpDocNode()); - } -} diff --git a/packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockNameImporter.php b/packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockNameImporter.php deleted file mode 100644 index 8b65869faaa..00000000000 --- a/packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockNameImporter.php +++ /dev/null @@ -1,31 +0,0 @@ -children === []) { - return; - } - - $this->nameImportingPhpDocNodeVisitor->setCurrentNode($node); - - $phpDocNodeTraverser = $this->importingPhpDocNodeTraverserFactory->create(); - $phpDocNodeTraverser->traverse($phpDocNode); - } -} diff --git a/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/ImportingPhpDocNodeTraverserFactory.php b/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/ImportingPhpDocNodeTraverserFactory.php deleted file mode 100644 index 955df5174c1..00000000000 --- a/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/ImportingPhpDocNodeTraverserFactory.php +++ /dev/null @@ -1,24 +0,0 @@ -addPhpDocNodeVisitor($this->nameImportingPhpDocNodeVisitor); - - return $phpDocNodeTraverser; - } -} diff --git a/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/RenamingPhpDocNodeVisitorFactory.php b/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/RenamingPhpDocNodeVisitorFactory.php deleted file mode 100644 index cec46781cbb..00000000000 --- a/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/RenamingPhpDocNodeVisitorFactory.php +++ /dev/null @@ -1,24 +0,0 @@ -addPhpDocNodeVisitor($this->classRenamePhpDocNodeVisitor); - - return $phpDocNodeTraverser; - } -} diff --git a/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/UnderscorePhpDocNodeTraverserFactory.php b/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/UnderscorePhpDocNodeTraverserFactory.php deleted file mode 100644 index 3a0bbad45de..00000000000 --- a/packages/NodeTypeResolver/PhpDoc/PhpDocNodeTraverser/UnderscorePhpDocNodeTraverserFactory.php +++ /dev/null @@ -1,24 +0,0 @@ -addPhpDocNodeVisitor($this->underscoreRenamePhpDocNodeVisitor); - - return $phpDocNodeTraverser; - } -} diff --git a/packages/NodeTypeResolver/PhpDoc/PhpDocTypeRenamer.php b/packages/NodeTypeResolver/PhpDoc/PhpDocTypeRenamer.php deleted file mode 100644 index cf2f518dd1d..00000000000 --- a/packages/NodeTypeResolver/PhpDoc/PhpDocTypeRenamer.php +++ /dev/null @@ -1,34 +0,0 @@ -getPhpDocNode(); - - $this->underscoreRenamePhpDocNodeVisitor->setPseudoNamespaceToNamespace($pseudoNamespaceToNamespace); - $this->underscoreRenamePhpDocNodeVisitor->setCurrentPhpParserNode($node); - - $phpDocNodeTraverser = $this->underscorePhpDocNodeTraverserFactory->create(); - $phpDocNodeTraverser->traverse($phpDocNode); - } -} diff --git a/packages/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php b/packages/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php deleted file mode 100644 index 2e4b0281874..00000000000 --- a/packages/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php +++ /dev/null @@ -1,87 +0,0 @@ -oldToNewTypes === []) { - throw new ShouldNotHappenException('Configure "$oldToNewClasses" first'); - } - } - - public function enterNode(Node $node): ?Node - { - if (! $node instanceof IdentifierTypeNode) { - return null; - } - - $phpParserNode = $this->currentNodeProvider->getNode(); - if (! $phpParserNode instanceof \PhpParser\Node) { - throw new ShouldNotHappenException(); - } - - $staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($node, $phpParserNode); - - // make sure to compare FQNs - if ($staticType instanceof ShortenedObjectType) { - $staticType = new ObjectType($staticType->getFullyQualifiedName()); - } - - foreach ($this->oldToNewTypes as $oldToNewType) { - if (! $staticType->equals($oldToNewType->getOldType())) { - continue; - } - - $newTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( - $oldToNewType->getNewType(), - TypeKind::ANY() - ); - - $parentType = $node->getAttribute(PhpDocAttributeKey::PARENT); - if ($parentType instanceof TypeNode) { - // mirror attributes - $newTypeNode->setAttribute(PhpDocAttributeKey::PARENT, $parentType); - } - - return $newTypeNode; - } - - return null; - } - - /** - * @param OldToNewType[] $oldToNewTypes - */ - public function setOldToNewTypes(array $oldToNewTypes): void - { - $this->oldToNewTypes = $oldToNewTypes; - } -} diff --git a/packages/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php b/packages/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php deleted file mode 100644 index ae1f08fc394..00000000000 --- a/packages/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php +++ /dev/null @@ -1,230 +0,0 @@ -currentPhpParserNode === null) { - throw new ShouldNotHappenException('Set "$currentPhpParserNode" first'); - } - } - - public function enterNode(Node $node): ?Node - { - if ($node instanceof SpacelessPhpDocTagNode) { - return $this->enterSpacelessPhpDocTagNode($node); - } - - if ($node instanceof DoctrineAnnotationTagValueNode) { - $this->processDoctrineAnnotationTagValueNode($node); - return $node; - } - - if (! $node instanceof IdentifierTypeNode) { - return null; - } - - $staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( - $node, - $this->currentPhpParserNode - ); - if (! $staticType instanceof FullyQualifiedObjectType) { - return null; - } - - // Importing root namespace classes (like \DateTime) is optional - if ($this->shouldSkipShortClassName($staticType)) { - return null; - } - - $file = $this->currentFileProvider->getFile(); - if (! $file instanceof File) { - return null; - } - - return $this->processFqnNameImport($this->currentPhpParserNode, $node, $staticType, $file); - } - - public function setCurrentNode(PhpParserNode $phpParserNode): void - { - $this->currentPhpParserNode = $phpParserNode; - } - - private function processFqnNameImport( - PhpParserNode $phpParserNode, - IdentifierTypeNode $identifierTypeNode, - FullyQualifiedObjectType $fullyQualifiedObjectType, - File $file - ): ?IdentifierTypeNode { - if ($this->classNameImportSkipper->shouldSkipNameForFullyQualifiedObjectType( - $file, - $phpParserNode, - $fullyQualifiedObjectType - )) { - return null; - } - - $parent = $identifierTypeNode->getAttribute(PhpDocAttributeKey::PARENT); - if ($parent instanceof TemplateTagValueNode) { - // might break - return null; - } - - $newNode = new IdentifierTypeNode($fullyQualifiedObjectType->getShortName()); - - // should skip because its already used - if ($this->useNodesToAddCollector->isShortImported($file, $fullyQualifiedObjectType)) { - if (! $this->useNodesToAddCollector->isImportShortable($file, $fullyQualifiedObjectType)) { - return null; - } - - if ($newNode->name !== $identifierTypeNode->name) { - $this->useNodesToAddCollector->addUseImport($fullyQualifiedObjectType); - return $newNode; - } - - return null; - } - - if ($newNode->name !== $identifierTypeNode->name) { - // do not import twice - if ($this->useNodesToAddCollector->isShortImported($file, $fullyQualifiedObjectType)) { - return null; - } - - $this->useNodesToAddCollector->addUseImport($fullyQualifiedObjectType); - return $newNode; - } - - return null; - } - - private function shouldSkipShortClassName(FullyQualifiedObjectType $fullyQualifiedObjectType): bool - { - $importShortClasses = $this->parameterProvider->provideBoolParameter(Option::IMPORT_SHORT_CLASSES); - if ($importShortClasses) { - return false; - } - - return substr_count($fullyQualifiedObjectType->getClassName(), '\\') === 0; - } - - private function processDoctrineAnnotationTagValueNode( - DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode - ): void { - $identifierTypeNode = $doctrineAnnotationTagValueNode->identifierTypeNode; - $staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( - $identifierTypeNode, - $this->currentPhpParserNode - ); - - if (! $staticType instanceof FullyQualifiedObjectType) { - if (! $staticType instanceof ObjectType) { - return; - } - - $staticType = new FullyQualifiedObjectType($staticType->getClassName()); - } - - $file = $this->currentFileProvider->getFile(); - if (! $file instanceof File) { - return; - } - - $shortentedIdentifierTypeNode = $this->processFqnNameImport( - $this->currentPhpParserNode, - $identifierTypeNode, - $staticType, - $file - ); - - if (! $shortentedIdentifierTypeNode instanceof IdentifierTypeNode) { - return; - } - - $doctrineAnnotationTagValueNode->identifierTypeNode = $shortentedIdentifierTypeNode; - $doctrineAnnotationTagValueNode->markAsChanged(); - } - - private function enterSpacelessPhpDocTagNode( - SpacelessPhpDocTagNode $spacelessPhpDocTagNode - ): SpacelessPhpDocTagNode | null { - if (! $spacelessPhpDocTagNode->value instanceof DoctrineAnnotationTagValueNode) { - return null; - } - - // special case for doctrine annotation - if (! str_starts_with($spacelessPhpDocTagNode->name, '@')) { - return null; - } - - $attributeClass = ltrim($spacelessPhpDocTagNode->name, '@\\'); - $identifierTypeNode = new IdentifierTypeNode($attributeClass); - - $staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( - new IdentifierTypeNode($attributeClass), - $this->currentPhpParserNode - ); - - if (! $staticType instanceof FullyQualifiedObjectType) { - if (! $staticType instanceof ObjectType) { - return null; - } - - $staticType = new FullyQualifiedObjectType($staticType->getClassName()); - } - - $file = $this->currentFileProvider->getFile(); - if (! $file instanceof File) { - return null; - } - - $importedName = $this->processFqnNameImport( - $this->currentPhpParserNode, - $identifierTypeNode, - $staticType, - $file - ); - - if ($importedName !== null) { - $spacelessPhpDocTagNode->name = '@' . $importedName->name; - return $spacelessPhpDocTagNode; - } - - return null; - } -} diff --git a/packages/NodeTypeResolver/PhpDocNodeVisitor/UnderscoreRenamePhpDocNodeVisitor.php b/packages/NodeTypeResolver/PhpDocNodeVisitor/UnderscoreRenamePhpDocNodeVisitor.php deleted file mode 100644 index 55f95f99da5..00000000000 --- a/packages/NodeTypeResolver/PhpDocNodeVisitor/UnderscoreRenamePhpDocNodeVisitor.php +++ /dev/null @@ -1,93 +0,0 @@ -pseudoNamespaceToNamespace === null) { - throw new ShouldNotHappenException('Set PseudoNamespaceToNamespace first'); - } - - if (! $this->currentPhpParserNode instanceof \PhpParser\Node) { - throw new ShouldNotHappenException('Set "$currentPhpParserNode" first'); - } - } - - public function enterNode(Node $node): ?Node - { - if (! $node instanceof IdentifierTypeNode) { - return null; - } - - if ($this->shouldSkip($node, $this->currentPhpParserNode, $this->pseudoNamespaceToNamespace)) { - return null; - } - - /** @var IdentifierTypeNode $node */ - $staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( - $node, - $this->currentPhpParserNode - ); - if (! $staticType instanceof ObjectType) { - return null; - } - - // change underscore to \\ - $slashedName = '\\' . Strings::replace($staticType->getClassName(), '#_#', '\\'); - return new IdentifierTypeNode($slashedName); - } - - public function setPseudoNamespaceToNamespace(PseudoNamespaceToNamespace $pseudoNamespaceToNamespace): void - { - $this->pseudoNamespaceToNamespace = $pseudoNamespaceToNamespace; - } - - public function setCurrentPhpParserNode(\PhpParser\Node $node): void - { - $this->currentPhpParserNode = $node; - } - - private function shouldSkip( - IdentifierTypeNode $identifierTypeNode, - \PhpParser\Node $phpParserNode, - PseudoNamespaceToNamespace $pseudoNamespaceToNamespace - ): bool { - $staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( - $identifierTypeNode, - $phpParserNode - ); - - if (! $staticType instanceof ObjectType) { - return true; - } - - if (! \str_starts_with($staticType->getClassName(), $pseudoNamespaceToNamespace->getNamespacePrefix())) { - return true; - } - - // excluded? - return in_array($staticType->getClassName(), $pseudoNamespaceToNamespace->getExcludedClasses(), true); - } -} diff --git a/packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocator/IntermediateSourceLocator.php b/packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocator/IntermediateSourceLocator.php deleted file mode 100644 index 66b6defd297..00000000000 --- a/packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocator/IntermediateSourceLocator.php +++ /dev/null @@ -1,60 +0,0 @@ -sourceLocatorProviders = $sourceLocatorProviders; - } - - public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?Reflection - { - foreach ($this->sourceLocatorProviders as $sourceLocatorProvider) { - $sourceLocator = $sourceLocatorProvider->provide(); - - $reflection = $sourceLocator->locateIdentifier($reflector, $identifier); - if ($reflection instanceof Reflection) { - return $reflection; - } - } - - return null; - } - - /** - * Find all identifiers of a type - * @return Reflection[] - */ - public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array - { - foreach ($this->sourceLocatorProviders as $sourceLocatorProvider) { - $sourceLocator = $sourceLocatorProvider->provide(); - - $reflections = $sourceLocator->locateIdentifiersByType($reflector, $identifierType); - if ($reflections !== []) { - return $reflections; - } - } - - return []; - } -} diff --git a/packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php b/packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php deleted file mode 100644 index 9ce82a94294..00000000000 --- a/packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php +++ /dev/null @@ -1,86 +0,0 @@ - - */ - private $filesByDirectory = []; - - /** - * @var FileNodesFetcher - */ - private $fileNodesFetcher; - - /** - * @var SourceLocator|null - */ - private $cachedSourceLocator; - - public function __construct(FileNodesFetcher $fileNodesFetcher) - { - $this->fileNodesFetcher = $fileNodesFetcher; - } - - public function setFileInfo(SmartFileInfo $fileInfo): void - { - $this->files = [$fileInfo->getRealPath()]; - } - - /** - * @param string[] $files - */ - public function addFiles(array $files): void - { - $this->files = array_merge($this->files, $files); - } - - public function provide(): SourceLocator - { - // do not cache for PHPUnit, as in test every fixture is different - $isPHPUnitRun = StaticPHPUnitEnvironment::isPHPUnitRun(); - - if ($this->cachedSourceLocator && $isPHPUnitRun === false) { - return $this->cachedSourceLocator; - } - - $sourceLocators = []; - foreach ($this->files as $file) { - $sourceLocators[] = new OptimizedSingleFileSourceLocator($this->fileNodesFetcher, $file); - } - - foreach ($this->filesByDirectory as $files) { - $sourceLocators[] = new OptimizedDirectorySourceLocator($this->fileNodesFetcher, $files); - } - - $this->cachedSourceLocator = new AggregateSourceLocator($sourceLocators); - - return $this->cachedSourceLocator; - } - - /** - * @param string[] $files - */ - public function addFilesByDirectory(string $directory, array $files): void - { - $this->filesByDirectory[$directory] = $files; - } -} diff --git a/packages/NodeTypeResolver/TypeAnalyzer/ArrayTypeAnalyzer.php b/packages/NodeTypeResolver/TypeAnalyzer/ArrayTypeAnalyzer.php deleted file mode 100644 index 1efda7acb70..00000000000 --- a/packages/NodeTypeResolver/TypeAnalyzer/ArrayTypeAnalyzer.php +++ /dev/null @@ -1,127 +0,0 @@ -nodeTypeResolver->resolve($node); - - $nodeStaticType = $this->pregMatchTypeCorrector->correct($node, $nodeStaticType); - if ($this->isIntersectionArrayType($nodeStaticType)) { - return true; - } - - // PHPStan false positive, when variable has type[] docblock, but default array is missing - if (($node instanceof PropertyFetch || $node instanceof StaticPropertyFetch) && ! $this->isPropertyFetchWithArrayDefault( - $node - )) { - return false; - } - - if ($nodeStaticType instanceof MixedType) { - if ($nodeStaticType->isExplicitMixed()) { - return false; - } - - if ($this->isPropertyFetchWithArrayDefault($node)) { - return true; - } - } - - return $nodeStaticType instanceof ArrayType; - } - - private function isIntersectionArrayType(Type $nodeType): bool - { - if (! $nodeType instanceof IntersectionType) { - return false; - } - - foreach ($nodeType->getTypes() as $intersectionNodeType) { - if ($intersectionNodeType instanceof ArrayType) { - continue; - } - if ($intersectionNodeType instanceof HasOffsetType) { - continue; - } - if ($intersectionNodeType instanceof NonEmptyArrayType) { - continue; - } - return false; - } - - return true; - } - - /** - * phpstan bug workaround - https://phpstan.org/r/0443f283-244c-42b8-8373-85e7deb3504c - */ - private function isPropertyFetchWithArrayDefault(Node $node): bool - { - if (! $node instanceof PropertyFetch && ! $node instanceof StaticPropertyFetch) { - return false; - } - - /** @var Class_|Trait_|Interface_|null $classLike */ - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if ($classLike instanceof Interface_) { - return false; - } - if ($classLike === null) { - return false; - } - - $propertyName = $this->nodeNameResolver->getName($node->name); - if ($propertyName === null) { - return false; - } - - $property = $classLike->getProperty($propertyName); - if ($property !== null) { - $propertyProperty = $property->props[0]; - return $propertyProperty->default instanceof Array_; - } - - // also possible 3rd party vendor - if ($node instanceof PropertyFetch) { - $propertyOwnerStaticType = $this->nodeTypeResolver->resolve($node->var); - } else { - $propertyOwnerStaticType = $this->nodeTypeResolver->resolve($node->class); - } - if ($propertyOwnerStaticType instanceof ThisType) { - return false; - } - return $propertyOwnerStaticType instanceof TypeWithClassName; - } -} diff --git a/packages/NodeTypeResolver/TypeAnalyzer/CountableTypeAnalyzer.php b/packages/NodeTypeResolver/TypeAnalyzer/CountableTypeAnalyzer.php deleted file mode 100644 index e50ff0ddb84..00000000000 --- a/packages/NodeTypeResolver/TypeAnalyzer/CountableTypeAnalyzer.php +++ /dev/null @@ -1,44 +0,0 @@ -countableObjectTypes = [ - new ObjectType('Countable'), - new ObjectType('SimpleXMLElement'), - new ObjectType('ResourceBundle'), - ]; - } - - public function isCountableType(Node $node): bool - { - $nodeType = $this->nodeTypeResolver->resolve($node); - $nodeType = $this->pregMatchTypeCorrector->correct($node, $nodeType); - - foreach ($this->countableObjectTypes as $countableObjectType) { - if ($countableObjectType->isSuperTypeOf($nodeType)->yes()) { - return true; - } - } - - return $this->arrayTypeAnalyzer->isArrayType($node); - } -} diff --git a/packages/NodeTypeResolver/TypeAnalyzer/StringTypeAnalyzer.php b/packages/NodeTypeResolver/TypeAnalyzer/StringTypeAnalyzer.php deleted file mode 100644 index f22dcf4a434..00000000000 --- a/packages/NodeTypeResolver/TypeAnalyzer/StringTypeAnalyzer.php +++ /dev/null @@ -1,38 +0,0 @@ -nodeTypeResolver->getStaticType($node); - if ($nodeType instanceof StringType) { - return true; - } - - if ($nodeType instanceof UnionType) { - foreach ($nodeType->getTypes() as $singleType) { - if ($singleType->isSuperTypeOf(new StringType())->no()) { - return false; - } - } - - return true; - } - - return false; - } -} diff --git a/packages/NodeTypeResolver/TypeComparator/ArrayTypeComparator.php b/packages/NodeTypeResolver/TypeComparator/ArrayTypeComparator.php deleted file mode 100644 index e0e1fdce68f..00000000000 --- a/packages/NodeTypeResolver/TypeComparator/ArrayTypeComparator.php +++ /dev/null @@ -1,79 +0,0 @@ -isSuperTypeOf($checkedType) - ->yes(); - } - - $checkedKeyType = $checkedType->getKeyType(); - $mainKeyType = $mainType->getKeyType(); - - $mainKeyType = $this->narrowArrayKeysUnionType($mainKeyType); - - $checkedKeyType = $this->narrowArrayKeysUnionType($checkedKeyType); - if (! $mainKeyType instanceof MixedType && $mainKeyType->isSuperTypeOf($checkedKeyType)->yes()) { - return true; - } - - $checkedItemType = $checkedType->getItemType(); - if ($checkedItemType instanceof UnionType) { - $checkedItemType = $this->unionTypeCommonTypeNarrower->narrowToGenericClassStringType($checkedItemType); - } - - $mainItemType = $mainType->getItemType(); - if ($mainItemType instanceof UnionType) { - $mainItemType = $this->unionTypeCommonTypeNarrower->narrowToGenericClassStringType($mainItemType); - } - - return $checkedItemType->isSuperTypeOf($mainItemType) - ->yes(); - } - - /** - * Native array order can be treated as mixed type - */ - private function narrowArrayKeysUnionType(Type $type): Type - { - if (! $type instanceof UnionType) { - return $type; - } - - foreach ($type->getTypes() as $key => $unionedType) { - if (! $unionedType instanceof ConstantIntegerType) { - return $type; - } - - if ($key === $unionedType->getValue()) { - continue; - } - - return $type; - } - - return new MixedType(); - } -} diff --git a/packages/NodeTypeResolver/TypeComparator/ScalarTypeComparator.php b/packages/NodeTypeResolver/TypeComparator/ScalarTypeComparator.php deleted file mode 100644 index ce8323e5da4..00000000000 --- a/packages/NodeTypeResolver/TypeComparator/ScalarTypeComparator.php +++ /dev/null @@ -1,42 +0,0 @@ -scalarTypeComparator->areEqualScalar($firstType, $secondType)) { - return true; - } - - // aliases and types - if ($this->areAliasedObjectMatchingFqnObject($firstType, $secondType)) { - return true; - } - - if ($this->areArrayUnionConstantEqualTypes($firstType, $secondType)) { - return true; - } - - $firstType = $this->typeNormalizer->normalizeArrayOfUnionToUnionArray($firstType); - $secondType = $this->typeNormalizer->normalizeArrayOfUnionToUnionArray($secondType); - - if ($this->typeHasher->areTypesEqual($firstType, $secondType)) { - return true; - } - - // is template of - return $this->areArrayTypeWithSingleObjectChildToParent($firstType, $secondType); - } - - public function arePhpParserAndPhpStanPhpDocTypesEqual( - Node $phpParserNode, - TypeNode $phpStanDocTypeNode, - Node $node - ): bool { - $phpParserNodeType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($phpParserNode); - $phpStanDocType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( - $phpStanDocTypeNode, - $node - ); - - // normalize bool union types - $phpParserNodeType = $this->normalizeConstantBooleanType($phpParserNodeType); - $phpStanDocType = $this->normalizeConstantBooleanType($phpStanDocType); - - return $this->areTypesEqual($phpParserNodeType, $phpStanDocType); - } - - public function isSubtype(Type $checkedType, Type $mainType): bool - { - if ($mainType instanceof MixedType) { - return false; - } - - if (! $mainType instanceof ArrayType) { - return $mainType->isSuperTypeOf($checkedType) - ->yes(); - } - - if (! $checkedType instanceof ArrayType) { - return $mainType->isSuperTypeOf($checkedType) - ->yes(); - } - - return $this->arrayTypeComparator->isSubtype($checkedType, $mainType); - } - - private function areAliasedObjectMatchingFqnObject(Type $firstType, Type $secondType): bool - { - if ($firstType instanceof AliasedObjectType && $secondType instanceof ObjectType && $firstType->getFullyQualifiedClass() === $secondType->getClassName()) { - return true; - } - if (! $secondType instanceof AliasedObjectType) { - return false; - } - if (! $firstType instanceof ObjectType) { - return false; - } - return $secondType->getFullyQualifiedClass() === $firstType->getClassName(); - } - - /** - * E.g. class A extends B, class B → A[] is subtype of B[] → keep A[] - */ - private function areArrayTypeWithSingleObjectChildToParent(Type $firstType, Type $secondType): bool - { - if (! $firstType instanceof ArrayType) { - return false; - } - if (! $secondType instanceof ArrayType) { - return false; - } - - $firstArrayItemType = $firstType->getItemType(); - $secondArrayItemType = $secondType->getItemType(); - - if ($this->isMutualObjectSubtypes($firstArrayItemType, $secondArrayItemType)) { - return true; - } - - if (! $firstArrayItemType instanceof GenericClassStringType) { - return false; - } - - if (! $secondArrayItemType instanceof GenericClassStringType) { - return false; - } - - // @todo resolve later better with template map, @see https://github.com/symplify/symplify/pull/3034/commits/4f6be8b87e52117b1aa1613b9b689ae958a9d6f4 - return $firstArrayItemType->getGenericType() instanceof ObjectType && $secondArrayItemType->getGenericType() instanceof ObjectType; - } - - private function isMutualObjectSubtypes(Type $firstArrayItemType, Type $secondArrayItemType): bool - { - if ($firstArrayItemType instanceof ObjectType && $secondArrayItemType instanceof ObjectType) { - if ($firstArrayItemType->isSuperTypeOf($secondArrayItemType)->yes()) { - return true; - } - - if ($secondArrayItemType->isSuperTypeOf($firstArrayItemType)->yes()) { - return true; - } - } - - return false; - } - - private function normalizeSingleUnionType(Type $type): Type - { - if ($type instanceof UnionType) { - $uniqueTypes = $this->typeFactory->uniquateTypes($type->getTypes()); - if (count($uniqueTypes) === 1) { - return $uniqueTypes[0]; - } - } - - return $type; - } - - private function areArrayUnionConstantEqualTypes(Type $firstType, Type $secondType): bool - { - if (! $firstType instanceof ArrayType) { - return false; - } - - if (! $secondType instanceof ArrayType) { - return false; - } - - $firstKeyType = $this->normalizeSingleUnionType($firstType->getKeyType()); - $secondKeyType = $this->normalizeSingleUnionType($secondType->getKeyType()); - - // mixed and integer type are mutual replaceable in practise - if ($firstKeyType instanceof MixedType) { - $firstKeyType = new IntegerType(); - } - - if ($secondKeyType instanceof MixedType) { - $secondKeyType = new IntegerType(); - } - - if (! $this->areTypesEqual($firstKeyType, $secondKeyType)) { - return false; - } - - $firstArrayType = $this->normalizeSingleUnionType($firstType->getItemType()); - $secondArrayType = $this->normalizeSingleUnionType($secondType->getItemType()); - - return $this->areTypesEqual($firstArrayType, $secondArrayType); - } - - private function normalizeConstantBooleanType(Type $type): Type - { - return TypeTraverser::map($type, function (Type $type, callable $callable): Type { - if ($type instanceof ConstantBooleanType) { - return new BooleanType(); - } - - return $callable($type); - }); - } -} diff --git a/packages/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php b/packages/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php deleted file mode 100644 index 8e5c88cc41d..00000000000 --- a/packages/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php +++ /dev/null @@ -1,35 +0,0 @@ - - */ - public function getNodeClass(): string; - - /** - * @param T $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode; - - /** - * @param T $type - * @return Name|NullableType|UnionType|null - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node; -} diff --git a/packages/PHPStanStaticTypeMapper/DoctrineTypeAnalyzer.php b/packages/PHPStanStaticTypeMapper/DoctrineTypeAnalyzer.php deleted file mode 100644 index a4b3f29debb..00000000000 --- a/packages/PHPStanStaticTypeMapper/DoctrineTypeAnalyzer.php +++ /dev/null @@ -1,47 +0,0 @@ -getTypes() as $unionedType) { - if ($this->isCollectionObjectType($unionedType)) { - $hasDoctrineCollectionType = true; - } - - if ($unionedType instanceof ArrayType) { - $arrayType = $unionedType; - } - } - - if (! $hasDoctrineCollectionType) { - return false; - } - - return $arrayType !== null; - } - - private function isCollectionObjectType(Type $type): bool - { - if (! $type instanceof TypeWithClassName) { - return false; - } - - return $type->getClassName() === 'Doctrine\Common\Collections\Collection'; - } -} diff --git a/packages/PHPStanStaticTypeMapper/PHPStanStaticTypeMapper.php b/packages/PHPStanStaticTypeMapper/PHPStanStaticTypeMapper.php deleted file mode 100644 index 18cfd50c5af..00000000000 --- a/packages/PHPStanStaticTypeMapper/PHPStanStaticTypeMapper.php +++ /dev/null @@ -1,57 +0,0 @@ -typeMappers as $typeMapper) { - if (! is_a($type, $typeMapper->getNodeClass(), true)) { - continue; - } - - return $typeMapper->mapToPHPStanPhpDocTypeNode($type, $typeKind); - } - - if ($type instanceof AccessoryNumericStringType) { - return new IdentifierTypeNode('string'); - } - - throw new NotImplementedYetException(__METHOD__ . ' for ' . $type::class); - } - - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): Name | NullableType | UnionType | null - { - foreach ($this->typeMappers as $typeMapper) { - if (! is_a($type, $typeMapper->getNodeClass(), true)) { - continue; - } - - return $typeMapper->mapToPhpParserNode($type, $typeKind); - } - - throw new NotImplementedYetException(__METHOD__ . ' for ' . $type::class); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeAnalyzer/BoolUnionTypeAnalyzer.php b/packages/PHPStanStaticTypeMapper/TypeAnalyzer/BoolUnionTypeAnalyzer.php deleted file mode 100644 index 9644de6b7b7..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeAnalyzer/BoolUnionTypeAnalyzer.php +++ /dev/null @@ -1,42 +0,0 @@ -getTypes() as $unionedType) { - if (! $unionedType instanceof BooleanType) { - return false; - } - } - - return true; - } - - public function isNullableBoolUnionType(UnionType $unionType): bool - { - $hasNullable = false; - foreach ($unionType->getTypes() as $unionedType) { - if ($unionedType instanceof NullType) { - $hasNullable = true; - continue; - } - - if ($unionedType instanceof BooleanType) { - continue; - } - - return false; - } - - return $hasNullable; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeAnalyzer/UnionTypeAnalyzer.php b/packages/PHPStanStaticTypeMapper/TypeAnalyzer/UnionTypeAnalyzer.php deleted file mode 100644 index 7d09cb31767..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeAnalyzer/UnionTypeAnalyzer.php +++ /dev/null @@ -1,123 +0,0 @@ -getTypes() as $unionedType) { - if ($unionedType instanceof IterableType) { - $hasIterable = true; - continue; - } - - if ($unionedType instanceof ArrayType) { - $hasArray = true; - continue; - } - - if ($unionedType instanceof NullType) { - $isNullableType = true; - continue; - } - - if ($unionedType instanceof ObjectType && $unionedType->getClassName() === Traversable::class) { - $hasIterable = true; - continue; - } - - return null; - } - - return new UnionTypeAnalysis($isNullableType, $hasIterable, $hasArray); - } - - public function hasTypeClassNameOnly(UnionType $unionType): bool - { - foreach ($unionType->getTypes() as $unionedType) { - if (! $unionedType instanceof TypeWithClassName) { - return false; - } - } - - return true; - } - - public function hasObjectWithoutClassType(UnionType $unionType): bool - { - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type instanceof ObjectWithoutClassType) { - return true; - } - } - - return false; - } - - public function hasObjectWithoutClassTypeWithOnlyFullyQualifiedObjectType(UnionType $unionType): bool - { - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type instanceof ObjectWithoutClassType) { - continue; - } - - if (! $type instanceof FullyQualifiedObjectType) { - return false; - } - } - - return true; - } - - public function isScalar(UnionType $unionType): bool - { - $types = $unionType->getTypes(); - - if (count($types) !== 4) { - return false; - } - - foreach ($types as $type) { - if ($type instanceof StringType && ! $type instanceof ConstantStringType) { - continue; - } - if ($type instanceof FloatType) { - continue; - } - if ($type instanceof IntegerType) { - continue; - } - if ($type instanceof BooleanType) { - continue; - } - return false; - } - - return true; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeAnalyzer/UnionTypeCommonTypeNarrower.php b/packages/PHPStanStaticTypeMapper/TypeAnalyzer/UnionTypeCommonTypeNarrower.php deleted file mode 100644 index 6415acd10e7..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeAnalyzer/UnionTypeCommonTypeNarrower.php +++ /dev/null @@ -1,198 +0,0 @@ -|class-string<\PHPStan\PhpDocParser\Ast\Node>|class-string>> - */ - private const PRIORITY_TYPES = [ - ClassLike::class => [ClassLike::class], - FunctionLike::class => [FunctionLike::class], - BinaryOp::class => [BinaryOp::class, Expr::class], - Expr::class => [Node::class, Expr::class], - Stmt::class => [Node::class, Stmt::class], - PhpDocTagValueNode::class => [PhpDocTagValueNode::class, \PHPStan\PhpDocParser\Ast\Node::class], - Node::class => [Node::class], - RectorInterface::class => [RectorInterface::class], - ]; - - public function __construct( - private GenericClassStringTypeCorrector $genericClassStringTypeCorrector, - private ReflectionProvider $reflectionProvider - ) { - } - - public function narrowToSharedObjectType(UnionType $unionType): ?ObjectType - { - $sharedTypes = $this->narrowToSharedTypes($unionType); - - if ($sharedTypes !== []) { - foreach (self::PRIORITY_TYPES as $winningType => $groupTypes) { - $intersectedGroupTypes = array_intersect($groupTypes, $sharedTypes); - if ($intersectedGroupTypes === $groupTypes) { - return new ObjectType($winningType); - } - } - - $firstSharedType = $sharedTypes[0]; - return new ObjectType($firstSharedType); - } - - return null; - } - - public function narrowToGenericClassStringType(UnionType $unionType): UnionType | GenericClassStringType - { - $availableTypes = []; - - foreach ($unionType->getTypes() as $unionedType) { - if ($unionedType instanceof ConstantStringType) { - $unionedType = $this->genericClassStringTypeCorrector->correct($unionedType); - } - - if (! $unionedType instanceof GenericClassStringType) { - return $unionType; - } - - $genericClassStrings = []; - if ($unionedType->getGenericType() instanceof ObjectType) { - $parentClassReflections = $this->resolveClassParentClassesAndInterfaces($unionedType->getGenericType()); - foreach ($parentClassReflections as $parentClassReflection) { - $genericClassStrings[] = $parentClassReflection->getName(); - } - } - - $availableTypes[] = $genericClassStrings; - } - - $genericClassStringType = $this->createGenericClassStringType($availableTypes); - if ($genericClassStringType instanceof GenericClassStringType) { - return $genericClassStringType; - } - - return $unionType; - } - - /** - * @return string[] - */ - private function narrowToSharedTypes(UnionType $unionType): array - { - $availableTypes = []; - - foreach ($unionType->getTypes() as $unionedType) { - if (! $unionedType instanceof ObjectType) { - return []; - } - - $typeClassReflections = $this->resolveClassParentClassesAndInterfaces($unionedType); - $typeClassNames = []; - - foreach ($typeClassReflections as $typeClassReflection) { - $typeClassNames[] = $typeClassReflection->getName(); - } - - if ($typeClassNames === []) { - continue; - } - - $availableTypes[] = $typeClassNames; - } - - return $this->narrowAvailableTypes($availableTypes); - } - - /** - * @return ClassReflection[] - */ - private function resolveClassParentClassesAndInterfaces(ObjectType $objectType): array - { - if (! $this->reflectionProvider->hasClass($objectType->getClassName())) { - return []; - } - - $classReflection = $this->reflectionProvider->getClass($objectType->getClassName()); - - // put earliest interfaces first - $implementedInterfaceClassReflections = array_reverse($classReflection->getInterfaces()); - - /** @var ClassReflection[] $parentClassAndInterfaceReflections */ - $parentClassAndInterfaceReflections = array_merge( - $implementedInterfaceClassReflections, - $classReflection->getParents() - ); - - return $this->filterOutNativeClassReflections($parentClassAndInterfaceReflections); - } - - /** - * @param string[][] $availableTypes - * @return string[] - */ - private function narrowAvailableTypes(array $availableTypes): array - { - if (count($availableTypes) < 2) { - return []; - } - - /** @var string[] $sharedTypes */ - $sharedTypes = array_intersect(...$availableTypes); - return array_values($sharedTypes); - } - - /** - * @param string[][] $availableTypes - */ - private function createGenericClassStringType(array $availableTypes): ?GenericClassStringType - { - $sharedTypes = $this->narrowAvailableTypes($availableTypes); - - if ($sharedTypes !== []) { - foreach (self::PRIORITY_TYPES as $winningType => $groupTypes) { - $intersectedGroupTypes = array_intersect($groupTypes, $sharedTypes); - if ($intersectedGroupTypes === $groupTypes) { - return new GenericClassStringType(new ObjectType($winningType)); - } - } - - $firstSharedType = $sharedTypes[0]; - return new GenericClassStringType(new ObjectType($firstSharedType)); - } - - return null; - } - - /** - * @param ClassReflection[] $classReflections - * @return ClassReflection[] - */ - private function filterOutNativeClassReflections(array $classReflections): array - { - return array_filter( - $classReflections, - fn (ClassReflection $classReflection): bool => ! $classReflection->isBuiltin() - ); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonEmptyStringTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonEmptyStringTypeMapper.php deleted file mode 100644 index a7bc57792a7..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonEmptyStringTypeMapper.php +++ /dev/null @@ -1,44 +0,0 @@ - - */ -final class AccessoryNonEmptyStringTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return AccessoryNonEmptyStringType::class; - } - - /** - * @param AccessoryNonEmptyStringType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('string'); - } - - /** - * @param AccessoryNonEmptyStringType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return new Name('string'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php deleted file mode 100644 index c7cf1a39b89..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php +++ /dev/null @@ -1,260 +0,0 @@ - - */ -final class ArrayTypeMapper implements TypeMapperInterface -{ - /** - * @var string - */ - public const HAS_GENERIC_TYPE_PARENT = 'has_generic_type_parent'; - - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - private UnionTypeCommonTypeNarrower $unionTypeCommonTypeNarrower; - - private ReflectionProvider $reflectionProvider; - - // To avoid circular dependency - - #[Required] - public function autowireArrayTypeMapper( - PHPStanStaticTypeMapper $phpStanStaticTypeMapper, - UnionTypeCommonTypeNarrower $unionTypeCommonTypeNarrower, - ReflectionProvider $reflectionProvider - ): void { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - $this->unionTypeCommonTypeNarrower = $unionTypeCommonTypeNarrower; - $this->reflectionProvider = $reflectionProvider; - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return ArrayType::class; - } - - /** - * @param ArrayType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - $itemType = $type->getItemType(); - - if ($itemType instanceof UnionType && ! $type instanceof ConstantArrayType) { - return $this->createArrayTypeNodeFromUnionType($itemType, $typeKind); - } - - if ($itemType instanceof ArrayType && $this->isGenericArrayCandidate($itemType)) { - return $this->createGenericArrayType($type, $typeKind, true); - } - - if ($this->isGenericArrayCandidate($type)) { - return $this->createGenericArrayType($type, $typeKind, true); - } - - $narrowedTypeNode = $this->narrowConstantArrayTypeOfUnionType($type, $itemType, $typeKind); - if ($narrowedTypeNode instanceof TypeNode) { - return $narrowedTypeNode; - } - - $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($itemType, $typeKind); - - return new SpacingAwareArrayTypeNode($itemTypeNode); - } - - /** - * @param ArrayType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return new Name('array'); - } - - private function createArrayTypeNodeFromUnionType( - UnionType $unionType, - TypeKind $typeKind - ): SpacingAwareArrayTypeNode { - $unionedArrayType = []; - foreach ($unionType->getTypes() as $unionedType) { - $typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($unionedType, $typeKind); - $unionedArrayType[(string) $typeNode] = $typeNode; - } - - if (count($unionedArrayType) > 1) { - return new SpacingAwareArrayTypeNode(new BracketsAwareUnionTypeNode($unionedArrayType)); - } - - /** @var TypeNode $arrayType */ - $arrayType = array_shift($unionedArrayType); - return new SpacingAwareArrayTypeNode($arrayType); - } - - private function isGenericArrayCandidate(ArrayType $arrayType): bool - { - if ($arrayType->getKeyType() instanceof MixedType) { - return false; - } - - if ($this->isClassStringArrayType($arrayType)) { - return true; - } - - // skip simple arrays, like "string[]", from converting to obvious "array" - if ($this->isIntegerKeyAndNonNestedArray($arrayType)) { - return false; - } - - if ($arrayType->getKeyType() instanceof NeverType) { - return false; - } - - // make sure the integer key type is not natural/implicit array int keys - $keysArrayType = $arrayType->getKeysArray(); - if (! $keysArrayType instanceof ConstantArrayType) { - return true; - } - - foreach ($keysArrayType->getValueTypes() as $key => $keyType) { - if (! $keyType instanceof ConstantIntegerType) { - return true; - } - - if ($key !== $keyType->getValue()) { - return true; - } - } - - return false; - } - - private function createGenericArrayType( - ArrayType $arrayType, - TypeKind $typeKind, - bool $withKey = false - ): GenericTypeNode { - $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode( - $arrayType->getItemType(), - $typeKind - ); - $identifierTypeNode = new IdentifierTypeNode('array'); - - // is class-string[] list only - if ($this->isClassStringArrayType($arrayType)) { - $withKey = false; - } - - if ($withKey) { - $keyTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode( - $arrayType->getKeyType(), - $typeKind - ); - $genericTypes = [$keyTypeNode, $itemTypeNode]; - } else { - $genericTypes = [$itemTypeNode]; - } - - // @see https://github.com/phpstan/phpdoc-parser/blob/98a088b17966bdf6ee25c8a4b634df313d8aa531/tests/PHPStan/Parser/PhpDocParserTest.php#L2692-L2696 - - foreach ($genericTypes as $genericType) { - /** @var \PHPStan\PhpDocParser\Ast\Node $genericType */ - $genericType->setAttribute(self::HAS_GENERIC_TYPE_PARENT, $withKey); - } - - $identifierTypeNode->setAttribute(self::HAS_GENERIC_TYPE_PARENT, $withKey); - return new GenericTypeNode($identifierTypeNode, $genericTypes); - } - - private function isIntegerKeyAndNonNestedArray(ArrayType $arrayType): bool - { - if (! $arrayType->getKeyType() instanceof IntegerType) { - return false; - } - - return ! $arrayType->getItemType() instanceof ArrayType; - } - - private function narrowConstantArrayTypeOfUnionType( - ArrayType $arrayType, - Type $itemType, - TypeKind $typeKind - ): ?TypeNode { - if ($arrayType instanceof ConstantArrayType && $itemType instanceof UnionType) { - $narrowedItemType = $this->unionTypeCommonTypeNarrower->narrowToSharedObjectType($itemType); - if ($narrowedItemType instanceof ObjectType) { - $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode( - $narrowedItemType, - $typeKind - ); - return new SpacingAwareArrayTypeNode($itemTypeNode); - } - - $narrowedItemType = $this->unionTypeCommonTypeNarrower->narrowToGenericClassStringType($itemType); - if ($narrowedItemType instanceof GenericClassStringType) { - return $this->createTypeNodeFromGenericClassStringType($narrowedItemType, $typeKind); - } - } - - return null; - } - - private function createTypeNodeFromGenericClassStringType( - GenericClassStringType $genericClassStringType, - TypeKind $typeKind - ): TypeNode { - $genericType = $genericClassStringType->getGenericType(); - if ($genericType instanceof ObjectType && ! $this->reflectionProvider->hasClass($genericType->getClassName())) { - return new IdentifierTypeNode($genericType->getClassName()); - } - - $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($genericClassStringType, $typeKind); - - return new GenericTypeNode(new IdentifierTypeNode('array'), [$itemTypeNode]); - } - - private function isClassStringArrayType(ArrayType $arrayType): bool - { - if ($arrayType->getKeyType() instanceof MixedType) { - return $arrayType->getItemType() instanceof GenericClassStringType; - } - - if ($arrayType->getKeyType() instanceof ConstantIntegerType) { - return $arrayType->getItemType() instanceof GenericClassStringType; - } - - return false; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php deleted file mode 100644 index d8657279db1..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php +++ /dev/null @@ -1,77 +0,0 @@ - - */ -final class BooleanTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return BooleanType::class; - } - - /** - * @param BooleanType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - if ($this->isFalseBooleanTypeWithUnion($type)) { - return new IdentifierTypeNode('false'); - } - - return new IdentifierTypeNode('bool'); - } - - /** - * @param BooleanType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { - return null; - } - - if ($this->isFalseBooleanTypeWithUnion($type)) { - return new Name('false'); - } - - return new Name('bool'); - } - - private function isFalseBooleanTypeWithUnion(Type $type): bool - { - if (! $type instanceof ConstantBooleanType) { - return false; - } - - if ($type->getValue()) { - return false; - } - - return $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php deleted file mode 100644 index 88ad659568a..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ -final class CallableTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - #[Required] - public function autowireCallableTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return CallableType::class; - } - - /** - * @param CallableType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - $returnTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($type->getReturnType(), $typeKind); - - return new SpacingAwareCallableTypeNode(new IdentifierTypeNode('callable'), [], $returnTypeNode); - } - - /** - * @param CallableType|ClosureType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if ($typeKind->equals(TypeKind::PROPERTY())) { - return null; - } - - return new Name('callable'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php deleted file mode 100644 index d4ab84bd11d..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php +++ /dev/null @@ -1,84 +0,0 @@ - - */ -final class ClassStringTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return ClassStringType::class; - } - - /** - * @param ClassStringType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - $attributeAwareIdentifierTypeNode = new IdentifierTypeNode('class-string'); - - if ($type instanceof GenericClassStringType) { - $genericType = $type->getGenericType(); - if ($genericType instanceof ObjectType) { - $className = $genericType->getClassName(); - $className = $this->normalizeType($className); - $genericType = new ObjectType($className); - } - - $genericTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($genericType, $typeKind); - return new GenericTypeNode($attributeAwareIdentifierTypeNode, [$genericTypeNode]); - } - - return $attributeAwareIdentifierTypeNode; - } - - /** - * @param ClassStringType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return null; - } - - #[Required] - public function autowireClassStringTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - private function normalizeType(string $classType): string - { - if (is_a($classType, Expr::class, true)) { - return Expr::class; - } - - if (is_a($classType, Node::class, true)) { - return Node::class; - } - - return $classType; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php deleted file mode 100644 index dbac3f1a982..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ -final class ClosureTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return ClosureType::class; - } - - /** - * @param ClosureType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - $identifierTypeNode = new IdentifierTypeNode($type->getClassName()); - - $returnDocTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode( - $type->getReturnType(), - $typeKind - ); - - return new SpacingAwareCallableTypeNode($identifierTypeNode, [], $returnDocTypeNode); - } - - /** - * @param ClosureType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if ($typeKind->equals(TypeKind::PROPERTY())) { - return null; - } - - return new Name('callable'); - } - - #[Required] - public function autowireClosureTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php deleted file mode 100644 index a499d62c8dd..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php +++ /dev/null @@ -1,55 +0,0 @@ - - */ -final class FloatTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return FloatType::class; - } - - /** - * @param FloatType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('float'); - } - - /** - * @param FloatType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { - return null; - } - - return new Name('float'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/HasOffsetTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/HasOffsetTypeMapper.php deleted file mode 100644 index a396ecd3c78..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/HasOffsetTypeMapper.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ -final class HasOffsetTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return HasOffsetType::class; - } - - /** - * @param HasOffsetType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new ArrayTypeNode(new IdentifierTypeNode('mixed')); - } - - /** - * @param HasOffsetType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - throw new ShouldNotHappenException(); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php deleted file mode 100644 index 92ed9a72b10..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php +++ /dev/null @@ -1,55 +0,0 @@ - - */ -final class IntegerTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return IntegerType::class; - } - - /** - * @param IntegerType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('int'); - } - - /** - * @param IntegerType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { - return null; - } - - return new Name('int'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php deleted file mode 100644 index ff1b77ff7c0..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ -final class IntersectionTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - #[Required] - public function autowireIntersectionTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return IntersectionType::class; - } - - /** - * @param IntersectionType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - $intersectionTypesNodes = []; - - foreach ($type->getTypes() as $intersectionedType) { - $intersectionTypesNodes[] = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode( - $intersectionedType, - $typeKind - ); - } - - $intersectionTypesNodes = array_unique($intersectionTypesNodes); - - return new BracketsAwareIntersectionTypeNode($intersectionTypesNodes); - } - - /** - * @param IntersectionType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - // intersection types in PHP are not yet supported - return null; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php deleted file mode 100644 index ad591881212..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php +++ /dev/null @@ -1,83 +0,0 @@ - - */ -final class IterableTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - #[Required] - public function autowireIterableTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return IterableType::class; - } - - /** - * @param IterableType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($type->getItemType(), $typeKind); - - if ($itemTypeNode instanceof UnionTypeNode) { - return $this->convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes($itemTypeNode); - } - - return new SpacingAwareArrayTypeNode($itemTypeNode); - } - - /** - * @param IterableType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return new Name('iterable'); - } - - private function convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes( - UnionTypeNode $unionTypeNode - ): BracketsAwareUnionTypeNode { - $unionedArrayType = []; - foreach ($unionTypeNode->types as $unionedType) { - if ($unionedType instanceof UnionTypeNode) { - foreach ($unionedType->types as $key => $subUnionedType) { - $unionedType->types[$key] = new ArrayTypeNode($subUnionedType); - } - - $unionedArrayType[] = $unionedType; - continue; - } - - $unionedArrayType[] = new ArrayTypeNode($unionedType); - } - - return new BracketsAwareUnionTypeNode($unionedArrayType); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php deleted file mode 100644 index 604b547d686..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -final class MixedTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return MixedType::class; - } - - /** - * @param MixedType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('mixed'); - } - - /** - * @param MixedType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return null; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php deleted file mode 100644 index 494d04fceba..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php +++ /dev/null @@ -1,47 +0,0 @@ - - */ -final class NeverTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return NeverType::class; - } - - /** - * @param NeverType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - if ($typeKind->equals(TypeKind::RETURN())) { - return new IdentifierTypeNode('never'); - } - - return new IdentifierTypeNode('mixed'); - } - - /** - * @param NeverType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return null; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/NonEmptyArrayTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/NonEmptyArrayTypeMapper.php deleted file mode 100644 index 7243ed88493..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/NonEmptyArrayTypeMapper.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ -final class NonEmptyArrayTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return NonEmptyArrayType::class; - } - - /** - * @param NonEmptyArrayType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new SpacingAwareArrayTypeNode(new IdentifierTypeNode('mixed')); - } - - /** - * @param NonEmptyArrayType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return new Name('array'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php deleted file mode 100644 index 130a0633b9f..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php +++ /dev/null @@ -1,53 +0,0 @@ - - */ -final class NullTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return NullType::class; - } - - /** - * @param NullType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('null'); - } - - /** - * @param NullType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if ($typeKind->equals(TypeKind::PROPERTY())) { - return null; - } - - // return type cannot be only null - if ($typeKind->equals(TypeKind::RETURN())) { - return null; - } - - return new Name('null'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php deleted file mode 100644 index e775e0e6f51..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php +++ /dev/null @@ -1,142 +0,0 @@ - - */ -final class ObjectTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return ObjectType::class; - } - - /** - * @param ObjectType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - if ($type instanceof ShortenedObjectType) { - return new IdentifierTypeNode($type->getClassName()); - } - - if ($type instanceof AliasedObjectType) { - return new IdentifierTypeNode($type->getClassName()); - } - - if ($type instanceof GenericObjectType) { - return $this->mapGenericObjectType($type, $typeKind); - } - - return new IdentifierTypeNode('\\' . $type->getClassName()); - } - - /** - * @param ObjectType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if ($type instanceof SelfObjectType) { - return new Name('self'); - } - - if ($type instanceof ShortenedObjectType) { - return new FullyQualified($type->getFullyQualifiedName()); - } - - if ($type instanceof AliasedObjectType) { - return new Name($type->getClassName()); - } - - if ($type instanceof FullyQualifiedObjectType) { - return new FullyQualified($type->getClassName()); - } - - if (! $type instanceof GenericObjectType) { - // fallback - return new FullyQualified($type->getClassName()); - } - - if ($type->getClassName() === 'iterable') { - // fallback - return new Name('iterable'); - } - - if ($type->getClassName() !== 'object') { - // fallback - return new FullyQualified($type->getClassName()); - } - - return new Name('object'); - } - - #[Required] - public function autowireObjectTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - private function mapGenericObjectType(GenericObjectType $genericObjectType, TypeKind $typeKind): TypeNode - { - $name = $this->resolveGenericObjectTypeName($genericObjectType); - $identifierTypeNode = new IdentifierTypeNode($name); - - $genericTypeNodes = []; - foreach ($genericObjectType->getTypes() as $key => $genericType) { - // mixed type on 1st item in iterator has no value - if ($name === 'Iterator' && $genericType instanceof MixedType && $key === 0) { - continue; - } - - $typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($genericType, $typeKind); - $genericTypeNodes[] = $typeNode; - } - - if ($genericTypeNodes === []) { - return $identifierTypeNode; - } - - return new GenericTypeNode($identifierTypeNode, $genericTypeNodes); - } - - private function resolveGenericObjectTypeName(GenericObjectType $genericObjectType): string - { - if ($genericObjectType instanceof FullyQualifiedGenericObjectType) { - return '\\' . $genericObjectType->getClassName(); - } - - if (\str_contains($genericObjectType->getClassName(), '\\')) { - return '\\' . $genericObjectType->getClassName(); - } - - return $genericObjectType->getClassName(); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php deleted file mode 100644 index 1fac9f1105d..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php +++ /dev/null @@ -1,77 +0,0 @@ - - */ -final class ObjectWithoutClassTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return ObjectWithoutClassType::class; - } - - /** - * @param ObjectWithoutClassType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - if ($type instanceof TemplateObjectWithoutClassType) { - $attributeAwareIdentifierTypeNode = new IdentifierTypeNode($type->getName()); - return new EmptyGenericTypeNode($attributeAwareIdentifierTypeNode); - } - - return new IdentifierTypeNode('object'); - } - - /** - * @param ObjectWithoutClassType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - $subtractedType = $type->getSubtractedType(); - if ($subtractedType !== null) { - return $this->phpStanStaticTypeMapper->mapToPhpParserNode($subtractedType, $typeKind); - } - - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::OBJECT_TYPE)) { - return null; - } - - return new Name('object'); - } - - #[Required] - public function autowireObjectWithoutClassTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php deleted file mode 100644 index a07358a81ce..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php +++ /dev/null @@ -1,44 +0,0 @@ - - */ -final class ParentStaticTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return ParentStaticType::class; - } - - /** - * @param ParentStaticType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('parent'); - } - - /** - * @param ParentStaticType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return new Name('parent'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php deleted file mode 100644 index 17e2dca2e40..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -final class ResourceTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return ResourceType::class; - } - - /** - * @param ResourceType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('resource'); - } - - /** - * @param ResourceType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return null; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/SelfObjectTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/SelfObjectTypeMapper.php deleted file mode 100644 index bacee57282a..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/SelfObjectTypeMapper.php +++ /dev/null @@ -1,44 +0,0 @@ - - */ -final class SelfObjectTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return SelfObjectType::class; - } - - /** - * @param SelfObjectType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('self'); - } - - /** - * @param SelfObjectType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return new Name('self'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php deleted file mode 100644 index ad58ccbd53c..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php +++ /dev/null @@ -1,63 +0,0 @@ - - */ -final class StaticTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return StaticType::class; - } - - /** - * @param StaticType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new ThisTypeNode(); - } - - /** - * @param StaticType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if ($type instanceof ThisType) { - // @todo wait for PHPStan to differentiate between self/static - if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::STATIC_RETURN_TYPE)) { - return new Name('static'); - } - - return new Name('self'); - } - - return null; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php deleted file mode 100644 index 247f54a74a0..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ -final class StrictMixedTypeMapper implements TypeMapperInterface -{ - /** - * @var string - */ - private const MIXED = 'mixed'; - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return StrictMixedType::class; - } - - /** - * @param StrictMixedType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode(self::MIXED); - } - - /** - * @param StrictMixedType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return new Name(self::MIXED); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php deleted file mode 100644 index 12d1e728f91..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php +++ /dev/null @@ -1,52 +0,0 @@ - - */ -final class StringTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return StringType::class; - } - - /** - * @param StringType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('string'); - } - - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { - return null; - } - - return new Name('string'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ThisTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ThisTypeMapper.php deleted file mode 100644 index 3cb0bb4c860..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ThisTypeMapper.php +++ /dev/null @@ -1,44 +0,0 @@ - - */ -final class ThisTypeMapper implements TypeMapperInterface -{ - /** - * @return class-string - */ - public function getNodeClass(): string - { - return ThisType::class; - } - - /** - * @param ThisType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new ThisTypeNode(); - } - - /** - * @param ThisType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - return new Name('self'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php deleted file mode 100644 index 520700822d6..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php +++ /dev/null @@ -1,55 +0,0 @@ - - */ -final class TypeWithClassNameTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return TypeWithClassName::class; - } - - /** - * @param TypeWithClassName $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode('string-class'); - } - - /** - * @param TypeWithClassName $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { - return null; - } - - return new Name('string'); - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php deleted file mode 100644 index e45de005ede..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php +++ /dev/null @@ -1,319 +0,0 @@ - - */ -final class UnionTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - public function __construct( - private DoctrineTypeAnalyzer $doctrineTypeAnalyzer, - private PhpVersionProvider $phpVersionProvider, - private UnionTypeAnalyzer $unionTypeAnalyzer, - private BoolUnionTypeAnalyzer $boolUnionTypeAnalyzer, - private UnionTypeCommonTypeNarrower $unionTypeCommonTypeNarrower - ) { - } - - #[Required] - public function autowireUnionTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return UnionType::class; - } - - /** - * @param UnionType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - $unionTypesNodes = []; - $skipIterable = $this->shouldSkipIterable($type); - - foreach ($type->getTypes() as $unionedType) { - if ($unionedType instanceof IterableType && $skipIterable) { - continue; - } - - $unionTypesNodes[] = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($unionedType, $typeKind); - } - - $unionTypesNodes = array_unique($unionTypesNodes); - return new BracketsAwareUnionTypeNode($unionTypesNodes); - } - - /** - * @param UnionType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - $arrayNode = $this->matchArrayTypes($type); - if ($arrayNode !== null) { - return $arrayNode; - } - - if ($this->boolUnionTypeAnalyzer->isNullableBoolUnionType( - $type - ) && ! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES)) { - return new NullableType(new Name('bool')); - } - - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES) && $this->isFalseBoolUnion( - $type - )) { - // return new Bool - return new Name('bool'); - } - - // special case for nullable - $nullabledType = $this->matchTypeForNullableUnionType($type); - if (! $nullabledType instanceof Type) { - // use first unioned type in case of unioned object types - return $this->matchTypeForUnionedObjectTypes($type, $typeKind); - } - - // void cannot be nullable - if ($nullabledType instanceof VoidType) { - return null; - } - - $nullabledTypeNode = $this->phpStanStaticTypeMapper->mapToPhpParserNode($nullabledType, $typeKind); - if (! $nullabledTypeNode instanceof Node) { - return null; - } - - if ($nullabledTypeNode instanceof NullableType) { - return $nullabledTypeNode; - } - - if ($nullabledTypeNode instanceof PhpParserUnionType) { - throw new ShouldNotHappenException(); - } - - return new NullableType($nullabledTypeNode); - } - - private function shouldSkipIterable(UnionType $unionType): bool - { - $unionTypeAnalysis = $this->unionTypeAnalyzer->analyseForNullableAndIterable($unionType); - if (! $unionTypeAnalysis instanceof UnionTypeAnalysis) { - return false; - } - if (! $unionTypeAnalysis->hasIterable()) { - return false; - } - return $unionTypeAnalysis->hasArray(); - } - - private function matchArrayTypes(UnionType $unionType): Name | NullableType | null - { - $unionTypeAnalysis = $this->unionTypeAnalyzer->analyseForNullableAndIterable($unionType); - if (! $unionTypeAnalysis instanceof UnionTypeAnalysis) { - return null; - } - - $type = $unionTypeAnalysis->hasIterable() ? 'iterable' : 'array'; - if ($unionTypeAnalysis->isNullableType()) { - return new NullableType($type); - } - - return new Name($type); - } - - private function matchTypeForNullableUnionType(UnionType $unionType): ?Type - { - if (count($unionType->getTypes()) !== 2) { - return null; - } - - $firstType = $unionType->getTypes()[0]; - $secondType = $unionType->getTypes()[1]; - - if ($firstType instanceof NullType) { - return $secondType; - } - - if ($secondType instanceof NullType) { - return $firstType; - } - - return null; - } - - /** - * @return Name|FullyQualified|PhpParserUnionType|null - */ - private function matchTypeForUnionedObjectTypes(UnionType $unionType, TypeKind $typeKind): ?Node - { - $phpParserUnionType = $this->matchPhpParserUnionType($unionType, $typeKind); - if ($phpParserUnionType !== null) { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES)) { - // maybe all one type? - if ($this->boolUnionTypeAnalyzer->isBoolUnionType($unionType)) { - return new Name('bool'); - } - - return null; - } - - return $phpParserUnionType; - } - - if ($this->boolUnionTypeAnalyzer->isBoolUnionType($unionType)) { - return new Name('bool'); - } - - // the type should be compatible with all other types, e.g. A extends B, B - $compatibleObjectType = $this->resolveCompatibleObjectCandidate($unionType); - if (! $compatibleObjectType instanceof ObjectType) { - return null; - } - - return new FullyQualified($compatibleObjectType->getClassName()); - } - - private function matchPhpParserUnionType(UnionType $unionType, TypeKind $typeKind): ?PhpParserUnionType - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES)) { - return null; - } - - $phpParserUnionedTypes = []; - - foreach ($unionType->getTypes() as $unionedType) { - // void type is not allowed in union - if ($unionedType instanceof VoidType) { - return null; - } - - /** @var Identifier|Name|null $phpParserNode */ - $phpParserNode = $this->phpStanStaticTypeMapper->mapToPhpParserNode($unionedType, $typeKind); - if ($phpParserNode === null) { - return null; - } - - $phpParserUnionedTypes[] = $phpParserNode; - } - - $phpParserUnionedTypes = array_unique($phpParserUnionedTypes); - return new PhpParserUnionType($phpParserUnionedTypes); - } - - private function resolveCompatibleObjectCandidate(UnionType $unionType): ?TypeWithClassName - { - if ($this->doctrineTypeAnalyzer->isDoctrineCollectionWithIterableUnionType($unionType)) { - return new ObjectType('Doctrine\Common\Collections\Collection'); - } - - if (! $this->unionTypeAnalyzer->hasTypeClassNameOnly($unionType)) { - return null; - } - - $sharedTypeWithClassName = $this->matchTwoObjectTypes($unionType); - if ($sharedTypeWithClassName instanceof TypeWithClassName) { - return $this->correctObjectType($sharedTypeWithClassName); - } - // find least common denominator - return $this->unionTypeCommonTypeNarrower->narrowToSharedObjectType($unionType); - } - - private function matchTwoObjectTypes(UnionType $unionType): ?TypeWithClassName - { - /** @var TypeWithClassName $unionedType */ - foreach ($unionType->getTypes() as $unionedType) { - /** @var TypeWithClassName $nestedUnionedType */ - foreach ($unionType->getTypes() as $nestedUnionedType) { - if (! $this->areTypeWithClassNamesRelated($unionedType, $nestedUnionedType)) { - continue 2; - } - } - - return $unionedType; - } - - return null; - } - - private function areTypeWithClassNamesRelated(TypeWithClassName $firstType, TypeWithClassName $secondType): bool - { - if ($firstType->accepts($secondType, false)->yes()) { - return true; - } - - return $secondType->accepts($firstType, false) - ->yes(); - } - - private function correctObjectType(TypeWithClassName $typeWithClassName): TypeWithClassName - { - if ($typeWithClassName->getClassName() === NodeAbstract::class) { - return new ObjectType('PhpParser\Node'); - } - - if ($typeWithClassName->getClassName() === AbstractRector::class) { - return new ObjectType('Rector\Core\Contract\Rector\RectorInterface'); - } - - return $typeWithClassName; - } - - private function isFalseBoolUnion(UnionType $unionType): bool - { - if (count($unionType->getTypes()) !== 2) { - return false; - } - - foreach ($unionType->getTypes() as $unionedType) { - if ($unionedType instanceof ConstantBooleanType) { - continue; - } - - return false; - } - - return true; - } -} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php deleted file mode 100644 index f70eaffaa0c..00000000000 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php +++ /dev/null @@ -1,64 +0,0 @@ - - */ -final class VoidTypeMapper implements TypeMapperInterface -{ - /** - * @var string - */ - private const VOID = 'void'; - - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return class-string - */ - public function getNodeClass(): string - { - return VoidType::class; - } - - /** - * @param VoidType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): TypeNode - { - return new IdentifierTypeNode(self::VOID); - } - - /** - * @param VoidType $type - */ - public function mapToPhpParserNode(Type $type, TypeKind $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::VOID_TYPE)) { - return null; - } - - if (in_array($typeKind->getValue(), [TypeKind::PARAM(), TypeKind::PROPERTY()], true)) { - return null; - } - - return new Name(self::VOID); - } -} diff --git a/packages/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php b/packages/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php deleted file mode 100644 index cd53419854d..00000000000 --- a/packages/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php +++ /dev/null @@ -1,59 +0,0 @@ -getTypes() as $unionedType) { - if (! $unionedType instanceof TypeWithClassName) { - continue; - } - - return $unionedType; - } - - return $type; - } - - public function removeNullTypeFromUnionType(UnionType $unionType): UnionType - { - $unionedTypesWithoutNullType = []; - - foreach ($unionType->getTypes() as $type) { - if ($type instanceof UnionType) { - continue; - } - - $unionedTypesWithoutNullType[] = $type; - } - - return $this->unionTypeFactory->createUnionObjectType($unionedTypesWithoutNullType); - } -} diff --git a/packages/PHPStanStaticTypeMapper/ValueObject/TypeKind.php b/packages/PHPStanStaticTypeMapper/ValueObject/TypeKind.php deleted file mode 100644 index 8b765c3166e..00000000000 --- a/packages/PHPStanStaticTypeMapper/ValueObject/TypeKind.php +++ /dev/null @@ -1,36 +0,0 @@ -isNullableType; - } - - public function hasIterable(): bool - { - return $this->hasIterable; - } - - public function hasArray(): bool - { - return $this->hasArray; - } -} diff --git a/packages/PhpAttribute/NodeAnalyzer/NamedArgumentsResolver.php b/packages/PhpAttribute/NodeAnalyzer/NamedArgumentsResolver.php deleted file mode 100644 index 97922ec942c..00000000000 --- a/packages/PhpAttribute/NodeAnalyzer/NamedArgumentsResolver.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ - public function resolveFromClass(string $class): array - { - // decorate args with names if attribute class was found - if (! $this->reflectionProvider->hasClass($class)) { - return []; - } - - $classReflection = $this->reflectionProvider->getClass($class); - if (! $classReflection->hasConstructor()) { - return []; - } - - $argumentNames = []; - $constructorMethodReflection = $classReflection->getConstructor(); - - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($constructorMethodReflection->getVariants()); - foreach ($parametersAcceptor->getParameters() as $key => $parameterReflection) { - /** @var ParameterReflection $parameterReflection */ - $argumentNames[$key] = $parameterReflection->getName(); - } - - return $argumentNames; - } -} diff --git a/packages/PhpAttribute/Printer/DoctrineAnnotationFactory.php b/packages/PhpAttribute/Printer/DoctrineAnnotationFactory.php deleted file mode 100644 index 3b2d67be489..00000000000 --- a/packages/PhpAttribute/Printer/DoctrineAnnotationFactory.php +++ /dev/null @@ -1,54 +0,0 @@ -createItemsFromArgs($attribute->args); - - $identifierTypeNode = new IdentifierTypeNode($className); - return new DoctrineAnnotationTagValueNode($identifierTypeNode, null, $items); - } - - /** - * @param Arg[] $args - * @return mixed[] - */ - private function createItemsFromArgs(array $args): array - { - $items = []; - foreach ($args as $arg) { - if ($arg->value instanceof String_) { - // standardize double quotes for annotations - $arg->value->setAttribute(AttributeKey::KIND, String_::KIND_DOUBLE_QUOTED); - } - - $itemValue = $this->betterStandardPrinter->print($arg->value); - if ($arg->name !== null) { - $name = $this->betterStandardPrinter->print($arg->name); - $items[$name] = $itemValue; - } else { - $items[] = $itemValue; - } - } - - return $items; - } -} diff --git a/packages/PhpAttribute/Printer/PhpAttributeGroupFactory.php b/packages/PhpAttribute/Printer/PhpAttributeGroupFactory.php deleted file mode 100644 index 33e61de2e4c..00000000000 --- a/packages/PhpAttribute/Printer/PhpAttributeGroupFactory.php +++ /dev/null @@ -1,160 +0,0 @@ -createFromClass($annotationToAttribute->getAttributeClass()); - } - - public function createFromClass(string $attributeClass): AttributeGroup - { - $fullyQualified = new FullyQualified($attributeClass); - $attribute = new Attribute($fullyQualified); - return new AttributeGroup([$attribute]); - } - - /** - * @param mixed[] $items - */ - public function createFromClassWithItems(string $attributeClass, array $items): AttributeGroup - { - $fullyQualified = new FullyQualified($attributeClass); - $args = $this->createArgsFromItems($items); - $attribute = new Attribute($fullyQualified, $args); - - return new AttributeGroup([$attribute]); - } - - public function create( - DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, - AnnotationToAttribute $annotationToAttribute - ): AttributeGroup { - $fullyQualified = new FullyQualified($annotationToAttribute->getAttributeClass()); - - $values = $doctrineAnnotationTagValueNode->getValuesWithExplicitSilentAndWithoutQuotes(); - - $args = $this->createArgsFromItems($values); - $argumentNames = $this->namedArgumentsResolver->resolveFromClass($annotationToAttribute->getAttributeClass()); - - $this->completeNamedArguments($args, $argumentNames); - - $attribute = new Attribute($fullyQualified, $args); - return new AttributeGroup([$attribute]); - } - - /** - * @param mixed[] $items - * @return Arg[] - */ - public function createArgsFromItems(array $items, ?string $silentKey = null): array - { - $args = []; - if ($silentKey !== null && isset($items[$silentKey])) { - $silentValue = BuilderHelpers::normalizeValue($items[$silentKey]); - $this->normalizeStringDoubleQuote($silentValue); - - $args[] = new Arg($silentValue); - unset($items[$silentKey]); - } - - foreach ($items as $key => $value) { - $value = $this->valueNormalizer->normalize($value); - $value = BuilderHelpers::normalizeValue($value); - - $this->normalizeStringDoubleQuote($value); - - $name = null; - if (is_string($key)) { - $name = new Identifier($key); - } - - // resolve argument name - $args[] = $this->isArrayArguments($items) - ? new Arg($value, false, false, [], $name) - : new Arg($value) - ; - } - - return $args; - } - - /** - * @param mixed[] $items - */ - private function isArrayArguments(array $items): bool - { - foreach (array_keys($items) as $key) { - if (! is_int($key)) { - return true; - } - } - - return false; - } - - private function normalizeStringDoubleQuote(Expr $expr): void - { - if (! $expr instanceof String_) { - return; - } - // avoid escaping quotes + preserve newlines - if (! str_contains($expr->value, "'")) { - return; - } - - if (str_contains($expr->value, "\n")) { - return; - } - - $expr->setAttribute(AttributeKey::KIND, String_::KIND_DOUBLE_QUOTED); - } - - /** - * @param Arg[] $args - * @param string[] $argumentNames - */ - private function completeNamedArguments(array $args, array $argumentNames): void - { - foreach ($args as $key => $arg) { - $argumentName = $argumentNames[$key] ?? null; - if ($argumentName === null) { - continue; - } - - if ($arg->name !== null) { - continue; - } - - $arg->name = new Identifier($argumentName); - } - } -} diff --git a/packages/PhpAttribute/Value/ValueNormalizer.php b/packages/PhpAttribute/Value/ValueNormalizer.php deleted file mode 100644 index dd6adcc7940..00000000000 --- a/packages/PhpAttribute/Value/ValueNormalizer.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ - public function normalize($value): bool | float | int | string | array | Expr - { - if ($value instanceof ConstExprIntegerNode) { - return (int) $value->value; - } - - if ($value instanceof ConstantFloatType || $value instanceof ConstantBooleanType) { - return $value->getValue(); - } - - if ($value instanceof ConstExprTrueNode) { - return true; - } - - if ($value instanceof ConstExprFalseNode) { - return false; - } - - if ($value instanceof CurlyListNode) { - return array_map( - fn ($node): array | bool | float | int | Expr | string => $this->normalize($node), - $value->getValuesWithExplicitSilentAndWithoutQuotes() - ); - } - - if (is_string($value) && str_contains($value, '::')) { - // class const fetch - [$class, $constant] = explode('::', $value); - return new ClassConstFetch(new Name($class), $constant); - } - - if ($value instanceof Node) { - return (string) $value; - } - - if (\is_array($value)) { - return array_map(fn ($item): array | bool | float | int | Expr | string => $this->normalize($item), $value); - } - - return $value; - } -} diff --git a/packages/PostRector/Application/PostFileProcessor.php b/packages/PostRector/Application/PostFileProcessor.php deleted file mode 100644 index 5edb624e347..00000000000 --- a/packages/PostRector/Application/PostFileProcessor.php +++ /dev/null @@ -1,87 +0,0 @@ -postRectors = $this->sortByPriority($postRectors); - } - - /** - * @param Stmt[] $nodes - * @return Stmt[] - */ - public function traverse(array $nodes): array - { - foreach ($this->postRectors as $postRector) { - if ($this->shouldSkipPostRector($postRector)) { - continue; - } - - $this->currentRectorProvider->changeCurrentRector($postRector); - - $nodeTraverser = new NodeTraverser(); - $nodeTraverser->addVisitor($postRector); - $nodes = $nodeTraverser->traverse($nodes); - } - - return $nodes; - } - - /** - * @param PostRectorInterface[] $postRectors - * @return PostRectorInterface[] - */ - private function sortByPriority(array $postRectors): array - { - $postRectorsByPriority = []; - - foreach ($postRectors as $postRector) { - if (isset($postRectorsByPriority[$postRector->getPriority()])) { - throw new ShouldNotHappenException(); - } - - $postRectorsByPriority[$postRector->getPriority()] = $postRector; - } - - krsort($postRectorsByPriority); - - return $postRectorsByPriority; - } - - private function shouldSkipPostRector(PostRectorInterface $postRector): bool - { - $file = $this->currentFileProvider->getFile(); - if (! $file instanceof File) { - return false; - } - - $smartFileInfo = $file->getSmartFileInfo(); - return $this->skipper->shouldSkipElementAndFileInfo($postRector, $smartFileInfo); - } -} diff --git a/packages/PostRector/Collector/NodesToAddCollector.php b/packages/PostRector/Collector/NodesToAddCollector.php deleted file mode 100644 index a32b8eb80a7..00000000000 --- a/packages/PostRector/Collector/NodesToAddCollector.php +++ /dev/null @@ -1,153 +0,0 @@ -nodesToAddAfter !== [] || $this->nodesToAddBefore !== []; - } - - public function addNodeBeforeNode(Node $addedNode, Node $positionNode): void - { - if ($positionNode->getAttributes() === []) { - $message = sprintf('Switch arguments in "%s()" method', __METHOD__); - throw new ShouldNotHappenException($message); - } - - $position = $this->resolveNearestStmtPosition($positionNode); - $this->nodesToAddBefore[$position][] = $this->wrapToExpression($addedNode); - - $this->rectorChangeCollector->notifyNodeFileInfo($positionNode); - } - - /** - * @param Node[] $addedNodes - */ - public function addNodesAfterNode(array $addedNodes, Node $positionNode): void - { - $position = $this->resolveNearestStmtPosition($positionNode); - foreach ($addedNodes as $addedNode) { - // prevent fluent method weird indent - $addedNode->setAttribute(AttributeKey::ORIGINAL_NODE, null); - $this->nodesToAddAfter[$position][] = $this->wrapToExpression($addedNode); - } - - $this->rectorChangeCollector->notifyNodeFileInfo($positionNode); - } - - public function addNodeAfterNode(Node $addedNode, Node $positionNode): void - { - $position = $this->resolveNearestStmtPosition($positionNode); - $this->nodesToAddAfter[$position][] = $this->wrapToExpression($addedNode); - - $this->rectorChangeCollector->notifyNodeFileInfo($positionNode); - } - - /** - * @return Stmt[] - */ - public function getNodesToAddAfterNode(Node $node): array - { - $position = spl_object_hash($node); - return $this->nodesToAddAfter[$position] ?? []; - } - - /** - * @return Stmt[] - */ - public function getNodesToAddBeforeNode(Node $node): array - { - $position = spl_object_hash($node); - return $this->nodesToAddBefore[$position] ?? []; - } - - public function clearNodesToAddAfter(Node $node): void - { - $objectHash = spl_object_hash($node); - unset($this->nodesToAddAfter[$objectHash]); - } - - public function clearNodesToAddBefore(Node $node): void - { - $objectHash = spl_object_hash($node); - unset($this->nodesToAddBefore[$objectHash]); - } - - /** - * @param Node[] $newNodes - */ - public function addNodesBeforeNode(array $newNodes, Node $positionNode): void - { - foreach ($newNodes as $newNode) { - $this->addNodeBeforeNode($newNode, $positionNode); - } - - $this->rectorChangeCollector->notifyNodeFileInfo($positionNode); - } - - private function resolveNearestStmtPosition(Node $node): string - { - if ($node instanceof Stmt) { - return spl_object_hash($node); - } - - $currentStmt = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - - if ($currentStmt instanceof Stmt) { - return spl_object_hash($currentStmt); - } - - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Return_) { - return spl_object_hash($parent); - } - - $foundNode = $this->betterNodeFinder->findParentType($node, Stmt::class); - - if (! $foundNode instanceof Stmt) { - $printedNode = $this->betterStandardPrinter->print($node); - $errorMessage = sprintf('Could not find parent Stmt of "%s" node', $printedNode); - throw new ShouldNotHappenException($errorMessage); - } - - return spl_object_hash($foundNode); - } - - private function wrapToExpression(Expr | Stmt $node): Stmt - { - return $node instanceof Stmt ? $node : new Expression($node); - } -} diff --git a/packages/PostRector/Collector/NodesToRemoveCollector.php b/packages/PostRector/Collector/NodesToRemoveCollector.php deleted file mode 100644 index 5e9e82eddf7..00000000000 --- a/packages/PostRector/Collector/NodesToRemoveCollector.php +++ /dev/null @@ -1,144 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode !== null && $this->isUsedInArg($node, $parentNode)) { - return; - } - - // chain call: "->method()->another()" - $this->ensureIsNotPartOfChainMethodCall($node); - - if (! $node instanceof Expression && $parentNode instanceof Expression) { - // only expressions can be removed - $node = $parentNode; - } else { - $this->breakingRemovalGuard->ensureNodeCanBeRemove($node); - } - - $file = $this->currentFileProvider->getFile(); - - // /** @var SmartFileInfo|null $fileInfo */ - if ($file !== null) { - $this->affectedFilesCollector->addFile($file); - } - - /** @var Stmt $node */ - $this->nodesToRemove[] = $node; - } - - public function isNodeRemoved(Node $node): bool - { - return in_array($node, $this->nodesToRemove, true); - } - - public function isActive(): bool - { - return $this->getCount() > 0; - } - - public function getCount(): int - { - return count($this->nodesToRemove); - } - - /** - * @return Node[] - */ - public function getNodesToRemove(): array - { - return $this->nodesToRemove; - } - - public function unset(int $key): void - { - unset($this->nodesToRemove[$key]); - } - - private function isUsedInArg(Node $node, Node $parentNode): bool - { - if (! $node instanceof Param) { - return false; - } - - if (! $parentNode instanceof ClassMethod) { - return false; - } - - $paramVariable = $node->var; - if ($paramVariable instanceof Variable) { - return (bool) $this->betterNodeFinder->findFirst((array) $parentNode->stmts, function (Node $variable) use ( - $paramVariable - ): bool { - if (! $this->nodeComparator->areNodesEqual($variable, $paramVariable)) { - return false; - } - - $hasArgParent = (bool) $this->betterNodeFinder->findParentType($variable, Arg::class); - if (! $hasArgParent) { - return false; - } - - return ! (bool) $this->betterNodeFinder->findParentType($variable, StaticCall::class); - }); - } - - return false; - } - - private function ensureIsNotPartOfChainMethodCall(Node $node): void - { - if (! $node instanceof MethodCall) { - return; - } - - if (! $node->var instanceof MethodCall) { - return; - } - - throw new ShouldNotHappenException( - 'Chain method calls cannot be removed this way. It would remove the whole tree of calls. Remove them manually by creating new parent node with no following method.' - ); - } -} diff --git a/packages/PostRector/Collector/NodesToReplaceCollector.php b/packages/PostRector/Collector/NodesToReplaceCollector.php deleted file mode 100644 index 1e44522884c..00000000000 --- a/packages/PostRector/Collector/NodesToReplaceCollector.php +++ /dev/null @@ -1,34 +0,0 @@ -nodesToReplace[] = [$node, $replaceWith]; - } - - public function isActive(): bool - { - return $this->nodesToReplace !== []; - } - - /** - * @return Node[][] - */ - public function getNodes(): array - { - return $this->nodesToReplace; - } -} diff --git a/packages/PostRector/Collector/PropertyToAddCollector.php b/packages/PostRector/Collector/PropertyToAddCollector.php deleted file mode 100644 index f72e29e8b60..00000000000 --- a/packages/PostRector/Collector/PropertyToAddCollector.php +++ /dev/null @@ -1,103 +0,0 @@ -> - */ - private array $constantsByClass = []; - - /** - * @var array - */ - private array $propertiesByClass = []; - - /** - * @var array> - */ - private array $propertiesWithoutConstructorByClass = []; - - public function __construct( - private NodeNameResolver $nodeNameResolver, - private RectorChangeCollector $rectorChangeCollector - ) { - } - - public function isActive(): bool - { - if ($this->propertiesByClass !== []) { - return true; - } - - if ($this->propertiesWithoutConstructorByClass !== []) { - return true; - } - - return $this->constantsByClass !== []; - } - - public function addPropertyToClass(Class_ $class, PropertyMetadata $propertyMetadata): void - { - $uniqueHash = spl_object_hash($class); - $this->propertiesByClass[$uniqueHash][] = $propertyMetadata; - - $this->rectorChangeCollector->notifyNodeFileInfo($class); - } - - public function addConstantToClass(Class_ $class, ClassConst $classConst): void - { - $constantName = $this->nodeNameResolver->getName($classConst); - $this->constantsByClass[spl_object_hash($class)][$constantName] = $classConst; - - $this->rectorChangeCollector->notifyNodeFileInfo($class); - } - - public function addPropertyWithoutConstructorToClass( - string $propertyName, - ?Type $propertyType, - Class_ $class - ): void { - $this->propertiesWithoutConstructorByClass[spl_object_hash($class)][$propertyName] = $propertyType; - - $this->rectorChangeCollector->notifyNodeFileInfo($class); - } - - /** - * @return ClassConst[] - */ - public function getConstantsByClass(Class_ $class): array - { - $classHash = spl_object_hash($class); - return $this->constantsByClass[$classHash] ?? []; - } - - /** - * @return PropertyMetadata[] - */ - public function getPropertiesByClass(Class_ $class): array - { - $classHash = spl_object_hash($class); - return $this->propertiesByClass[$classHash] ?? []; - } - - /** - * @return array - */ - public function getPropertiesWithoutConstructorByClass(Class_ $class): array - { - $classHash = spl_object_hash($class); - return $this->propertiesWithoutConstructorByClass[$classHash] ?? []; - } -} diff --git a/packages/PostRector/Collector/UseNodesToAddCollector.php b/packages/PostRector/Collector/UseNodesToAddCollector.php deleted file mode 100644 index 2f356492a54..00000000000 --- a/packages/PostRector/Collector/UseNodesToAddCollector.php +++ /dev/null @@ -1,168 +0,0 @@ - - */ - private array $functionUseImportTypesInFilePath = []; - - /** - * @var array - */ - private array $useImportTypesInFilePath = []; - - public function __construct( - private CurrentFileProvider $currentFileProvider - ) { - } - - public function isActive(): bool - { - return $this->useImportTypesInFilePath !== [] || $this->functionUseImportTypesInFilePath !== []; - } - - public function addUseImport(FullyQualifiedObjectType | AliasedObjectType $objectType): void - { - $file = $this->currentFileProvider->getFile(); - $smartFileInfo = $file->getSmartFileInfo(); - - $this->useImportTypesInFilePath[$smartFileInfo->getRealPath()][] = $objectType; - } - - public function addFunctionUseImport(Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): void - { - $file = $this->currentFileProvider->getFile(); - $smartFileInfo = $file->getSmartFileInfo(); - - $this->functionUseImportTypesInFilePath[$smartFileInfo->getRealPath()][] = $fullyQualifiedObjectType; - } - - /** - * @return AliasedObjectType[]|FullyQualifiedObjectType[] - */ - public function getUseImportTypesByNode(File $file, Node $node): array - { - $fileInfo = $file->getSmartFileInfo(); - $filePath = $fileInfo->getRealPath(); - - $objectTypes = $this->useImportTypesInFilePath[$filePath] ?? []; - - /** @var Use_[] $useNodes */ - $useNodes = (array) $node->getAttribute(AttributeKey::USE_NODES); - foreach ($useNodes as $useNode) { - foreach ($useNode->uses as $useUse) { - if ($useUse->alias !== null) { - $objectTypes[] = new AliasedObjectType($useUse->alias->toString(), (string) $useUse->name); - } else { - $objectTypes[] = new FullyQualifiedObjectType((string) $useUse->name); - } - } - } - - return $objectTypes; - } - - public function hasImport(File $file, Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): bool - { - $useImports = $this->getUseImportTypesByNode($file, $node); - - foreach ($useImports as $useImport) { - if ($useImport->equals($fullyQualifiedObjectType)) { - return true; - } - } - - return false; - } - - public function isShortImported(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType): bool - { - $fileInfo = $file->getSmartFileInfo(); - $filePath = $fileInfo->getRealPath(); - - $shortName = $fullyQualifiedObjectType->getShortName(); - - if ($this->isShortClassImported($file, $shortName)) { - return true; - } - - $fileFunctionUseImportTypes = $this->functionUseImportTypesInFilePath[$filePath] ?? []; - foreach ($fileFunctionUseImportTypes as $fileFunctionUseImportType) { - if ($fileFunctionUseImportType->getShortName() === $fullyQualifiedObjectType->getShortName()) { - return true; - } - } - - return false; - } - - public function isImportShortable(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType): bool - { - $fileInfo = $file->getSmartFileInfo(); - $filePath = $fileInfo->getRealPath(); - - $fileUseImportTypes = $this->useImportTypesInFilePath[$filePath] ?? []; - - foreach ($fileUseImportTypes as $fileUseImportType) { - if ($fullyQualifiedObjectType->equals($fileUseImportType)) { - return true; - } - } - - $functionImports = $this->functionUseImportTypesInFilePath[$filePath] ?? []; - foreach ($functionImports as $functionImport) { - if ($fullyQualifiedObjectType->equals($functionImport)) { - return true; - } - } - - return false; - } - - /** - * @return AliasedObjectType[]|FullyQualifiedObjectType[] - */ - public function getObjectImportsByFileInfo(SmartFileInfo $smartFileInfo): array - { - return $this->useImportTypesInFilePath[$smartFileInfo->getRealPath()] ?? []; - } - - /** - * @return FullyQualifiedObjectType[] - */ - public function getFunctionImportsByFileInfo(SmartFileInfo $smartFileInfo): array - { - return $this->functionUseImportTypesInFilePath[$smartFileInfo->getRealPath()] ?? []; - } - - private function isShortClassImported(File $file, string $shortName): bool - { - $fileInfo = $file->getSmartFileInfo(); - $realPath = $fileInfo->getRealPath(); - - $fileUseImports = $this->useImportTypesInFilePath[$realPath] ?? []; - - foreach ($fileUseImports as $fileUseImport) { - if ($fileUseImport->getShortName() === $shortName) { - return true; - } - } - - return false; - } -} diff --git a/packages/PostRector/Contract/Collector/NodeCollectorInterface.php b/packages/PostRector/Contract/Collector/NodeCollectorInterface.php deleted file mode 100644 index e19fd4c4a1f..00000000000 --- a/packages/PostRector/Contract/Collector/NodeCollectorInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -isInjectPropertyAlreadyInTheClass($class)) { - return true; - } - - return $this->hasParentClassConstructor($class); - } - - private function isInjectPropertyAlreadyInTheClass(Class_ $class): bool - { - foreach ($class->getProperties() as $property) { - if (! $property->isPublic()) { - continue; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - if ($phpDocInfo->hasByName('inject')) { - return true; - } - } - - return false; - } - - private function hasParentClassConstructor(Class_ $class): bool - { - $className = $this->nodeNameResolver->getName($class); - if ($className === null) { - return false; - } - - if (! $this->reflectionProvider->hasClass($className)) { - return false; - } - - $classReflection = $this->reflectionProvider->getClass($className); - if (! $classReflection->isSubclassOf('Nette\Application\IPresenter')) { - return false; - } - - // has no parent class - if ($class->extends === null) { - return false; - } - - $parentClass = $this->nodeNameResolver->getName($class->extends); - // is not the nette class - we don't care about that - if ($parentClass === 'Nette\Application\UI\Presenter') { - return false; - } - - // prefer local constructor - $classReflection = $this->reflectionProvider->getClass($className); - - if ($classReflection->hasMethod(MethodName::CONSTRUCT)) { - $constructorReflectionMethod = $classReflection->getConstructor(); - $declaringClass = $constructorReflectionMethod->getDeclaringClass(); - - // be sure its local constructor - if ($declaringClass->getName() === $className) { - return false; - } - } - - $classReflection = $this->reflectionProvider->getClass($parentClass); - return $classReflection->hasMethod(MethodName::CONSTRUCT); - } -} diff --git a/packages/PostRector/Rector/AbstractPostRector.php b/packages/PostRector/Rector/AbstractPostRector.php deleted file mode 100644 index 6890062e6f0..00000000000 --- a/packages/PostRector/Rector/AbstractPostRector.php +++ /dev/null @@ -1,12 +0,0 @@ -renamedClassesDataCollector->getOldToNewClasses(); - if ($oldToNewClasses === []) { - return $node; - } - - return $this->classRenamer->renameNode($node, $oldToNewClasses); - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Rename references for classes that were renamed during Rector run', [ - new CodeSample( - <<<'CODE_SAMPLE' -function (OriginalClass $someClass) -{ -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -function (RenamedClass $someClass) -{ -} -CODE_SAMPLE - ), - ]); - } -} diff --git a/packages/PostRector/Rector/NameImportingPostRector.php b/packages/PostRector/Rector/NameImportingPostRector.php deleted file mode 100644 index 86e62cb57c0..00000000000 --- a/packages/PostRector/Rector/NameImportingPostRector.php +++ /dev/null @@ -1,131 +0,0 @@ -parameterProvider->provideBoolParameter(Option::AUTO_IMPORT_NAMES)) { - return null; - } - - if ($node instanceof Name) { - $file = $this->currentFileProvider->getFile(); - if (! $file instanceof File) { - return null; - } - - return $this->processNodeName($node, $file); - } - - if (! $this->parameterProvider->provideBoolParameter(Option::IMPORT_DOC_BLOCKS)) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $this->docBlockNameImporter->importNames($phpDocInfo->getPhpDocNode(), $node); - - return $node; - } - - public function getPriority(): int - { - // this must run after NodeRemovingPostRector, sine renamed use imports can block next import - return 600; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Imports fully qualified names', [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(App\AnotherClass $anotherClass) - { - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -use App\AnotherClass; - -class SomeClass -{ - public function run(AnotherClass $anotherClass) - { - } -} -CODE_SAMPLE - ), - ]); - } - - private function processNodeName(Name $name, File $file): ?Node - { - if ($name->isSpecialClassName()) { - return $name; - } - - // @todo test if old stmts or new stmts! or both? :) - /** @var Use_[] $currentUses */ - $currentUses = $this->betterNodeFinder->findInstanceOf($file->getNewStmts(), Use_::class); - - if ($this->shouldImportName($name, $file, $currentUses)) { - return $this->nameImporter->importName($name, $file, $currentUses); - } - - return null; - } - - /** - * @param Use_[] $currentUses - */ - private function shouldImportName(Name $name, File $file, array $currentUses): bool - { - if (substr_count($name->toCodeString(), '\\') <= 1) { - return true; - } - - if (! $this->classNameImportSkipper->isFoundInUse($name, $currentUses)) { - return true; - } - - if ($this->classNameImportSkipper->isAlreadyImported($name, $currentUses)) { - return true; - } - - return $this->reflectionProvider->hasFunction(new Name($name->getLast()), null); - } -} diff --git a/packages/PostRector/Rector/NodeAddingPostRector.php b/packages/PostRector/Rector/NodeAddingPostRector.php deleted file mode 100644 index 54da5b6561a..00000000000 --- a/packages/PostRector/Rector/NodeAddingPostRector.php +++ /dev/null @@ -1,91 +0,0 @@ -someCall(); - * - * To: - * - $this->someCall(); - * - $value = this->someNewCall(); // added expression - */ -final class NodeAddingPostRector extends AbstractPostRector -{ - public function __construct( - private NodesToAddCollector $nodesToAddCollector - ) { - } - - public function getPriority(): int - { - return 1000; - } - - /** - * @return array|Node - */ - public function leaveNode(Node $node): array | Node - { - $newNodes = [$node]; - - $nodesToAddAfter = $this->nodesToAddCollector->getNodesToAddAfterNode($node); - if ($nodesToAddAfter !== []) { - $this->nodesToAddCollector->clearNodesToAddAfter($node); - $newNodes = array_merge($newNodes, $nodesToAddAfter); - } - - $nodesToAddBefore = $this->nodesToAddCollector->getNodesToAddBeforeNode($node); - if ($nodesToAddBefore !== []) { - $this->nodesToAddCollector->clearNodesToAddBefore($node); - $newNodes = array_merge($nodesToAddBefore, $newNodes); - } - - if ($newNodes === [$node]) { - return $node; - } - - return $newNodes; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Add nodes on weird positions', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($value) - { - return 1; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($value) - { - if ($value) { - return 1; - } - } -} -CODE_SAMPLE - ), ] - ); - } -} diff --git a/packages/PostRector/Rector/NodeRemovingPostRector.php b/packages/PostRector/Rector/NodeRemovingPostRector.php deleted file mode 100644 index 767f966b31d..00000000000 --- a/packages/PostRector/Rector/NodeRemovingPostRector.php +++ /dev/null @@ -1,159 +0,0 @@ -nodesToRemoveCollector->isActive()) { - return null; - } - - // special case for fluent methods - foreach ($this->nodesToRemoveCollector->getNodesToRemove() as $key => $nodeToRemove) { - if (! $node instanceof MethodCall) { - continue; - } - - if (! $nodeToRemove instanceof MethodCall) { - continue; - } - - // replace chain method call by non-chain method call - if (! $this->isChainMethodCallNodeToBeRemoved($node, $nodeToRemove)) { - continue; - } - - $this->nodesToRemoveCollector->unset($key); - - $methodName = $this->nodeNameResolver->getName($node->name); - - /** @var MethodCall $nestedMethodCall */ - $nestedMethodCall = $node->var; - - /** @var string $methodName */ - return $this->nodeFactory->createMethodCall($nestedMethodCall->var, $methodName, $node->args); - } - - if (! $node instanceof BinaryOp) { - return null; - } - - return $this->removePartOfBinaryOp($node); - } - - public function leaveNode(Node $node): int | Node - { - foreach ($this->nodesToRemoveCollector->getNodesToRemove() as $key => $nodeToRemove) { - $nodeToRemoveParent = $nodeToRemove->getAttribute(AttributeKey::PARENT_NODE); - if ($nodeToRemoveParent instanceof BinaryOp) { - continue; - } - - if ($node === $nodeToRemove) { - $this->nodesToRemoveCollector->unset($key); - - return NodeTraverser::REMOVE_NODE; - } - } - - return $node; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove nodes from weird positions', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($value) - { - if ($value) { - return 1; - } - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($value) - { - return 1; - } -} -CODE_SAMPLE - ), ] - ); - } - - private function isChainMethodCallNodeToBeRemoved( - MethodCall $mainMethodCall, - MethodCall $toBeRemovedMethodCall - ): bool { - if (! $mainMethodCall->var instanceof MethodCall) { - return false; - } - if ($toBeRemovedMethodCall !== $mainMethodCall->var) { - return false; - } - - $methodName = $this->nodeNameResolver->getName($mainMethodCall->name); - - return $methodName !== null; - } - - private function removePartOfBinaryOp(BinaryOp $binaryOp): ?Node - { - // handle left/right binary remove, e.g. "true && false" → remove false → "true" - foreach ($this->nodesToRemoveCollector->getNodesToRemove() as $key => $nodeToRemove) { - // remove node - $nodeToRemoveParentNode = $nodeToRemove->getAttribute(AttributeKey::PARENT_NODE); - if (! $nodeToRemoveParentNode instanceof BinaryOp) { - continue; - } - - if ($binaryOp->left === $nodeToRemove) { - $this->nodesToRemoveCollector->unset($key); - return $binaryOp->right; - } - - if ($binaryOp->right === $nodeToRemove) { - $this->nodesToRemoveCollector->unset($key); - return $binaryOp->left; - } - } - - return null; - } -} diff --git a/packages/PostRector/Rector/NodeToReplacePostRector.php b/packages/PostRector/Rector/NodeToReplacePostRector.php deleted file mode 100644 index 9b36374e590..00000000000 --- a/packages/PostRector/Rector/NodeToReplacePostRector.php +++ /dev/null @@ -1,63 +0,0 @@ -nodesToReplaceCollector->getNodes() as [$nodeToFind, $replacement]) { - if ($node === $nodeToFind) { - return $replacement; - } - } - - return null; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Replaces nodes on weird positions', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($value) - { - return 1; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($value) - { - return $value; - } -} -CODE_SAMPLE - ), ] - ); - } -} diff --git a/packages/PostRector/Rector/PropertyAddingPostRector.php b/packages/PostRector/Rector/PropertyAddingPostRector.php deleted file mode 100644 index 1146c3b3eb6..00000000000 --- a/packages/PostRector/Rector/PropertyAddingPostRector.php +++ /dev/null @@ -1,115 +0,0 @@ -classAnalyzer->isAnonymousClass($node)) { - return null; - } - $this->addConstants($node); - $this->addProperties($node); - $this->addPropertiesWithoutConstructor($node); - - return $node; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Add dependency properties', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - return $this->value; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - private $value; - public function run() - { - return $this->value; - } -} -CODE_SAMPLE - ), ] - ); - } - - private function addConstants(Class_ $class): void - { - $constants = $this->propertyToAddCollector->getConstantsByClass($class); - - foreach ($constants as $constantName => $nodeConst) { - $this->classInsertManipulator->addConstantToClass($class, $constantName, $nodeConst); - } - } - - private function addProperties(Class_ $class): void - { - $propertiesMetadatas = $this->propertyToAddCollector->getPropertiesByClass($class); - - $isNetteInjectPreferred = $this->netteInjectDetector->isNetteInjectPreferred($class); - - foreach ($propertiesMetadatas as $propertyMetadata) { - if (! $isNetteInjectPreferred) { - $this->classDependencyManipulator->addConstructorDependency($class, $propertyMetadata); - } else { - $this->classDependencyManipulator->addInjectProperty($class, $propertyMetadata); - } - } - } - - private function addPropertiesWithoutConstructor(Class_ $class): void - { - $propertiesWithoutConstructor = $this->propertyToAddCollector->getPropertiesWithoutConstructorByClass( - $class - ); - - foreach ($propertiesWithoutConstructor as $propertyName => $propertyType) { - $this->classInsertManipulator->addPropertyToClass($class, $propertyName, $propertyType); - } - } -} diff --git a/packages/PostRector/Rector/UseAddingPostRector.php b/packages/PostRector/Rector/UseAddingPostRector.php deleted file mode 100644 index b5d3f1dbb0b..00000000000 --- a/packages/PostRector/Rector/UseAddingPostRector.php +++ /dev/null @@ -1,142 +0,0 @@ -currentFileProvider->getFile(); - $smartFileInfo = $file->getSmartFileInfo(); - - $useImportTypes = $this->useNodesToAddCollector->getObjectImportsByFileInfo($smartFileInfo); - $functionUseImportTypes = $this->useNodesToAddCollector->getFunctionImportsByFileInfo($smartFileInfo); - - $oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses(); - - // nothing to import or remove - if ($useImportTypes === [] && $functionUseImportTypes === [] && $oldToNewClasses === []) { - return $nodes; - } - - /** @var FullyQualifiedObjectType[] $useImportTypes */ - $useImportTypes = $this->typeFactory->uniquateTypes($useImportTypes); - - // A. has namespace? add under it - $namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class); - if ($namespace instanceof Namespace_) { - // first clean - //$this->useImportsRemover->removeImportsFromNamespace($namespace, $removedShortUses); - // then add, to prevent adding + removing false positive of same short use - $this->useImportsAdder->addImportsToNamespace($namespace, $useImportTypes, $functionUseImportTypes); - - return $nodes; - } - - $firstNode = $nodes[0]; - if ($firstNode instanceof FileWithoutNamespace) { - $nodes = $firstNode->stmts; - } - - $removedShortUses = $this->renamedClassesDataCollector->getOldClasses(); - - // B. no namespace? add in the top - // first clean - $nodes = $this->useImportsRemover->removeImportsFromStmts($nodes, $removedShortUses); - $useImportTypes = $this->filterOutNonNamespacedNames($useImportTypes); - // then add, to prevent adding + removing false positive of same short use - - return $this->useImportsAdder->addImportsToStmts($nodes, $useImportTypes, $functionUseImportTypes); - } - - public function getPriority(): int - { - // must be after name importing - return 500; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Add unique use imports collected during Rector run', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(AnotherClass $anotherClass) - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -use App\AnotherClass; - -class SomeClass -{ - public function run(AnotherClass $anotherClass) - { - } -} -CODE_SAMPLE - ), ] - ); - } - - /** - * Prevents - * @param FullyQualifiedObjectType[] $useImportTypes - * @return FullyQualifiedObjectType[] - */ - private function filterOutNonNamespacedNames(array $useImportTypes): array - { - $namespacedUseImportTypes = []; - - foreach ($useImportTypes as $useImportType) { - if (! \str_contains($useImportType->getClassName(), '\\')) { - continue; - } - - $namespacedUseImportTypes[] = $useImportType; - } - - return $namespacedUseImportTypes; - } -} diff --git a/packages/ReadWrite/Contract/ReadNodeAnalyzerInterface.php b/packages/ReadWrite/Contract/ReadNodeAnalyzerInterface.php deleted file mode 100644 index a00f86ead5a..00000000000 --- a/packages/ReadWrite/Contract/ReadNodeAnalyzerInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -> - */ - private array $referencePositionsByFunctionName = []; - - public function __construct( - private NodeNameResolver $nodeNameResolver, - private ReflectionProvider $reflectionProvider, - private PrivatesAccessor $privatesAccessor - ) { - } - - public function isReadArg(Arg $arg): bool - { - $parentParent = $arg->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentParent instanceof FuncCall) { - return true; - } - - $functionNameString = $this->nodeNameResolver->getName($parentParent); - if ($functionNameString === null) { - return true; - } - - $functionName = new Name($functionNameString); - $argScope = $arg->getAttribute(AttributeKey::SCOPE); - - if (! $this->reflectionProvider->hasFunction($functionName, $argScope)) { - // we don't know - return true; - } - - $functionReflection = $this->reflectionProvider->getFunction($functionName, $argScope); - - $referenceParametersPositions = $this->resolveFunctionReferencePositions($functionReflection); - if ($referenceParametersPositions === []) { - // no reference always only write - return true; - } - - $argumentPosition = $this->getArgumentPosition($parentParent, $arg); - return ! in_array($argumentPosition, $referenceParametersPositions, true); - } - - /** - * @return int[] - */ - private function resolveFunctionReferencePositions(FunctionReflection $functionReflection): array - { - if (isset($this->referencePositionsByFunctionName[$functionReflection->getName()])) { - return $this->referencePositionsByFunctionName[$functionReflection->getName()]; - } - - // this is needed, as native function reflection does not have access to referenced parameters - if ($functionReflection instanceof NativeFunctionReflection) { - $functionName = $functionReflection->getName(); - if (! function_exists($functionName)) { - return []; - } - - $nativeFunctionReflection = new ReflectionFunction($functionName); - } else { - $nativeFunctionReflection = $this->privatesAccessor->getPrivateProperty($functionReflection, 'reflection'); - } - - $referencePositions = []; - /** @var int $position */ - foreach ($nativeFunctionReflection->getParameters() as $position => $reflectionParameter) { - if (! $reflectionParameter->isPassedByReference()) { - continue; - } - - $referencePositions[] = $position; - } - - $this->referencePositionsByFunctionName[$functionReflection->getName()] = $referencePositions; - - return $referencePositions; - } - - private function getArgumentPosition(FuncCall $funcCall, Arg $desiredArg): int - { - foreach ($funcCall->args as $position => $arg) { - if ($arg !== $desiredArg) { - continue; - } - - return $position; - } - - throw new ShouldNotHappenException(); - } -} diff --git a/packages/ReadWrite/NodeAnalyzer/ReadExprAnalyzer.php b/packages/ReadWrite/NodeAnalyzer/ReadExprAnalyzer.php deleted file mode 100644 index 551a11daa4d..00000000000 --- a/packages/ReadWrite/NodeAnalyzer/ReadExprAnalyzer.php +++ /dev/null @@ -1,36 +0,0 @@ -readNodeAnalyzers as $readNodeAnalyzer) { - if (! $readNodeAnalyzer->supports($expr)) { - continue; - } - - return $readNodeAnalyzer->isRead($expr); - } - - throw new NotImplementedYetException($expr::class); - } -} diff --git a/packages/ReadWrite/NodeAnalyzer/ReadWritePropertyAnalyzer.php b/packages/ReadWrite/NodeAnalyzer/ReadWritePropertyAnalyzer.php deleted file mode 100644 index 374a65c533c..00000000000 --- a/packages/ReadWrite/NodeAnalyzer/ReadWritePropertyAnalyzer.php +++ /dev/null @@ -1,96 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { - throw new ShouldNotHappenException(); - } - - $parent = $this->unwrapPostPreIncDec($parent); - - if ($parent instanceof Arg) { - $readArg = $this->variableToConstantGuard->isReadArg($parent); - if ($readArg) { - return true; - } - } - - if ($parent instanceof ArrayDimFetch && $parent->dim === $node && $this->isNotInsideIssetUnset($parent)) { - return $this->isArrayDimFetchRead($parent); - } - - return ! $this->assignManipulator->isLeftPartOfAssign($node); - } - - private function unwrapPostPreIncDec(Node $node): Node - { - if ($node instanceof PreInc || $node instanceof PreDec || $node instanceof PostInc || $node instanceof PostDec) { - $node = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $node instanceof Node) { - throw new ShouldNotHappenException(); - } - } - - return $node; - } - - private function isNotInsideIssetUnset(ArrayDimFetch $arrayDimFetch): bool - { - return ! (bool) $this->parentFinder->findByTypes($arrayDimFetch, [Isset_::class, Unset_::class]); - } - - private function isArrayDimFetchRead(ArrayDimFetch $arrayDimFetch): bool - { - $parentParent = $arrayDimFetch->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentParent instanceof Node) { - throw new ShouldNotHappenException(); - } - - if (! $this->assignManipulator->isLeftPartOfAssign($arrayDimFetch)) { - return false; - } - - if ($arrayDimFetch->var instanceof ArrayDimFetch) { - return true; - } - - // the array dim fetch is assing here only; but the variable might be used later - if ($this->readExprAnalyzer->isExprRead($arrayDimFetch->var)) { - return true; - } - - return ! $this->assignManipulator->isLeftPartOfAssign($arrayDimFetch); - } -} diff --git a/packages/ReadWrite/NodeFinder/NodeUsageFinder.php b/packages/ReadWrite/NodeFinder/NodeUsageFinder.php deleted file mode 100644 index 0cfb403a9db..00000000000 --- a/packages/ReadWrite/NodeFinder/NodeUsageFinder.php +++ /dev/null @@ -1,61 +0,0 @@ -nodeNameResolver->getName($variable); - if ($variableName === null) { - return []; - } - - return $this->betterNodeFinder->find($nodes, function (Node $node) use ($variable, $variableName): bool { - if (! $node instanceof Variable) { - return false; - } - - if ($node === $variable) { - return false; - } - - return $this->nodeNameResolver->isName($node, $variableName); - }); - } - - public function findPreviousForeachNodeUsage(Foreach_ $foreach, Expr $expr): ?Node - { - return $this->scopeAwareNodeFinder->findParent($foreach, function (Node $node) use ($expr): bool { - // skip itself - if ($node === $expr) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node, $expr); - }, [Foreach_::class]); - } -} diff --git a/packages/ReadWrite/ReadNodeAnalyzer/JustReadExprAnalyzer.php b/packages/ReadWrite/ReadNodeAnalyzer/JustReadExprAnalyzer.php deleted file mode 100644 index 368e0e39a30..00000000000 --- a/packages/ReadWrite/ReadNodeAnalyzer/JustReadExprAnalyzer.php +++ /dev/null @@ -1,40 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Return_) { - return true; - } - - if ($parent instanceof Arg) { - return true; - } - - if ($parent instanceof ArrayDimFetch) { - $parentParent = $parent->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentParent instanceof Assign) { - return true; - } - - return $parentParent->var !== $parent; - } - - // assume it's used by default - return ! $parent instanceof Expression; - } -} diff --git a/packages/ReadWrite/ReadNodeAnalyzer/LocalPropertyFetchReadNodeAnalyzer.php b/packages/ReadWrite/ReadNodeAnalyzer/LocalPropertyFetchReadNodeAnalyzer.php deleted file mode 100644 index 662066ca880..00000000000 --- a/packages/ReadWrite/ReadNodeAnalyzer/LocalPropertyFetchReadNodeAnalyzer.php +++ /dev/null @@ -1,55 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { - // assume worse to keep node protected - return true; - } - - $propertyName = $this->nodeNameResolver->getName($node->name); - if ($propertyName === null) { - // assume worse to keep node protected - return true; - } - - $propertyFetches = $this->propertyFetchFinder->findLocalPropertyFetchesByName($class, $propertyName); - foreach ($propertyFetches as $propertyFetch) { - if ($this->justReadExprAnalyzer->isReadContext($propertyFetch)) { - return true; - } - } - - return false; - } -} diff --git a/packages/ReadWrite/ReadNodeAnalyzer/VariableReadNodeAnalyzer.php b/packages/ReadWrite/ReadNodeAnalyzer/VariableReadNodeAnalyzer.php deleted file mode 100644 index 66ae1954e24..00000000000 --- a/packages/ReadWrite/ReadNodeAnalyzer/VariableReadNodeAnalyzer.php +++ /dev/null @@ -1,46 +0,0 @@ -parentScopeFinder->find($node); - if ($parentScope === null) { - return false; - } - - $variableUsages = $this->nodeUsageFinder->findVariableUsages((array) $parentScope->stmts, $node); - foreach ($variableUsages as $variableUsage) { - if ($this->justReadExprAnalyzer->isReadContext($variableUsage)) { - return true; - } - } - - return false; - } -} diff --git a/packages/Set/Contract/SetListInterface.php b/packages/Set/Contract/SetListInterface.php deleted file mode 100644 index 22d48b8d49a..00000000000 --- a/packages/Set/Contract/SetListInterface.php +++ /dev/null @@ -1,9 +0,0 @@ - - */ - public function getNodeType(): string; - - public function mapToPHPStan(Node $node): Type; -} diff --git a/packages/StaticTypeMapper/Mapper/PhpParserNodeMapper.php b/packages/StaticTypeMapper/Mapper/PhpParserNodeMapper.php deleted file mode 100644 index 469b6b14d97..00000000000 --- a/packages/StaticTypeMapper/Mapper/PhpParserNodeMapper.php +++ /dev/null @@ -1,51 +0,0 @@ -hasAttribute(AttributeKey::NAMESPACED_NAME)) { - $node = new FullyQualified($node->getAttribute(AttributeKey::NAMESPACED_NAME)); - } - - foreach ($this->phpParserNodeMappers as $phpParserNodeMapper) { - if (! is_a($node, $phpParserNodeMapper->getNodeType())) { - continue; - } - - // do not let Expr collect all the types - // note: can be solve later with priorities on mapper interface, making this last - if ($phpParserNodeMapper->getNodeType() !== Expr::class) { - return $phpParserNodeMapper->mapToPHPStan($node); - } - - if (! $node instanceof String_) { - return $phpParserNodeMapper->mapToPHPStan($node); - } - } - - throw new NotImplementedYetException($node::class); - } -} diff --git a/packages/StaticTypeMapper/Mapper/ScalarStringToTypeMapper.php b/packages/StaticTypeMapper/Mapper/ScalarStringToTypeMapper.php deleted file mode 100644 index 2739e8b9c94..00000000000 --- a/packages/StaticTypeMapper/Mapper/ScalarStringToTypeMapper.php +++ /dev/null @@ -1,74 +0,0 @@ -, string[]> - */ - private const SCALAR_NAME_BY_TYPE = [ - StringType::class => ['string'], - FloatType::class => ['float', 'real', 'double'], - IntegerType::class => ['int', 'integer'], - BooleanType::class => ['bool', 'boolean'], - NullType::class => ['null'], - VoidType::class => ['void'], - ResourceType::class => ['resource'], - CallableType::class => ['callback', 'callable'], - ObjectWithoutClassType::class => ['object'], - ]; - - public function mapScalarStringToType(string $scalarName): Type - { - $loweredScalarName = Strings::lower($scalarName); - - if ($loweredScalarName === 'false') { - return new ConstantBooleanType(false); - } - - if ($loweredScalarName === 'true') { - return new ConstantBooleanType(true); - } - - foreach (self::SCALAR_NAME_BY_TYPE as $objectType => $scalarNames) { - if (! in_array($loweredScalarName, $scalarNames, true)) { - continue; - } - - return new $objectType(); - } - - if ($loweredScalarName === 'array') { - return new ArrayType(new MixedType(), new MixedType()); - } - - if ($loweredScalarName === 'iterable') { - return new IterableType(new MixedType(), new MixedType()); - } - - if ($loweredScalarName === 'mixed') { - return new MixedType(true); - } - - return new MixedType(); - } -} diff --git a/packages/StaticTypeMapper/Naming/NameScopeFactory.php b/packages/StaticTypeMapper/Naming/NameScopeFactory.php deleted file mode 100644 index ff784eb1619..00000000000 --- a/packages/StaticTypeMapper/Naming/NameScopeFactory.php +++ /dev/null @@ -1,133 +0,0 @@ -phpDocInfoFactory = $phpDocInfoFactory; - $this->staticTypeMapper = $staticTypeMapper; - } - - public function createNameScopeFromNodeWithoutTemplateTypes(Node $node): NameScope - { - $scope = $node->getAttribute(AttributeKey::SCOPE); - $namespace = $scope instanceof Scope ? $scope->getNamespace() : null; - - /** @var Use_[] $useNodes */ - $useNodes = (array) $node->getAttribute(AttributeKey::USE_NODES); - - $uses = $this->resolveUseNamesByAlias($useNodes); - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - - return new NameScope($namespace, $uses, $className); - } - - public function createNameScopeFromNode(Node $node): NameScope - { - $nameScope = $this->createNameScopeFromNodeWithoutTemplateTypes($node); - $templateTypeMap = $this->templateTemplateTypeMap($node); - - return new NameScope( - $nameScope->getNamespace(), - $nameScope->getUses(), - $nameScope->getClassName(), - null, - $templateTypeMap - ); - } - -// public function setStaticTypeMapper(StaticTypeMapper $staticTypeMapper): void -// { -// $this->staticTypeMapper = $staticTypeMapper; -// } - - /** - * @param Use_[] $useNodes - * @return array - */ - private function resolveUseNamesByAlias(array $useNodes): array - { - $useNamesByAlias = []; - - foreach ($useNodes as $useNode) { - foreach ($useNode->uses as $useUse) { - /** @var UseUse $useUse */ - $aliasName = $useUse->getAlias() - ->name; - - $useName = $useUse->name->toString(); - if (! is_string($useName)) { - throw new ShouldNotHappenException(); - } - - // uses must be lowercase, as PHPStan lowercases it - $lowercasedAliasName = strtolower($aliasName); - - $useNamesByAlias[$lowercasedAliasName] = $useName; - } - } - - return $useNamesByAlias; - } - - private function templateTemplateTypeMap(Node $node): TemplateTypeMap - { - $nodeTemplateTypes = $this->resolveTemplateTypesFromNode($node); - - $class = $node->getAttribute(AttributeKey::CLASS_NODE); - $classTemplateTypes = []; - if ($class instanceof ClassLike) { - $classTemplateTypes = $this->resolveTemplateTypesFromNode($class); - } - - $templateTypes = array_merge($nodeTemplateTypes, $classTemplateTypes); - return new TemplateTypeMap($templateTypes); - } - - /** - * @return Type[] - */ - private function resolveTemplateTypesFromNode(Node $node): array - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - $templateTypes = []; - - foreach ($phpDocInfo->getTemplateTagValueNodes() as $templateTagValueNode) { - $phpstanType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($templateTagValueNode, $node); - $templateTypes[$templateTagValueNode->name] = $phpstanType; - } - - return $templateTypes; - } -} diff --git a/packages/StaticTypeMapper/PhpDocParser/IdentifierTypeMapper.php b/packages/StaticTypeMapper/PhpDocParser/IdentifierTypeMapper.php deleted file mode 100644 index 194b5fff777..00000000000 --- a/packages/StaticTypeMapper/PhpDocParser/IdentifierTypeMapper.php +++ /dev/null @@ -1,115 +0,0 @@ - - */ - public function getNodeType(): string - { - return IdentifierTypeNode::class; - } - - /** - * @param IdentifierTypeNode $typeNode - */ - public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type - { - $type = $this->scalarStringToTypeMapper->mapScalarStringToType($typeNode->name); - if (! $type instanceof MixedType) { - return $type; - } - - if ($type->isExplicitMixed()) { - return $type; - } - - $loweredName = strtolower($typeNode->name); - - if ($loweredName === 'class-string') { - return new ClassStringType(); - } - - if ($loweredName === 'self') { - return $this->mapSelf($node); - } - - if ($loweredName === 'parent') { - return $this->mapParent($node); - } - - if ($loweredName === 'static') { - return $this->mapStatic($node); - } - - if ($loweredName === 'iterable') { - return new IterableType(new MixedType(), new MixedType()); - } - - $objectType = new ObjectType($typeNode->name); - - return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType($node, $objectType); - } - - private function mapSelf(Node $node): MixedType | SelfObjectType - { - /** @var string|null $className */ - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - // self outside the class, e.g. in a function - return new MixedType(); - } - - return new SelfObjectType($className); - } - - private function mapParent(Node $node): ParentStaticType | MixedType - { - $parentClassName = $this->parentClassScopeResolver->resolveParentClassName($node); - if ($parentClassName !== null) { - return new ParentStaticType($parentClassName); - } - - return new MixedType(); - } - - private function mapStatic(Node $node): MixedType | StaticType - { - /** @var string|null $className */ - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return new MixedType(); - } - - return new StaticType($className); - } -} diff --git a/packages/StaticTypeMapper/PhpDocParser/UnionTypeMapper.php b/packages/StaticTypeMapper/PhpDocParser/UnionTypeMapper.php deleted file mode 100644 index 0aff5dc2dd4..00000000000 --- a/packages/StaticTypeMapper/PhpDocParser/UnionTypeMapper.php +++ /dev/null @@ -1,53 +0,0 @@ - - */ - public function getNodeType(): string - { - return UnionTypeNode::class; - } - - #[Required] - public function autowireUnionTypeMapper(PhpDocTypeMapper $phpDocTypeMapper): void - { - $this->phpDocTypeMapper = $phpDocTypeMapper; - } - - /** - * @param UnionTypeNode $typeNode - */ - public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type - { - $unionedTypes = []; - foreach ($typeNode->types as $unionedTypeNode) { - $unionedTypes[] = $this->phpDocTypeMapper->mapToPHPStanType($unionedTypeNode, $node, $nameScope); - } - - // to prevent missing class error, e.g. in tests - return $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($unionedTypes); - } -} diff --git a/packages/StaticTypeMapper/PhpParser/NameNodeMapper.php b/packages/StaticTypeMapper/PhpParser/NameNodeMapper.php deleted file mode 100644 index 084a49da238..00000000000 --- a/packages/StaticTypeMapper/PhpParser/NameNodeMapper.php +++ /dev/null @@ -1,123 +0,0 @@ - - */ - public function getNodeType(): string - { - return Name::class; - } - - /** - * @param Name $node - */ - public function mapToPHPStan(Node $node): Type - { - $name = $node->toString(); - if ($this->isExistingClass($name)) { - return new FullyQualifiedObjectType($name); - } - - if (in_array($name, ['static', 'self', 'parent'], true)) { - return $this->createClassReferenceType($node, $name); - } - - return $this->createScalarType($name); - } - - private function isExistingClass(string $name): bool - { - if ($this->reflectionProvider->hasClass($name)) { - return true; - } - - // to be existing class names - $oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses(); - - return in_array($name, $oldToNewClasses, true); - } - - private function createClassReferenceType(Name $name, string $reference): MixedType | StaticType | ThisType - { - $className = $name->getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return new MixedType(); - } - - if ($reference === 'static') { - return new StaticType($className); - } - - if ($reference === 'parent') { - return new ParentStaticType($className); - } - - if ($this->reflectionProvider->hasClass($className)) { - $classReflection = $this->reflectionProvider->getClass($className); - return new ThisType($classReflection); - } - - return new ThisType($className); - } - - private function createScalarType( - string $name - ): ArrayType | IntegerType | FloatType | StringType | ConstantBooleanType | BooleanType | MixedType { - if ($name === 'array') { - return new ArrayType(new MixedType(), new MixedType()); - } - - if ($name === 'int') { - return new IntegerType(); - } - - if ($name === 'float') { - return new FloatType(); - } - - if ($name === 'string') { - return new StringType(); - } - - if ($name === 'false') { - return new ConstantBooleanType(false); - } - - if ($name === 'bool') { - return new BooleanType(); - } - - return new MixedType(); - } -} diff --git a/packages/StaticTypeMapper/PhpParser/NullableTypeNodeMapper.php b/packages/StaticTypeMapper/PhpParser/NullableTypeNodeMapper.php deleted file mode 100644 index 282fc06feae..00000000000 --- a/packages/StaticTypeMapper/PhpParser/NullableTypeNodeMapper.php +++ /dev/null @@ -1,50 +0,0 @@ -phpParserNodeMapper = $phpParserNodeMapper; - } - - /** - * @return class-string - */ - public function getNodeType(): string - { - return NullableType::class; - } - - /** - * @param NullableType $node - */ - public function mapToPHPStan(Node $node): Type - { - $types = []; - $types[] = $this->phpParserNodeMapper->mapToPHPStanType($node->type); - $types[] = new NullType(); - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } -} diff --git a/packages/StaticTypeMapper/PhpParser/UnionTypeNodeMapper.php b/packages/StaticTypeMapper/PhpParser/UnionTypeNodeMapper.php deleted file mode 100644 index 3cd09f0d647..00000000000 --- a/packages/StaticTypeMapper/PhpParser/UnionTypeNodeMapper.php +++ /dev/null @@ -1,50 +0,0 @@ -phpParserNodeMapper = $phpParserNodeMapper; - } - - /** - * @return class-string - */ - public function getNodeType(): string - { - return UnionType::class; - } - - /** - * @param UnionType $node - */ - public function mapToPHPStan(Node $node): Type - { - $types = []; - foreach ($node->types as $unionedType) { - $types[] = $this->phpParserNodeMapper->mapToPHPStanType($unionedType); - } - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } -} diff --git a/packages/StaticTypeMapper/StaticTypeMapper.php b/packages/StaticTypeMapper/StaticTypeMapper.php deleted file mode 100644 index b6a1c9277be..00000000000 --- a/packages/StaticTypeMapper/StaticTypeMapper.php +++ /dev/null @@ -1,107 +0,0 @@ - PHPStan <=> PHPStan doc <=> string type nodes between all possible formats - * @see \Rector\Tests\NodeTypeResolver\StaticTypeMapper\StaticTypeMapperTest - */ -final class StaticTypeMapper -{ - public function __construct( - private NameScopeFactory $nameScopeFactory, - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper, - private PhpDocTypeMapper $phpDocTypeMapper, - private PhpParserNodeMapper $phpParserNodeMapper, - ) { - } - - public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType, TypeKind $typeKind): TypeNode - { - return $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($phpStanType, $typeKind); - } - - /** - * @return Name|NullableType|PhpParserUnionType|null - */ - public function mapPHPStanTypeToPhpParserNode(Type $phpStanType, TypeKind $typeKind): ?Node - { - return $this->phpStanStaticTypeMapper->mapToPhpParserNode($phpStanType, $typeKind); - } - - public function mapPhpParserNodePHPStanType(Node $node): Type - { - return $this->phpParserNodeMapper->mapToPHPStanType($node); - } - - public function mapPHPStanPhpDocTypeToPHPStanType(PhpDocTagValueNode $phpDocTagValueNode, Node $node): Type - { - if ($phpDocTagValueNode instanceof TemplateTagValueNode) { - // special case - $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($node); - if ($phpDocTagValueNode->bound === null) { - return new MixedType(); - } - - return $this->phpDocTypeMapper->mapToPHPStanType($phpDocTagValueNode->bound, $node, $nameScope); - } - - if ($phpDocTagValueNode instanceof ReturnTagValueNode || $phpDocTagValueNode instanceof ParamTagValueNode || $phpDocTagValueNode instanceof VarTagValueNode || $phpDocTagValueNode instanceof ThrowsTagValueNode) { - return $this->mapPHPStanPhpDocTypeNodeToPHPStanType($phpDocTagValueNode->type, $node); - } - - throw new NotImplementedYetException(__METHOD__ . ' for ' . $phpDocTagValueNode::class); - } - - public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $node): Type - { - if ($node instanceof Param) { - $classMethod = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($classMethod instanceof ClassMethod) { - // param does not hany any clue about template map, but class method has - $node = $classMethod; - } - } - - $nameScope = $this->nameScopeFactory->createNameScopeFromNode($node); - - return $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $node, $nameScope); - } - - public function mapPHPStanPhpDocTypeNodeToPHPStanTypeWithTemplateTypeMap( - TypeNode $typeNode, - Node $node, - TemplateTypeMap $templateTypeMap - ): Type { - $nameScope = $this->nameScopeFactory->createNameScopeFromNode($node); - $nameScope = $nameScope->withTemplateTypeMap($templateTypeMap); - - return $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $node, $nameScope); - } -} diff --git a/packages/StaticTypeMapper/TypeFactory/UnionTypeFactory.php b/packages/StaticTypeMapper/TypeFactory/UnionTypeFactory.php deleted file mode 100644 index 1f4e2404a5f..00000000000 --- a/packages/StaticTypeMapper/TypeFactory/UnionTypeFactory.php +++ /dev/null @@ -1,36 +0,0 @@ -newInstanceWithoutConstructor(); - - $privatesAccessor = new PrivatesAccessor(); - $privatesAccessor->setPrivateProperty($unionType, 'types', $objectTypes); - - return $unionType; - } -} diff --git a/packages/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php b/packages/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php deleted file mode 100644 index cc9390312e1..00000000000 --- a/packages/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php +++ /dev/null @@ -1,56 +0,0 @@ -fullyQualifiedClass; - } - - public function getUseNode(): Use_ - { - $name = new Name($this->fullyQualifiedClass); - $useUse = new UseUse($name, $this->getClassName()); - return new Use_([$useUse]); - } - - public function getShortName(): string - { - return $this->getClassName(); - } - - public function areShortNamesEqual(self | FullyQualifiedObjectType $comparedObjectType): bool - { - return $this->getShortName() === $comparedObjectType->getShortName(); - } - - public function getFunctionUseNode(): Use_ - { - $name = new Name($this->fullyQualifiedClass); - $useUse = new UseUse($name, $this->getClassName()); - - $name->setAttribute(AttributeKey::PARENT_NODE, $useUse); - - $use = new Use_([$useUse]); - $use->type = Use_::TYPE_FUNCTION; - - return $use; - } -} diff --git a/packages/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php b/packages/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php deleted file mode 100644 index 113e26a6a1d..00000000000 --- a/packages/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php +++ /dev/null @@ -1,80 +0,0 @@ -getShortName(), $this->getClassName()); - } - - public function areShortNamesEqual( - AliasedObjectType | \Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType $comparedObjectType - ): bool { - return $this->getShortName() === $comparedObjectType->getShortName(); - } - - public function getShortName(): string - { - if (! \str_contains($this->getClassName(), '\\')) { - return $this->getClassName(); - } - - return (string) Strings::after($this->getClassName(), '\\', -1); - } - - public function getShortNameNode(): Name - { - $name = new Name($this->getShortName()); - - // to avoid processing short name twice - $name->setAttribute(AttributeKey::VIRTUAL_NODE, true); - // keep original to avoid loss on while importing - $name->setAttribute(AttributeKey::NAMESPACED_NAME, $this->getClassName()); - - return $name; - } - - public function getUseNode(): Use_ - { - $name = new Name($this->getClassName()); - $useUse = new UseUse($name); - - $name->setAttribute(AttributeKey::PARENT_NODE, $useUse); - - return new Use_([$useUse]); - } - - public function getFunctionUseNode(): Use_ - { - $name = new Name($this->getClassName()); - $useUse = new UseUse($name, null); - - $name->setAttribute(AttributeKey::PARENT_NODE, $useUse); - - $use = new Use_([$useUse]); - $use->type = Use_::TYPE_FUNCTION; - - return $use; - } - - public function getShortNameLowered(): string - { - return strtolower($this->getShortName()); - } - - public function getClassNameLowered(): string - { - return strtolower($this->getClassName()); - } -} diff --git a/packages/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php b/packages/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php deleted file mode 100644 index 35e865d5c6a..00000000000 --- a/packages/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php +++ /dev/null @@ -1,41 +0,0 @@ -fullyQualifiedName); - return $fullyQualifiedObjectType->isSuperTypeOf($type); - } - - public function getShortName(): string - { - return $this->getClassName(); - } - - /** - * @return class-string - */ - public function getFullyQualifiedName(): string - { - return $this->fullyQualifiedName; - } -} diff --git a/packages/Testing/Contract/ConfigFileAwareInterface.php b/packages/Testing/Contract/ConfigFileAwareInterface.php deleted file mode 100644 index 46a672ee498..00000000000 --- a/packages/Testing/Contract/ConfigFileAwareInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -provideConfigFilePath()); - $this->bootFromConfigFileInfos([$configFileInfo]); - - $this->applicationFileProcessor = $this->getService(ApplicationFileProcessor::class); - $this->parameterProvider = $this->getService(ParameterProvider::class); - $this->dynamicSourceLocatorProvider = $this->getService(DynamicSourceLocatorProvider::class); - - $this->removedAndAddedFilesCollector = $this->getService(RemovedAndAddedFilesCollector::class); - $this->removedAndAddedFilesCollector->reset(); - - /** @var AdditionalAutoloader $additionalAutoloader */ - $additionalAutoloader = $this->getService(AdditionalAutoloader::class); - - $additionalAutoloader->autoloadPaths(); - - /** @var BootstrapFilesIncluder $bootstrapFilesIncluder */ - $bootstrapFilesIncluder = $this->getService(BootstrapFilesIncluder::class); - $bootstrapFilesIncluder->includeBootstrapFiles(); - } - - protected function tearDown(): void - { - // free memory and trigger gc to reduce memory peak consumption on windows - unset( - $this->applicationFileProcessor, - $this->parameterProvider, - $this->dynamicSourceLocatorProvider, - $this->removedAndAddedFilesCollector, - $this->originalTempFileInfo, - ); - gc_collect_cycles(); - } - - /** - * @return Iterator - */ - protected function yieldFilesFromDirectory(string $directory, string $suffix = '*.php.inc'): Iterator - { - return StaticFixtureFinder::yieldDirectoryExclusively($directory, $suffix); - } - - protected function isWindows(): bool - { - return strncasecmp(PHP_OS, 'WIN', 3) === 0; - } - - protected function doTestFileInfo(SmartFileInfo $fixtureFileInfo, bool $allowMatches = true): void - { - $inputFileInfoAndExpectedFileInfo = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpectedFileInfos( - $fixtureFileInfo - ); - - $inputFileInfo = $inputFileInfoAndExpectedFileInfo->getInputFileInfo(); - $this->originalTempFileInfo = $inputFileInfo; - - $expectedFileInfo = $inputFileInfoAndExpectedFileInfo->getExpectedFileInfo(); - $this->doTestFileMatchesExpectedContent($inputFileInfo, $expectedFileInfo, $fixtureFileInfo, $allowMatches); - } - - protected function getFixtureTempDirectory(): string - { - return sys_get_temp_dir() . '/_temp_fixture_easy_testing'; - } - - private function doTestFileMatchesExpectedContent( - SmartFileInfo $originalFileInfo, - SmartFileInfo $expectedFileInfo, - SmartFileInfo $fixtureFileInfo, - bool $allowMatches = true - ): void { - $this->parameterProvider->changeParameter(Option::SOURCE, [$originalFileInfo->getRealPath()]); - - $changedContent = $this->processFileInfo($originalFileInfo); - - // file is removed, we cannot compare it - if ($this->removedAndAddedFilesCollector->isFileRemoved($originalFileInfo)) { - return; - } - - $relativeFilePathFromCwd = $fixtureFileInfo->getRelativeFilePathFromCwd(); - - try { - $this->assertStringEqualsFile($expectedFileInfo->getRealPath(), $changedContent, $relativeFilePathFromCwd); - } catch (ExpectationFailedException $expectationFailedException) { - if (! $allowMatches) { - throw $expectationFailedException; - } - - StaticFixtureUpdater::updateFixtureContent($originalFileInfo, $changedContent, $fixtureFileInfo); - $contents = $expectedFileInfo->getContents(); - - // make sure we don't get a diff in which every line is different (because of differences in EOL) - $contents = $this->normalizeNewlines($contents); - - // if not exact match, check the regex version (useful for generated hashes/uuids in the code) - $this->assertStringMatchesFormat($contents, $changedContent, $relativeFilePathFromCwd); - } - } - - private function normalizeNewlines(string $string): string - { - return str_replace("\r\n", "\n", $string); - } - - private function processFileInfo(SmartFileInfo $fileInfo): string - { - $this->dynamicSourceLocatorProvider->setFileInfo($fileInfo); - - // needed for PHPStan, because the analyzed file is just created in /temp - need for trait and similar deps - /** @var NodeScopeResolver $nodeScopeResolver */ - $nodeScopeResolver = $this->getService(NodeScopeResolver::class); - $nodeScopeResolver->setAnalysedFiles([$fileInfo->getRealPath()]); - - /** @var ConfigurationFactory $configurationFactory */ - $configurationFactory = $this->getService(ConfigurationFactory::class); - $configuration = $configurationFactory->createForTests(); - - $file = new File($fileInfo, $fileInfo->getContents()); - $this->applicationFileProcessor->run([$file], $configuration); - - return $file->getFileContent(); - } -} diff --git a/packages/Testing/PHPUnit/AbstractTestCase.php b/packages/Testing/PHPUnit/AbstractTestCase.php deleted file mode 100644 index 4f8c819562c..00000000000 --- a/packages/Testing/PHPUnit/AbstractTestCase.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ - private static array $kernelsByHash = []; - - private static ?ContainerInterface $currentContainer = null; - - protected function boot(): void - { - $this->bootFromConfigFileInfos([]); - } - - /** - * @param SmartFileInfo[] $configFileInfos - */ - protected function bootFromConfigFileInfos(array $configFileInfos): void - { - $configsHash = $this->createConfigsHash($configFileInfos); - - if (isset(self::$kernelsByHash[$configsHash])) { - $rectorKernel = self::$kernelsByHash[$configsHash]; - self::$currentContainer = $rectorKernel->getContainer(); - } else { - $rectorKernel = new RectorKernel('test_' . $configsHash, true, $configFileInfos); - $rectorKernel->boot(); - - self::$kernelsByHash[$configsHash] = $rectorKernel; - self::$currentContainer = $rectorKernel->getContainer(); - } - } - - /** - * Syntax-sugar to remove static - * - * @template T of object - * @param class-string $type - * @return T - */ - protected function getService(string $type): object - { - if (self::$currentContainer === null) { - throw new ShouldNotHappenException('First, create container with "bootWithConfigFileInfos([...])"'); - } - - $object = self::$currentContainer->get($type); - if ($object === null) { - $message = sprintf('Service "%s" was not found', $type); - throw new ShouldNotHappenException($message); - } - - return $object; - } - - /** - * @param SmartFileInfo[] $configFileInfos - */ - private function createConfigsHash(array $configFileInfos): string - { - $configHash = ''; - foreach ($configFileInfos as $configFileInfo) { - $configHash .= md5_file($configFileInfo->getRealPath()); - } - - return $configHash; - } -} diff --git a/packages/Testing/PHPUnit/Behavior/MovingFilesTrait.php b/packages/Testing/PHPUnit/Behavior/MovingFilesTrait.php deleted file mode 100644 index 9e30ebc2a68..00000000000 --- a/packages/Testing/PHPUnit/Behavior/MovingFilesTrait.php +++ /dev/null @@ -1,101 +0,0 @@ -removedAndAddedFilesCollector->isFileRemoved($smartFileInfo); - $this->assertFalse($hasFileInfo); - } - - protected function assertFileWasAdded(AddedFileWithContent $addedFileWithContent): void - { - $this->assertFilesWereAdded([$addedFileWithContent]); - } - - protected function assertFileWasRemoved(SmartFileInfo $smartFileInfo): void - { - $isFileRemoved = $this->removedAndAddedFilesCollector->isFileRemoved($smartFileInfo); - $this->assertTrue($isFileRemoved); - } - - /** - * @param AddedFileWithContent[] $expectedAddedFileWithContents - */ - protected function assertFilesWereAdded(array $expectedAddedFileWithContents): void - { - Assert::allIsAOf($expectedAddedFileWithContents, AddedFileWithContent::class); - sort($expectedAddedFileWithContents); - - $addedFilePathsWithContents = $this->resolveAddedFilePathsWithContents(); - sort($addedFilePathsWithContents); - - // there should be at least some added files - Assert::notEmpty($addedFilePathsWithContents); - - foreach ($addedFilePathsWithContents as $key => $addedFilePathWithContent) { - $expectedFilePathWithContent = $expectedAddedFileWithContents[$key]; - - /** - * use relative path against _temp_fixture_easy_testing - * to make work in all OSs, for example: - * In MacOS, the realpath() of sys_get_temp_dir() pointed to /private/var/* which symlinked of /var/* - */ - [, $expectedFilePathWithContentFilePath] = explode( - '_temp_fixture_easy_testing', - $expectedFilePathWithContent->getFilePath() - ); - [, $addedFilePathWithContentFilePath] = explode( - '_temp_fixture_easy_testing', - $addedFilePathWithContent->getFilePath() - ); - - $this->assertSame($expectedFilePathWithContentFilePath, $addedFilePathWithContentFilePath); - - $this->assertSame( - $expectedFilePathWithContent->getFileContent(), - $addedFilePathWithContent->getFileContent() - ); - } - } - - /** - * @return AddedFileWithContent[] - */ - private function resolveAddedFilePathsWithContents(): array - { - $addedFilePathsWithContents = $this->removedAndAddedFilesCollector->getAddedFilesWithContent(); - $nodesWithFileDestinationPrinter = $this->getService(NodesWithFileDestinationPrinter::class); - - $movedFiles = $this->removedAndAddedFilesCollector->getMovedFiles(); - foreach ($movedFiles as $movedFile) { - $fileContent = $nodesWithFileDestinationPrinter->printNodesWithFileDestination($movedFile); - $addedFilePathsWithContents[] = new AddedFileWithContent($movedFile->getNewFilePath(), $fileContent); - } - - $addedFilesWithNodes = $this->removedAndAddedFilesCollector->getAddedFilesWithNodes(); - if ($addedFilesWithNodes === []) { - return $addedFilePathsWithContents; - } - - foreach ($addedFilesWithNodes as $addedFileWithNode) { - $fileContent = $nodesWithFileDestinationPrinter->printNodesWithFileDestination($addedFileWithNode); - $addedFilePathsWithContents[] = new AddedFileWithContent($addedFileWithNode->getFilePath(), $fileContent); - } - - return $addedFilePathsWithContents; - } -} diff --git a/packages/Testing/PHPUnit/Behavior/MultipleFilesChangedTrait.php b/packages/Testing/PHPUnit/Behavior/MultipleFilesChangedTrait.php deleted file mode 100644 index 3fdbd51cf4d..00000000000 --- a/packages/Testing/PHPUnit/Behavior/MultipleFilesChangedTrait.php +++ /dev/null @@ -1,109 +0,0 @@ -getContents(), - 3 - ); - - $additionalChanges = explode($separator, $additionalInfo); - /** @var array $additionalFileChanges */ - $additionalFileChanges = array_chunk($additionalChanges, 3); - $expectedFileChanges = $this->prepareAdditionalChangedFiles($additionalFileChanges); - - $fixturePath = $this->getFixtureTempDirectory() . '/' . $fixtureFileInfo->getFilename(); - $this->createFixtureDir($fixturePath); - $fixtureContent = $originalContent; - if (trim($expectedContent)) { - $fixtureContent .= $separator . $expectedContent; - } - FileSystem::write($fixturePath, $fixtureContent); - $newFileInfo = new SmartFileInfo($fixturePath); - $this->doTestFileInfo($newFileInfo, $allowMatches); - - $this->checkAdditionalChanges($expectedFileChanges); - - if (file_exists($fixturePath)) { - FileSystem::delete($fixturePath); - } - } - - /** - * @param array $additionalFileChanges - * @return array - */ - private function prepareAdditionalChangedFiles(array $additionalFileChanges): array - { - $expectedFileChanges = []; - foreach ($additionalFileChanges as $additionalFileChange) { - $path = isset($additionalFileChange[0]) ? trim($additionalFileChange[0]) : null; - if ($path === null) { - throw new ShouldNotHappenException('Path for additional change must be set'); - } - $fullPath = $this->getFixtureTempDirectory() . '/' . $path; - - $input = isset($additionalFileChange[1]) ? trim($additionalFileChange[1]) : null; - if ($input) { - $this->createFixtureDir($fullPath); - FileSystem::write($fullPath, $input); - } - - $expectedFileChanges[$fullPath] = isset($additionalFileChange[2]) ? trim($additionalFileChange[2]) : ''; - } - return $expectedFileChanges; - } - - /** - * @param array $expectedFileChanges - */ - private function checkAdditionalChanges(array $expectedFileChanges): void - { - $addedFilesWithContent = $this->removedAndAddedFilesCollector->getAddedFilesWithContent(); - $addedFiles = []; - foreach ($addedFilesWithContent as $addedFileWithContent) { - [, $addedFilePathWithContentFilePath] = explode( - '_temp_fixture_easy_testing', - $addedFileWithContent->getFilePath() - ); - $addedFiles[$addedFilePathWithContentFilePath] = $addedFileWithContent; - } - - foreach ($expectedFileChanges as $path => $expectedFileChange) { - [, $relativePath] = explode('_temp_fixture_easy_testing', $path); - $addedFile = $addedFiles[$relativePath] ?? null; - [, $addedFilePathWithContentFilePath] = $addedFile - ? explode('_temp_fixture_easy_testing', $addedFile->getFilePath()) - : null; - - $this->assertSame($relativePath, $addedFilePathWithContentFilePath); - $realFileContent = $addedFile ? trim($addedFile->getFileContent()) : null; - $this->assertSame($expectedFileChange, $realFileContent); - if (file_exists($path)) { - FileSystem::delete($path); - } - } - } - - private function createFixtureDir(string $fileName): void - { - $dirName = dirname($fileName); - if (! file_exists($dirName)) { - mkdir($dirName, 0777, true); - } - } -} diff --git a/packages/Testing/PHPUnit/PlatformAgnosticAssertions.php b/packages/Testing/PHPUnit/PlatformAgnosticAssertions.php deleted file mode 100644 index 6323b9adb6b..00000000000 --- a/packages/Testing/PHPUnit/PlatformAgnosticAssertions.php +++ /dev/null @@ -1,83 +0,0 @@ -parameterProvider->changeParameter(Option::SOURCE, [$file]); - - $nodes = $this->parser->parseFileInfo($smartFileInfo); - - $file = new File($smartFileInfo, $smartFileInfo->getContents()); - return $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($file, $nodes); - } - - /** - * @template T of Node - * @param class-string $nodeClass - * @return Node[] - */ - public function parseFileToDecoratedNodesAndFindNodesByType(string $file, string $nodeClass): array - { - $nodes = $this->parseFileToDecoratedNodes($file); - return $this->betterNodeFinder->findInstanceOf($nodes, $nodeClass); - } -} diff --git a/packages/Testing/ValueObject/InputFilePathWithExpectedFile.php b/packages/Testing/ValueObject/InputFilePathWithExpectedFile.php deleted file mode 100644 index 24aabfea22c..00000000000 --- a/packages/Testing/ValueObject/InputFilePathWithExpectedFile.php +++ /dev/null @@ -1,27 +0,0 @@ -inputFilePath); - } - - public function getAddedFileWithContent(): AddedFileWithContent - { - return $this->addedFileWithContent; - } -} diff --git a/packages/VendorLocker/Contract/NodeVendorLockerInterface.php b/packages/VendorLocker/Contract/NodeVendorLockerInterface.php deleted file mode 100644 index 8d48221b426..00000000000 --- a/packages/VendorLocker/Contract/NodeVendorLockerInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -isMagic()) { - return true; - } - - $classReflection = $this->resolveClassReflection($classMethod); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - /** @var string $methodName */ - $methodName = $this->nodeNameResolver->getName($classMethod); - - if ($this->hasTraitMethodVendorLock($classReflection, $methodName)) { - return true; - } - - // has interface vendor lock? → better skip it, as PHPStan has access only to just analyzed classes - if ($this->hasParentInterfaceMethod($classReflection, $methodName)) { - return true; - } - - $methodName = $this->nodeNameResolver->getName($classMethod); - foreach ($classReflection->getAncestors() as $ancestorClassReflection) { - // skip self - if ($ancestorClassReflection === $classReflection) { - continue; - } - - // parent type - if (! $ancestorClassReflection->hasNativeMethod($methodName)) { - continue; - } - - // is file in vendor? - $fileName = $ancestorClassReflection->getFileName(); - // probably internal class - if ($fileName === false) { - continue; - } - - $normalizedFileName = $this->pathNormalizer->normalizePath($fileName, '/'); - return str_contains($normalizedFileName, '/vendor/'); - } - - return false; - } - - private function hasTraitMethodVendorLock(ClassReflection $classReflection, string $methodName): bool - { - $relatedReflectionClasses = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); - - foreach ($relatedReflectionClasses as $relatedReflectionClass) { - foreach ($relatedReflectionClass->getTraits() as $traitReflectionClass) { - /** @var ClassReflection $traitReflectionClass */ - if ($traitReflectionClass->hasMethod($methodName)) { - return true; - } - } - } - - return false; - } - - private function resolveClassReflection(ClassMethod $classMethod): ClassReflection | null - { - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - return $scope->getClassReflection(); - } - - /** - * Has interface even in our project? - * Better skip it, as PHPStan has access only to just analyzed classes. - * This might change type, that works for current class, but breaks another implementer. - */ - private function hasParentInterfaceMethod(ClassReflection $classReflection, string $methodName): bool - { - foreach ($classReflection->getInterfaces() as $interfaceClassReflection) { - if ($interfaceClassReflection->hasMethod($methodName)) { - return true; - } - } - - return false; - } -} diff --git a/packages/VendorLocker/NodeVendorLocker/ClassMethodReturnTypeOverrideGuard.php b/packages/VendorLocker/NodeVendorLocker/ClassMethodReturnTypeOverrideGuard.php deleted file mode 100644 index 3ece2b4548b..00000000000 --- a/packages/VendorLocker/NodeVendorLocker/ClassMethodReturnTypeOverrideGuard.php +++ /dev/null @@ -1,153 +0,0 @@ -> - */ - private const CHAOTIC_CLASS_METHOD_NAMES = [ - 'PhpParser\NodeVisitor' => ['enterNode', 'leaveNode', 'beforeTraverse', 'afterTraverse'], - ]; - - public function __construct( - private NodeNameResolver $nodeNameResolver, - private ReflectionProvider $reflectionProvider, - private FamilyRelationsAnalyzer $familyRelationsAnalyzer, - private BetterNodeFinder $betterNodeFinder - ) { - } - - public function shouldSkipClassMethod(ClassMethod $classMethod): bool - { - // 1. skip magic methods - if ($classMethod->isMagic()) { - return true; - } - - // 2. skip chaotic contract class methods - if ($this->shouldSkipChaoticClassMethods($classMethod)) { - return true; - } - - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - throw new ShouldNotHappenException(); - } - - $childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); - if ($childrenClassReflections === []) { - return false; - } - - if ($this->hasClassMethodExprReturn($classMethod)) { - return false; - } - - return $classMethod->returnType === null; - } - - public function shouldSkipClassMethodOldTypeWithNewType(Type $oldType, Type $newType): bool - { - if ($oldType instanceof MixedType) { - return false; - } - - // new generic string type is more advanced than old array type - if ($this->isFirstArrayTypeMoreAdvanced($oldType, $newType)) { - return false; - } - - return $oldType->isSuperTypeOf($newType) - ->yes(); - } - - private function shouldSkipChaoticClassMethods(ClassMethod $classMethod): bool - { - /** @var string|null $className */ - $className = $classMethod->getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return false; - } - - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - foreach (self::CHAOTIC_CLASS_METHOD_NAMES as $chaoticClass => $chaoticMethodNames) { - if (! $this->reflectionProvider->hasClass($chaoticClass)) { - continue; - } - - $chaoticClassReflection = $this->reflectionProvider->getClass($chaoticClass); - if (! $classReflection->isSubclassOf($chaoticClassReflection->getName())) { - continue; - } - - return $this->nodeNameResolver->isNames($classMethod, $chaoticMethodNames); - } - - return false; - } - - private function hasClassMethodExprReturn(ClassMethod $classMethod): bool - { - return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node): bool { - if (! $node instanceof Return_) { - return false; - } - - return $node->expr instanceof Expr; - }); - } - - private function isFirstArrayTypeMoreAdvanced(Type $oldType, Type $newType): bool - { - if (! $oldType instanceof ArrayType) { - return false; - } - - if (! $newType instanceof ArrayType) { - return false; - } - - if (! $oldType->getItemType() instanceof StringType) { - return false; - } - - return $newType->getItemType() instanceof GenericClassStringType; - } -} diff --git a/packages/VendorLocker/NodeVendorLocker/ClassMethodReturnVendorLockResolver.php b/packages/VendorLocker/NodeVendorLocker/ClassMethodReturnVendorLockResolver.php deleted file mode 100644 index 857b83025bf..00000000000 --- a/packages/VendorLocker/NodeVendorLocker/ClassMethodReturnVendorLockResolver.php +++ /dev/null @@ -1,68 +0,0 @@ -getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - $methodName = $this->nodeNameResolver->getName($classMethod); - if ($this->isVendorLockedByAncestors($classReflection, $methodName)) { - return true; - } - - return $classReflection->isTrait(); - } - - private function isVendorLockedByAncestors(ClassReflection $classReflection, string $methodName): bool - { - foreach ($classReflection->getAncestors() as $ancestorClassReflections) { - if ($ancestorClassReflections === $classReflection) { - continue; - } - - $nativeClassReflection = $ancestorClassReflections->getNativeReflection(); - - // this should avoid detecting @method as real method - if (! $nativeClassReflection->hasMethod($methodName)) { - continue; - } - - $parentClassMethodReflection = $ancestorClassReflections->getNativeMethod($methodName); - $parametersAcceptor = $parentClassMethodReflection->getVariants()[0]; - if (! $parametersAcceptor instanceof FunctionVariantWithPhpDocs) { - continue; - } - - // here we count only on strict types, not on docs - return ! $parametersAcceptor->getNativeReturnType() instanceof MixedType; - } - - return false; - } -} diff --git a/packages/VendorLocker/NodeVendorLocker/PropertyTypeVendorLockResolver.php b/packages/VendorLocker/NodeVendorLocker/PropertyTypeVendorLockResolver.php deleted file mode 100644 index 33ffa21b712..00000000000 --- a/packages/VendorLocker/NodeVendorLocker/PropertyTypeVendorLockResolver.php +++ /dev/null @@ -1,95 +0,0 @@ -getAttribute(AttributeKey::SCOPE); - // possibly trait - if (! $scope instanceof Scope) { - return true; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - if (count($classReflection->getAncestors()) === 1) { - return false; - } - - /** @var string $propertyName */ - $propertyName = $this->nodeNameResolver->getName($property); - - if ($this->isParentClassLocked($classReflection, $propertyName)) { - return true; - } - - return $this->isChildClassLocked($property, $classReflection, $propertyName); - } - - private function isParentClassLocked(ClassReflection $classReflection, string $propertyName): bool - { - // extract to some "inherited parent method" service - foreach ($classReflection->getParents() as $parentClassReflection) { - if ($parentClassReflection->hasProperty($propertyName)) { - // validate type is conflicting - // parent class property in external scope → it's not ok - return true; - } - } - - return false; - } - - private function isChildClassLocked( - Property $property, - ClassReflection $classReflection, - string $propertyName - ): bool { - if (! $classReflection->isClass()) { - return false; - } - - // is child class locked? - if ($property->isPrivate()) { - return false; - } - - $childClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); - - foreach ($childClassReflections as $childClassReflection) { - if (! $childClassReflection->hasProperty($propertyName)) { - continue; - } - - $propertyReflection = $childClassReflection->getNativeProperty($propertyName); - - // ensure the property is not in the parent class - $propertyReflectionDeclaringClass = $propertyReflection->getDeclaringClass(); - if ($propertyReflectionDeclaringClass->getName() === $childClassReflection->getName()) { - return true; - } - } - - return false; - } -} diff --git a/packages/VendorLocker/NodeVendorLocker/PropertyVisibilityVendorLockResolver.php b/packages/VendorLocker/NodeVendorLocker/PropertyVisibilityVendorLockResolver.php deleted file mode 100644 index 332653a420c..00000000000 --- a/packages/VendorLocker/NodeVendorLocker/PropertyVisibilityVendorLockResolver.php +++ /dev/null @@ -1,80 +0,0 @@ -resolveClassReflection($property); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - $propertyName = $this->nodeNameResolver->getName($property); - - foreach ($classReflection->getParents() as $parentClassReflection) { - if ($parentClassReflection->hasProperty($propertyName)) { - return true; - } - } - - return false; - } - - public function isChildLockedProperty(Property $property): bool - { - $classReflection = $this->resolveClassReflection($property); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - $propertyName = $this->nodeNameResolver->getName($property); - - $childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); - foreach ($childrenClassReflections as $childClassReflection) { - if ($childClassReflection === $classReflection) { - continue; - } - - if ($childClassReflection->hasProperty($propertyName)) { - return true; - } - } - - return false; - } - - private function resolveClassReflection(Node $node): ?ClassReflection - { - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - return $scope->getClassReflection(); - } -} diff --git a/packages/VendorLocker/VendorLockResolver.php b/packages/VendorLocker/VendorLockResolver.php deleted file mode 100644 index f3be66f4c2d..00000000000 --- a/packages/VendorLocker/VendorLockResolver.php +++ /dev/null @@ -1,41 +0,0 @@ -classMethodParamVendorLockResolver->isVendorLocked($node); - } - - public function isReturnChangeVendorLockedIn(ClassMethod $classMethod): bool - { - return $this->classMethodReturnVendorLockResolver->isVendorLocked($classMethod); - } - - public function isPropertyTypeChangeVendorLockedIn(Property $property): bool - { - return $this->propertyTypeVendorLockResolver->isVendorLocked($property); - } -} diff --git a/packages/VersionBonding/Application/MissedRectorDueVersionChecker.php b/packages/VersionBonding/Application/MissedRectorDueVersionChecker.php deleted file mode 100644 index 56407cf0b3c..00000000000 --- a/packages/VersionBonding/Application/MissedRectorDueVersionChecker.php +++ /dev/null @@ -1,103 +0,0 @@ -phpVersionProvider->provide(); - - $missedRectors = $this->resolveMissedRectors($rectors, $minProjectPhpVersion); - if ($missedRectors === []) { - return; - } - - $this->reportWarningMessage($minProjectPhpVersion, $missedRectors); - - $this->reportMissedRectors($missedRectors); - - $solutionMessage = sprintf( - 'Do you want to run them? Make "require" > "php" in `composer.json` higher,%sor add "Option::PHP_VERSION_FEATURES" parameter to your `rector.php`.', - PHP_EOL - ); - $this->symfonyStyle->note($solutionMessage); - } - - /** - * @param RectorInterface[] $rectors - * @return MinPhpVersionInterface[] - */ - private function resolveMissedRectors(array $rectors, int $minProjectPhpVersion): array - { - $missedRectors = []; - foreach ($rectors as $rector) { - if (! $rector instanceof MinPhpVersionInterface) { - continue; - } - - // the conditions are met → skip it - if ($rector->provideMinPhpVersion() <= $minProjectPhpVersion) { - continue; - } - - $missedRectors[] = $rector; - } - - return $missedRectors; - } - - /** - * @param MinPhpVersionInterface[] $minPhpVersions - */ - private function reportMissedRectors(array $minPhpVersions): void - { - if (! $this->symfonyStyle->isVerbose()) { - return; - } - - foreach ($minPhpVersions as $minPhpVersion) { - $phpVersion = new PhpVersion($minPhpVersion->provideMinPhpVersion()); - $shortRectorClass = Strings::after($minPhpVersion::class, '\\', -1); - - $rectorMessage = sprintf(' * [%s] %s', $phpVersion->getVersionString(), $shortRectorClass); - $this->symfonyStyle->writeln($rectorMessage); - } - } - - /** - * @param MinPhpVersionInterface[] $missedRectors - */ - private function reportWarningMessage(int $minProjectPhpVersion, array $missedRectors): void - { - $phpVersion = new PhpVersion($minProjectPhpVersion); - - $warningMessage = sprintf( - 'Your project requires min PHP version "%s". %s%d Rector rules defined in your configuration require higher PHP version and will not run,%sto avoid breaking your codebase, use -vvv for detailed info.', - $phpVersion->getVersionString(), - PHP_EOL . PHP_EOL, - count($missedRectors), - PHP_EOL - ); - - $this->symfonyStyle->warning($warningMessage); - } -} diff --git a/packages/VersionBonding/Contract/MinPhpVersionInterface.php b/packages/VersionBonding/Contract/MinPhpVersionInterface.php deleted file mode 100644 index 9e65652ca13..00000000000 --- a/packages/VersionBonding/Contract/MinPhpVersionInterface.php +++ /dev/null @@ -1,25 +0,0 @@ - $rectors - * @return array - */ - public function filter(array $rectors): array - { - $minProjectPhpVersion = $this->phpVersionProvider->provide(); - - $activeRectors = []; - foreach ($rectors as $rector) { - if (! $rector instanceof MinPhpVersionInterface) { - $activeRectors[] = $rector; - continue; - } - - // does satify version? → include - if ($rector->provideMinPhpVersion() <= $minProjectPhpVersion) { - $activeRectors[] = $rector; - } - } - - return $activeRectors; - } -} diff --git a/patches/symfony-console-style-symfonystyle-php.patch b/patches/symfony-console-style-symfonystyle-php.patch deleted file mode 100644 index cfad2102ce7..00000000000 --- a/patches/symfony-console-style-symfonystyle-php.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- /dev/null -+++ ../Style/SymfonyStyle.php -@@ -288,7 +288,7 @@ - /** - * {@inheritdoc} - */ -- public function confirm($question, $default = true) -+ public function confirm(string $question, bool $default = true) - { - return $this->askQuestion(new ConfirmationQuestion($question, $default)); - } diff --git a/phpstan-for-rector.neon b/phpstan-for-rector.neon deleted file mode 100644 index fc3f352636a..00000000000 --- a/phpstan-for-rector.neon +++ /dev/null @@ -1,18 +0,0 @@ -# this config has extensions, that helps PHPStan inside Rector to resolve more precise types -parameters: - inferPrivatePropertyTypeFromConstructor: true - - scanDirectories: - - stubs - - # see https://github.com/rectorphp/rector/issues/3490#issue-634342324 - featureToggles: - disableRuntimeReflectionProvider: true - -includes: - - vendor/rector/phpstan-rules/config/config.neon - - vendor/symplify/phpstan-extensions/config/config.neon - - - vendor/symplify/phpstan-rules/config/services/services.neon - - vendor/symplify/phpstan-rules/packages/object-calisthenics/config/object-calisthenics-services.neon - - vendor/symplify/phpstan-rules/packages/cognitive-complexity/config/cognitive-complexity-services.neon diff --git a/phpstan.neon b/phpstan.neon index dfff48c0419..9d964bd4225 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,552 +1,486 @@ includes: - - vendor/symplify/phpstan-rules/config/array-rules.neon - - vendor/symplify/phpstan-rules/config/code-complexity-rules.neon - - vendor/symplify/phpstan-rules/config/doctrine-rules.neon - - vendor/symplify/phpstan-rules/config/forbidden-static-rules.neon - - vendor/symplify/phpstan-rules/config/naming-rules.neon - - vendor/symplify/phpstan-rules/config/regex-rules.neon - - vendor/symplify/phpstan-rules/config/services-rules.neon - - vendor/symplify/phpstan-rules/config/static-rules.neon - - vendor/symplify/phpstan-rules/config/size-rules.neon - - vendor/symplify/phpstan-rules/config/string-to-constant-rules.neon - - vendor/symplify/phpstan-rules/config/symfony-rules.neon - - vendor/symplify/phpstan-rules/config/test-rules.neon + - vendor/symplify/phpstan-rules/config/symplify-rules.neon + - vendor/symplify/phpstan-rules/config/rector-rules.neon parameters: - level: max + level: 8 + + reportUnmatchedIgnoredErrors: false + + errorFormat: symplify + + # see https://phpstan.org/writing-php-code/phpdoc-types#global-type-aliases + typeAliases: + StmtsAware: \PhpParser\Node\Stmt\Block | \PhpParser\Node\Expr\Closure | \PhpParser\Node\Stmt\Case_ | \PhpParser\Node\Stmt\Catch_ | \PhpParser\Node\Stmt\ClassMethod | \PhpParser\Node\Stmt\Do_ | \PhpParser\Node\Stmt\Else_ | \PhpParser\Node\Stmt\ElseIf_ | \PhpParser\Node\Stmt\Finally_ | \PhpParser\Node\Stmt\For_ | \PhpParser\Node\Stmt\Foreach_ | \PhpParser\Node\Stmt\Function_ | \PhpParser\Node\Stmt\If_ | \PhpParser\Node\Stmt\Namespace_ | \PhpParser\Node\Stmt\TryCatch | \PhpParser\Node\Stmt\While_ | \Rector\PhpParser\Node\FileNode | \PhpParser\Node\Stmt\Declare_ + + # requires exact closure types + checkMissingCallableSignature: true + treatPhpDocTypesAsCertain: false paths: - rector.php - bin + - config - src - rules - - packages + # tests - tests + - rules-tests - utils - # this cannot be put it, because it wipes PHPStan cache on each run :( - must run in separate - #- config - - # to allow installing with various phsptan versions without reporting old errors here - bootstrapFiles: - - vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php + - scripts + - e2e/e2eTestRunnerWithCache.php + - e2e/e2eTestRunner.php scanDirectories: - stubs - reportUnmatchedIgnoredErrors: false - checkGenericClassInNonGenericObjectType: false - - excludes_analyse: - # temporary stinrgable migration from template type provider - - src/Console/Command/InitCommand.php - - - */config.php - - tests/debug_functions.php - - # broken in PHPStan https://github.com/rectorphp/rector/runs/1305002460#step:5:56 - - packages/BetterPhpDocParser/ValueObject/PhpDocNode/AbstractTagValueNode.php - - packages/Testing/PHPUnit/*.php - - packages-tests/NodeVisitor/FunctionMethodAndClassNodeVisitor/FunctionMethodAndClassNodeVisitorTest.php - - rules/DowngradePhp70/Rector/FunctionLike/AbstractDowngradeParamDeclarationRector.php - - # demo rule - - tests/Issues/AliasedImportDouble/Rector/ClassMethod/AddAliasImportRector.php - - - "*/Expected/*" - # complex printer - - '*tests/Rector/MethodCall/RenameMethodRector/**/SomeClass.php' - # tests files + excludePaths: - '*tests/*/Fixture/*' + - '*tests/*/Fixture*' - '*tests/*/Source/*' - - '*tests/Source/*' - # part of composer - - '*/tests/Rector/Psr4/MultipleClassFileToPsr4ClassesRector/Expected/Just*ExceptionWithoutNamespace.php' - - packages-tests/BetterPhpDocPraser/PhpDocParser/TagValueNodeReprint/Fixture/SymfonyRoute/RouteName.php + - '*tests/*/Source*' - # tests - - tests/DependencyInjection/config + # https://github.com/rectorphp/type-perfect/ + type_perfect: + no_mixed: true + null_over_false: true + narrow_param: true + narrow_return: true - ignoreErrors: - # PHP 7.4 1_000 support - - '#Property PhpParser\\Node\\Scalar\\DNumber\:\:\$value \(float\) does not accept string#' - - # mixed - - '#Offset int\|string\|null does not exist on array\|null#' - - - '#Parameter \#1 \$node of method Rector\\PostRector\\Collector\\NodesToAddCollector\:\:wrapToExpression\(\) expects PhpParser\\Node\\Expr\|PhpParser\\Node\\Stmt, PhpParser\\Node given#' - - - '#Cognitive complexity for "Rector\\Php80\\NodeResolver\\SwitchExprsResolver\:\:resolve\(\)" is (.*?), keep it under 9#' + # see https://github.com/TomasVotruba/unused-public + unused_public: + methods: true + properties: true + constants: true + ignoreErrors: + # required generics on interface array, not helpful - - message: "#^Cognitive complexity for \"Rector\\\\PhpSpecToPHPUnit\\\\Rector\\\\MethodCall\\\\PhpSpecPromisesToPHPUnitAssertRector\\:\\:refactor\\(\\)\" is 13, keep it under 9$#" - path: rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecPromisesToPHPUnitAssertRector.php - + message: '#Method (.*?)\:\:__construct\(\) has parameter#' + identifier: missingType.generics - - message: '#Class cognitive complexity is \d+, keep it under \d+#' - paths: - - rules/Php70/EregToPcreTransformer.php - - packages/NodeTypeResolver/NodeTypeResolver.php - - rules/Php80/Rector/If_/NullsafeOperatorRector.php - - rules/Renaming/NodeManipulator/ClassRenamer.php - - rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php + message: '#(.*?) with generic interface (.*?)Interface does not specify its types#' + identifier: missingType.generics - - "#^Cognitive complexity for \"Rector\\\\Php70\\\\EregToPcreTransformer\\:\\:(.*?)\" is (.*?), keep it under 9$#" - - '#Cognitive complexity for "Rector\\CodeQuality\\Rector\\If_\\SimplifyIfIssetToNullCoalescingRector\:\:shouldSkip\(\)" is 10, keep it under 9#' - - '#Cognitive complexity for "Rector\\TypeDeclaration\\PHPStan\\Type\\ObjectTypeSpecifier\:\:matchShortenedObjectType\(\)" is 10, keep it under 9#' - - '#Cognitive complexity for "Rector\\Core\\PhpParser\\Node\\Value\\ValueResolver\:\:getValue\(\)" is \d+, keep it under 9#' - - '#Cognitive complexity for "Rector\\DeadCode\\NodeManipulator\\LivingCodeManipulator\:\:keepLivingCodeFromExpr\(\)" is \d+, keep it under 9#' - - '#Cognitive complexity for "Rector\\Transform\\Rector\\Class_\\AddInterfaceByParentRector\:\:refactor\(\)" is \d+, keep it under 9#' + # phpstan class instance + - identifier: phpstanApi.class + # phpstan class constant value + - identifier: phpstanApi.classConstant - - '#Parameter \#1 \$type of class PhpParser\\Node\\NullableType constructor expects PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|string, PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType given#' - - '#Parameter \#1 \$objectType of method Rector\\Naming\\Naming\\PropertyNaming\:\:fqnToVariableName\(\) expects PHPStan\\Type\\ObjectType\|string, PHPStan\\Type\\Type given#' - # known value - - '#Property PhpParser\\Node\\Stmt\\Foreach_\:\:\$valueVar \(PhpParser\\Node\\Expr\) does not accept PhpParser\\Node\\Expr\|null#' + # phpstan class construction + - identifier: phpstanApi.constructor - - '#Parameter \#1 \$variable of class Rector\\Php70\\ValueObject\\VariableAssignPair constructor expects PhpParser\\Node\\Expr\\ArrayDimFetch\|PhpParser\\Node\\Expr\\PropertyFetch\|PhpParser\\Node\\Expr\\StaticPropertyFetch\|PhpParser\\Node\\Expr\\Variable, PhpParser\\Node\\Expr given#' + # phpstan instanceof + - identifier: phpstanApi.instanceofAssumption - # is nested expr - - '#Access to an undefined property PhpParser\\Node\\Expr\:\:\$expr#' + # assert phpunit + - + identifier: method.alreadyNarrowedType + path: tests + # runtime php id - - message: '#Array (with keys|destruct) is not allowed\. Use value object to pass data instead#' - paths: - # 3rd party package - - rules/Php70/EregToPcreTransformer.php + identifier: greaterOrEqual.alwaysTrue + path: src/Util/FileHasher.php + # on runtime check - - message: '#Use explicit return value over magic &reference#' + identifier: deadCode.unreachable paths: - # 3rd party code - - rules/Php70/EregToPcreTransformer.php + - src/Console/Notifier.php + - src/Util/FileHasher.php - # symfony/console + # is nested expr - - message: '#Use value object over return of values#' - path: 'rules/Php70/EregToPcreTransformer.php' + message: '#Access to an undefined property PhpParser\\Node\\Expr\:\:\$expr#' + path: rules/DeadCode/NodeManipulator/LivingCodeManipulator.php + # know type - - message: '#Nested foreach with empty statement is not allowed#' - path: packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TagValueNodeReprintTest.php + message: '#Access to an undefined property PhpParser\\Node\:\:\$expr#' + path: src/PhpParser/Printer/BetterStandardPrinter.php - - message: '#Function "dump\(\)" cannot be used/left in the code#' - path: tests/debug_functions.php + message: '#Function "var_dump\(\)" cannot be used/left in the code#' + path: src/functions/node_helper.php - # symplify 9 - - '#Use another value object over array with string\-keys and objects, array#' - - '#Do not use factory/method call in constructor\. Put factory in config and get service with dependency injection#' + # lack of generic array in nikic/php-parser + - '#Method (.*?) should return array but returns array#' + # generics nullable bugs - - message: '#"(getComments|getDocComment|setDocComment)\(\)" call on "PhpParser\\Node" type is not allowed#' - paths: - - src/PhpParser/NodeTransformer.php - - src/Rector/AbstractRector.php - - src/Exclusion/ExclusionManager.php - # playing around with doc block format - - packages/Comments/CommentRemover.php - - rules/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector.php - - rules/CodeQuality/Rector/Return_/SimplifyUselessVariableRector.php - - rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php - - rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php - - rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecMocksToPHPUnitMocksRector.php - - rules/CodingStyle/Node/DocAliasResolver.php - - packages/BetterPhpDocParser/Comment/CommentsMerger.php - - packages/Comments/NodeDocBlock/DocBlockUpdater.php - - packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfoFactory.php + message: '#Method (.*?) should return (.*?)\|null but returns PhpParser\\Node\|null#' + path: src/PhpParser/Node/BetterNodeFinder.php - # false positives checked in another method - - message: '#If condition is always false#' + message: '#Function "property_exists\(\)" cannot be used/left in the code#' paths: - - rules/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector.php - - rules/CodeQuality/Rector/Concat/JoinStringConcatRector.php - - packages/NodeNestingScope/NodeFinder/ScopeAwareNodeFinder.php - - - '#Method (.*?) should return array but returns array#' - - '#Parameter \#1 (.*?) expects Symfony\\Component\\DependencyInjection\\ContainerBuilder, Symfony\\Component\\DependencyInjection\\ContainerInterface given#' - - # intersection - - '#Property Rector\\TypeDeclaration\\TypeInferer\\ReturnTypeInferer\:\:\$returnTypeInferers \(array\) does not accept array#' - - '#Property Rector\\TypeDeclaration\\TypeInferer\\PropertyTypeInferer\:\:\$propertyTypeInferers \(array\) does not accept array#' + # on PhpParser Nodes + - src/NodeNameResolver/NodeNameResolver.php + - src/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php + - src/BetterPhpDocParser/PhpDocParser/DoctrineAnnotationDecorator.php - - '#Access to an undefined property PhpParser\\Node\\Expr\\Error\|PhpParser\\Node\\Expr\\Variable\:\:\$name#' + - '#(.*?) class\-string, string given#' - # @todo loop magic, resolve later - - message: '#Access to an undefined property PhpParser\\Node\:\:\$expr#' + message: '#Use explicit return value over magic &reference#' paths: - - rules/Php80/Rector/If_/NullsafeOperatorRector.php + - src/PhpDocParser/PhpDocParser/PhpDocNodeTraverser.php + - rules/Php70/EregToPcreTransformer.php + - src/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php + - src/NodeTypeResolver/PHPStan/Type/TypeFactory.php + + - '#Method Rector\\Arguments\\ArgumentDefaultValueReplacer\:\:processReplaces\(\) should return \(TCall of PhpParser\\Node\\Expr\\FuncCall\|PhpParser\\Node\\Expr\\MethodCall\|PhpParser\\Node\\Expr\\New_\|PhpParser\\Node\\Expr\\StaticCall\|PhpParser\\Node\\Stmt\\ClassMethod\)\|null but returns PhpParser\\Node\\Stmt\\ClassMethod\|null#' + # native filesystem calls, required for performance reasons - - message: '#Comparison operation "<" between 0 and 2 is always true#' + message: '#@(.*?) is forbidden to use#' paths: - - rules/Php70/Rector/FuncCall/MultiDirnameRector.php -# - '#PHPDoc tag @param for parameter \$node with type float is incompatible with native type PhpParser\\Node#' + - src/Caching/ValueObject/Storage/FileCacheStorage.php - # false postives - - '#Parameter \#2 \$right of class PhpParser\\Node\\Expr\\BinaryOp\\Spaceship constructor expects PhpParser\\Node\\Expr, PhpParser\\Node\\Expr\|null given#' + # many internal cases + - '#Calling (.*?) is not covered by backward compatibility promise\. The method might change in a minor PHPStan version#' + # known value object, nullable due to typed property - - message: '#Call to function is_string\(\) with float will always evaluate to false#' - path: src/PhpParser/Printer/BetterStandardPrinter.php + message: '#Cannot call method (.*?)\(\) on (.*?)\\ProcessPool\|null#' + path: src/Parallel/Application/ParallelFileProcessor.php - # class_exists is forbidden to enforce static reflection, but in a compiler pass we want runtime autoloading - - - message: '#Function "class_exists\(\)" cannot be used/left in the code#' - path: src/DependencyInjection/CompilerPass/VerifyRectorServiceExistsCompilerPass.php + # internal reflection + - '#Instead of "new ClassReflection\(\)" use ReflectionProvider service or "\(new PHPStan\\Reflection\\ClassReflection\(\)\)" for static reflection to work#' + - '#Callable callable\(PHPStan\\Type\\Type\)\: PHPStan\\Type\\Type invoked with 2 parameters, 1 required#' - # known values from other methods - - - message: '#Negated boolean expression is always true#' - path: rules/PhpSpecToPHPUnit/NodeFactory/AssertMethodCallFactory.php + # known value + - '#Method (.*?) should return 50200\|50300\|50400\|50500\|50600\|70000\|70100\|70200\|70300\|70400\|80000\|80100\|80200\|80300\|80400\|80500\|80600\|100000 but returns int#' - - message: '#Parameter \#1 \$left of class PhpParser\\Node\\Expr\\BinaryOp\\Spaceship constructor expects PhpParser\\Node\\Expr, PhpParser\\Node\\Expr\|null given#' - path: rules/Php70/Rector/If_/IfToSpaceshipRector.php - - - '#PhpParser\\Node\\Expr\\Error\|PhpParser\\Node\\Expr\\Variable given#' + message: '#Function "class_exists\(\)" cannot be used/left in the code#' + paths: + # autoload check in bin file + - bin/rector.php + # for config class reflection + - src/Bootstrap/ExtensionConfigResolver.php + - src/Validation/RectorConfigValidator.php + # for phpunit version check + - src/Testing/PHPUnit/AbstractLazyTestCase.php + # future node class exists check + - src/Reporting/DeprecatedRulesReporter.php + # use of internal phpstan classes - - message: '#Use `\$class\-\>namespaceName` instead of `\$class\-\>name` that only returns short class name#' - paths: - - rules/CodingStyle/Naming/NameRenamer.php - - packages/NodeNameResolver/NodeNameResolver/ClassNameResolver.php + message: '#Creating new PHPStan\\Reflection\\BetterReflection\\SourceLocator\\Optimized(.*?)SourceLocator is not covered by backward compatibility promise\. The class might change in a minor PHPStan version#' + path: src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php - - message: '#Property with protected modifier is not allowed\. Use interface contract method instead#' - paths: - - rules/Defluent/ValueObject/* + message: '#@\\ini_set\(.*\)" is forbidden to use#' + path: bin/rector.php - - '#Method Rector\\CodeQuality\\Rector\\Foreach_\\SimplifyForeachToCoalescingRector\:\:matchReturnOrAssignNode\(\) should return PhpParser\\Node\\Expr\\Assign\|PhpParser\\Node\\Stmt\\Return_\|null but returns PhpParser\\Node\|null#' + # known existing class + - + message: '#Instead of "instanceof/is_a\(\)" use ReflectionProvider service or "\(new ObjectType\(\)\)\->isSuperTypeOf\(\)" for static reflection to work#' + path: src/Skipper/Skipper/SkipSkipper.php - - '#Instanceof between PhpParser\\Node\\Stmt and Rector\\Core\\PhpParser\\Node\\CustomNode\\FileWithoutNamespace will always evaluate to false#' + # the local instanceof for known types + - '#Instead of "instanceof/is_a\(\)" use ReflectionProvider service or "\(new ObjectType\(\)\)\->isSuperTypeOf\(\)" for static reflection to work#' + # required for reflection - - message: '#Unreachable statement \- code above always terminates#' - paths: - - bin/rector.php - - rules/Php70/Rector/FuncCall/MultiDirnameRector.php + message: '#Function "(.*?)\(\)" cannot be used/left in the code#' + path: src/Util/Reflection/PrivatesAccessor.php - - message: '#Function "class_exists\(\)" cannot be used/left in the code#' - paths: - - src/Bootstrap/ExtensionConfigResolver.php + message: '#Method Rector\\Util\\ArrayParametersMerger\:\:mergeLeftToRightWithCallable\(\) has parameter \$mergeCallback with no signature specified for callable#' + path: src/Util/ArrayParametersMerger.php - # @todo fix later + # fixture Rector rules - - message: '#There should be no empty class#' - paths: - - packages/StaticTypeMapper/ValueObject/Type/*Type.php - - # generics nullable bugs - - '#Method (.*?) should return (.*?)\|null but returns PhpParser\\Node\|null#' - - '#Method (.*?) should return array but returns array#' - - # fixed in php-parser master - - '#Parameter \#4 \$classWithConstants of class Rector\\Privatization\\ValueObject\\ReplaceStringWithClassConstant constructor expects class\-string, string given#' - - # buggy phpstan clas-string - - '#Method (.*?) should return class\-string but returns string#' + identifier: symplify.seeAnnotationToTest + path: tests/Issues/ + # classes are part of *.php.inc fixture - - message: '#\$this as argument is not allowed\. Refactor method to service composition#' + message: '#Class (.*?) not found#' paths: - - src/Rector/AbstractRector.php + - rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/config + - rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/config + - rules-tests/Renaming/Rector/Name/RenameClassRector/config + - rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/config + - rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/config - - message: '#Use defined constant Symplify\\ComposerJsonManipulator\\ValueObject\\ComposerJsonSection\:\:REQUIRE over string require#' - paths: - # is "require" in PHP - - src/Php/ReservedKeywordAnalyzer.php + message: '#Function "(function_exists|dump_node)\(\)" cannot be used/left in the code#' + path: src/functions/node_helper.php - - '#Method Rector\\Core\\PhpParser\\Node\\BetterNodeFinder\:\:findParentType\(\) should return T of PhpParser\\Node\|null but returns class\-string\|T of PhpParser\\Node#' + # stmts aware/expression generics + - '#PhpParser\\Node\\Stmt\\Expression is not generic#' + # chicken/egg - - message: '#"%s" in sprintf\(\) format must be quoted#' - paths: - - packages/BetterPhpDocParser/ValueObject/PhpDoc/VariadicAwareParamTagValueNode.php + message: '#Function "(d|dd)\(\)" cannot be used/left in the code#' + path: tests/debug_functions.php - - '#Property Rector\\Core\\PhpParser\\Node\\AssignAndBinaryMap\:\:\$binaryOpToAssignClasses \(array, class\-string\>\) does not accept array#' + # debug functions + - + message: '#Function "function_exists\(\)" cannot be used/left in the code\: use ReflectionProvider\->has\*\(\) instead#' + path: tests/debug_functions.php + # checks for rector always autoloaded rules only - - message: '#Function "property_exists\(\)" cannot be used/left in the code#' - paths: - # on PhpParser Nodes - - src/NodeManipulator/ClassMethodAssignManipulator.php - - rules/Php80/Rector/If_/NullsafeOperatorRector.php - - packages/NodeTypeResolver/NodeVisitor/FunctionMethodAndClassNodeVisitor.php - - packages/NodeTypeResolver/NodeVisitor/StatementNodeVisitor.php - - packages/NodeNameResolver/NodeNameResolver.php - - packages/NodeNameResolver/NodeNameResolver/ClassNameResolver.php - - packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php + message: '#Function "(class_exists|interface_exists)\(\)" cannot be used/left in the code\: use ReflectionProvider\->has\*\(\) instead#' + path: src/Skipper/SkipCriteriaResolver/SkippedClassResolver.php - # known types - - '#Call to an undefined method PHPStan\\Type\\ConstantType\:\:getValue\(\)#' - - '#Parameter \#1 \$stmts of method Rector\\EarlyReturn\\Rector\\Return_\\PreparedValueToEarlyReturnRector\:\:collectIfs\(\) expects array, array given#' - - '#Access to an undefined property PhpParser\\Node\\FunctionLike\|PhpParser\\Node\\Stmt\\If_\:\:\$stmts#' + # dev rule + - '#Class "Rector\\Utils\\Rector\\MoveAbstractRectorToChildrenRector" is missing @see annotation with test case class reference#' - - - message: '#Parameter \#1 \$types of method Rector\\Defluent\\NodeAnalyzer\\FluentCallStaticTypeResolver\:\:filterOutAlreadyPresentParentClasses\(\) expects array, array given#' - paths: - - rules/Defluent/NodeAnalyzer/FluentCallStaticTypeResolver.php + # optional as changes behavior, should be used explicitly outside PHP upgrade + - '#Register "Rector\\Php73\\Rector\\FuncCall\\JsonThrowOnErrorRector" service to "php73\.php" config set#' + - '#Register "Rector\\Php80\\Rector\\NotIdentical\\MbStrContainsRector" service to "php80\.php" config set#' + - '#Register "Rector\\Php85\\Rector\\StmtsAwareInterface\\SequentialAssignmentsToPipeOperatorRector" service to "php85\.php" config set#' + - '#Register "Rector\\Php85\\Rector\\Expression\\NestedFuncCallsToPipeOperatorRector" service to "php85\.php" config set#' - - '#Cognitive complexity for "Rector\\CodeQuality\\Rector\\Isset_\\IssetOnPropertyObjectToPropertyExistsRector\:\:refactor\(\)" is \d+, keep it under 9#' + # closure detailed + - '#Method Rector\\Config\\RectorConfig\:\:singleton\(\) has parameter \$concrete with no signature specified for Closure#' + # dynamic printer - - message: '#Argument and options "debug" got confused#' - paths: - - src/Console/Command/ProcessCommand.php + message: '#Use explicit names over dynamic ones#' + paths: + - src/CustomRules/SimpleNodeDumper.php + - src/PhpDocParser/PhpDocParser/PhpDocNodeTraverser.php + - src/PhpParser/Printer/BetterStandardPrinter.php - - '#(.*?) class\-string, string given#' + # known node variables + - + message: '#Access to an undefined property PhpParser\\Node\\Scalar\:\:\$value#' + path: src/CustomRules/SimpleNodeDumper.php - # part of refactor() API, node must be returned, or does bring any value - - message: '#Use void instead of modify and return self object#' + message: '#Avoid static access of constants, as they can change value\. Use interface and contract method instead#' paths: - - '*Rector.php' + # caching and message of specific child Rector rules + - src/Rector/AbstractRector.php + - src/PostRector/Rector/AbstractPostRector.php + # for cache + - src/Testing/PHPUnit/AbstractRectorTestCase.php - # PHP 7_4 literal syntax - - '#Property PhpParser\\Node\\Scalar\\DNumber\:\:\$value \(float\|int\) does not accept string#' + # generated class in /vendor + - + message: '#Offset (.*?) on null on left side of \?\? does not exist#' + path: src/Bootstrap/ExtensionConfigResolver.php - - '#Unable to resolve the template type T in call to method Rector\\NodeNestingScope\\ParentFinder\:\:findByTypes\(\)#' + # wider types for external use + - + message: '#Parameters should have "PhpParser\\Node\\Stmt\\ClassMethod" types as the only types passed to this method#' + path: src/Reflection/ClassModifierChecker.php - # mimics original doctrine/annotations parser, improve later when finished + # false positive - should be fixed - - message: '#Array destruct is not allowed\. Use value object to pass data instead#' + message: '#Parameters should have "PhpParser\\Node\\Expr\\Closure" types as the only types passed to this method#' + - + message: '#Parameters should have "PhpParser\\Node\\Stmt\\ClassMethod" types as the only types passed to this method#' paths: - - packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParser.php + - src/VendorLocker/ParentClassMethodTypeOverrideGuard.php + + # more advanced usage, but not always working + # see https://github.com/rectorphp/rector-src/actions/runs/11798721617/job/32865546672?pr=6422#step:5:110 + - '#Doing instanceof PHPStan\\Type\\.+ is error\-prone and deprecated#' + # BC layer on Rector deprecation - - message: '#Use value object over return of values#' paths: - - packages/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParser.php + - src/Configuration/RectorConfigBuilder.php + - src/Reporting/DeprecatedRulesReporter.php + identifier: classConstant.deprecatedInterface + # allowed internally only - - message: '#\$this as argument is not allowed\. Refactor method to service composition#' - paths: - - packages/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php + message: '#Fetching (deprecated )?class constant (.*?) of (deprecated )?class (Rector\\Set\\ValueObject\\DowngradeLevelSetList|Rector\\Symfony\\Set\\(.*?))#' + path: src/Configuration/RectorConfigBuilder.php - - '#Cognitive complexity for "Rector\\BetterPhpDocParser\\PhpDocParser\\DoctrineAnnotationDecorator\:\:mergeNestedDoctrineAnnotations\(\)" is \d+, keep it under 9#' + # runtime comparison + - '#Comparison operation ".*" between int<\d+, \d+> and \d+ is always true#' - - '#Cognitive complexity for "Rector\\BetterPhpDocParser\\Printer\\PhpDocInfoPrinter\:\:printDocChildNode\(\)" is \d+, keep it under 9#' - - '#Cognitive complexity for "Rector\\NodeTypeResolver\\NodeTypeResolver\:\:resolve\(\)" is \d+, keep it under 9#' + # from mapper interface + - '#mapToPhpParserNode\(\) never returns PhpParser\\.* so it can be removed from the return#' - - - message: '#Property with protected modifier is not allowed\. Use interface contract method instead#' - path: 'packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php' + # from constant string + - '#Method Rector\\.*::filterPolyfillPackages\(\) should return array\<#' - - message: '#Do not use setter on a service#' + identifier: typePerfect.noMixedMethodCaller paths: - - packages/NodeTypeResolver/PhpDocNodeVisitor/*PhpDocNodeVisitor.php + - src/PhpParser/Parser/RectorParser.php - # known type - - '#Parameter \#3 \$pseudoNamespaceToNamespace of method Rector\\NodeTypeResolver\\PhpDocNodeVisitor\\UnderscoreRenamePhpDocNodeVisitor\:\:shouldSkip\(\) expects Rector\\Renaming\\ValueObject\\PseudoNamespaceToNamespace, Rector\\Renaming\\ValueObject\\PseudoNamespaceToNamespace\|null given#' + - + path: src/NodeTypeResolver/PHPStan/Type/TypeFactory.php + message: '#Method Rector\\NodeTypeResolver\\PHPStan\\Type\\TypeFactory\:\:uniquateTypes\(\) should return array but returns list#' # known types - - '#Parameter (.*?) expects PhpParser\\Node, PhpParser\\Node\|null given#' - - - message: '#Class cognitive complexity is \d+, keep it under 50#' - paths: - - packages/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php - - - '#Cognitive complexity for "Rector\\PHPStanStaticTypeMapper\\TypeMapper\\UnionTypeMapper\:\:mapToPhpParserNode\(\)" is 10, keep it under 9#' + message: '#PHPDoc tag @var with type array is not subtype of native type array>#' + path: rules/Naming/PhpArray/ArrayFilter.php - - message: '#Property with protected modifier is not allowed\. Use interface contract method instead#' - paths: - - src/Rector/AbstractRector.php - - - '#Cannot call method getSmartFileInfo\(\) on Rector\\Core\\ValueObject\\Application\\File\|null#' - - # doc typo in php-parser - - '#Parameter \#2 \$args of class PhpParser\\Node\\Expr\\FuncCall constructor expects array, array given#' + message: '#PHPDoc tag @var with type int is not subtype of native type array\|PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprNode\|Rector\\BetterPhpDocParser\\PhpDoc\\DoctrineAnnotationTagValueNode\|Rector\\BetterPhpDocParser\\PhpDoc\\StringNode\|Rector\\BetterPhpDocParser\\ValueObject\\PhpDoc\\DoctrineAnnotation\\CurlyListNode\|string#' + path: src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php - # php-parser bug - - '#Parameter \#1 \$type of method PhpParser\\Builder\\FunctionLike\:\:setReturnType\(\) expects PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|string, PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType given#' + - '#Parameter \#1 \$phpVersion of method Rector\\Config\\RectorConfig\:\:phpVersion\(\) expects 50200\|50300\|50400\|50500\|50600\|70000\|70100\|70200\|70300\|70400\|80000\|80100\|80200\|80300\|80400\|80500\|80600\|100000, 79999 given#' - - '#Method Rector\\Core\\Tests\\DependencyInjection\\ConfigurableRectorImportConfigCallsMergeTest\:\:provideData\(\) return type has no value type specified in iterable type Iterator#' + # node vs stmts mix + - '#expects array, array given#' + - '#should return non\-empty\-string but returns string#' + # known non-empty class method - - message: '#Use value object over return of values#' - paths: - - rules/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector.php + message: '#Offset 0 might not exist on array\|null#' + path: rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector.php - - - message: '#Function "function_exists\(\)" cannot be used/left in the code#' - paths: - - src/functions/node_helper.php - - packages/ReadWrite/Guard/VariableToConstantGuard.php - - packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php - - # upgrade to PHP 7.4 wip - - '#This property type might be inlined to PHP\. Do you have confidence it is correct\? Put it here#' + # false positive, can accept non-class string + - '#Parameter \#1 \$name of method PHPStan\\BetterReflection\\Reflection\\Adapter\\ReflectionClass\:\:getAttributes\(\) expects class\-string\|null, string given#' - # special case - cleanup in the future + # false positive, checked above - - message: '#File processor must require Rector rules in constructor via TypeRectorInterface\[\] \$typeRectors array autowire#' - path: src/Application/FileProcessor/PhpFileProcessor.php + path: rules/Php71/Rector/List_/ListToArrayDestructRector.php + message: '#Parameter \#1 \$items of class PhpParser\\Node\\Expr\\Array_ constructor expects array, array given#' + - + path: rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php + message: '#Parameter \#3 \$assign of method Rector\\CodeQuality\\Rector\\FunctionLike\\SimplifyUselessVariableRector\:\:processSimplifyUselessVariable\(\) expects PhpParser\\Node\\Expr\\Assign\|PhpParser\\Node\\Expr\\AssignOp, PhpParser\\Node\\Expr given#' - - '#Use required typed property over of nullable property#' - - '#Method Rector\\BetterPhpDocParser\\PhpDocParser\\BetterPhpDocParser\:\:parseChildAndStoreItsPositions\(\) should return PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagNode\|PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTextNode but returns PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocChildNode#' + # scope vs mutating scope + - '#Parameter \#3 \$nodeCallback of method PHPStan\\Analyser\\NodeScopeResolver\:\:processNodes\(\) expects callable\(PhpParser\\Node, PHPStan\\Analyser\\Scope\)\: void, callable\(PhpParser\\Node, PHPStan\\Analyser\\MutatingScope\)\: void given#' + # list vs array + - '#Parameter (.*?) expects list<(.*?)>, array<(.*?)> given#' - - '#Cognitive complexity for "Rector\\Core\\Rector\\AbstractRector\:\:enterNode\(\)" is \d+, keep it under 9#' + - identifier: symplify.noConstructorOverride + path: src/StaticTypeMapper/ValueObject/Type/SimpleStaticType.php - - message: '#Class cognitive complexity is 33, keep it under 30#' - path: src/Rector/AbstractRector.php + identifier: arrayValues.list + path: rules/CodingStyle/Application/UseImportsAdder.php - # deserves better architecture - - message: '#Do not use chained method calls#' - path: packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php + message: '#Offset float\|int\|string might not exist on string#' + path: rules/Php70/EregToPcreTransformer.php - - message: '#Function "dump_node\(\)" cannot be used/left in the code#' - path: src/functions/node_helper.php + identifier: symplify.noReference + message: '#Use explicit return value over magic &reference#' + # false positive - - message: '#Use separate function calls with readable variable names#' - path: 'src/FileSystem/PhpFilesFinder.php' + identifier: offsetAccess.invalidOffset + path: src/CustomRules/SimpleNodeDumper.php - # union false positive - - '#Method Rector\\Comments\\CommentRemover\:\:removeFromNode\(\) has parameter \$node with no value type specified in iterable type array#' - - # should be refactored to collector - - '#Cognitive complexity for "Rector\\CodingStyle\\Naming\\NameRenamer\:\:renameNameNode\(\)" is 10, keep it under 9#' + - '#Method Rector\\Utils\\Rector\\RemoveRefactorDuplicatedNodeInstanceCheckRector\:\:getInstanceofNodeClass\(\) should return class\-string\|null but returns class\-string#' + # copied from /vendor, to keep as original as possible - - message: '#Class with base "StaticTypeMapper" name is already used in "Rector\\PHPStanStaticTypeMapper\\TypeMapper\\StaticTypeMapper", "Rector\\StaticTypeMapper\\StaticTypeMapper"\. Use unique name to make classes easy to recognize#' - path: packages/StaticTypeMapper/StaticTypeMapper.php #31 - - # false positive - - '#Attribute class JetBrains\\PhpStorm\\Immutable does not exist#' - - # generics single place - - '#Method Rector\\Php80\\NodeResolver\\ArgumentSorter\:\:sortArgsByExpectedParamOrder\(\) should return array but returns array#' - - - '#Class with base "StaticTypeMapper" name is already used in "Rector\\StaticTypeMapper\\StaticTypeMapper", "Rector\\PHPStanStaticTypeMapper\\TypeMapper\\StaticTypeMapper"\. Use unique name to make classes easy to recognize#' - - '#Class with base "UnionTypeMapper" name is already used in "Rector\\StaticTypeMapper\\PhpDocParser\\UnionTypeMapper", "Rector\\PHPStanStaticTypeMapper\\TypeMapper\\UnionTypeMapper"\. Use unique name to make classes easy to recognize#' - - # allowed for ease api + identifier: symplify.noDynamicName + path: src/PhpParser/NodeTraverser/RectorNodeTraverser.php - - message: '#Cannot return include_once/require_once#' - path: rules/Renaming/Rector/Name/RenameClassRector.php + identifier: offsetAccess.nonArray + path: src/PhpParser/NodeTraverser/RectorNodeTraverser.php - # required assigns - parent looping + # false positive - - message: '#foreach\(\.\.\.\), while\(\), for\(\) or if\(\.\.\.\) cannot contains a complex expression\. Extract it to a new variable assign on line before#' + message: '#Method (.*?)refactor\(\) (never returns|should return) (.*?)#' paths: - - packages/NodeNestingScope/FlowOfControlLocator.php - - src/PhpParser/Node/BetterNodeFinder.php - - rules/Php70/Rector/FuncCall/MultiDirnameRector.php - - src/Application/FileProcessor.php - - rules/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector.php - - rules/CodeQuality/Rector/Return_/SimplifyUselessVariableRector.php - - # class-string miss match - - '#Parameter \#1 \$classes of method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:getByAnnotationClasses\(\) expects array, array given#' - - '#Parameter \#1 \$classes of method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:getByAnnotationClasses\(\) expects array, array given#' - - - '#expects class\-string, string given#' - # weird generics - - '#Method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:getByType\(\) should return array but returns array#' + - rules/Php70/Rector/If_/IfToSpaceshipRector.php - # should be allowed, as continue check - fix in symplify - - message: '#foreach\(\.\.\.\), while\(\), for\(\) or if\(\.\.\.\) cannot contains a complex expression\. Extract it to a new variable assign on line before#' + identifier: rector.noIntegerRefactorReturn paths: - - packages/NodeNestingScope/ParentFinder.php - - - '#Callable callable\(PHPStan\\Type\\Type\)\: PHPStan\\Type\\Type invoked with 2 parameters, 1 required#' - - '#Method Rector\\NodeNestingScope\\ParentFinder\:\:findByTypes\(\) should return T of PhpParser\\Node\|null but returns class\-string\|T of PhpParser\\Node#' - - # array_index on generic types - - '#Method Rector\\NodeTypeResolver\\PHPStan\\Type\\TypeFactory\:\:uniquateTypes\(\) should return array but returns array#' - - # complex - - '#Cognitive complexity for "Rector\\NodeNameResolver\\NodeNameResolver\:\:getName\(\)" is 10, keep it under 9#' - - # resolve later - - '#Method "(.*?)\(\)" only calling another method call and has no added value\. Use the inlined call instead#' - - '#Method "processRenameVariable\(\)" returns bool type, so the name should start with is/has/was#' - - '#Method "refactorParamType\(\)" returns bool type, so the name should start with is/has/was#' - - '#Method "decorateReturnWithSpecificType\(\)" returns bool type, so the name should start with is/has/was#' - - '#Method "resolveObjectType\(\)" returns bool type, so the name should start with is/has/was#' + # valid, as use REMOVE_NODE + - rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php + - rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php + - rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php + - rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php + # deprecated - - message: '#Use dependency injection instead of dependency juggling#' + identifier: public.method.unused paths: - - packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider + - rules/Transform/ValueObject/ClassMethodReference.php + - rules/CodeQuality/ValueObject/KeyAndExpr.php + - + identifier: symplify.forbiddenExtendOfNonAbstractClass + path: rules/Php81/Rector/Array_/FirstClassCallableRector.php - # native filesystem calls, required for performance reasons - - message: '#"@\\unlink\(\$tmpPath\)" is forbidden to use#' + identifier: public.classConstant.unused paths: - - packages/Caching/ValueObject/Storage/FileCacheStorage.php + - rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php + - rules/Php55/Rector/String_/StringClassNameToClassConstantRector.php + - rules/Php81/Enum/AttributeName.php + - - message: '#"@\\rename\(\$tmpPath, \$path\)" is forbidden to use#' + identifier: symplify.seeAnnotationToTest paths: - - packages/Caching/ValueObject/Storage/FileCacheStorage.php + - tests/PhpParser/NodeTraverser/StopTraverseOnTypeChange/Class_ + + # deprecated rule + - '#Rule Rector\\Php81\\Rector\\Array_\\FirstClassCallableRector must implements Rector\\VersionBonding\\Contract\\MinPhpVersionInterface#' + - '#Register "Rector\\Php81\\Rector\\Array_\\FirstClassCallableRector" service to "php81\.php" config set#' + - '#Register "Rector\\Php81\\Rector\\ClassMethod\\NewInInitializerRector" service to "php81\.php" config set#' + - '#Class "Rector\\CodingStyle\\Rector\\String_\\SymplifyQuoteEscapeRector" is missing @see annotation with test case class reference#' + - '#Class "Rector\\Php81\\Rector\\ClassMethod\\NewInInitializerRector" is missing @see annotation with test case class reference#' + + # BC layer for FileWithoutNamespace node - - message: '#"%s" in sprintf\(\) format must be quoted#' - paths: - - packages/Caching/ValueObject/Storage/FileCacheStorage.php + path: src/PhpParser/Node/CustomNode/FileWithoutNamespace.php + identifier: symplify.forbiddenExtendOfNonAbstractClass + + - message: '#Use @see \\Rector\\PhpParser\\Node\\FileNode instead#' + + # false positive - - message: '#"@\\var_export\(new \\Rector\\Caching\\ValueObject\\CacheItem\(\$variableKey, \$data\), true\)" is forbidden to use#' - paths: - - packages/Caching/ValueObject/Storage/FileCacheStorage.php + identifier: phpstanApi.varTagAssumption + path: rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php + # @todo fix in phpstan-rules - - message: '#Arguments names conflicts with parent class method\: "\$(.*?)" should be "\$(.*?)"\. This will break named arguments#' - paths: - - src/PhpParser/Printer/BetterStandardPrinter.php - - packages/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php - - packages/BetterPhpDocParser/PhpDocParser/BetterTypeParser.php + identifier: symplify.noMissnamedDocTag + path: rules/TypeDeclarationDocblocks/TypeResolver/ConstantArrayTypeGeneralizer.php - # resolve later - - '#Variables "\$(.*?)" are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#' + - + identifier: typePerfect.narrowReturnObjectType + path: src/PHPStan/ScopeFetcher.php - - message: '#Instead of array shape, use value object with specific types in constructor and getters#' - path: src/NodeFactory/ClassWithPublicPropertiesFactory.php #26 + identifier: varTag.nativeType + path: src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php - - message: '#Instead of abstract class, use specific service with composition#' + identifier: argument.type paths: - - packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php - - packages/PostRector/Rector/AbstractPostRector.php - - rules/Defluent/ValueObject/AbstractRootExpr.php - - rules/PhpSpecToPHPUnit/Rector/AbstractPhpSpecToPHPUnitRector.php - - src/Rector/AbstractRector.php + - rules/Privatization/TypeManipulator/TypeNormalizer.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php + - rules/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector.php + - src/NodeTypeResolver/PHPStan/Type/TypeFactory.php - - message: '#This call has duplicate argument#' + identifier: argument.templateType paths: - - rules/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php + - src/NodeTypeResolver/PHPStan/Type/TypeFactory.php - - message: '#foreach\(\), while\(\), for\(\) or if\(\) cannot contain a complex expression\. Extract it to a new variable on a line before#' - paths: - - packages/NodeNestingScope/FlowOfControlLocator.php - - packages/NodeNestingScope/ParentFinder.php - - rules/CodeQuality/Rector/Return_/SimplifyUselessVariableRector.php - - rules/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector.php - - rules/Php70/Rector/FuncCall/MultiDirnameRector.php - - src/Application/FileProcessor.php - - src/PhpParser/Node/BetterNodeFinder.php + message: '#Parameter \#1 \$value of static method Webmozart\\Assert\\Assert\:\:isInstanceOfAny\(\) expects object, PhpParser\\Node\\ComplexType\|PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|null given#' + path: rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php - - message: '#Parameter \#1 \$separator of function explode expects non\-empty\-string, string given#' - paths: - - rules/PSR4/FileRelocationResolver.php - - rules/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector.php - - rules/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector.php + message: '#Unable to resolve the template type T in call to static method Webmozart\\Assert\\Assert\:\:isInstanceOfAny\(\)#' + path: rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php + + - '#Provide more specific return type "Iterator|PhpParser\\Node" over abstract one#' + # BC layer - - message: '#Parameter \#2 \$length of function str_split expects int<1, max\>, int given#' - paths: - - rules/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector.php + message: '#Access to deprecated property \$file of class Rector\\Rector\\AbstractRector#' + path: src/Rector/AbstractRector.php diff --git a/phpunit.xml b/phpunit.xml index e00a8e3d5a7..deaec9b43a2 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -5,19 +5,20 @@ bootstrap="tests/bootstrap.php" colors="true" executionOrder="defects" + defaultTestSuite="main" + displayDetailsOnTestsThatTriggerWarnings="true" + displayDetailsOnPhpunitDeprecations="true" > tests rules-tests - packages-tests - utils/*/tests + utils-tests + rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Source/SomeAbstractTest.php - - - + diff --git a/preload-split-package.php b/preload-split-package.php new file mode 100644 index 00000000000..da953d5a7e5 --- /dev/null +++ b/preload-split-package.php @@ -0,0 +1,380 @@ +import(SetList::CODING_STYLE); - $containerConfigurator->import(SetList::CODING_STYLE_ADVANCED); - $containerConfigurator->import(SetList::CODE_QUALITY); - $containerConfigurator->import(SetList::DEAD_CODE); - $containerConfigurator->import(SetList::PRIVATIZATION); - $containerConfigurator->import(SetList::NAMING); - $containerConfigurator->import(SetList::TYPE_DECLARATION); - $containerConfigurator->import(SetList::PHP_71); - $containerConfigurator->import(SetList::PHP_72); - $containerConfigurator->import(SetList::PHP_73); - $containerConfigurator->import(SetList::PHP_74); - $containerConfigurator->import(SetList::PHP_80); - $containerConfigurator->import(SetList::EARLY_RETURN); - $containerConfigurator->import(SetList::TYPE_DECLARATION_STRICT); - $containerConfigurator->import(NetteSetList::NETTE_UTILS_CODE_QUALITY); - $containerConfigurator->import(PHPUnitSetList::PHPUNIT_CODE_QUALITY); - - $configuration = ValueObjectInliner::inline([ - new InferParamFromClassMethodReturn(AbstractRector::class, 'refactor', 'getNodeTypes'), - ]); - - $services = $containerConfigurator->services(); - $services->set(InferParamFromClassMethodReturnRector::class) - ->call('configure', [[ - InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => $configuration, - ]]); - - // phpunit - $services->set(PreferThisOrSelfMethodCallRector::class) - ->call('configure', [[ - PreferThisOrSelfMethodCallRector::TYPE_TO_PREFERENCE => [ - TestCase::class => ValueObjectInliner::inline(PreferenceSelfThis::PREFER_THIS()), - ], - ]]); - - $services->set(ReturnArrayClassMethodToYieldRector::class) - ->call('configure', [[ - ReturnArrayClassMethodToYieldRector::METHODS_TO_YIELDS => ValueObjectInliner::inline([ - new ReturnArrayClassMethodToYield('PHPUnit\Framework\TestCase', '*provide*'), - ]), - ]]); - - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::PATHS, [ +use Rector\PHPUnit\CodeQuality\Rector\Class_\AddSeeTestAnnotationRector; +use Rector\Utils\Rector\RemoveRefactorDuplicatedNodeInstanceCheckRector; + +return RectorConfig::configure() + ->withPreparedSets( + deadCode: true, + codeQuality: true, + codingStyle: true, + typeDeclarations: true, + typeDeclarationDocblocks: true, + privatization: true, + naming: true, + instanceOf: true, + earlyReturn: true, + rectorPreset: true, + phpunitCodeQuality: true + ) + ->withAttributesSets() + ->withComposerBased(phpunit: true) + ->withPhpSets() + ->withPaths([ + __DIR__ . '/bin', + __DIR__ . '/config', __DIR__ . '/src', __DIR__ . '/rules', __DIR__ . '/rules-tests', - __DIR__ . '/packages', - __DIR__ . '/packages-tests', __DIR__ . '/tests', __DIR__ . '/utils', - __DIR__ . '/config', - __DIR__ . '/scoper.php', - ]); - - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $parameters->set(Option::SKIP, [ + __DIR__ . '/scripts', + __DIR__ . '/build/build-preload.php', + ]) + ->withRootFiles() + ->withImportNames(removeUnusedImports: true) + ->withRules([RemoveRefactorDuplicatedNodeInstanceCheckRector::class, AddSeeTestAnnotationRector::class]) + ->withSkip([ StringClassNameToClassConstantRector::class, - // some classes in config might not exist without dev dependencies - SplitStringClassConstantToClassConstFetchRector::class, - - // test paths - '*/Fixture/*', - '*/Fixture*/*', - '*/Source/*', - '*/Source*/*', + // tests + '*/Fixture*', + '*/Source*', '*/Expected/*', - '*/Expected*/*', - // to keep original API from PHPStan untouched - __DIR__ . '/packages/Caching/ValueObject/Storage/FileCacheStorage.php', - ]); + // keep configs untouched, as the classes are just strings + UseClassKeywordForClassNameResolutionRector::class => [__DIR__ . '/config', '*/config/*'], - $parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, __DIR__ . '/phpstan-for-rector.neon'); -}; + RemovePhpVersionIdCheckRector::class => [ + __DIR__ . '/src/Util/FileHasher.php', + __DIR__ . '/src/Configuration/RectorConfigBuilder.php', + __DIR__ . '/src/Console/Notifier.php', + ], + ]); diff --git a/rule-doc-generator.php b/rule-doc-generator.php deleted file mode 100644 index 18dddca93c0..00000000000 --- a/rule-doc-generator.php +++ /dev/null @@ -1,15 +0,0 @@ -services(); - - $services->defaults() - ->autowire(); - - $services->set(RectorCategoryInferer::class); -}; diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/ArgumentAdderRectorTest.php b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/ArgumentAdderRectorTest.php index 8a9a0bf9464..e2339264728 100644 --- a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/ArgumentAdderRectorTest.php +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/ArgumentAdderRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ArgumentAdderRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/argument_without_type_or_default_value.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/argument_without_type_or_default_value.php.inc new file mode 100644 index 00000000000..ed30e3c2695 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/argument_without_type_or_default_value.php.inc @@ -0,0 +1,57 @@ + +----- + diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/by_pass_2nd_arg_value.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/by_pass_2nd_arg_value.php.inc new file mode 100644 index 00000000000..f2d5254c2d3 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/by_pass_2nd_arg_value.php.inc @@ -0,0 +1,33 @@ +thirdArgument(1); + } +} + +?> +----- +thirdArgument(1, 2, 6); + } +} + +?> diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/named_arguments.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/named_arguments.php.inc new file mode 100644 index 00000000000..0713619f103 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/named_arguments.php.inc @@ -0,0 +1,59 @@ +firstArgument(b: 1); + $containerBuilder->firstArgument(c: 1); + $containerBuilder->firstArgument(b: 1, c: 2); + $containerBuilder->firstArgument(c: 1, b: 2); + $containerBuilder->secondArgument(a: 1); + $containerBuilder->secondArgument(c: 1); + $containerBuilder->secondArgument(a: 1, c: 2); + $containerBuilder->secondArgument(c: 1, a: 2); + $containerBuilder->secondArgument(1, c: 2); + $containerBuilder->thirdArgument(a: 1); + $containerBuilder->thirdArgument(b: 1); + $containerBuilder->thirdArgument(a: 1, b: 2); + $containerBuilder->thirdArgument(b: 1, a: 2); + $containerBuilder->thirdArgument(1, b: 2); + } +} + +?> +----- +firstArgument(b: 1, a: 4); + $containerBuilder->firstArgument(c: 1, a: 4); + $containerBuilder->firstArgument(b: 1, c: 2, a: 4); + $containerBuilder->firstArgument(c: 1, b: 2, a: 4); + $containerBuilder->secondArgument(a: 1, b: 5); + $containerBuilder->secondArgument(c: 1, b: 5); + $containerBuilder->secondArgument(a: 1, c: 2, b: 5); + $containerBuilder->secondArgument(c: 1, a: 2, b: 5); + $containerBuilder->secondArgument(1, c: 2, b: 5); + $containerBuilder->thirdArgument(a: 1, c: 6); + $containerBuilder->thirdArgument(b: 1, c: 6); + $containerBuilder->thirdArgument(a: 1, b: 2, c: 6); + $containerBuilder->thirdArgument(b: 1, a: 2, c: 6); + $containerBuilder->thirdArgument(1, b: 2, c: 6); + } +} + +?> diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_add_with_default_value.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_add_with_default_value.php.inc new file mode 100644 index 00000000000..82f065b3021 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_add_with_default_value.php.inc @@ -0,0 +1,14 @@ +someMethod(); + } +} diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..bc8eb626ec3 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,14 @@ +addCompilerPass(...); + } +} diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_mixed_scope.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_mixed_scope.php.inc index 085e010f4cc..5f3134ac012 100644 --- a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_mixed_scope.php.inc +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_mixed_scope.php.inc @@ -2,14 +2,6 @@ namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture; -class Value -{ - public function doSomething() - { - - } -} - class SomeProperty { public $value; diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_named_arguments.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_named_arguments.inc new file mode 100644 index 00000000000..e4bbac86b95 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_named_arguments.inc @@ -0,0 +1,21 @@ +firstArgument(a: 7); + $containerBuilder->firstArgument(1, b: 1); + $containerBuilder->secondArgument(a: 7, b: 8); + $containerBuilder->secondArgument(b: 7, a: 8); + $containerBuilder->secondArgument(1, 2, c: 8); + $containerBuilder->thirdArgument(c: 8); + $containerBuilder->thirdArgument(b: 7, c: 8, a: 9); + $containerBuilder->thirdArgument(1, b: 8, c: 9); + } +} diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_static_named_arguments.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_static_named_arguments.php.inc new file mode 100644 index 00000000000..f813f873d2a --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/skip_static_named_arguments.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/without_default_value.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/without_default_value.php.inc new file mode 100644 index 00000000000..2ffdf51d4f5 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Fixture/without_default_value.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Source/SomeClass.php b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Source/SomeClass.php new file mode 100644 index 00000000000..e341b1b5a77 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ArgumentAdderRector/Source/SomeClass.php @@ -0,0 +1,11 @@ +services(); - $services->set(ArgumentAdderRector::class) - ->call('configure', [[ - ArgumentAdderRector::ADDED_ARGUMENTS => ValueObjectInliner::inline([ - // covers https://github.com/rectorphp/rector/issues/4267 - new ArgumentAdder( - SomeContainerBuilder::class, - 'sendResetLinkResponse', - 0, - 'request', - null, - 'Illuminate\Http\Illuminate\Http' - ), - new ArgumentAdder(SomeContainerBuilder::class, 'compile', 0, 'isCompiled', false), - new ArgumentAdder(SomeContainerBuilder::class, 'addCompilerPass', 2, 'priority', 0, 'int'), - // scoped - new ArgumentAdder( - SomeParentClient::class, - 'submit', - 2, - 'serverParameters', - [], - 'array', - ArgumentAddingScope::SCOPE_PARENT_CALL - ), - new ArgumentAdder( - SomeParentClient::class, - 'submit', - 2, - 'serverParameters', - [], - 'array', - ArgumentAddingScope::SCOPE_CLASS_METHOD - ), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $arrayType = new ArrayType(new MixedType(), new MixedType()); + + $rectorConfig + ->ruleWithConfiguration(ArgumentAdderRector::class, [ + // covers https://github.com/rectorphp/rector/issues/4267 + new ArgumentAdder( + SomeContainerBuilder::class, + 'sendResetLinkResponse', + 0, + 'request', + null, + new ObjectType('Illuminate\Http\Illuminate\Http') + ), + new ArgumentAdder(SomeContainerBuilder::class, 'compile', 0, 'isCompiled', false), + new ArgumentAdder(SomeContainerBuilder::class, 'addCompilerPass', 2, 'priority', 0, new IntegerType()), + // scoped + new ArgumentAdder( + SomeParentClient::class, + 'submit', + 2, + 'serverParameters', + [], + $arrayType, + ArgumentAddingScope::SCOPE_PARENT_CALL + ), + new ArgumentAdder( + SomeParentClient::class, + 'submit', + 2, + 'serverParameters', + [], + $arrayType, + ArgumentAddingScope::SCOPE_CLASS_METHOD + ), + new ArgumentAdder(SomeClass::class, 'withoutTypeOrDefaultValue', 0, 'arguments', [], $arrayType), + new ArgumentAdder(SomeMultiArg::class, 'firstArgument', 0, 'a', 4), + new ArgumentAdder(SomeMultiArg::class, 'secondArgument', 1, 'b', 5), + new ArgumentAdder(SomeMultiArg::class, 'thirdArgument', 2, 'c', 6), + new ArgumentAdder(SomeClass::class, 'someMethod', 0, 'default', 1), + new ArgumentAdderWithoutDefaultValue(WithoutDefaultValue::class, 'someMethod', 0, 'foo', $arrayType), + ]); }; diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture.php.inc deleted file mode 100644 index 1169a171bb1..00000000000 --- a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -register('foo', 'stdClass') - ->setScope(ContainerBuilder::SCOPE_PROTOTYPE); -} - -?> ------ -register('foo', 'stdClass') - ->setScope(false); -} - -?> diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture2.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture2.php.inc index 6e32687ba6f..cee22ca907b 100644 --- a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture2.php.inc +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture2.php.inc @@ -4,12 +4,11 @@ namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueR use Symfony\Component\Yaml\Yaml; -function argumentDefaultValue2() -{ - Yaml::parse('...', true); - Yaml::parse('...', false); - Yaml::parse('...', false, true); - Yaml::parse('...', false, false, true); +class Fixture2 { + function argumentDefaultValue2() + { + Yaml::parse('...', false, false, true); + } } ?> @@ -20,12 +19,11 @@ namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueR use Symfony\Component\Yaml\Yaml; -function argumentDefaultValue2() -{ - Yaml::parse('...', \Symfony\Component\Yaml\Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE); - Yaml::parse('...', 0); - Yaml::parse('...', \Symfony\Component\Yaml\Yaml::PARSE_OBJECT); - Yaml::parse('...', \Symfony\Component\Yaml\Yaml::PARSE_OBJECT_FOR_MAP); +class Fixture2 { + function argumentDefaultValue2() + { + Yaml::parse('...', \Symfony\Component\Yaml\Yaml::PARSE_OBJECT_FOR_MAP); + } } ?> diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture3.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture3.php.inc deleted file mode 100644 index e39c66e0809..00000000000 --- a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - new A(), 'bar' => 1), 0, 0, true); - - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true); -} - -?> ------ - new A(), 'bar' => 1), 0, 0, \Symfony\Component\Yaml\Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE); - - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, \Symfony\Component\Yaml\Yaml::DUMP_OBJECT); -} - -?> diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/from_other_base_class.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/from_other_base_class.php.inc new file mode 100644 index 00000000000..489db75f211 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/from_other_base_class.php.inc @@ -0,0 +1,31 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/param_with_any_values.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/param_with_any_values.php.inc new file mode 100644 index 00000000000..d46373579d6 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/param_with_any_values.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/param_with_null.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/param_with_null.php.inc new file mode 100644 index 00000000000..e02f23d0e98 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/param_with_null.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_in_constructor.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_in_constructor.php.inc new file mode 100644 index 00000000000..769eb07edd0 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_in_constructor.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_method_argument_with_constant.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_method_argument_with_constant.php.inc new file mode 100644 index 00000000000..20f1907efa2 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_method_argument_with_constant.php.inc @@ -0,0 +1,36 @@ +setSomeMethod('some value'); +$object->setSomeMethod('some extra value'); + +?> + +----- +setSomeMethod(\SomeClass::SOME_CONSTANT); +$object->setSomeMethod(\SomeClass::SOME_EXTRA_CONSTANT); + +?> diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_multiple_constructors.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_multiple_constructors.php.inc new file mode 100644 index 00000000000..d329a65de03 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_multiple_constructors.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_named_arguments.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_named_arguments.php.inc new file mode 100644 index 00000000000..23c42ec1c40 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/replace_named_arguments.php.inc @@ -0,0 +1,32 @@ +firstArgument(0, b: 1); +$object->firstArgument(a: 0); +$object->firstArgument(b: 1, a: 0); +$object->secondArgument(0, b: 1); +$object->secondArgument(b: 1); +$object->secondArgument(b: 1, a: 0); + +?> + +----- +firstArgument(3, b: 1); +$object->firstArgument(a: 3); +$object->firstArgument(b: 1, a: 3); +$object->secondArgument(0, b: 4); +$object->secondArgument(b: 4); +$object->secondArgument(b: 4, a: 0); + +?> diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/skip_named_arguments.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/skip_named_arguments.php.inc new file mode 100644 index 00000000000..e91280a69d3 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/skip_named_arguments.php.inc @@ -0,0 +1,13 @@ +firstArgument(b: 1); +$object->secondArgument(a: 1); +$object->secondArgument(0, c: 2); +Yaml::parse('...', flags: false, other: false, another: true); diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/skip_self.php.inc b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/skip_self.php.inc new file mode 100644 index 00000000000..5bdd4e5aac9 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Fixture/skip_self.php.inc @@ -0,0 +1,13 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Source/SomeClassWithAnyDefaultValue.php b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Source/SomeClassWithAnyDefaultValue.php new file mode 100644 index 00000000000..0af454147a8 --- /dev/null +++ b/rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/Source/SomeClassWithAnyDefaultValue.php @@ -0,0 +1,8 @@ +services(); - $services->set(ReplaceArgumentDefaultValueRector::class) - ->call('configure', [[ - ReplaceArgumentDefaultValueRector::REPLACED_ARGUMENTS => ValueObjectInliner::inline([ +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(ReplaceArgumentDefaultValueRector::class, [ + // special case for constructor + new ReplaceArgumentDefaultValue( + ReplaceInConstructor::class, + MethodName::CONSTRUCT, + 0, + 'some value', + 'SomeClass::SOME_CONSTANT' + ), + new ReplaceArgumentDefaultValue( + ReplaceInConstructor::class, + MethodName::CONSTRUCT, + 0, + 'some extra value', + 'SomeClass::SOME_EXTRA_CONSTANT' + ), - new ReplaceArgumentDefaultValue( - 'Symfony\Component\DependencyInjection\Definition', - 'setScope', - 0, - 'Symfony\Component\DependencyInjection\ContainerBuilder::SCOPE_PROTOTYPE', - false - ), - new ReplaceArgumentDefaultValue('Symfony\Component\Yaml\Yaml', 'parse', 1, [ - false, - false, - true, - ], 'Symfony\Component\Yaml\Yaml::PARSE_OBJECT_FOR_MAP'), - new ReplaceArgumentDefaultValue('Symfony\Component\Yaml\Yaml', 'parse', 1, [ - false, - true, - ], 'Symfony\Component\Yaml\Yaml::PARSE_OBJECT'), - new ReplaceArgumentDefaultValue('Symfony\Component\Yaml\Yaml', 'parse', 1, false, 0), - new ReplaceArgumentDefaultValue( - 'Symfony\Component\Yaml\Yaml', - 'parse', - 1, - true, - 'Symfony\Component\Yaml\Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE' - ), - new ReplaceArgumentDefaultValue('Symfony\Component\Yaml\Yaml', 'dump', 3, [ - false, - true, - ], 'Symfony\Component\Yaml\Yaml::DUMP_OBJECT'), - new ReplaceArgumentDefaultValue( - 'Symfony\Component\Yaml\Yaml', - 'dump', - 3, - true, - 'Symfony\Component\Yaml\Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE' - ), + new ReplaceArgumentDefaultValue( + ReplaceMethodArgumentWithConstant::class, + 'setSomeMethod', + 0, + 'some value', + 'SomeClass::SOME_CONSTANT' + ), + new ReplaceArgumentDefaultValue( + ReplaceMethodArgumentWithConstant::class, + 'setSomeMethod', + 0, + 'some extra value', + 'SomeClass::SOME_EXTRA_CONSTANT' + ), - ]), - ]]); + new ReplaceArgumentDefaultValue('Symfony\Component\Yaml\Yaml', 'parse', 1, [ + false, + false, + true, + ], 'Symfony\Component\Yaml\Yaml::PARSE_OBJECT_FOR_MAP'), + + new ReplaceArgumentDefaultValue( + 'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeClassWithAnyDefaultValue', + 'someMethod', + 0, + ReplaceArgumentDefaultValue::ANY_VALUE_BEFORE, + [] + ), + new ReplaceArgumentDefaultValue( + 'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeClassWithAnyDefaultValue', + 'paramWithNull', + 0, + null, + [] + ), + new ReplaceArgumentDefaultValue( + 'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeMultiArg', + 'firstArgument', + 0, + 0, + 3 + ), + new ReplaceArgumentDefaultValue( + 'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeMultiArg', + 'secondArgument', + 1, + 1, + 4 + ), + + new ReplaceArgumentDefaultValue( + Mage::class, + 'getStoreConfig', + 0, + 'path', + 'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Fixture\SomeBase::PATH' + ), + ]); }; diff --git a/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..bd73d0f451b --- /dev/null +++ b/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,11 @@ + diff --git a/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/Fixture/version_compare.php.inc b/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/Fixture/version_compare.php.inc index faffaf9efd9..4dbbec111b9 100644 --- a/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/Fixture/version_compare.php.inc +++ b/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/Fixture/version_compare.php.inc @@ -6,10 +6,8 @@ class VersionCompare { public function run() { - version_compare(1, 2); version_compare(1, 2, ''); version_compare(PHP_VERSION, '5.6', 'lte'); - version_compare(PHP_VERSION, '5.6', 'le'); } } @@ -23,10 +21,8 @@ class VersionCompare { public function run() { - version_compare(1, 2); version_compare(1, 2, '!='); version_compare(PHP_VERSION, '5.6', 'le'); - version_compare(PHP_VERSION, '5.6', 'le'); } } diff --git a/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/FunctionArgumentDefaultValueReplacerRectorTest.php b/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/FunctionArgumentDefaultValueReplacerRectorTest.php index 81afc69d690..2459d910864 100644 --- a/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/FunctionArgumentDefaultValueReplacerRectorTest.php +++ b/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/FunctionArgumentDefaultValueReplacerRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class FunctionArgumentDefaultValueReplacerRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/config/configured_rule.php b/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/config/configured_rule.php index 77d30e4f46b..e11407caf02 100644 --- a/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/config/configured_rule.php +++ b/rules-tests/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector/config/configured_rule.php @@ -4,22 +4,18 @@ use Rector\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector; use Rector\Arguments\ValueObject\ReplaceFuncCallArgumentDefaultValue; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(FunctionArgumentDefaultValueReplacerRector::class) - ->call('configure', [[ - FunctionArgumentDefaultValueReplacerRector::REPLACED_ARGUMENTS => ValueObjectInliner::inline([ - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'), - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '', '!='), - new ReplaceFuncCallArgumentDefaultValue( - 'some_function', - 0, - true, - 'Symfony\Component\Yaml\Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE' - ), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(FunctionArgumentDefaultValueReplacerRector::class, [ + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'), + new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '', '!='), + new ReplaceFuncCallArgumentDefaultValue( + 'some_function', + 0, + true, + 'Symfony\Component\Yaml\Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE' + ), + ]); }; diff --git a/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/Fixture/fixture.php.inc b/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/Fixture/fixture.php.inc deleted file mode 100644 index 239222c4cee..00000000000 --- a/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/SwapFuncCallArgumentsRectorTest.php b/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/SwapFuncCallArgumentsRectorTest.php deleted file mode 100644 index f9691c673df..00000000000 --- a/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/SwapFuncCallArgumentsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/config/configured_rule.php b/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/config/configured_rule.php deleted file mode 100644 index 7f5f8871b7b..00000000000 --- a/rules-tests/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector/config/configured_rule.php +++ /dev/null @@ -1,18 +0,0 @@ -services(); - $services->set(SwapFuncCallArgumentsRector::class) - ->call('configure', [[ - SwapFuncCallArgumentsRector::FUNCTION_ARGUMENT_SWAPS => ValueObjectInliner::inline( - [new SwapFuncCallArguments('some_function', [1, 0])] - ), - ]]); -}; diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/include_static_call.php.inc b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/include_static_call.php.inc new file mode 100644 index 00000000000..711aa2ac9dd --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/include_static_call.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/remove_named_arguments.php.inc b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/remove_named_arguments.php.inc new file mode 100644 index 00000000000..682f5bcc919 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/remove_named_arguments.php.inc @@ -0,0 +1,32 @@ +firstArgument(0, b: 1); +$object->firstArgument(a: 0); +$object->firstArgument(b: 1, a: 0); +$object->secondArgument(0, b: 1); +$object->secondArgument(b: 1); +$object->secondArgument(b: 1, a: 0); + +?> + +----- +firstArgument(b: 1); +$object->firstArgument(); +$object->firstArgument(b: 1); +$object->secondArgument(0); +$object->secondArgument(); +$object->secondArgument(a: 0); + +?> diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_already_removed.php.inc b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_already_removed.php.inc new file mode 100644 index 00000000000..b7fb422d773 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_already_removed.php.inc @@ -0,0 +1,13 @@ +process(1); + } +} diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_named_arguments.php.inc b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_named_arguments.php.inc new file mode 100644 index 00000000000..bf33f0f5840 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_named_arguments.php.inc @@ -0,0 +1,10 @@ +firstArgument(b: 1); +$object->secondArgument(a: 1); +$object->secondArgument(0, c: 2); diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/some_class.php.inc b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..52e2e8d7e11 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/some_class.php.inc @@ -0,0 +1,31 @@ +process(1, 2); + } +} + +?> +----- +process(1); + } +} + +?> diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/RemoveMethodCallParamRectorTest.php b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/RemoveMethodCallParamRectorTest.php new file mode 100644 index 00000000000..e76a4b4c481 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/RemoveMethodCallParamRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Source/MethodCaller.php b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Source/MethodCaller.php new file mode 100644 index 00000000000..5632b010946 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Source/MethodCaller.php @@ -0,0 +1,7 @@ +ruleWithConfiguration(RemoveMethodCallParamRector::class, [ + new RemoveMethodCallParam(MethodCaller::class, 'process', 1), + new RemoveMethodCallParam(StaticCaller::class, 'remove', 3), + new RemoveMethodCallParam(SomeMultiArg::class, 'firstArgument', 0), + new RemoveMethodCallParam(SomeMultiArg::class, 'secondArgument', 1), + ]); +}; diff --git a/rules-tests/Arguments/Rector/RectorOrder/Fixture/fixture.php.inc b/rules-tests/Arguments/Rector/RectorOrder/Fixture/fixture.php.inc deleted file mode 100644 index d3c2a9597f1..00000000000 --- a/rules-tests/Arguments/Rector/RectorOrder/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -assertTrue(strpos('foo', 'f') !== false); - $this->assertNotSame(false, strpos('foo', 'f')); - - $this->assertTrue(strpos($headers['Authorization'][0], 'oauth_body_hash') !== false); - } -} - -?> ------ -assertContains('f', 'foo'); - $this->assertContains('f', 'foo'); - - $this->assertContains('oauth_body_hash', $headers['Authorization'][0]); - } -} - -?> diff --git a/rules-tests/Arguments/Rector/RectorOrder/RectorOrderTest.php b/rules-tests/Arguments/Rector/RectorOrder/RectorOrderTest.php deleted file mode 100644 index b0727866f15..00000000000 --- a/rules-tests/Arguments/Rector/RectorOrder/RectorOrderTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Arguments/Rector/RectorOrder/config/configured_rule.php b/rules-tests/Arguments/Rector/RectorOrder/config/configured_rule.php deleted file mode 100644 index f77d808551d..00000000000 --- a/rules-tests/Arguments/Rector/RectorOrder/config/configured_rule.php +++ /dev/null @@ -1,15 +0,0 @@ -services(); - $services->set(AssertComparisonToSpecificMethodRector::class); - $services->set(AssertSameBoolNullToSpecificMethodRector::class); - $services->set(AssertFalseStrposToContainsRector::class); -}; diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/AddAssertArrayFromClassMethodDocblockRectorTest.php b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/AddAssertArrayFromClassMethodDocblockRectorTest.php new file mode 100644 index 00000000000..5bfabbcf375 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/AddAssertArrayFromClassMethodDocblockRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/BeberleiTest.php b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/BeberleiTest.php new file mode 100644 index 00000000000..5b013169cfa --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/BeberleiTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureBeberlei'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/beberlei_assert.php'; + } +} diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/float_int_bool.php.inc b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/float_int_bool.php.inc new file mode 100644 index 00000000000..d7e47c0a078 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/float_int_bool.php.inc @@ -0,0 +1,38 @@ + +----- + diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/multiple_parameters.php.inc b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/multiple_parameters.php.inc new file mode 100644 index 00000000000..40cb00376b5 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/multiple_parameters.php.inc @@ -0,0 +1,36 @@ + $items + * @param string[] $names + */ + public function run(array $items, array $names) + { + } +} + +?> +----- + $items + * @param string[] $names + */ + public function run(array $items, array $names) + { + \Webmozart\Assert\Assert::allString($items); + \Webmozart\Assert\Assert::allString(array_keys($items)); + \Webmozart\Assert\Assert::allString($names); + } +} + +?> diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/simple_array.php.inc b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/simple_array.php.inc new file mode 100644 index 00000000000..693cec4da04 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/simple_array.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/skip_already_set.php.inc b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/skip_already_set.php.inc new file mode 100644 index 00000000000..b3163619454 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/skip_already_set.php.inc @@ -0,0 +1,14 @@ + +----- + diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/config/beberlei_assert.php b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/config/beberlei_assert.php new file mode 100644 index 00000000000..3382248f654 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/config/beberlei_assert.php @@ -0,0 +1,13 @@ +ruleWithConfiguration(AddAssertArrayFromClassMethodDocblockRector::class, [ + AssertClassName::BEBERLEI, + ]); +}; diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/config/configured_rule.php b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/config/configured_rule.php new file mode 100644 index 00000000000..f8575aebe01 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(AddAssertArrayFromClassMethodDocblockRector::class); +}; diff --git a/rules-tests/Autodiscovery/Rector/Class_/MoveEntitiesToEntityDirectoryRector/Expected/ExpectedRandomEntity.php b/rules-tests/Autodiscovery/Rector/Class_/MoveEntitiesToEntityDirectoryRector/Expected/ExpectedRandomEntity.php deleted file mode 100644 index 058526eab95..00000000000 --- a/rules-tests/Autodiscovery/Rector/Class_/MoveEntitiesToEntityDirectoryRector/Expected/ExpectedRandomEntity.php +++ /dev/null @@ -1,12 +0,0 @@ -doTestFileInfo($originalFileInfo); - $this->assertFileWasAdded($expectedAddedFileWithContent); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $smartFileSystem = new SmartFileSystem(); - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Controller/RandomEntity.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/Entity/RandomEntity.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/ExpectedRandomEntity.php') - ), - ]; - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Autodiscovery/Rector/Class_/MoveEntitiesToEntityDirectoryRector/Source/Controller/RandomEntity.php b/rules-tests/Autodiscovery/Rector/Class_/MoveEntitiesToEntityDirectoryRector/Source/Controller/RandomEntity.php deleted file mode 100644 index ddd1af93a78..00000000000 --- a/rules-tests/Autodiscovery/Rector/Class_/MoveEntitiesToEntityDirectoryRector/Source/Controller/RandomEntity.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(MoveEntitiesToEntityDirectoryRector::class); -}; diff --git a/rules-tests/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector/Expected/Command/ExpectedBananaCommand.php b/rules-tests/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector/Expected/Command/ExpectedBananaCommand.php deleted file mode 100644 index 6d9ce95c8b4..00000000000 --- a/rules-tests/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector/Expected/Command/ExpectedBananaCommand.php +++ /dev/null @@ -1,12 +0,0 @@ -doTestFileInfo($originalFileInfo); - $this->assertFileWasAdded($expectedAddedFileWithContent); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $smartFileSystem = new SmartFileSystem(); - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Entity/AppleRepository.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/Repository/AppleRepository.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/Repository/ExpectedAppleRepository.php') - ), - ]; - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Controller/BananaCommand.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/Command/BananaCommand.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/Command/ExpectedBananaCommand.php') - ), - ]; - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Command/MissPlacedController.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/Controller/MissPlacedController.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/Controller/MissPlacedController.php') - ), - ]; - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Controller/Nested/AbstractBaseWithSpaceMapper.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/Mapper/AbstractBaseWithSpaceMapper.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/Mapper/Nested/AbstractBaseWithSpaceMapper.php.inc') - ), - ]; - - // inversed order, but should have the same effect - yield [ - new SmartFileInfo(__DIR__ . '/Source/Entity/UserMapper.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/Mapper/UserMapper.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/Mapper/UserMapper.php.inc') - ), - ]; - } - - /** - * @dataProvider provideDataSkipped() - */ - public function testSkipped(SmartFileInfo $originalFileInfo): void - { - $this->doTestFileInfo($originalFileInfo); - - // no change - file should have the original location - $this->assertFileWasNotChanged($this->originalTempFileInfo); - } - - /** - * @return Iterator - */ - public function provideDataSkipped(): Iterator - { - // nothing changes - yield [new SmartFileInfo(__DIR__ . '/Source/Mapper/SkipCorrectMapper.php'), null]; - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector/Source/Command/MissPlacedController.php b/rules-tests/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector/Source/Command/MissPlacedController.php deleted file mode 100644 index 46333018ea0..00000000000 --- a/rules-tests/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector/Source/Command/MissPlacedController.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(MoveServicesBySuffixToDirectoryRector::class) - ->call('configure', [[ - MoveServicesBySuffixToDirectoryRector::GROUP_NAMES_BY_SUFFIX => [ - 'Repository', - 'Command', - 'Mapper', - 'Controller', - ], - ]]); -}; diff --git a/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Expected/ValueObject/MeSearch.php b/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Expected/ValueObject/MeSearch.php deleted file mode 100644 index ae7f0c76a14..00000000000 --- a/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Expected/ValueObject/MeSearch.php +++ /dev/null @@ -1,8 +0,0 @@ -name = $name; - } - public function getName(): string - { - return $this->name; - } -} diff --git a/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Expected/ValueObject/SomeName.php b/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Expected/ValueObject/SomeName.php deleted file mode 100644 index 1a0c4f4b01c..00000000000 --- a/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Expected/ValueObject/SomeName.php +++ /dev/null @@ -1,9 +0,0 @@ -doTestFileInfo($fixtureFileInfo); - - if ($expectedAddedFileWithContent !== null) { - $this->assertFileWasAdded($expectedAddedFileWithContent); - } else { - $this->assertFileWasNotChanged($this->originalTempFileInfo); - } - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $smartFileSystem = new SmartFileSystem(); - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Repository/PrimitiveValueObject.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/ValueObject/PrimitiveValueObject.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/ValueObject/PrimitiveValueObject.php') - ), - ]; - - // type - yield [ - new SmartFileInfo(__DIR__ . '/Source/Command/SomeName.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/ValueObject/SomeName.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/ValueObject/SomeName.php') - ), - ]; - - // suffix - yield [ - new SmartFileInfo(__DIR__ . '/Source/Command/MeSearch.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/ValueObject/MeSearch.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/ValueObject/MeSearch.php') - ), - ]; - - // skip known service types - yield [new SmartFileInfo(__DIR__ . '/Source/Utils/SomeSuffixedTest.php.inc'), null]; - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Source/Command/MeSearch.php b/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Source/Command/MeSearch.php deleted file mode 100644 index 7e490c38edb..00000000000 --- a/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Source/Command/MeSearch.php +++ /dev/null @@ -1,9 +0,0 @@ -name = $name; - } - - public function getName(): string - { - return $this->name; - } -} diff --git a/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Source/Utils/SomeSuffixedTest.php.inc b/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Source/Utils/SomeSuffixedTest.php.inc deleted file mode 100644 index 3c01346db1d..00000000000 --- a/rules-tests/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector/Source/Utils/SomeSuffixedTest.php.inc +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(MoveValueObjectsToValueObjectDirectoryRector::class) - ->call('configure', [[ - MoveValueObjectsToValueObjectDirectoryRector::TYPES => [ObviousValueObjectInterface::class], - MoveValueObjectsToValueObjectDirectoryRector::SUFFIXES => ['Search'], - MoveValueObjectsToValueObjectDirectoryRector::ENABLE_VALUE_OBJECT_GUESSING => true, - ]]); -}; diff --git a/rules-tests/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector/Expected/Entity/ExpectedSameClassImplementEntity.php b/rules-tests/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector/Expected/Entity/ExpectedSameClassImplementEntity.php deleted file mode 100644 index 0ed8c661cad..00000000000 --- a/rules-tests/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector/Expected/Entity/ExpectedSameClassImplementEntity.php +++ /dev/null @@ -1,16 +0,0 @@ -doTestFileInfo($originalFileInfo); - $this->assertFileWasAdded($expectedAddedFileWithContent); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $smartFileSystem = new SmartFileSystem(); - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Entity/RandomInterface.php'), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/Contract/RandomInterface.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/ExpectedRandomInterface.php') - ), - ]; - } - - /** - * @dataProvider provideDataSkip() - */ - public function testSkip(SmartFileInfo $originalFileInfo): void - { - $this->doTestFileInfo($originalFileInfo); - $this->assertFileWasNotChanged($this->originalTempFileInfo); - } - - /** - * @return Iterator - */ - public function provideDataSkip(): Iterator - { - // skip already in correct location - yield [new SmartFileInfo(__DIR__ . '/Source/Contract/KeepThisSomeInterface.php')]; - - // skip already in correct location - yield [new SmartFileInfo(__DIR__ . '/Source/Contract/Foo/KeepThisSomeInterface.php')]; - - // skip nette control factory - yield [new SmartFileInfo(__DIR__ . '/Source/Control/ControlFactory.php')]; - - // skip form control factory, even in docblock - yield [new SmartFileInfo(__DIR__ . '/Source/Control/FormFactory.php')]; - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector/Source/Contract/Foo/KeepThisSomeInterface.php b/rules-tests/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector/Source/Contract/Foo/KeepThisSomeInterface.php deleted file mode 100644 index 5b5abcde2cc..00000000000 --- a/rules-tests/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector/Source/Contract/Foo/KeepThisSomeInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::TYPED_PROPERTIES - 1); - - $services = $containerConfigurator->services(); - $services->set(MoveInterfacesToContractNamespaceDirectoryRector::class); -}; diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/DateFuncCallToCarbonRectorTest.php b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/DateFuncCallToCarbonRectorTest.php new file mode 100644 index 00000000000..104af1e5f88 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/DateFuncCallToCarbonRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_strtotime.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_strtotime.php.inc new file mode 100644 index 00000000000..171a288e045 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_strtotime.php.inc @@ -0,0 +1,27 @@ + +----- +format('d.m.Y'); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_var_from_strtotime.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_var_from_strtotime.php.inc new file mode 100644 index 00000000000..2dcad135e88 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_var_from_strtotime.php.inc @@ -0,0 +1,29 @@ + +----- +getTimestamp(); + $formatted = \Carbon\Carbon::createFromTimestamp($var)->format('d.m.Y'); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_var_from_time.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_var_from_time.php.inc new file mode 100644 index 00000000000..3c73b234508 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/date_with_var_from_time.php.inc @@ -0,0 +1,29 @@ + +----- +format('d.m.Y'); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/simple_date.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/simple_date.php.inc new file mode 100644 index 00000000000..f8fb2b34bdf --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/simple_date.php.inc @@ -0,0 +1,27 @@ + +----- +format('Y-m-d'); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/skip_calculation_first_class_callable.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/skip_calculation_first_class_callable.php.inc new file mode 100644 index 00000000000..723655bc890 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/skip_calculation_first_class_callable.php.inc @@ -0,0 +1,11 @@ + +----- +format('Y-m-d'); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime.php.inc new file mode 100644 index 00000000000..7a7df721c5a --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime.php.inc @@ -0,0 +1,27 @@ + +----- +getTimestamp(); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime_base_timestamp.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime_base_timestamp.php.inc new file mode 100644 index 00000000000..eaf04bab8dd --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime_base_timestamp.php.inc @@ -0,0 +1,27 @@ + +----- +getTimestamp()); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime_base_timestamp_as_var.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime_base_timestamp_as_var.php.inc new file mode 100644 index 00000000000..e18d53fd40b --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/strtotime_base_timestamp_as_var.php.inc @@ -0,0 +1,29 @@ + +----- +getTimestamp(); + $timestamp = strtotime('+1 day', $var); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/time_calculation.php.inc b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/time_calculation.php.inc new file mode 100644 index 00000000000..8c9a6e45d39 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/Fixture/time_calculation.php.inc @@ -0,0 +1,35 @@ + +----- +subSeconds(2)->getTimestamp(); + $twoMinutesAgo = \Carbon\Carbon::now()->subMinutes(2)->getTimestamp(); + $twoHoursAgo = \Carbon\Carbon::now()->subHours(2)->getTimestamp(); + $twoDaysAgo = \Carbon\Carbon::now()->subDays(2)->getTimestamp(); + $twoWeeksAgo = \Carbon\Carbon::now()->subWeeks(2)->getTimestamp(); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/config/configured_rule.php b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/config/configured_rule.php new file mode 100644 index 00000000000..19c0c19d710 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(DateFuncCallToCarbonRector::class); +}; diff --git a/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/Fixture/first_class_callable.php.inc b/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/Fixture/first_class_callable.php.inc new file mode 100644 index 00000000000..3e862c04bc2 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/Fixture/first_class_callable.php.inc @@ -0,0 +1,27 @@ + +----- + \Carbon\Carbon::now()->getTimestamp()); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/Fixture/some_class.php.inc b/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..2dbe03dfa29 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/Fixture/some_class.php.inc @@ -0,0 +1,27 @@ + +----- +getTimestamp(); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/TimeFuncCallToCarbonRectorTest.php b/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/TimeFuncCallToCarbonRectorTest.php new file mode 100644 index 00000000000..21175b2a4f9 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/TimeFuncCallToCarbonRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/config/configured_rule.php b/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/config/configured_rule.php new file mode 100644 index 00000000000..cd63e3d80a5 --- /dev/null +++ b/rules-tests/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(TimeFuncCallToCarbonRector::class); +}; diff --git a/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/ChangeCarbonSingularMethodCallToPluralRectorTest.php b/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/ChangeCarbonSingularMethodCallToPluralRectorTest.php deleted file mode 100644 index 62aee4ea208..00000000000 --- a/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/ChangeCarbonSingularMethodCallToPluralRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/Fixture/fixture.php.inc b/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/Fixture/fixture.php.inc deleted file mode 100644 index b4af27304c4..00000000000 --- a/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -addMinute($value); - } -} - -?> ------ -addMinutes($value); - } -} - -?> diff --git a/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/Fixture/skip_no_value.php.inc b/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/Fixture/skip_no_value.php.inc deleted file mode 100644 index f2cc3d3ff61..00000000000 --- a/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/Fixture/skip_no_value.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -addMinute(); - } -} diff --git a/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/config/configured_rule.php b/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/config/configured_rule.php deleted file mode 100644 index 895dfe28642..00000000000 --- a/rules-tests/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ChangeCarbonSingularMethodCallToPluralRector::class); -}; diff --git a/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/ChangeDiffForHumansArgsRectorTest.php b/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/ChangeDiffForHumansArgsRectorTest.php deleted file mode 100644 index 227496c5275..00000000000 --- a/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/ChangeDiffForHumansArgsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/Fixture/fixture.php.inc b/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/Fixture/fixture.php.inc deleted file mode 100644 index b9f9f0a5ee3..00000000000 --- a/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -diffForHumans(null, true); - - $carbon->diffForHumans(null, false); - } -} - -?> ------ -diffForHumans(null, \Carbon\CarbonInterface::DIFF_ABSOLUTE); - - $carbon->diffForHumans(null, \Carbon\CarbonInterface::DIFF_RELATIVE_AUTO); - } -} - -?> diff --git a/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/config/configured_rule.php b/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/config/configured_rule.php deleted file mode 100644 index fc0f91606e9..00000000000 --- a/rules-tests/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ChangeDiffForHumansArgsRector::class); -}; diff --git a/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/DateTimeMethodCallToCarbonRectorTest.php b/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/DateTimeMethodCallToCarbonRectorTest.php new file mode 100644 index 00000000000..c44bebaa613 --- /dev/null +++ b/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/DateTimeMethodCallToCarbonRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/Fixture/immutable.php.inc b/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/Fixture/immutable.php.inc new file mode 100644 index 00000000000..ea95c31e6fa --- /dev/null +++ b/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/Fixture/immutable.php.inc @@ -0,0 +1,27 @@ +format('Y-m-d'); + } +} + +?> +----- +addDays(20))->format('Y-m-d'); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/Fixture/some_class.php.inc b/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..6aebd732e11 --- /dev/null +++ b/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/Fixture/some_class.php.inc @@ -0,0 +1,27 @@ +format('Y-m-d'); + } +} + +?> +----- +addDays(20))->format('Y-m-d'); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/config/configured_rule.php b/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/config/configured_rule.php new file mode 100644 index 00000000000..cf00fc340f0 --- /dev/null +++ b/rules-tests/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(DateTimeMethodCallToCarbonRector::class); +}; diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/DateTimeInstanceToCarbonRectorTest.php b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/DateTimeInstanceToCarbonRectorTest.php new file mode 100644 index 00000000000..52c08b892e1 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/DateTimeInstanceToCarbonRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now.php.inc new file mode 100644 index 00000000000..55be7e7de77 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now_add_sub.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now_add_sub.php.inc new file mode 100644 index 00000000000..eaf253d51b7 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now_add_sub.php.inc @@ -0,0 +1,55 @@ + +----- +addSeconds(5); + $addMinutes = \Carbon\Carbon::now()->addMinutes(5); + $addHours = \Carbon\Carbon::now()->addHours(5); + $addDays = \Carbon\Carbon::now()->addDays(5); + $addWeeks = \Carbon\Carbon::now()->addWeeks(5); + $addMonths = \Carbon\Carbon::now()->addMonths(5); + $addYears = \Carbon\Carbon::now()->addYears(5); + + $subSeconds = \Carbon\Carbon::now()->subSeconds(5); + $subMinutes = \Carbon\Carbon::now()->subMinutes(5); + $subHours = \Carbon\Carbon::now()->subHours(5); + $subDays = \Carbon\Carbon::now()->subDays(5); + $subWeeks = \Carbon\Carbon::now()->subWeeks(5); + $subMonths = \Carbon\Carbon::now()->subMonths(5); + $subYears = \Carbon\Carbon::now()->subYears(5); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_parse.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_parse.php.inc new file mode 100644 index 00000000000..e58bcef300b --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_parse.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today.php.inc new file mode 100644 index 00000000000..f507eaa1264 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today_with_5_days.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today_with_5_days.php.inc new file mode 100644 index 00000000000..5e91d5359be --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today_with_5_days.php.inc @@ -0,0 +1,35 @@ + +----- +addDays(5); + + $middleDate = \Carbon\Carbon::today()->addDays(7); + + $nextDate = \Carbon\Carbon::today()->addDays(10); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today_with_negate_5_days.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today_with_negate_5_days.php.inc new file mode 100644 index 00000000000..bde4cdd2d32 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today_with_negate_5_days.php.inc @@ -0,0 +1,35 @@ + +----- +subDays(5); + + $middleDate = \Carbon\Carbon::today()->subDays(7); + + $nextDate = \Carbon\Carbon::today()->subDays(10); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_tomorrow.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_tomorrow.php.inc new file mode 100644 index 00000000000..efa02e57bd7 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_tomorrow.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_date_time.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_date_time.php.inc new file mode 100644 index 00000000000..040724664a6 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_date_time.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_yesterday.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_yesterday.php.inc new file mode 100644 index 00000000000..fc08ec57e79 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_yesterday.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/include_datetime_immutable.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/include_datetime_immutable.php.inc new file mode 100644 index 00000000000..4aa5fa52884 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/include_datetime_immutable.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/some_class.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..1ee9ccc3948 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/some_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/config/configured_rule.php b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/config/configured_rule.php new file mode 100644 index 00000000000..2389b987f84 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(DateTimeInstanceToCarbonRector::class); +}; diff --git a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/ArrayThisCallToThisMethodCallRectorTest.php b/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/ArrayThisCallToThisMethodCallRectorTest.php deleted file mode 100644 index 13a12bb1a3a..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/ArrayThisCallToThisMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index ab4d1ae4236..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -giveMeMore(); - } - - public function giveMeMore() - { - return 'more'; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/private_method.php.inc b/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/private_method.php.inc deleted file mode 100644 index b66f3cc1cd3..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/private_method.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ -giveMeMore(); - } - - private function giveMeMore() - { - return 'more'; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_call_with_args.php.inc b/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_call_with_args.php.inc deleted file mode 100644 index e6725cac1cd..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_call_with_args.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - $second; - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_function_arguments.php.inc b/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_function_arguments.php.inc deleted file mode 100644 index 351d832d238..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_function_arguments.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - [self::class, 'run'], - ]; - - public function __invoke() - { - $handler = self::$listen['run']; - call_user_func($handler, 'data'); - } - - public function run(string $data) - { - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_non_callable.php.inc b/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_non_callable.php.inc deleted file mode 100644 index 77ea2e3eb6f..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_non_callable.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -process('tags', [ - 'usage' => SomeConstantInteger::VALUE, - 'region' => 'tags', - ]); - } - - public function process() - { - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_non_existing_method.php.inc b/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_non_existing_method.php.inc deleted file mode 100644 index c86c7e1fd35..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Fixture/skip_non_existing_method.php.inc +++ /dev/null @@ -1,12 +0,0 @@ -onSuccess[] = [$this, 'giveMeMore']; - } - - public function giveMeMore() - { - return 'more'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Source/SomeConstantInteger.php b/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Source/SomeConstantInteger.php deleted file mode 100644 index 2bd594e0b50..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector/Source/SomeConstantInteger.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(ArrayThisCallToThisMethodCallRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/CallableThisArrayToAnonymousFunctionRectorTest.php b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/CallableThisArrayToAnonymousFunctionRectorTest.php deleted file mode 100644 index 1d14d675a19..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/CallableThisArrayToAnonymousFunctionRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/another_class.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/another_class.php.inc deleted file mode 100644 index f5bff2684e7..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/another_class.php.inc +++ /dev/null @@ -1,63 +0,0 @@ -sortingClass = $sortingClass; - } - - public function go($values) - { - $sortingClass = new SortingClass(); - - usort($values, [$this->sortingClass, 'publicSort']); - - usort($values, [$sortingClass, 'publicSort']); - } -} - -?> ------ -sortingClass = $sortingClass; - } - - public function go($values) - { - $sortingClass = new SortingClass(); - - usort($values, function ($a, $b) { - return $this->sortingClass->publicSort($a, $b); - }); - - usort($values, function ($a, $b) use ($sortingClass) { - return $sortingClass->publicSort($a, $b); - }); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/external_class.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/external_class.php.inc deleted file mode 100644 index f4593325092..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/external_class.php.inc +++ /dev/null @@ -1,61 +0,0 @@ -sortingClass = $sortingClass; - } - - public function noGo($values) - { - $sortingClass = new SortingClass(); - - usort($values, [$this->sortingClass, 'protectedSort']); - usort($values, [$sortingClass, 'privateSort']); - } -} - -?> ------ -sortingClass = $sortingClass; - } - - public function noGo($values) - { - $sortingClass = new SortingClass(); - - usort($values, function ($a, $b) { - return $this->sortingClass->protectedSort($a, $b); - }); - usort($values, function ($a, $b) use ($sortingClass) { - return $sortingClass->privateSort($a, $b); - }); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/fixture.php.inc deleted file mode 100644 index d604f0fd82a..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - $second; - } -} - -?> ------ -compareSize($first, $second); - }); - - return $values; - } - - private function compareSize(int $first, $second): bool - { - return $first <=> $second; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/has_construct_with_default_value_parameter.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/has_construct_with_default_value_parameter.php.inc deleted file mode 100644 index 1f6f0f6531b..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/has_construct_with_default_value_parameter.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ -someMethod(); - }; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/no_return_for_void.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/no_return_for_void.php.inc deleted file mode 100644 index 7f1ede7bc0c..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/no_return_for_void.php.inc +++ /dev/null @@ -1,55 +0,0 @@ - ------ -thisReturnsVoid(); - }); - usort($values, function () : void { - $this->thisReturnsNothing(); - }); - - return $values; - } - - private function thisReturnsVoid(): void - { - } - - private function thisReturnsNothing(): void - { - return; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/other_class_not_has_construct_parameter.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/other_class_not_has_construct_parameter.php.inc deleted file mode 100644 index 682672eacd6..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/other_class_not_has_construct_parameter.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ -someMethod(); - }; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/same_class_not_has_construct_parameter.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/same_class_not_has_construct_parameter.php.inc deleted file mode 100644 index 50f072dda81..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/same_class_not_has_construct_parameter.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ -someMethod(); - }; - } - - public function someMethod() - { - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip.php.inc deleted file mode 100644 index 2f766467fd3..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - $second; - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip_empty_first_array.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip_empty_first_array.php.inc deleted file mode 100644 index b787dad3ae5..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip_empty_first_array.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip_same_class_has_construct_parameter.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip_same_class_has_construct_parameter.php.inc deleted file mode 100644 index a4827cff72d..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip_same_class_has_construct_parameter.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -property = $property; - } - - public function run() - { - return [SkipSameClassHasConstructParameter::class, 'someMethod']; - } - - public function someMethod() - { - - } -} -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/some_static_call.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/some_static_call.php.inc deleted file mode 100644 index 2dc962f5587..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/some_static_call.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - $second; - } -} - -?> ------ - $second; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/with_parent.php.inc b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/with_parent.php.inc deleted file mode 100644 index a56bac55a29..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/with_parent.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - $second; - } -} - -?> ------ -compareSize($first, $second); - }); - - return $values; - } - - private function compareSize(int $first, $second): bool - { - return $first <=> $second; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Source/OtherClass.php b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Source/OtherClass.php deleted file mode 100644 index b26c6526446..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Source/OtherClass.php +++ /dev/null @@ -1,19 +0,0 @@ -property = $property; - } - - public function someMethod() - { - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Source/OtherClass2.php b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Source/OtherClass2.php deleted file mode 100644 index e4ff41e6603..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Source/OtherClass2.php +++ /dev/null @@ -1,16 +0,0 @@ - $b; - } - - protected function protectedSort($a, $b) - { - return $a <=> $b; - } - - private function privateSort($a, $b) - { - return $a <=> $b; - } -} diff --git a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/config/configured_rule.php deleted file mode 100644 index 54b7e189460..00000000000 --- a/rules-tests/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(CallableThisArrayToAnonymousFunctionRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/CombinedAssignRectorTest.php b/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/CombinedAssignRectorTest.php index 27f21b64f45..f51620f5aa2 100644 --- a/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/CombinedAssignRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/CombinedAssignRectorTest.php @@ -5,8 +5,8 @@ namespace Rector\Tests\CodeQuality\Rector\Assign\CombinedAssignRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * Some tests used from: @@ -15,20 +15,15 @@ */ final class CombinedAssignRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/Fixture/skip_xor_array_dim_fetch.php.inc b/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/Fixture/skip_xor_array_dim_fetch.php.inc new file mode 100644 index 00000000000..d94bf202a65 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/Fixture/skip_xor_array_dim_fetch.php.inc @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/config/configured_rule.php index ea9f00d78c0..eb45af98dc1 100644 --- a/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Assign/CombinedAssignRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Assign\CombinedAssignRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CombinedAssignRector::class); -}; +return RectorConfig::configure() + ->withRules([CombinedAssignRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/fixture.php.inc deleted file mode 100644 index a1e715f2889..00000000000 --- a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/not_scalar.php.inc b/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/not_scalar.php.inc deleted file mode 100644 index e75da8ffa68..00000000000 --- a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/not_scalar.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/skip_split_of_array.php.inc b/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/skip_split_of_array.php.inc deleted file mode 100644 index 5e4868e9de8..00000000000 --- a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture/skip_split_of_array.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/SplitListAssignToSeparateLineRectorTest.php b/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/SplitListAssignToSeparateLineRectorTest.php deleted file mode 100644 index 85400de4fa1..00000000000 --- a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/SplitListAssignToSeparateLineRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/config/configured_rule.php deleted file mode 100644 index 1549b332f50..00000000000 --- a/rules-tests/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SplitListAssignToSeparateLineRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/Fixture/attribute.php.inc b/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/Fixture/attribute.php.inc new file mode 100644 index 00000000000..e632e542822 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/Fixture/attribute.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/SortAttributeNamedArgsRectorTest.php b/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/SortAttributeNamedArgsRectorTest.php new file mode 100644 index 00000000000..66c21fe2de1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/SortAttributeNamedArgsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/Source/MyAttribute.php b/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/Source/MyAttribute.php new file mode 100644 index 00000000000..489494211a3 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector/Source/MyAttribute.php @@ -0,0 +1,11 @@ +withRules([SortAttributeNamedArgsRector::class]); diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/left_instanceof_right_is_object.php.inc b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/left_instanceof_right_is_object.php.inc new file mode 100644 index 00000000000..f9c4fd4d369 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/left_instanceof_right_is_object.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/left_is_object_right_instanceof.php.inc b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/left_is_object_right_instanceof.php.inc new file mode 100644 index 00000000000..022be644152 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/left_is_object_right_instanceof.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/skip_different_expr.php.inc b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/skip_different_expr.php.inc new file mode 100644 index 00000000000..9b7a9eb0f74 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/Fixture/skip_different_expr.php.inc @@ -0,0 +1,11 @@ +data) === 1; + } +} diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/RemoveUselessIsObjectCheckRectorTest.php b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/RemoveUselessIsObjectCheckRectorTest.php new file mode 100644 index 00000000000..fc932e2b8cb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/RemoveUselessIsObjectCheckRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/config/configured_rule.php new file mode 100644 index 00000000000..c4735385d7b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveUselessIsObjectCheckRector::class]); diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/Fixture/compare_same_variable.php.inc b/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/Fixture/compare_same_variable.php.inc new file mode 100644 index 00000000000..6cc81071406 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/Fixture/compare_same_variable.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/RepeatedAndNotEqualToNotInArrayRectorTest.php b/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/RepeatedAndNotEqualToNotInArrayRectorTest.php new file mode 100644 index 00000000000..88bb87c45d9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/RepeatedAndNotEqualToNotInArrayRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/config/configured_rule.php new file mode 100644 index 00000000000..ac5b0e50f35 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RepeatedAndNotEqualToNotInArrayRector::class]); diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/Fixture/fixture.php.inc index bd906325176..8aeb13550e4 100644 --- a/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/Fixture/fixture.php.inc @@ -13,11 +13,6 @@ final class Fixture { return empty($values) && is_array($values); } - - public function functionCallInsideEmpty($values): bool - { - return is_array($values) && empty(array_filter($values)); - } } ?> @@ -37,11 +32,6 @@ final class Fixture { return $values === []; } - - public function functionCallInsideEmpty($values): bool - { - return array_filter($values) === []; - } } ?> diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/Fixture/skip_different_array_filter_value.php.inc b/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/Fixture/skip_different_array_filter_value.php.inc new file mode 100644 index 00000000000..0c6f27eee69 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/Fixture/skip_different_array_filter_value.php.inc @@ -0,0 +1,11 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/config/configured_rule.php index bf1fa23362f..fbc12c00a30 100644 --- a/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\BooleanAnd\SimplifyEmptyArrayCheckRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyEmptyArrayCheckRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyEmptyArrayCheckRector::class]); diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/2_not_false.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/2_not_false.php.inc new file mode 100644 index 00000000000..f46d291f9f5 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/2_not_false.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/2_not_true.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/2_not_true.php.inc new file mode 100644 index 00000000000..8641a83101c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/2_not_true.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/3_not_false.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/3_not_false.php.inc new file mode 100644 index 00000000000..df6540fdb42 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/3_not_false.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/3_not_true.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/3_not_true.php.inc new file mode 100644 index 00000000000..15ae43f88a0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/3_not_true.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/if_condition.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/if_condition.php.inc new file mode 100644 index 00000000000..13c13f3b839 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/if_condition.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/not_false.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/not_false.php.inc new file mode 100644 index 00000000000..17ae28668d0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/not_false.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/not_true.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/not_true.php.inc new file mode 100644 index 00000000000..a99ef6f2227 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/Fixture/not_true.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/ReplaceConstantBooleanNotRectorTest.php b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/ReplaceConstantBooleanNotRectorTest.php new file mode 100644 index 00000000000..06f47e776dc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/ReplaceConstantBooleanNotRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/config/configured_rule.php new file mode 100644 index 00000000000..9a35874773d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReplaceConstantBooleanNotRector::class]); diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/2_not.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/2_not.php.inc new file mode 100644 index 00000000000..4cee67c26e1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/2_not.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/3_not.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/3_not.php.inc new file mode 100644 index 00000000000..ee1988d85eb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/3_not.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/4_not.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/4_not.php.inc new file mode 100644 index 00000000000..3abc07fc9fb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/4_not.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/skip_1_not.php.inc b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/skip_1_not.php.inc new file mode 100644 index 00000000000..b3812f5410a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/Fixture/skip_1_not.php.inc @@ -0,0 +1,5 @@ + diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/ReplaceMultipleBooleanNotRectorTest.php b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/ReplaceMultipleBooleanNotRectorTest.php new file mode 100644 index 00000000000..d68458e2013 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/ReplaceMultipleBooleanNotRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/config/configured_rule.php new file mode 100644 index 00000000000..6dd3444eb7d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReplaceMultipleBooleanNotRector::class]); diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector/SimplifyDeMorganBinaryRectorTest.php b/rules-tests/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector/SimplifyDeMorganBinaryRectorTest.php index eb9ee8ee1c8..8bb3ad651c2 100644 --- a/rules-tests/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector/SimplifyDeMorganBinaryRectorTest.php +++ b/rules-tests/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector/SimplifyDeMorganBinaryRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\BooleanNot\SimplifyDeMorganBinaryRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyDeMorganBinaryRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector/config/configured_rule.php index e62306d104c..71c83151933 100644 --- a/rules-tests/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\BooleanNot\SimplifyDeMorganBinaryRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyDeMorganBinaryRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyDeMorganBinaryRector::class]); diff --git a/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/compare_same_variable.php.inc b/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/compare_same_variable.php.inc new file mode 100644 index 00000000000..4a53a137abf --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/compare_same_variable.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/non_strict_in_array.php.inc b/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/non_strict_in_array.php.inc new file mode 100644 index 00000000000..3dfc1a169cd --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/non_strict_in_array.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/skip_mix_equal_and_identical.php.inc b/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/skip_mix_equal_and_identical.php.inc new file mode 100644 index 00000000000..63461f730a6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/Fixture/skip_mix_equal_and_identical.php.inc @@ -0,0 +1,12 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/config/configured_rule.php new file mode 100644 index 00000000000..1d9c0ea9aaa --- /dev/null +++ b/rules-tests/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RepeatedOrEqualToInArrayRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/empty_brackets.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/empty_brackets.php.inc index a9d94007688..2a51e74803e 100644 --- a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/empty_brackets.php.inc +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/empty_brackets.php.inc @@ -8,8 +8,8 @@ class EmptyBrackets { try { $someCode = 1; - } catch (Throwable $throwable) { - throw new AnotherException; + } catch (\Throwable $throwable) { + throw new \RuntimeException; } } } @@ -26,8 +26,8 @@ class EmptyBrackets { try { $someCode = 1; - } catch (Throwable $throwable) { - throw new AnotherException($throwable->getMessage(), $throwable->getCode(), $throwable); + } catch (\Throwable $throwable) { + throw new \RuntimeException($throwable->getMessage(), $throwable->getCode(), $throwable); } } } diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/fixture.php.inc index fcac2a63b66..8e1085cee31 100644 --- a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/fixture.php.inc @@ -8,8 +8,8 @@ class Fixture { try { $someCode = 1; - } catch (Throwable $throwable) { - throw new AnotherException('ups'); + } catch (\Throwable $throwable) { + throw new \RuntimeException('ups'); } } } @@ -26,8 +26,8 @@ class Fixture { try { $someCode = 1; - } catch (Throwable $throwable) { - throw new AnotherException('ups', $throwable->getCode(), $throwable); + } catch (\Throwable $throwable) { + throw new \RuntimeException('ups', $throwable->getCode(), $throwable); } } } diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument.php.inc new file mode 100644 index 00000000000..792b460802d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument.php.inc @@ -0,0 +1,35 @@ +run(); + }catch(\Throwable $throwable) { + throw new \LogicException('Some exception', previous: $throwable); + } + } +} + +?> +----- +run(); + }catch(\Throwable $throwable) { + throw new \LogicException('Some exception', $throwable->getCode(), previous: $throwable); + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_for_message.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_for_message.php.inc new file mode 100644 index 00000000000..03ba153ebc7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_for_message.php.inc @@ -0,0 +1,35 @@ +run(); + }catch(\Throwable $throwable) { + throw new \LogicException(message: 'Some exception'); + } + } +} + +?> +----- +run(); + }catch(\Throwable $throwable) { + throw new \LogicException(message: 'Some exception', code: $throwable->getCode(), previous: $throwable); + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_for_message_and_previous.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_for_message_and_previous.php.inc new file mode 100644 index 00000000000..288f9996267 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_for_message_and_previous.php.inc @@ -0,0 +1,39 @@ +run(); + }catch(\Throwable $throwable) { + throw new LogicException(message: 'Some exception', previous: $throwable); + } + } +} + +?> +----- +run(); + }catch(\Throwable $throwable) { + throw new LogicException(message: 'Some exception', code: $throwable->getCode(), previous: $throwable); + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_previous.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_previous.php.inc new file mode 100644 index 00000000000..5c75e453adb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/named_argument_previous.php.inc @@ -0,0 +1,35 @@ + +----- +getMessage(), code: $e->getCode(), previous: $e); + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_custom_exception_no_code_param.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_custom_exception_no_code_param.php.inc new file mode 100644 index 00000000000..7c8ffbe7677 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_custom_exception_no_code_param.php.inc @@ -0,0 +1,27 @@ +statusCode = $statusCode; + $this->headers = $headers; + + parent::__construct($message, $code, $previous); + } +} + +class BadGatewayHttpException extends HttpException +{ + public function __construct(string $message = '', ?\Throwable $previous = null, array $headers = [], int $code = 0) + { + parent::__construct(Response::HTTP_BAD_GATEWAY, $message, $previous, $headers, $code); + } +} + + +try { + // do sth +} catch (\Throwable $exception) { + throw new BadGatewayHttpException('test exception', $exception); +} diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_filled_exception_with_different_location.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_filled_exception_with_different_location.php.inc index 0fe4594d56a..ef240251c1e 100644 --- a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_filled_exception_with_different_location.php.inc +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_filled_exception_with_different_location.php.inc @@ -10,7 +10,7 @@ class SkipFilledExceptionWithDifferentLocation public function run() { try { - } catch (Throwable $throwable) { + } catch (\Throwable $throwable) { throw new BadRequestHttpException('message some', $throwable); } } @@ -18,7 +18,7 @@ class SkipFilledExceptionWithDifferentLocation class BadRequestHttpException extends Exception { - public function __construct(string $message = null, \Throwable $previous = null, int $code = 0, array $headers = []) + public function __construct(string $message = null, ?\Throwable $previous = null, int $code = 0, array $headers = []) { parent::__construct('message', 400, $previous); } diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_missing_location.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_missing_location.php.inc new file mode 100644 index 00000000000..06658b3886f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Fixture/skip_missing_location.php.inc @@ -0,0 +1,25 @@ +getMessage(), previous: $e); + } + } +} diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/FixturePhp8/named_argument.php.inc b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/FixturePhp8/named_argument.php.inc deleted file mode 100644 index 60290c6eaae..00000000000 --- a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/FixturePhp8/named_argument.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -run(); - }catch(\Throwable $throwable) { - throw new LogicException('Some exception', previous: $throwable); - } - } -} - -?> ------ -run(); - }catch(\Throwable $throwable) { - throw new LogicException('Some exception', $throwable->getCode(), previous: $throwable); - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Php8Test.php b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Php8Test.php deleted file mode 100644 index ba4cfcad60d..00000000000 --- a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/Php8Test.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp8'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/ThrowWithPreviousExceptionRectorTest.php b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/ThrowWithPreviousExceptionRectorTest.php index bdfed91333c..a16afbc8e07 100644 --- a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/ThrowWithPreviousExceptionRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/ThrowWithPreviousExceptionRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Catch_\ThrowWithPreviousExceptionRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ThrowWithPreviousExceptionRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/config/configured_rule.php index 1508bb5707c..03b8a332908 100644 --- a/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Catch_\ThrowWithPreviousExceptionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ThrowWithPreviousExceptionRector::class); -}; +return RectorConfig::configure() + ->withRules([ThrowWithPreviousExceptionRector::class]); diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/allow_final_class_constant.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/allow_final_class_constant.php.inc new file mode 100644 index 00000000000..72074123515 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/allow_final_class_constant.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/allow_override.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/allow_override.php.inc new file mode 100644 index 00000000000..b942c9a08c4 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/allow_override.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/parent_hop_fetch.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/parent_hop_fetch.php.inc new file mode 100644 index 00000000000..baad0ae5c26 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/parent_hop_fetch.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/skip_class_constant_from_non_final_class.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/skip_class_constant_from_non_final_class.php.inc new file mode 100644 index 00000000000..39379be4d16 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/skip_class_constant_from_non_final_class.php.inc @@ -0,0 +1,15 @@ +sideEffect()::SOME_VALUE; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/skip_unknown_class.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/skip_unknown_class.php.inc new file mode 100644 index 00000000000..0c834a83d34 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/Fixture/skip_unknown_class.php.inc @@ -0,0 +1,13 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/config/configured_rule.php new file mode 100644 index 00000000000..ed190ac0165 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(VariableConstFetchToClassConstFetchRector::class); +}; diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/DateTimeToDateTimeInterfaceRectorTest.php b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/DateTimeToDateTimeInterfaceRectorTest.php deleted file mode 100644 index 63c45c78368..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/DateTimeToDateTimeInterfaceRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_fqn.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_fqn.php.inc deleted file mode 100644 index 37670470350..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_fqn.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_full.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_full.php.inc deleted file mode 100644 index 07c3e3080cc..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_full.php.inc +++ /dev/null @@ -1,73 +0,0 @@ -date = $date; - $this->nullableDate = $nullableDate; - } - - public function getDate(): \DateTime - { - return $this->date; - } - - public function getNullableDate(): ?\DateTime - { - return $this->nullableDate; - } -} - -?> ------ -date = $date; - $this->nullableDate = $nullableDate; - } - - /** - * @return \DateTime|\DateTimeImmutable - */ - public function getDate(): \DateTimeInterface - { - return $this->date; - } - - /** - * @return \DateTime|\DateTimeImmutable|null - */ - public function getNullableDate(): ?\DateTimeInterface - { - return $this->nullableDate; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_nullable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_nullable.php.inc deleted file mode 100644 index 3527dcd62f8..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_nullable.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_use.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_use.php.inc deleted file mode 100644 index b87e4e29f48..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_use.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage.php.inc deleted file mode 100644 index 57a82d099fb..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -modify('+1 day'); - - return $dateTime; - } -} - -?> ------ -modify('+1 day'); - - return $dateTime; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage_assign.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage_assign.php.inc deleted file mode 100644 index 4a0db57d61f..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage_assign.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -modify('+1 day'); - - return $dateTime; - } -} - -?> ------ -modify('+1 day'); - - return $dateTime; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage_param.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage_param.php.inc deleted file mode 100644 index 0df394d33d1..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_variable_usage_param.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -modify('+1 day')); - - return $dateTime; - } -} - -?> ------ -modify('+1 day')); - - return $dateTime; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_child_class.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_child_class.php.inc deleted file mode 100644 index d5676ed5133..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_child_class.php.inc +++ /dev/null @@ -1,26 +0,0 @@ -format('Y-m-d H:i:s'); - } -} - -class SkipChildClass -{ - public static function bar(?FunctionDateTime $datetime) - { - // ... - } - - public static function baz(FunctionDateTime $datetime) - { - // ... - } -} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_child_class_return.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_child_class_return.php.inc deleted file mode 100644 index 836ab5ccb0f..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_child_class_return.php.inc +++ /dev/null @@ -1,26 +0,0 @@ -format('Y-m-d H:i:s'); - } -} - -class SkipChildClassReturn -{ - public static function bar(): ?ReturnDateTime - { - // ... - } - - public static function baz(): ReturnDateTime - { - // ... - } -} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_different_type.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_different_type.php.inc deleted file mode 100644 index ea881631d2f..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_different_type.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - $name . ' thing'; - - return $thingMaker($name); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Php74Test.php b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Php74Test.php deleted file mode 100644 index 408aacb83ce..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Php74Test.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/config/configured_rule.php deleted file mode 100644 index 93885fca31c..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DateTimeToDateTimeInterfaceRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/ExplicitReturnNullRectorTest.php b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/ExplicitReturnNullRectorTest.php new file mode 100644 index 00000000000..4000f6fe52e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/ExplicitReturnNullRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/already_union_null_doc.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/already_union_null_doc.php.inc new file mode 100644 index 00000000000..1a051fb0ab4 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/already_union_null_doc.php.inc @@ -0,0 +1,38 @@ + 50) { + return 'yes'; + } + } +} + +?> +----- + 50) { + return 'yes'; + } + return null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/array_constant.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/array_constant.php.inc new file mode 100644 index 00000000000..403e66450a4 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/array_constant.php.inc @@ -0,0 +1,14 @@ + ['foo' => 'fooValue']]], + [null, []], + ]; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned.php.inc new file mode 100644 index 00000000000..7ca52ea09e0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned2.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned2.php.inc new file mode 100644 index 00000000000..9e4ed5c6aa9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned2.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned3.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned3.php.inc new file mode 100644 index 00000000000..6eba0ae8155 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/do_while_maybe_returned3.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/if_elseif_else_partial_no_return.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/if_elseif_else_partial_no_return.php.inc new file mode 100644 index 00000000000..d53b3c95a52 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/if_elseif_else_partial_no_return.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/if_elseif_without_else.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/if_elseif_without_else.php.inc new file mode 100644 index 00000000000..86167cf8549 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/if_elseif_without_else.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/on_function.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/on_function.php.inc new file mode 100644 index 00000000000..feaee223771 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/on_function.php.inc @@ -0,0 +1,42 @@ + 50) { + return 'yes'; + } + } + + return test($number); + } +} + +?> +----- + 50) { + return 'yes'; + } + return null; + } + + return test($number); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/return_no_value_and_return_value.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/return_no_value_and_return_value.php.inc new file mode 100644 index 00000000000..42da2221260 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/return_no_value_and_return_value.php.inc @@ -0,0 +1,46 @@ + 50) { + return; + } + + if ($number < 50) { + return 100; + } + } +} + +?> +----- + 50) { + return null; + } + + if ($number < 50) { + return 100; + } + return null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/return_no_value_and_return_value2.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/return_no_value_and_return_value2.php.inc new file mode 100644 index 00000000000..4ac6fa51731 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/return_no_value_and_return_value2.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip-goto.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip-goto.php.inc new file mode 100644 index 00000000000..5fc4ddfba5e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip-goto.php.inc @@ -0,0 +1,21 @@ +neverReturns(); + } + + private function neverReturns($value): never + { + exit(); + } +} +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_already_return.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_already_return.php.inc new file mode 100644 index 00000000000..63228c7bc24 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_already_return.php.inc @@ -0,0 +1,15 @@ + 50) { + return 'yes'; + } + + return 100; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_always_returned_in_try_catch_with_finally.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_always_returned_in_try_catch_with_finally.php.inc new file mode 100644 index 00000000000..a0c0dca6e8e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_always_returned_in_try_catch_with_finally.php.inc @@ -0,0 +1,21 @@ + 50) { + return 'yes'; + } + + echo 'test'; + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_do_while_always_returned.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_do_while_always_returned.php.inc new file mode 100644 index 00000000000..78efe8c22ea --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_do_while_always_returned.php.inc @@ -0,0 +1,17 @@ + 50) { + return 'yes'; + } else { + return 100; + } + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_if_else_return_in_try_catch.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_if_else_return_in_try_catch.php.inc new file mode 100644 index 00000000000..05f11a02c86 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_if_else_return_in_try_catch.php.inc @@ -0,0 +1,19 @@ +storage->download($file, $headers); + } catch (RemoteFileNotFoundException $e) { + return $this->localStorage->download($file, $headers); + } + } catch (FileNotFoundException $e) { + abort(404); + } + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_never_type_in_default_switch.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_never_type_in_default_switch.php.inc new file mode 100644 index 00000000000..42dea479b55 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_never_type_in_default_switch.php.inc @@ -0,0 +1,21 @@ +error(); + } + } + + private function error(): never { + throw new \RuntimeException('Nope'); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_return_all_return_without_expr.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_return_all_return_without_expr.php.inc new file mode 100644 index 00000000000..ef52d980019 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_return_all_return_without_expr.php.inc @@ -0,0 +1,19 @@ + 50) { + echo 'test'; + return; + } + + if ($number < 50) { + echo 'yes'; + return; + } + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_switch_always_return.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_switch_always_return.php.inc new file mode 100644 index 00000000000..e7951bfc47a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/skip_switch_always_return.php.inc @@ -0,0 +1,17 @@ + 50) { + return 1; + } + + yield 'foo'; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/some_class.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..3b277ec9e2c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/some_class.php.inc @@ -0,0 +1,32 @@ + 50) { + return 'yes'; + } + } +} + +?> +----- + 50) { + return 'yes'; + } + return null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/switch_maybe_return.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/switch_maybe_return.php.inc new file mode 100644 index 00000000000..e3c4e0c410f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/switch_maybe_return.php.inc @@ -0,0 +1,38 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/with_union_void_doc.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/with_union_void_doc.php.inc new file mode 100644 index 00000000000..77b475c104e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/Fixture/with_union_void_doc.php.inc @@ -0,0 +1,38 @@ + 50) { + return 'yes'; + } + } +} + +?> +----- + 50) { + return 'yes'; + } + return null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/config/configured_rule.php new file mode 100644 index 00000000000..137ae2727c5 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ExplicitReturnNullRector::class); +}; diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/empty_array.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/empty_array.php.inc new file mode 100644 index 00000000000..72f22bfb62c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/empty_array.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_empty_assign.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_empty_assign.php.inc new file mode 100644 index 00000000000..084e1f7e4c7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_empty_assign.php.inc @@ -0,0 +1,32 @@ + +----- + 'Back']; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_if.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_if.php.inc new file mode 100644 index 00000000000..ca90279ad5f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_if.php.inc @@ -0,0 +1,41 @@ + +----- + 'Timmy', 'surname' => 'Back']; + } + + return null; + } +} + + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_nested_variable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_nested_variable.php.inc new file mode 100644 index 00000000000..9f2d0393d81 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/include_nested_variable.php.inc @@ -0,0 +1,30 @@ + +----- + 'John', 'surname' => 'Doe', 'age' => 50]]; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/nested_array_with_multiple_keys.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/nested_array_with_multiple_keys.php.inc new file mode 100644 index 00000000000..09586b81094 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/nested_array_with_multiple_keys.php.inc @@ -0,0 +1,32 @@ + 1, 'b' => 2]; + + return $arr; + } +} + + +?> +----- + ['bar' => ['baz' => ['a' => 1, 'b' => 2]]]]; + } +} + + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_append_from_params.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_append_from_params.php.inc new file mode 100644 index 00000000000..7a1a80b15d8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_append_from_params.php.inc @@ -0,0 +1,14 @@ + 'type', + 'id' => (string) $someObject->getId(), + ]; + $message['link'] = $this->getSome($someObject); + + return $message; + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_multiple_returns.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_multiple_returns.php.inc new file mode 100644 index 00000000000..37abc89ec47 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_multiple_returns.php.inc @@ -0,0 +1,19 @@ + 123, + ]; + + $data['info']['nested'] = 123; + + return $data; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_use_closure_use_by_ref.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_use_closure_use_by_ref.php.inc new file mode 100644 index 00000000000..c35a374c51d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/skip_use_closure_use_by_ref.php.inc @@ -0,0 +1,20 @@ +person['name'] = 'Back'; + + return $person; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/some_class.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..dd37a3608a6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/some_class.php.inc @@ -0,0 +1,25 @@ + +----- + 'Timmy', 'surname' => 'Back']; +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/some_object.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/some_object.php.inc new file mode 100644 index 00000000000..264c0f48746 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/some_object.php.inc @@ -0,0 +1,30 @@ + +----- + 'John', 'surname' => 'Doe', 'age' => 50]; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_first_class_callable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_first_class_callable.php.inc new file mode 100644 index 00000000000..06a06e84544 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_first_class_callable.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_property_fetch.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_property_fetch.php.inc new file mode 100644 index 00000000000..81fb58cc089 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_property_fetch.php.inc @@ -0,0 +1,48 @@ +name; + $event['payload'] = $this->bar(); + $event['data'] = $obj?->data; + $event['item'] = $obj?->getItem(); + $event['list'] = clone $obj; + $event['verify'] = $obj instanceof \stdClass; + + return $event; + } + + private function bar(): array + { + return []; + } +} + +?> +----- + $this->name, 'payload' => $this->bar(), 'data' => $obj?->data, 'item' => $obj?->getItem(), 'list' => clone $obj, 'verify' => $obj instanceof \stdClass]; + } + + private function bar(): array + { + return []; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_unary.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_unary.php.inc new file mode 100644 index 00000000000..cce6690a2ae --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Fixture/with_unary.php.inc @@ -0,0 +1,30 @@ + +----- + '2025-09-15', 'plus' => +50000], ['minus' => -67500]]; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/InlineArrayReturnAssignRectorTest.php b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/InlineArrayReturnAssignRectorTest.php new file mode 100644 index 00000000000..122a79ff72d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/InlineArrayReturnAssignRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Source/SomeAssignedObject.php b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Source/SomeAssignedObject.php new file mode 100644 index 00000000000..afec31e4a63 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector/Source/SomeAssignedObject.php @@ -0,0 +1,11 @@ +withRules([InlineArrayReturnAssignRector::class]); diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/case_with_parent.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/case_with_parent.php.inc new file mode 100644 index 00000000000..889076218aa --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/case_with_parent.php.inc @@ -0,0 +1,39 @@ + +----- +someStatic(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/double_static_private_method.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/double_static_private_method.php.inc new file mode 100644 index 00000000000..078159246a0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/double_static_private_method.php.inc @@ -0,0 +1,45 @@ + +----- +run(); + } + + private function run() + { + $this->someStatic(); + } + + private function someStatic() + { + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/first_method_then_call.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/first_method_then_call.php.inc new file mode 100644 index 00000000000..f89e353bf4a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/first_method_then_call.php.inc @@ -0,0 +1,35 @@ + +----- +someStatic(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..b844e7475be --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/fixture.php.inc @@ -0,0 +1,41 @@ + +----- +someStatic(); + $this->someStatic(); + $this->someStatic(); + $this->someStatic(); + } + + private function someStatic() + { + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/method_with_arg.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/method_with_arg.php.inc new file mode 100644 index 00000000000..4c59eb7cb05 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/method_with_arg.php.inc @@ -0,0 +1,35 @@ + +----- +someStatic($arg); + } + + private function someStatic($arg) + { + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_commented.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_commented.php.inc new file mode 100644 index 00000000000..c9ad3e61ccf --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_commented.php.inc @@ -0,0 +1,25 @@ + 'time']; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_in_static_closure.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_in_static_closure.php.inc new file mode 100644 index 00000000000..3b4ea4dfe77 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_in_static_closure.php.inc @@ -0,0 +1,18 @@ + $b; + } + + public static function foo(): void + { + $array = []; + usort($array, [self::class, 'bar']); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/LocallyCalledStaticMethodToNonStaticRectorTest.php b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/LocallyCalledStaticMethodToNonStaticRectorTest.php new file mode 100644 index 00000000000..6116fdd32d9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/LocallyCalledStaticMethodToNonStaticRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Source/SomeParent.php b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Source/SomeParent.php new file mode 100644 index 00000000000..2b2f4bc1103 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Source/SomeParent.php @@ -0,0 +1,9 @@ +withRules([LocallyCalledStaticMethodToNonStaticRector::class]); diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/fixture.php.inc deleted file mode 100644 index 9ff814e4dee..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/param_scalar.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/param_scalar.php.inc deleted file mode 100644 index 8cf3d9c969c..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/param_scalar.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_constant_union.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_constant_union.php.inc deleted file mode 100644 index 9fbf4bb7ba4..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_constant_union.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_literal_union.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_literal_union.php.inc deleted file mode 100644 index 7c0f7a2f64f..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_literal_union.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_non_union.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_non_union.php.inc deleted file mode 100644 index 4404dd80713..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_non_union.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_scalar_partial.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_scalar_partial.php.inc deleted file mode 100644 index 25e1aedeb7d..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/Fixture/skip_scalar_partial.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/NarrowUnionTypeDocRectorTest.php b/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/NarrowUnionTypeDocRectorTest.php deleted file mode 100644 index 50cb2e37e86..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/NarrowUnionTypeDocRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/config/configured_rule.php deleted file mode 100644 index e9b51ddd16c..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(NarrowUnionTypeDocRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/for_loop.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/for_loop.php.inc new file mode 100644 index 00000000000..9dd5bf8bc72 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/for_loop.php.inc @@ -0,0 +1,27 @@ +getRepository(Product::class)->findAll(); + $visitedProducts = []; + for ($i = 0; $i < count($products), count($visitedProducts) <= 3; ++$i) { + $product = $products[$i]; + if (!in_array($product, $exclude)) { + $varName = 'visitedProduct' . ($i + 1); + $visitedProducts[$varName] = $product; + } + } + + extract($visitedProducts); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/mashup.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/mashup.php.inc new file mode 100644 index 00000000000..24c4e75b9ce --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/mashup.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/new_the_constructor.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/new_the_constructor.php.inc new file mode 100644 index 00000000000..860c55635a1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/new_the_constructor.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/on_function.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/on_function.php.inc new file mode 100644 index 00000000000..768f593ffb1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/on_function.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_array_dim_fetch_class_callable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_array_dim_fetch_class_callable.php.inc new file mode 100644 index 00000000000..eaec7caf658 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_array_dim_fetch_class_callable.php.inc @@ -0,0 +1,17 @@ +call(...)]; + } + + public function call() + { + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_assign_ref_first_class_callable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_assign_ref_first_class_callable.php.inc new file mode 100644 index 00000000000..da161c4f188 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_assign_ref_first_class_callable.php.inc @@ -0,0 +1,17 @@ +call(...); + } + + public function call() + { + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_call_on_closure_named_arg.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_call_on_closure_named_arg.php.inc new file mode 100644 index 00000000000..5c97f356382 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_call_on_closure_named_arg.php.inc @@ -0,0 +1,18 @@ +businesses() + ->when($request->isMethod('get'), function (EloquentQueryBuilder $query): void { + // + })->paginate( + perPage: $request->validated('length') + ); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_correct_optional_after_required.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_correct_optional_after_required.php.inc new file mode 100644 index 00000000000..499019a6c0e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_correct_optional_after_required.php.inc @@ -0,0 +1,9 @@ +call(...)); + } + + public function call() + { + return []; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_external_new.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_external_new.php.inc new file mode 100644 index 00000000000..21d1b75bc76 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_external_new.php.inc @@ -0,0 +1,15 @@ + $this->textElement(...); + } + + public function textElement() { /* ... */ } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_call.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_call.php.inc new file mode 100644 index 00000000000..59fe6fdb0fc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_call.php.inc @@ -0,0 +1,17 @@ +textElement(...)->execute(...); + } + + public function textElement() + { + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_call_function.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_call_function.php.inc new file mode 100644 index 00000000000..134b84cec13 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_call_function.php.inc @@ -0,0 +1,17 @@ +execute(...); + } + + public function textElement() + { + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_do.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_do.php.inc new file mode 100644 index 00000000000..ce8b282f440 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_do.php.inc @@ -0,0 +1,20 @@ +textElement(...)); + } + + public function textElement() + { + return 1; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_echo.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_echo.php.inc new file mode 100644 index 00000000000..67527f5a95a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_echo.php.inc @@ -0,0 +1,15 @@ +textElement(...); + } + + public function textElement() { return 'test'; } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_if.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_if.php.inc new file mode 100644 index 00000000000..5123da5868e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_if.php.inc @@ -0,0 +1,22 @@ +textElement(...)) { + + } elseif ($this->textElement(...)) { + + } + } + + public function textElement() + { + return 1; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_unary.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_unary.php.inc new file mode 100644 index 00000000000..b9fa95601a1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_unary.php.inc @@ -0,0 +1,15 @@ +textElement(...); + } + + public function textElement() { return 1; } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_while.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_while.php.inc new file mode 100644 index 00000000000..138f271687b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_while.php.inc @@ -0,0 +1,20 @@ +textElement(...)) { + + } + } + + public function textElement() + { + return 1; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_yield.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_yield.php.inc new file mode 100644 index 00000000000..a3b577669a1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_yield.php.inc @@ -0,0 +1,15 @@ +textElement(...); + } + + public function textElement() { /* ... */ } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_yield_from.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_yield_from.php.inc new file mode 100644 index 00000000000..df08ede20cc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_first_class_callable_in_yield_from.php.inc @@ -0,0 +1,15 @@ +textElement(...); + } + + public function textElement() { return []; } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_fluent_call_from_func_call.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_fluent_call_from_func_call.php.inc new file mode 100644 index 00000000000..3eb65d6b7cf --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_fluent_call_from_func_call.php.inc @@ -0,0 +1,25 @@ +map(function ($column, $key) { + switch ($key) { + case 'id': + return "{$key}:id"; + default: + return match ($column['type']) { + 'array', 'object' => "{$key}:json:nullable", + }; + } + }) + ->filter() + ->all(); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_func_get_args.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_func_get_args.php.inc new file mode 100644 index 00000000000..90be976c8df --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_func_get_args.php.inc @@ -0,0 +1,19 @@ +go(1, 5, 100); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_include_first_class_callable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_include_first_class_callable.php.inc new file mode 100644 index 00000000000..3ccf9896c29 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_include_first_class_callable.php.inc @@ -0,0 +1,17 @@ +call(...); + } + + public function call() + { + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_inside_block_statements.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_inside_block_statements.php.inc new file mode 100644 index 00000000000..11f0dcc466e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_inside_block_statements.php.inc @@ -0,0 +1,28 @@ +createForm(ArticleFormType::class, $article); + return $this->render('Article/edit.html.twig', [ + 'media' => $media, + 'form' => $form->createView(), + 'headline' => $headline, + 'article' => $article + ]); + + $form = $this->createForm(HeadlineType::class, $headline); + return $this->render('headline/new.html.twig', [ + 'headline' => $headline, + 'media' => $media, + 'form' => $form->createView(), + ]); + } + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_instanceof_first_class_callable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_instanceof_first_class_callable.php.inc new file mode 100644 index 00000000000..0157eb70dae --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_instanceof_first_class_callable.php.inc @@ -0,0 +1,19 @@ +call(...) instanceof stdClass; + } + + public function call() + { + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_isset_first_class_callable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_isset_first_class_callable.php.inc new file mode 100644 index 00000000000..7226448ba5b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_isset_first_class_callable.php.inc @@ -0,0 +1,17 @@ +call(...)); + } + + public function call() + { + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_match_with_first_class_callable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_match_with_first_class_callable.php.inc new file mode 100644 index 00000000000..e8df956b3a8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_match_with_first_class_callable.php.inc @@ -0,0 +1,18 @@ + SomeClass::fromString(...), + 'int' => function () { + return SomeClass::fromInt(...); + } + }; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_native_func_call.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_native_func_call.php.inc new file mode 100644 index 00000000000..01e0736f322 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_native_func_call.php.inc @@ -0,0 +1,5 @@ + $a > get(CookieManager::class)->all(); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_on_property_hook2.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_on_property_hook2.php.inc new file mode 100644 index 00000000000..2b2d5b08482 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_on_property_hook2.php.inc @@ -0,0 +1,10 @@ + get(CookieManager::class)->all(); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_throw_first_class_callable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_throw_first_class_callable.php.inc new file mode 100644 index 00000000000..cb995cc8134 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_throw_first_class_callable.php.inc @@ -0,0 +1,20 @@ +call(...); + } + + public function call() + { + return new InvalidArgumentException(); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable.php.inc new file mode 100644 index 00000000000..257e80c4ad6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable.php.inc @@ -0,0 +1,10 @@ +item(0)->textContent); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable2.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable2.php.inc new file mode 100644 index 00000000000..beb61f3e49d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable2.php.inc @@ -0,0 +1,10 @@ +item(0)->textContent); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable3.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable3.php.inc new file mode 100644 index 00000000000..391fe3157fd --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable3.php.inc @@ -0,0 +1,11 @@ +getItem()->getTextContent()); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable5.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable5.php.inc new file mode 100644 index 00000000000..d528f64a6b9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_variable_variable5.php.inc @@ -0,0 +1,13 @@ +Where(function ($query) use ($field, $key) { + $query->where($key, '!=', $field['search_value'][0]['val'])->orWhere($key, 'exists', false); + }); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/some_object.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/some_object.php.inc new file mode 100644 index 00000000000..1e50fc489a1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/some_object.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/treat_variadic_parameter_as_optional.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/treat_variadic_parameter_as_optional.php.inc new file mode 100644 index 00000000000..3d442865ebb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/treat_variadic_parameter_as_optional.php.inc @@ -0,0 +1,13 @@ +products = $products; + return $cart; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/typed_params.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/typed_params.php.inc new file mode 100644 index 00000000000..719bb40be34 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/typed_params.php.inc @@ -0,0 +1,89 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call.php.inc new file mode 100644 index 00000000000..7d0dd962d2c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call.php.inc @@ -0,0 +1,39 @@ +run(1, 5); + } +} + +?> +----- +run(1, 5); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call_by_fluent.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call_by_fluent.php.inc new file mode 100644 index 00000000000..8e81d249666 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call_by_fluent.php.inc @@ -0,0 +1,51 @@ +run(1, 5) + ->execute(); + } +} + +?> +----- +run(1, 5) + ->execute(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call_by_fluent2.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call_by_fluent2.php.inc new file mode 100644 index 00000000000..39e8adae837 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call_by_fluent2.php.inc @@ -0,0 +1,53 @@ +execute() + ->run(1, 5); + } +} + +?> +----- +execute() + ->run(1, 5); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_params_order_of_static_method_call.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_params_order_of_static_method_call.php.inc new file mode 100644 index 00000000000..07cac15f239 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_params_order_of_static_method_call.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/uses_with_less_params.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/uses_with_less_params.php.inc new file mode 100644 index 00000000000..28fe322e1f8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/uses_with_less_params.php.inc @@ -0,0 +1,41 @@ +blah(1, 5); + $this->blah(1, 5, 2); + } +} + +?> +----- +blah(1, 5); + $this->blah(1, 5, 2); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/with_closure.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/with_closure.php.inc new file mode 100644 index 00000000000..31da5e8a3da --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/with_closure.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/OptionalParametersAfterRequiredRectorTest.php b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/OptionalParametersAfterRequiredRectorTest.php new file mode 100644 index 00000000000..32f5fe2d1b3 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/OptionalParametersAfterRequiredRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Source/SomeClass.php b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Source/SomeClass.php new file mode 100644 index 00000000000..d3209fa71f0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Source/SomeClass.php @@ -0,0 +1,11 @@ +withRules([OptionalParametersAfterRequiredRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/CompleteDynamicPropertiesRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/CompleteDynamicPropertiesRectorTest.php index c627c24e855..451b011fc12 100644 --- a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/CompleteDynamicPropertiesRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/CompleteDynamicPropertiesRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CompleteDynamicPropertiesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/class_string_case_insensitive.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/class_string_case_insensitive.php.inc new file mode 100644 index 00000000000..619c2861e7c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/class_string_case_insensitive.php.inc @@ -0,0 +1,29 @@ +value = 'classStringCaseInSensitive'; + } +} + +?> +----- +value = 'classStringCaseInSensitive'; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/class_string_case_sensitive.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/class_string_case_sensitive.php.inc new file mode 100644 index 00000000000..29ba6a5af3b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/class_string_case_sensitive.php.inc @@ -0,0 +1,29 @@ +value = 'ClassStringCaseSensitive'; + } +} + +?> +----- + + */ + public $value; + public function set() + { + $this->value = 'ClassStringCaseSensitive'; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/constructor_private.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/constructor_private.php.inc new file mode 100644 index 00000000000..d55b5e114a2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/constructor_private.php.inc @@ -0,0 +1,28 @@ +value = 'constructorPrivate'; + } +} + +?> +----- +value = 'constructorPrivate'; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/fixture.php.inc index cfdb69c468e..71bcd344f6b 100644 --- a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/fixture.php.inc @@ -18,7 +18,10 @@ namespace Rector\Tests\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector class Fixture { - public int $value; + /** + * @var int + */ + public $value; public function set() { $this->value = 5; diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/multiple_props.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/multiple_props.php.inc new file mode 100644 index 00000000000..ca3e3ababef --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/multiple_props.php.inc @@ -0,0 +1,35 @@ +c = 5; + } +} + +?> +----- +c = 5; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/setup_private.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/setup_private.php.inc new file mode 100644 index 00000000000..35f463e49d6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/setup_private.php.inc @@ -0,0 +1,28 @@ +initialValue = 123; + } +} + +?> +----- +initialValue = 123; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_allow_dynamic_attribute.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_allow_dynamic_attribute.php.inc new file mode 100644 index 00000000000..36f2ac581e9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_allow_dynamic_attribute.php.inc @@ -0,0 +1,14 @@ +value = 5; + + $this->value = 'hey'; + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_inner_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_inner_class.php.inc new file mode 100644 index 00000000000..3db4284261d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_inner_class.php.inc @@ -0,0 +1,20 @@ +logged = $value; + } + }; + + echo $box->logged; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_late_static_binding.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_late_static_binding.php.inc new file mode 100644 index 00000000000..a30ca6cba33 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_late_static_binding.php.inc @@ -0,0 +1,13 @@ +{static::PRIMARY_KEY} = 1; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_pull_from_different_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_pull_from_different_class.php.inc new file mode 100644 index 00000000000..ed12ee3ecc8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_pull_from_different_class.php.inc @@ -0,0 +1,14 @@ +property; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_static_return_object.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_static_return_object.php.inc index e972e3e0cf9..8c446642c74 100644 --- a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_static_return_object.php.inc +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_static_return_object.php.inc @@ -2,20 +2,12 @@ namespace Rector\Tests\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector\Fixture; -final class Carbon -{ - public $timestamp; - - public static function now() - { - return new static(); - } -} +use Rector\Tests\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector\Source\StaticFactory; -class SkipStaticReturnObject +final class SkipStaticReturnObject { public function getCurrentTimestamp() { - return Carbon::now()->timestamp; + return StaticFactory::now()->timestamp; } -} \ No newline at end of file +} diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_undefined_parent_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_undefined_parent_class.php.inc new file mode 100644 index 00000000000..22e4b0dd072 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/skip_undefined_parent_class.php.inc @@ -0,0 +1,11 @@ +value = 5; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/with_array_type.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/with_array_type.php.inc index 19d3bd586a6..ceb7537d426 100644 --- a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/with_array_type.php.inc +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Fixture/with_array_type.php.inc @@ -21,7 +21,7 @@ final class WithArrayType /** * @var array */ - public array $someProperty; + public $someProperty; public function addSome(string $name) { $this->someProperty[$name] = true; diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/FixtureUnionTypes/multiple_types.php.inc b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/FixtureUnionTypes/multiple_types.php.inc index 3a14aedb13e..eade72ab4c9 100644 --- a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/FixtureUnionTypes/multiple_types.php.inc +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/FixtureUnionTypes/multiple_types.php.inc @@ -22,7 +22,10 @@ namespace Rector\Tests\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector class MultipleTypes { - public int|string|bool $value; + /** + * @var bool|int|string + */ + public $value; public function set() { $this->value = 5; diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Source/DifferentClass.php b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Source/DifferentClass.php new file mode 100644 index 00000000000..7499b5e76ac --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/Source/DifferentClass.php @@ -0,0 +1,10 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureUnionTypes'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureUnionTypes'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/config/configured_rule.php index 58bbcca55f5..5b19208ea16 100644 --- a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CompleteDynamicPropertiesRector::class); -}; +return RectorConfig::configure() + ->withRules([CompleteDynamicPropertiesRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/config/pre_union_types.php b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/config/pre_union_types.php index 325f35a2d33..1cadc358a63 100644 --- a/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/config/pre_union_types.php +++ b/rules-tests/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector/config/pre_union_types.php @@ -3,14 +3,10 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector; -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersionFeature; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(CompleteDynamicPropertiesRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES - 1); + $rectorConfig->rule(CompleteDynamicPropertiesRector::class); }; diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/ConvertStaticToSelfRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/ConvertStaticToSelfRectorTest.php new file mode 100644 index 00000000000..5b306f205af --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/ConvertStaticToSelfRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_basic.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_basic.php.inc new file mode 100644 index 00000000000..e40e5146a49 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_basic.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_inherited_members.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_inherited_members.php.inc new file mode 100644 index 00000000000..5786499fe0a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_inherited_members.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_class_final_members.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_class_final_members.php.inc new file mode 100644 index 00000000000..ceb6d92b41b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_class_final_members.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_private_members.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_private_members.php.inc new file mode 100644 index 00000000000..bfa0ec502cc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_private_members.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_abstract_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_abstract_class.php.inc new file mode 100644 index 00000000000..4fc323b9c55 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_abstract_class.php.inc @@ -0,0 +1,21 @@ + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_non_existent_members.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_non_existent_members.php.inc new file mode 100644 index 00000000000..b4b6b798766 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_non_existent_members.php.inc @@ -0,0 +1,13 @@ +withRules([ConvertStaticToSelfRector::class]) + ->withPhpVersion(PhpVersionFeature::FINAL_CLASS_CONSTANTS); diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/DynamicDocBlockPropertyToNativePropertyRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/DynamicDocBlockPropertyToNativePropertyRectorTest.php new file mode 100644 index 00000000000..2655facb652 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/DynamicDocBlockPropertyToNativePropertyRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/aliased_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/aliased_class.php.inc new file mode 100644 index 00000000000..935f20dd3f7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/aliased_class.php.inc @@ -0,0 +1,36 @@ +someDependency = new SomeSource\SomeDependency(); + } +} + +?> +----- +someDependency = new SomeSource\SomeDependency(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/bare_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/bare_class.php.inc new file mode 100644 index 00000000000..bd35375ab18 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/bare_class.php.inc @@ -0,0 +1,36 @@ +someDependency = new SomeDependency(); + } +} + +?> +----- +someDependency = new SomeDependency(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/existing_property.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/existing_property.php.inc new file mode 100644 index 00000000000..f8da48afb29 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/existing_property.php.inc @@ -0,0 +1,42 @@ +someDependency = new SomeDependency(); + } +} + +?> +----- +someDependency = new SomeDependency(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/improve_existing_property_type.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/improve_existing_property_type.php.inc new file mode 100644 index 00000000000..1eef510f6be --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/improve_existing_property_type.php.inc @@ -0,0 +1,42 @@ +someDependency = new SomeDependency(); + } +} + +?> +----- +someDependency = new SomeDependency(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/include_null.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/include_null.php.inc new file mode 100644 index 00000000000..582ef425fa3 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/include_null.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/promoted_property_priority.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/promoted_property_priority.php.inc new file mode 100644 index 00000000000..c58930362af --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/promoted_property_priority.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_get_magic.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_get_magic.php.inc new file mode 100644 index 00000000000..610d6d58240 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_get_magic.php.inc @@ -0,0 +1,21 @@ +someDependency = new SomeDependency(); + } + + public function __get($name) + { + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_no_allow_dynamic_properties_attribute.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_no_allow_dynamic_properties_attribute.php.inc new file mode 100644 index 00000000000..b00462d7500 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_no_allow_dynamic_properties_attribute.php.inc @@ -0,0 +1,16 @@ +someDependency = new SomeDependency(); + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_no_property_doc.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_no_property_doc.php.inc new file mode 100644 index 00000000000..2eba11931e9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_no_property_doc.php.inc @@ -0,0 +1,17 @@ +someDependency = new SomeDependency(); + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_set_magic.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_set_magic.php.inc new file mode 100644 index 00000000000..b7b1bdaa8dc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/skip_set_magic.php.inc @@ -0,0 +1,21 @@ +someDependency = new SomeDependency(); + } + + public function __set($name, $value) + { + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/some_test.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/some_test.php.inc new file mode 100644 index 00000000000..85dfee2299d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/some_test.php.inc @@ -0,0 +1,40 @@ +someDependency = new SomeDependency(); + } +} + +?> +----- +someDependency = new SomeDependency(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/with_other_existing_attribute.php.inc b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/with_other_existing_attribute.php.inc new file mode 100644 index 00000000000..12fa3c20eaa --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Fixture/with_other_existing_attribute.php.inc @@ -0,0 +1,38 @@ +someDependency = new SomeDependency(); + } +} + +?> +----- +someDependency = new SomeDependency(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Source/SomeDependency.php b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Source/SomeDependency.php new file mode 100644 index 00000000000..f1e41a1cf5d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector/Source/SomeDependency.php @@ -0,0 +1,7 @@ +phpVersion(PhpVersionFeature::DEPRECATE_DYNAMIC_PROPERTIES); + + $rectorConfig->rule(DynamicDocBlockPropertyToNativePropertyRector::class); +}; diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_has_before_if.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_has_before_if.php.inc new file mode 100644 index 00000000000..ffbf141ace1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_has_before_if.php.inc @@ -0,0 +1,47 @@ +age = 20; + + if (!$initName) { + $this->gender = 'M'; + + return; + } + + $this->name = 'John'; + } +} +?> +----- +gender = 'M'; + + return; + } + + $this->name = 'John'; + } +} +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_readonly.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_readonly.php.inc new file mode 100644 index 00000000000..4d7296a7f14 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_readonly.php.inc @@ -0,0 +1,13 @@ +name = 'John'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_readonly_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_readonly_class.php.inc new file mode 100644 index 00000000000..3f7dea6fc28 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/do_not_change_readonly_class.php.inc @@ -0,0 +1,13 @@ +name = 'John'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/mirror_comment.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/mirror_comment.php.inc new file mode 100644 index 00000000000..735b6a51796 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/mirror_comment.php.inc @@ -0,0 +1,32 @@ +thing = 1; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/mirror_comment_multi_properties.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/mirror_comment_multi_properties.php.inc new file mode 100644 index 00000000000..219c8a00d02 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/mirror_comment_multi_properties.php.inc @@ -0,0 +1,44 @@ +thing1 = 1; + // second + $this->thing2 = 2; + // third + $this->thing3 = 3; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/multi_properties.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/multi_properties.php.inc new file mode 100644 index 00000000000..036c55a0b00 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/multi_properties.php.inc @@ -0,0 +1,32 @@ +thing1 = 1; + $this->thing2 = 2; + $this->thing3 = 3; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_constant.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_constant.php.inc new file mode 100644 index 00000000000..38f7de59e35 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_constant.php.inc @@ -0,0 +1,65 @@ +number1 = +FLOAT_CONSTANT_1; + $this->number2 = FLOAT_CONSTANT_1; + $this->number3 = -FLOAT_CONSTANT_1; + $this->number4 = +self::INT_CONSTANT_2; + $this->number5 = self::INT_CONSTANT_2; + $this->number6 = -self::INT_CONSTANT_2; + $this->number7 = +static::INT_CONSTANT_2; + $this->number8 = static::INT_CONSTANT_2; + $this->number9 = -static::INT_CONSTANT_2; + } +} + +?> +----- +number7 = +static::INT_CONSTANT_2; + $this->number8 = static::INT_CONSTANT_2; + $this->number9 = -static::INT_CONSTANT_2; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_float.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_float.php.inc new file mode 100644 index 00000000000..13b3f3e9fe7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_float.php.inc @@ -0,0 +1,36 @@ +propertyA = -100.1; + $this->propertyB = 100; + $this->propertyC = +100.2; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_int.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_int.php.inc new file mode 100644 index 00000000000..e0687c10239 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_int.php.inc @@ -0,0 +1,36 @@ +propertyA = -100; + $this->propertyB = 100; + $this->propertyC = +100; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_numeric_string.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_numeric_string.php.inc new file mode 100644 index 00000000000..155055565f7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/negative_positive_numeric_string.php.inc @@ -0,0 +1,36 @@ +propertyA = -"100"; + $this->propertyB = "100"; + $this->propertyC = +"100"; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/simple_array.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/simple_array.php.inc new file mode 100644 index 00000000000..9ba542564cd --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/simple_array.php.inc @@ -0,0 +1,30 @@ +items = ['John', true, 252 => [null]]; + } +} + +?> +----- + [null]]; + + public function __construct() + { + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_complex_assign.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_complex_assign.php.inc new file mode 100644 index 00000000000..1cf85e7444f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_complex_assign.php.inc @@ -0,0 +1,13 @@ +name = $name; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_conditional_default.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_conditional_default.php.inc new file mode 100644 index 00000000000..bba451c811b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_conditional_default.php.inc @@ -0,0 +1,15 @@ +name = 'John'; + } + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_dynamic_negative_positive.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_dynamic_negative_positive.php.inc new file mode 100644 index 00000000000..4bdc3d88890 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_dynamic_negative_positive.php.inc @@ -0,0 +1,20 @@ +propertyA = -$this->init(); + $this->propertyB = +$this->init(); + } + + private function init() + { + return 1; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_static_class_const_fetch.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_static_class_const_fetch.php.inc new file mode 100644 index 00000000000..3e96c4c8baf --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_static_class_const_fetch.php.inc @@ -0,0 +1,13 @@ +name = static::class; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_static_class_const_fetch2.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_static_class_const_fetch2.php.inc new file mode 100644 index 00000000000..173c95a5c56 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_static_class_const_fetch2.php.inc @@ -0,0 +1,15 @@ +name = static::NUMBER; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_string_interpolation.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_string_interpolation.php.inc new file mode 100644 index 00000000000..51d8c83c116 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_string_interpolation.php.inc @@ -0,0 +1,13 @@ +name = "interpolated {$name}"; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_used_by_property_hook.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_used_by_property_hook.php.inc new file mode 100644 index 00000000000..783394091b7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/skip_used_by_property_hook.php.inc @@ -0,0 +1,33 @@ +value; + } + set { + if (!$this->isConstructed) { + $this->value = $value; + return; + } + + if (!is_string($value)) { + throw new \TypeError('$value must be a string when it is already constructed!'); + } + + $this->value = $value; + } + } + + public function __construct( + mixed $value = null, + ) { + $this->value = $value; + $this->isConstructed = true; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/some_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..7ff6b790298 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/Fixture/some_class.php.inc @@ -0,0 +1,30 @@ +name = 'John'; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/InlineConstructorDefaultToPropertyRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/InlineConstructorDefaultToPropertyRectorTest.php new file mode 100644 index 00000000000..e6612c5b155 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/InlineConstructorDefaultToPropertyRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/config/configured_rule.php new file mode 100644 index 00000000000..a85c00656b2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([InlineConstructorDefaultToPropertyRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_with_attribute.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_with_attribute.php.inc new file mode 100644 index 00000000000..63bbdd463a9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_with_attribute.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_with_attribute_inline.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_with_attribute_inline.php.inc new file mode 100644 index 00000000000..c290ca5bca1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_with_attribute_inline.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_without_attribute.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_without_attribute.php.inc new file mode 100644 index 00000000000..cb63b1c0412 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/class_without_attribute.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/combine_promo_and_property_readonly.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/combine_promo_and_property_readonly.php.inc new file mode 100644 index 00000000000..1d78a687567 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/combine_promo_and_property_readonly.php.inc @@ -0,0 +1,35 @@ +b = $b ?? 'foo'; + } +} + +?> +----- +b = $b ?? 'foo'; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/implicit_public_readonly_property.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/implicit_public_readonly_property.php.inc new file mode 100644 index 00000000000..950575aab59 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/implicit_public_readonly_property.php.inc @@ -0,0 +1,31 @@ +foo = 'bar'; + } +} + +?> +----- +foo = 'bar'; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/implicit_public_readonly_property_promotion.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/implicit_public_readonly_property_promotion.php.inc new file mode 100644 index 00000000000..28bbd50cbd7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/implicit_public_readonly_property_promotion.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/only_readonly_property.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/only_readonly_property.php.inc new file mode 100644 index 00000000000..d104a6a6290 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/only_readonly_property.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/only_readonly_property2.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/only_readonly_property2.php.inc new file mode 100644 index 00000000000..d44061393cb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/only_readonly_property2.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/with_attribute_on_property.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/with_attribute_on_property.php.inc new file mode 100644 index 00000000000..264004169fa --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/with_attribute_on_property.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/with_attribute_on_property_promotion.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/with_attribute_on_property_promotion.php.inc new file mode 100644 index 00000000000..d323c4e1373 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/Fixture/with_attribute_on_property_promotion.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/RemoveReadonlyPropertyVisibilityOnReadonlyClassRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/RemoveReadonlyPropertyVisibilityOnReadonlyClassRectorTest.php new file mode 100644 index 00000000000..983b49bba82 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/RemoveReadonlyPropertyVisibilityOnReadonlyClassRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/config/configured_rule.php new file mode 100644 index 00000000000..2a2f36a23bc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(RemoveReadonlyPropertyVisibilityOnReadonlyClassRector::class); +}; diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/CoalesceToTernaryRectorTest.php b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/CoalesceToTernaryRectorTest.php new file mode 100644 index 00000000000..794029d9f2d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/CoalesceToTernaryRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc new file mode 100644 index 00000000000..5c094d481ec --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_array_dim_fetch.php.inc b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_array_dim_fetch.php.inc new file mode 100644 index 00000000000..0eb5a0df2f0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_array_dim_fetch.php.inc @@ -0,0 +1,11 @@ +withRules([CoalesceToTernaryRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/DirnameDirConcatStringToDirectStringPathRectorTest.php b/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/DirnameDirConcatStringToDirectStringPathRectorTest.php new file mode 100644 index 00000000000..ffde19f8e14 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/DirnameDirConcatStringToDirectStringPathRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..a7f089f0ece --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/Fixture/skip_multi_dirnames.php.inc b/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/Fixture/skip_multi_dirnames.php.inc new file mode 100644 index 00000000000..1b1e55f306c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/Fixture/skip_multi_dirnames.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/config/configured_rule.php new file mode 100644 index 00000000000..65be33931b3 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rules([DirnameDirConcatStringToDirectStringPathRector::class]); +}; diff --git a/rules-tests/CodeQuality/Rector/Concat/JoinStringConcatRector/Fixture/skip_multiple_lines.php.inc b/rules-tests/CodeQuality/Rector/Concat/JoinStringConcatRector/Fixture/skip_multiple_lines.php.inc new file mode 100644 index 00000000000..898c0630afc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Concat/JoinStringConcatRector/Fixture/skip_multiple_lines.php.inc @@ -0,0 +1,14 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Concat/JoinStringConcatRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Concat/JoinStringConcatRector/config/configured_rule.php index 44dcacee0f2..adf827d06d1 100644 --- a/rules-tests/CodeQuality/Rector/Concat/JoinStringConcatRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Concat/JoinStringConcatRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Concat\JoinStringConcatRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(JoinStringConcatRector::class); -}; +return RectorConfig::configure() + ->withRules([JoinStringConcatRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/non_typed_property_has_default_value_never_assigned.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/non_typed_property_has_default_value_never_assigned.php.inc new file mode 100644 index 00000000000..d89c12c714e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/non_typed_property_has_default_value_never_assigned.php.inc @@ -0,0 +1,33 @@ +property); + } +} + +?> +----- +property === []; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/property_fetch_from_other_object.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/property_fetch_from_other_object.php.inc new file mode 100644 index 00000000000..26e9c158775 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/property_fetch_from_other_object.php.inc @@ -0,0 +1,31 @@ +prop); + } +} + +?> +----- +prop === []; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/replace_array_type.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/replace_array_type.php.inc new file mode 100644 index 00000000000..4f88f6a5942 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/replace_array_type.php.inc @@ -0,0 +1,83 @@ +arrayProperty); + } +} + +?> +----- +arrayProperty === []; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/replace_is_array_check.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/replace_is_array_check.php.inc new file mode 100644 index 00000000000..f49bee5dac3 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/replace_is_array_check.php.inc @@ -0,0 +1,51 @@ +arrayProperty) && empty($this->arrayProperty); + } +} + +?> +----- +arrayProperty) && $this->arrayProperty === []; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_boolean_not_array.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_boolean_not_array.php.inc new file mode 100644 index 00000000000..44769119d3f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_boolean_not_array.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_from_non_typed_param.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_from_non_typed_param.php.inc new file mode 100644 index 00000000000..2af8fbbe053 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_from_non_typed_param.php.inc @@ -0,0 +1,14 @@ +arrayProperty); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_has_default_value_assigned_nullable.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_has_default_value_assigned_nullable.php.inc new file mode 100644 index 00000000000..134311b3010 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_has_default_value_assigned_nullable.php.inc @@ -0,0 +1,23 @@ +property = null; + } else { + $this->property = []; + } + } + + public function isEmpty(): bool + { + return empty($this->property); + } +} diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_has_default_value_has_assign_dynamic_property.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_has_default_value_has_assign_dynamic_property.php.inc new file mode 100644 index 00000000000..9f8635ab89f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_has_default_value_has_assign_dynamic_property.php.inc @@ -0,0 +1,19 @@ +$dynamicProperty = 'value'; + } + + public function isEmpty(): bool + { + return empty($this->property); + } +} diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_no_default_value_never_assigned.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_no_default_value_never_assigned.php.inc new file mode 100644 index 00000000000..dc17d033bff --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_no_default_value_never_assigned.php.inc @@ -0,0 +1,14 @@ +property); + } +} diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_promotion.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_promotion.php.inc new file mode 100644 index 00000000000..b91537bb52e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_non_typed_property_promotion.php.inc @@ -0,0 +1,18 @@ +property); + } +} diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_nullable.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_nullable.php.inc new file mode 100644 index 00000000000..bc667299c87 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/skip_nullable.php.inc @@ -0,0 +1,19 @@ +property = null; + } + + public function isEmpty(): bool + { + return empty($this->property); + } +} diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/typed_array_property_promotion.php.inc b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/typed_array_property_promotion.php.inc new file mode 100644 index 00000000000..f20671b2053 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Fixture/typed_array_property_promotion.php.inc @@ -0,0 +1,35 @@ +property); + } +} + +?> +----- +property === []; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/SimplifyEmptyCheckOnEmptyArrayRectorTest.php b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/SimplifyEmptyCheckOnEmptyArrayRectorTest.php new file mode 100644 index 00000000000..f239fd2f6f3 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/SimplifyEmptyCheckOnEmptyArrayRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Source/OtherClass.php b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Source/OtherClass.php new file mode 100644 index 00000000000..84439092ea2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector/Source/OtherClass.php @@ -0,0 +1,10 @@ +withRules([SimplifyEmptyCheckOnEmptyArrayRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/fixture.php.inc index 2c4abe88bda..daf3c73bc88 100644 --- a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/fixture.php.inc @@ -7,7 +7,7 @@ class Fixture public function run(int $firstValue, int $secondValue) { $isSame = $firstValue == $secondValue; - $isDiffernt = $firstValue != $secondValue; + $isDifferent = $firstValue != $secondValue; } } @@ -22,7 +22,7 @@ class Fixture public function run(int $firstValue, int $secondValue) { $isSame = $firstValue === $secondValue; - $isDiffernt = $firstValue !== $secondValue; + $isDifferent = $firstValue !== $secondValue; } } diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_bool.php.inc b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_bool.php.inc new file mode 100644 index 00000000000..46aa0de51fb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_bool.php.inc @@ -0,0 +1,45 @@ +getValue() == true) { + return 'yes'; + } + + return 'no'; + } + + private function getValue(): bool + { + return true; + } +} + +?> +----- +getValue() === true) { + return 'yes'; + } + + return 'no'; + } + + private function getValue(): bool + { + return true; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_enclosed_ternary.php.inc b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_enclosed_ternary.php.inc new file mode 100644 index 00000000000..be36a00554c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_enclosed_ternary.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_float.php.inc b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_float.php.inc new file mode 100644 index 00000000000..0dd52318d46 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_float.php.inc @@ -0,0 +1,51 @@ +getValue() == 1.0) { + return 'yes'; + } + + return 'no'; + } + + /** + * @return float<0, max> + */ + private function getValue(): float + { + return 1.0; + } +} + +?> +----- +getValue() === 1.0) { + return 'yes'; + } + + return 'no'; + } + + /** + * @return float<0, max> + */ + private function getValue(): float + { + return 1.0; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_integer.php.inc b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_integer.php.inc new file mode 100644 index 00000000000..c2762c2c0a8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_integer.php.inc @@ -0,0 +1,51 @@ +getValue() == 1) { + return 'yes'; + } + + return 'no'; + } + + /** + * @return int<0, max> + */ + private function getValue(): int + { + return 1; + } +} + +?> +----- +getValue() === 1) { + return 'yes'; + } + + return 'no'; + } + + /** + * @return int<0, max> + */ + private function getValue(): int + { + return 1; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_string.php.inc b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_string.php.inc new file mode 100644 index 00000000000..9ff1e51cb49 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/identical_string.php.inc @@ -0,0 +1,45 @@ +getValue() == 'hi') { + return 'yes'; + } + + return 'no'; + } + + private function getValue(): string + { + return 'hello'; + } +} + +?> +----- +getValue() === 'hi') { + return 'yes'; + } + + return 'no'; + } + + private function getValue(): string + { + return 'hello'; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/skip_bool_equal_float.php.inc b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/skip_bool_equal_float.php.inc new file mode 100644 index 00000000000..2d61271790b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/skip_bool_equal_float.php.inc @@ -0,0 +1,18 @@ +} + */ + public function getContent(): array + { + } + + public function import() + { + $content = $this->getContent(); + + if (! isset($content['sql']) || $content['sql'] == '') { + } + } +} diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/this_class.php.inc b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/this_class.php.inc new file mode 100644 index 00000000000..9f2f0480855 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/Fixture/this_class.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/UseIdenticalOverEqualWithSameTypeRectorTest.php b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/UseIdenticalOverEqualWithSameTypeRectorTest.php index 1fdd72687d2..fea80ff58be 100644 --- a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/UseIdenticalOverEqualWithSameTypeRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/UseIdenticalOverEqualWithSameTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Equal\UseIdenticalOverEqualWithSameTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class UseIdenticalOverEqualWithSameTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/config/configured_rule.php index 67ef27b1f34..8ad559a7e32 100644 --- a/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Equal\UseIdenticalOverEqualWithSameTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UseIdenticalOverEqualWithSameTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([UseIdenticalOverEqualWithSameTypeRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/assign_op.php.inc b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/assign_op.php.inc deleted file mode 100644 index 2c71578bca6..00000000000 --- a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/assign_op.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/boolean_or.php.inc b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/boolean_or.php.inc deleted file mode 100644 index 00bc7d48014..00000000000 --- a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/boolean_or.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/inline_assign_op.php.inc b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/inline_assign_op.php.inc new file mode 100644 index 00000000000..d32f1a64cd9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/inline_assign_op.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/some_boolean_or.php.inc b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/some_boolean_or.php.inc new file mode 100644 index 00000000000..f1f5648b37f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/Fixture/some_boolean_or.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/InlineIfToExplicitIfRectorTest.php b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/InlineIfToExplicitIfRectorTest.php index 30db7a5f22c..18464320f47 100644 --- a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/InlineIfToExplicitIfRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/InlineIfToExplicitIfRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class InlineIfToExplicitIfRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/config/configured_rule.php index 98b9c503ee0..a00f27ebc11 100644 --- a/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(InlineIfToExplicitIfRector::class); -}; +return RectorConfig::configure() + ->withRules([InlineIfToExplicitIfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/include_no_side_effect_expr.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/include_no_side_effect_expr.php.inc new file mode 100644 index 00000000000..15251bb4368 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/include_no_side_effect_expr.php.inc @@ -0,0 +1,41 @@ +call($value) : true; + } + + public function run2($value, $someMethod, $variable) + { + $value ? $someMethod->call($value) : $variable; + } +} + +?> +----- +call($value); + } + } + + public function run2($value, $someMethod, $variable) + { + if ($value) { + $someMethod->call($value); + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/negated_value.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/negated_value.php.inc new file mode 100644 index 00000000000..aebb2154725 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/negated_value.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_increment_variable_on_else.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_increment_variable_on_else.php.inc new file mode 100644 index 00000000000..8a7291e065e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_increment_variable_on_else.php.inc @@ -0,0 +1,30 @@ + $failed) { + return $success; + } + + return $failed; + } + + public function run2($id) + { + $success = $failed = 0; + ! $id ? ++$failed : ++$success; + + if ($success > $failed) { + return $success; + } + + return $failed; + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_method_call_on_else.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_method_call_on_else.php.inc new file mode 100644 index 00000000000..97b4351625b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_method_call_on_else.php.inc @@ -0,0 +1,22 @@ +a() : $this->b(); + } + + public function a(): void + { + new HeavyObject1(); + } + + public function b(): null + { + new HeavyObject2(); + return 'b'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_method_call_with_assign_on_else.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_method_call_with_assign_on_else.php.inc new file mode 100644 index 00000000000..f585e3eb474 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_method_call_with_assign_on_else.php.inc @@ -0,0 +1,22 @@ +a() : $x = $this->b(); + } + + public function a(): void + { + new HeavyObject1(); + } + + public function b(): null + { + new HeavyObject2(); + return 'b'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_null_ternary.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_null_ternary.php.inc new file mode 100644 index 00000000000..e4838167739 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_null_ternary.php.inc @@ -0,0 +1,11 @@ +call($value); + } +} diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_sort_ksort.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_sort_ksort.php.inc new file mode 100644 index 00000000000..764174ff393 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_sort_ksort.php.inc @@ -0,0 +1,21 @@ + $result && is_numeric($item), true); + $isIndexedArray + ? sort($array) + : ksort($array); + + foreach ($array as &$value) { + if (is_array($value)) { + self::mksort($value); + } + } + } +} diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_with_html.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_with_html.php.inc new file mode 100644 index 00000000000..04db19849be --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/skip_with_html.php.inc @@ -0,0 +1,4 @@ + +call($a, 'v') ? ' class="x"' : '' ?>> diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/some_class.php.inc b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..0aa00170ea4 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/Fixture/some_class.php.inc @@ -0,0 +1,29 @@ +call($value) : false; + } +} + +?> +----- +call($value); + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/TernaryFalseExpressionToIfRectorTest.php b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/TernaryFalseExpressionToIfRectorTest.php new file mode 100644 index 00000000000..b34002f7e3a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/TernaryFalseExpressionToIfRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/config/configured_rule.php new file mode 100644 index 00000000000..13fe1623e29 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([TernaryFalseExpressionToIfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/already_existing_items_count.php.inc b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/already_existing_items_count.php.inc index 764aeab543e..1679797fbc2 100644 --- a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/already_existing_items_count.php.inc +++ b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/already_existing_items_count.php.inc @@ -27,9 +27,9 @@ class AlreadyExistingItemsCount public function run($items, \stdClass $someObject) { $itemsCount = 500000; - $itemsCount2 = count($someObject->getItems() + 10); + $counter = count($someObject->getItems() + 10); - for ($i = 5; $i <= $itemsCount2; $i++) { + for ($i = 5; $i <= $counter; $i++) { echo $items[$i]; } diff --git a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/fallback_for_complex.php.inc b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/fallback_for_complex.php.inc index cb94dd48927..2093a7ea9a6 100644 --- a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/fallback_for_complex.php.inc +++ b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/fallback_for_complex.php.inc @@ -22,8 +22,8 @@ class FallbackForComplex { public function run($items, \stdClass $someObject) { - $itemsCount = count($someObject->getItems() + 10); - for ($i = 5; $i <= $itemsCount; $i++) { + $counter = count($someObject->getItems() + 10); + for ($i = 5; $i <= $counter; $i++) { echo $items[$i]; } } diff --git a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/fixture.php.inc index 880020a440f..9868c392aed 100644 --- a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/fixture.php.inc @@ -22,8 +22,8 @@ class Fixture { public function run($items) { - $itemsCount = count($items); - for ($i = 5; $i <= $itemsCount; $i++) { + $counter = count($items); + for ($i = 5; $i <= $counter; $i++) { echo $items[$i]; } } diff --git a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/method_call_count.php.inc b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/method_call_count.php.inc index 0b491836d6f..59f928b4d11 100644 --- a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/method_call_count.php.inc +++ b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/method_call_count.php.inc @@ -22,8 +22,8 @@ class MethodCallCount { public function run($items, \stdClass $someObject) { - $getItemsCount = count($someObject->getItems()); - for ($i = 5; $i <= $getItemsCount; $i++) { + $counter = count($someObject->getItems()); + for ($i = 5; $i <= $counter; $i++) { echo $items[$i]; } } diff --git a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/skip_counter_variable_exists.php.inc b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/skip_counter_variable_exists.php.inc new file mode 100644 index 00000000000..c59f0cd76db --- /dev/null +++ b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/Fixture/skip_counter_variable_exists.php.inc @@ -0,0 +1,15 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/config/configured_rule.php index efbdd18dbfb..f271a4603d1 100644 --- a/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\For_\ForRepeatedCountToOwnVariableRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ForRepeatedCountToOwnVariableRector::class); -}; +return RectorConfig::configure() + ->withRules([ForRepeatedCountToOwnVariableRector::class]); diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/add_single_prefix_singular.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/add_single_prefix_singular.php.inc deleted file mode 100644 index 9fdee3ce369..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/add_single_prefix_singular.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - $singleToken) { - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/dont_change_passed_count_arg_as_dimfetch.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/dont_change_passed_count_arg_as_dimfetch.php.inc deleted file mode 100644 index 35c19ab6527..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/dont_change_passed_count_arg_as_dimfetch.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - $token) { - for ($i2 = 0, $c2 = count($tokens[$i]); $i2 < $c2; $i2++) { - var_dump($token); - } - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/fixture.php.inc deleted file mode 100644 index 4e76a41bad1..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,51 +0,0 @@ -getPreviousNonSpaceToken($tokens, $i); - if ($previousNonSpaceToken !== null && $previousNonSpaceToken[0] === T_OBJECT_OPERATOR) { - continue; - } - $tokens[$i][0] = self::T_FN; - $tokens[$i][0][1] = self::T_FN; - - $tokens[$i][0][1][4] = self::T_FN; - $value = $tokens[$i][0][1][4]; - } - } - } -} - -?> ------ - $token) { - if ($token[0] === T_STRING && $token[1] === 'fn') { - $previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $i); - if ($previousNonSpaceToken !== null && $previousNonSpaceToken[0] === T_OBJECT_OPERATOR) { - continue; - } - $tokens[$i][0] = self::T_FN; - $tokens[$i][0][1] = self::T_FN; - - $tokens[$i][0][1][4] = self::T_FN; - $value = $token[0][1][4]; - } - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/for_with_count.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/for_with_count.php.inc deleted file mode 100644 index 500ecefaee6..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/for_with_count.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - $token) { - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/for_with_switched_compare.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/for_with_switched_compare.php.inc deleted file mode 100644 index c8266109769..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/for_with_switched_compare.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - $i; $i++) { - } - } -} - -?> ------ - $token) { - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/mirror_comment.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/mirror_comment.php.inc deleted file mode 100644 index 2165dbde187..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/mirror_comment.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - $token) { - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/not_add_single_prefix_singular_if_used_next.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/not_add_single_prefix_singular_if_used_next.php.inc deleted file mode 100644 index a35c3d4fb9b..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/not_add_single_prefix_singular_if_used_next.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - $token) { - } - } - - echo $singleToken; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/other_use_of_loop_var.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/other_use_of_loop_var.php.inc deleted file mode 100644 index 4752f246e19..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/other_use_of_loop_var.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - $token) { - $value = $token[0][1][4]; - return $other[$i]; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_assign_count_used.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_assign_count_used.php.inc deleted file mode 100644 index 4c950692b0f..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_assign_count_used.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_complex_init.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_complex_init.php.inc deleted file mode 100644 index 4b2fb4f2a47..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_complex_init.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -getResult(), $c = count($query); $i < $c; $i ++) - { - echo $query[$i]; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_has_array_dim_fetch_in_assign_var.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_has_array_dim_fetch_in_assign_var.php.inc deleted file mode 100644 index b2574eb3d49..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_has_array_dim_fetch_in_assign_var.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_unset.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_unset.php.inc deleted file mode 100644 index 3d361347484..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_unset.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_value_var_used.php.inc b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_value_var_used.php.inc deleted file mode 100644 index 5fece6bd4d0..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/Fixture/skip_value_var_used.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/ForToForeachRectorTest.php b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/ForToForeachRectorTest.php deleted file mode 100644 index 9f2c193ae62..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/ForToForeachRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/config/configured_rule.php deleted file mode 100644 index c8be97677d0..00000000000 --- a/rules-tests/CodeQuality/Rector/For_/ForToForeachRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ForToForeachRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/fixture.php.inc index 862700cfdd2..7cb9a0f0558 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/fixture.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\ForeachItemsAssignToEmptyArra class Fixture { - public function run($items) + public function run(array $items) { $items2 = []; foreach ($items as $item) { @@ -21,7 +21,7 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\ForeachItemsAssignToEmptyArra class Fixture { - public function run($items) + public function run(array $items) { $items2 = []; $items2 = $items; diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_append_non_empty_array.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_append_non_empty_array.php.inc new file mode 100644 index 00000000000..12f71e60ee9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_append_non_empty_array.php.inc @@ -0,0 +1,21 @@ +getItems() as $item) { + if ($item->hasGroup()) { + $groupList[] = ['id' => $item->getGroupId()]; + } else { + $list[] = ['id' => $item->getId()]; + } + } + + foreach ($groupList as $group) { + $list[] = $group; + } + + return $list; + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_deep_append_foreach.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_deep_append_foreach.php.inc new file mode 100644 index 00000000000..f7fb00e9ff2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_deep_append_foreach.php.inc @@ -0,0 +1,22 @@ +dependentFiles = []; + $this->files = []; } private function resolveDependentFiles( - DependencyResolver $dependencyResolver, + SomeDependency $someDependency, Node $node, MutatingScope $mutatingScope ): void { - foreach ($dependencyResolver->resolveDependencies($node, $mutatingScope) as $dependentFile) { - $this->dependentFiles[] = $dependentFile; + foreach ($someDependency->resolve($node, $mutatingScope) as $file) { + $this->files[] = $file; } } } diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_mixed_type.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_mixed_type.php.inc new file mode 100644 index 00000000000..d12521a9bb4 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_mixed_type.php.inc @@ -0,0 +1,14 @@ +getTraversable() as $item) { + $items[] = $item; + } + } + + private function getTraversable(): iterable + { + yield 123; + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_with_init_non_empty_dynamic_array.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_with_init_non_empty_dynamic_array.php.inc new file mode 100644 index 00000000000..dca55cd4c64 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/Fixture/skip_with_init_non_empty_dynamic_array.php.inc @@ -0,0 +1,28 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/config/configured_rule.php index b743fcb78d8..9d4f8f7879c 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Foreach_\ForeachItemsAssignToEmptyArrayToAssignRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ForeachItemsAssignToEmptyArrayToAssignRector::class); -}; +return RectorConfig::configure() + ->withRules([ForeachItemsAssignToEmptyArrayToAssignRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/execute_on_calls_with_non_foreach_variable.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/execute_on_calls_with_non_foreach_variable.php.inc new file mode 100644 index 00000000000..6eb3a22b954 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/execute_on_calls_with_non_foreach_variable.php.inc @@ -0,0 +1,98 @@ +strtoupper($local)) { + return true; + } + } + + return false; + } + + public function foreachToInArrayWithStaticCall(array $items): bool + { + $local = 'local'; + + foreach ($items as $item) { + if ($item == SkipMethodCalls::strtolower($local)) { + return true; + } + } + + return false; + } + + public static function strtolower($item): string + { + return strtolower((string) $item); + } + + private function strtoupper($item): string + { + return strtoupper((string) $item); + } +} + +?> +----- +strtoupper($local), $items); + } + + public function foreachToInArrayWithStaticCall(array $items): bool + { + $local = 'local'; + return in_array(SkipMethodCalls::strtolower($local), $items); + } + + public static function strtolower($item): string + { + return strtolower((string) $item); + } + + private function strtoupper($item): string + { + return strtoupper((string) $item); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/my_class.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/my_class.php.inc index 1d6b0debed3..4e9828af3ea 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/my_class.php.inc +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/my_class.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\ForeachToInArrayRector\Fixtur final class MyClass { - public function foreachToInArray($items): bool + public function foreachToInArray(array $items): bool { foreach ($items as $item) { if ($item == 'something') { @@ -15,7 +15,7 @@ final class MyClass return false; } - public function foreachToInArrayYoda($items): bool + public function foreachToInArrayYoda(array $items): bool { foreach ($items as $item) { if ('something' == $item) { @@ -26,7 +26,7 @@ final class MyClass return false; } - public function foreachToInArrayStrict($items): bool + public function foreachToInArrayStrict(array $items): bool { foreach ($items as $item) { if ($item === 'something') { @@ -37,7 +37,7 @@ final class MyClass return false; } - public function invertedForeachToInArrayStrict($items): bool + public function invertedForeachToInArrayStrict(array $items): bool { foreach ($items as $item) { if ($item === 'something') { @@ -48,7 +48,7 @@ final class MyClass return true; } - public function foreachToInArrayWithToVariables($items): bool + public function foreachToInArrayWithToVariables(array $items): bool { foreach ($items as $item) { if ($something === $item) { @@ -59,7 +59,7 @@ final class MyClass return false; } - public function foreachWithoutReturnFalse($items) + public function foreachWithoutReturnFalse(array $items) { foreach ($items as $item) { if ($item === 'something') { @@ -68,7 +68,7 @@ final class MyClass } } - public function foreachReturnString($items) + public function foreachReturnString(array $items) { foreach ($items as $item) { if ($item === 'something') { @@ -79,7 +79,7 @@ final class MyClass return 'false'; } - public function foreachWithSomethingElseAfterIt($items) + public function foreachWithSomethingElseAfterIt(array $items) { foreach ($items as $item) { if ($item === 'something') { @@ -90,7 +90,7 @@ final class MyClass $foo = 'bar'; } - public function foreachWithElseNullable($items) + public function foreachWithElseNullable(array $items) { foreach ($items as $item) { if ('string') { @@ -101,7 +101,7 @@ final class MyClass return; } - public function alwaysTrue($items): bool + public function alwaysTrue(array $items): bool { foreach ($items as $item) { if ($something === $item) { @@ -121,32 +121,32 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\ForeachToInArrayRector\Fixtur final class MyClass { - public function foreachToInArray($items): bool + public function foreachToInArray(array $items): bool { return in_array('something', $items); } - public function foreachToInArrayYoda($items): bool + public function foreachToInArrayYoda(array $items): bool { return in_array('something', $items); } - public function foreachToInArrayStrict($items): bool + public function foreachToInArrayStrict(array $items): bool { return in_array('something', $items, true); } - public function invertedForeachToInArrayStrict($items): bool + public function invertedForeachToInArrayStrict(array $items): bool { return !in_array('something', $items, true); } - public function foreachToInArrayWithToVariables($items): bool + public function foreachToInArrayWithToVariables(array $items): bool { return in_array($something, $items, true); } - public function foreachWithoutReturnFalse($items) + public function foreachWithoutReturnFalse(array $items) { foreach ($items as $item) { if ($item === 'something') { @@ -155,7 +155,7 @@ final class MyClass } } - public function foreachReturnString($items) + public function foreachReturnString(array $items) { foreach ($items as $item) { if ($item === 'something') { @@ -166,7 +166,7 @@ final class MyClass return 'false'; } - public function foreachWithSomethingElseAfterIt($items) + public function foreachWithSomethingElseAfterIt(array $items) { foreach ($items as $item) { if ($item === 'something') { @@ -177,7 +177,7 @@ final class MyClass $foo = 'bar'; } - public function foreachWithElseNullable($items) + public function foreachWithElseNullable(array $items) { foreach ($items as $item) { if ('string') { @@ -188,7 +188,7 @@ final class MyClass return; } - public function alwaysTrue($items): bool + public function alwaysTrue(array $items): bool { foreach ($items as $item) { if ($something === $item) { diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_more_inner_foreach.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_more_inner_foreach.php.inc new file mode 100644 index 00000000000..c993627a9ec --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_more_inner_foreach.php.inc @@ -0,0 +1,21 @@ +items as $item) { + $value = 100; + + if (100 === $item) { + return true; + } + } + + return $value; + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_on_calls_with_foreach_variable.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_on_calls_with_foreach_variable.php.inc new file mode 100644 index 00000000000..aba47a4aab9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_on_calls_with_foreach_variable.php.inc @@ -0,0 +1,51 @@ +strtoupper($item)) { + return true; + } + } + + return false; + } + + public function foreachToInArrayWithStaticCall($items): bool + { + foreach ($items as $item) { + if ($item == SkipMethodCalls::strtolower($item)) { + return true; + } + } + + return false; + } + + public static function strtolower($item): string + { + return strtolower((string) $item); + } + + private function strtoupper($item): string + { + return strtoupper((string) $item); + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_return_value_in_return.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_return_value_in_return.php.inc new file mode 100644 index 00000000000..0558f6a1700 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/skip_return_value_in_return.php.inc @@ -0,0 +1,17 @@ +items as $item) { + if (100 === $item) { + return true; + } + } + + return $value; + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/some_class.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/some_class.php.inc index af8ad7521da..fa805167359 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/some_class.php.inc +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/Fixture/some_class.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\ForeachToInArrayRector\Fixtur class SomeClass { - public function foreachWithElseNullable($items) + public function foreachWithElseNullable(array $items) { foreach ($items as $item) { // some comment @@ -25,9 +25,8 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\ForeachToInArrayRector\Fixtur class SomeClass { - public function foreachWithElseNullable($items) + public function foreachWithElseNullable(array $items) { - // some comment return in_array('something', $items); } } diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/ForeachToInArrayRectorTest.php b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/ForeachToInArrayRectorTest.php index d2f1577441c..3385476adcd 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/ForeachToInArrayRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/ForeachToInArrayRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\ForeachToInArrayRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ForeachToInArrayRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/config/configured_rule.php index eebcd967b80..e3b87f0aa31 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Foreach_/ForeachToInArrayRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Foreach_\ForeachToInArrayRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ForeachToInArrayRector::class); -}; +return RectorConfig::configure() + ->withRules([ForeachToInArrayRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/fixture.php.inc deleted file mode 100644 index ff4f2cccd54..00000000000 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/same_var.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/same_var.php.inc deleted file mode 100644 index ec44d6f66f7..00000000000 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/same_var.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - $value) { - if (is_string($value)) { - $responseData[$key] = $value; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/skip.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/skip.php.inc deleted file mode 100644 index 9c841811454..00000000000 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/skip.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - $possibleDirectory) { - if (file_exists($key)) { - $directories[] = $possibleDirectory; - } - } - - foreach ($possibleDirectories as $key => $possibleDirectory) { - if (file_exists($possibleDirectory)) { - $directories[] = $key; - } - } - - foreach ($possibleDirectories as $key => $possibleDirectory) { - if ($this->check($possibleDirectory)) { - $directories[] = $possibleDirectory; - } - } - - foreach ($possibleDirectories as $key => $possibleDirectory) { - if (file_exists($possibleDirectory, true)) { - $directories[] = $possibleDirectory; - } - } - - foreach ($possibleDirectories as $key => $possibleDirectory) { - if (file_exists()) { - $directories[] = $possibleDirectory; - } - } - } - - private function check(string $possibleDirectory): bool - { - return true; - } -} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/skip_if_else.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/skip_if_else.php.inc deleted file mode 100644 index 876a7bb9841..00000000000 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/Fixture/skip_if_else.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/config/configured_rule.php deleted file mode 100644 index b04c2cb05f4..00000000000 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SimplifyForeachToArrayFilterRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/fixture2.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/fixture2.php.inc index 8b7fd247be1..c777b6142c8 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/fixture2.php.inc +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/fixture2.php.inc @@ -1,8 +1,9 @@ oldToNewFunctions as $oldFunction => $newFunction) { + $oldToNewFunctions = ['a' => 'b']; + foreach ($oldToNewFunctions as $oldFunction => $newFunction) { if ($oldFunction === $currentFunction) { return $newFunction; } @@ -15,9 +16,10 @@ function simplifyForeachToCoalescing2() ----- oldToNewFunctions[$currentFunction] ?? 45; + $oldToNewFunctions = ['a' => 'b']; + return $oldToNewFunctions[$currentFunction] ?? 45; } ?> diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/fixture3.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/fixture3.php.inc deleted file mode 100644 index 58d941afa54..00000000000 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - $value) { - if ($key === $input) { - $newValue = $value; - } - } - - foreach ($values as $key => $value) { - if ($input === $key) { - $newValue = $value; - } - } - } -} - -?> ------ - diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/foreach_key_value.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/foreach_key_value.php.inc index 760b3eb1a3f..bc7b556a37b 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/foreach_key_value.php.inc +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/foreach_key_value.php.inc @@ -7,7 +7,7 @@ class ForeachKeyValue /** * @var mixed[] */ - private $oldToNewOption = []; + private array $oldToNewOption = []; public function run() { @@ -33,7 +33,7 @@ class ForeachKeyValue /** * @var mixed[] */ - private $oldToNewOption = []; + private array $oldToNewOption = []; public function run() { diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/multiple_foreaches.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/multiple_foreaches.php.inc new file mode 100644 index 00000000000..ba9acf45bf7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/multiple_foreaches.php.inc @@ -0,0 +1,51 @@ + $value) { + if ($key === $input) { + $newValue = $value; + } + } + + foreach ($values as $key => $value) { + if ($input === $key) { + $nextValue = $value; + } + } + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_call_inside.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_call_inside.php.inc new file mode 100644 index 00000000000..043ceb825b7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_call_inside.php.inc @@ -0,0 +1,18 @@ + $v) { + if (strtolower($k) === $lowercaseHeader) { + return $v; + } + } + + return null; + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_generator.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_generator.php.inc new file mode 100644 index 00000000000..9f9ef1840b9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_generator.php.inc @@ -0,0 +1,23 @@ + 1, 'b' => 2] as $key => $value) { + yield $key => $value; + } + } + + public function loadByKey(string $name): ?int + { + foreach ($this->load() as $key => $value) { + if ($key === $name) { + return $value; + } + } + return null; + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_reassign_key.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_reassign_key.php.inc new file mode 100644 index 00000000000..1479d00e3f4 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_reassign_key.php.inc @@ -0,0 +1,20 @@ + $value) { + if ($key === 0) { + $namedData['default'] = $value; + } elseif (!\is_int($key)) { + $namedData[$key] = $value; + } + } + + return $namedData; + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_throw_after_foreach.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_throw_after_foreach.php.inc new file mode 100644 index 00000000000..513371b7e13 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_throw_after_foreach.php.inc @@ -0,0 +1,26 @@ + + * + * @throws Exception + */ + private function getFilterConfig(): array + { + $shopConfig = clxMobileNet::config('shopconfig'); + + foreach ($shopConfig as $key => $mixedValues) { + if (self::SHOPCONFIGKEY_FILTER === $key) { + return $mixedValues; + } + } + + throw new \Exception('Usage of "'.self::class.'" assumes that ShopConfig "'.self::SHOPCONFIGKEY_FILTER.'" is defined!'); + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_with_else.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_with_else.php.inc new file mode 100644 index 00000000000..d4081716090 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/Fixture/skip_with_else.php.inc @@ -0,0 +1,22 @@ + 'App\Tests\Unit']; + + foreach ($namespaceReplaces as $old => $new) { + if ($old === $testNamespace) { + $testNamespace = $new; + } else { + $testNamespace = \str_replace($old . '\\', $new . '\\', $testNamespace); + } + } + + return $testNamespace; + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/SimplifyForeachToCoalescingRectorTest.php b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/SimplifyForeachToCoalescingRectorTest.php index 7563d7741dd..f731903c132 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/SimplifyForeachToCoalescingRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/SimplifyForeachToCoalescingRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\SimplifyForeachToCoalescingRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyForeachToCoalescingRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/config/configured_rule.php index 3dc16074c35..13fea2822d9 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Foreach_\SimplifyForeachToCoalescingRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyForeachToCoalescingRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyForeachToCoalescingRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_foreach_destruct_keys.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_foreach_destruct_keys.php.inc new file mode 100644 index 00000000000..4de013bb955 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_foreach_destruct_keys.php.inc @@ -0,0 +1,15 @@ + [$domElement, $file]) { + if ($file) { + return $file; + } + } + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_used_in_next_stmt.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_used_in_next_stmt.php.inc new file mode 100644 index 00000000000..0889db446d9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_used_in_next_stmt.php.inc @@ -0,0 +1,19 @@ + $regRule) { + if (strpos($dateTimeFormat, $supportedFormat, 0) !== false) { + $isSupportedFormatFound = true; + break; + } + } + + $regex = array_shift($regRule); + $mask = array_shift($regRule); + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_used_in_throw_stmts_in_catch.php.inc b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_used_in_throw_stmts_in_catch.php.inc new file mode 100644 index 00000000000..1ff2c4f8a9e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/Fixture/skip_used_in_throw_stmts_in_catch.php.inc @@ -0,0 +1,20 @@ + $val) { + throw new \Exception('test'); + } + } catch (\Throwable) { + echo 'Failed at value '.$val; + } + } +} diff --git a/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/UnusedForeachValueToArrayKeysRectorTest.php b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/UnusedForeachValueToArrayKeysRectorTest.php index 3756dc0db13..efb6ec835cc 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/UnusedForeachValueToArrayKeysRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/UnusedForeachValueToArrayKeysRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Foreach_\UnusedForeachValueToArrayKeysRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class UnusedForeachValueToArrayKeysRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/config/configured_rule.php index 5d1b4c82de4..80f0518a685 100644 --- a/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Foreach_\UnusedForeachValueToArrayKeysRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UnusedForeachValueToArrayKeysRector::class); -}; +return RectorConfig::configure() + ->withRules([UnusedForeachValueToArrayKeysRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/AddPregQuoteDelimiterRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/AddPregQuoteDelimiterRectorTest.php deleted file mode 100644 index 875160763f8..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/AddPregQuoteDelimiterRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/fixture.php.inc deleted file mode 100644 index 021797918d1..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/skip.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/skip.php.inc deleted file mode 100644 index 001b85414f2..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/Fixture/skip.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/config/configured_rule.php deleted file mode 100644 index db0f1758e1b..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(AddPregQuoteDelimiterRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/ArrayKeysAndInArrayToArrayKeyExistsRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/ArrayKeysAndInArrayToArrayKeyExistsRectorTest.php deleted file mode 100644 index 92ddb0c307d..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/ArrayKeysAndInArrayToArrayKeyExistsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/Fixture/fixture.php.inc deleted file mode 100644 index 68b593847ca..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - "bar"]; - - $keys = array_keys($values); - - return in_array($packageName, $keys, true); - } -} - -?> ------ - "bar"]; - - return array_key_exists($packageName, $values); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/Fixture/skip_keys_used.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/Fixture/skip_keys_used.php.inc deleted file mode 100644 index 5f592b1d7e0..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector/Fixture/skip_keys_used.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -services(); - $services->set(ArrayKeysAndInArrayToArrayKeyExistsRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/ArrayMergeOfNonArraysToSimpleArrayRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/ArrayMergeOfNonArraysToSimpleArrayRectorTest.php index 9b0f20ae2b0..d697fd5cfc1 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/ArrayMergeOfNonArraysToSimpleArrayRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/ArrayMergeOfNonArraysToSimpleArrayRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\ArrayMergeOfNonArraysToSimpleArrayRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ArrayMergeOfNonArraysToSimpleArrayRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/Fixture/destructuring_arrays.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/Fixture/destructuring_arrays.php.inc new file mode 100644 index 00000000000..cc99e2e3134 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/Fixture/destructuring_arrays.php.inc @@ -0,0 +1,46 @@ + 'bar'], + ...$this->deconstructable(), + ] + ); + + return $values; + } + + private function deconstructable() + { + return ['rector']; + } +} + +?> +----- + 'bar'], ...$this->deconstructable()]; + + return $values; + } + + private function deconstructable() + { + return ['rector']; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/config/configured_rule.php index 51a62e2349c..87dfa7b1664 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\ArrayMergeOfNonArraysToSimpleArrayRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ArrayMergeOfNonArraysToSimpleArrayRector::class); -}; +return RectorConfig::configure() + ->withRules([ArrayMergeOfNonArraysToSimpleArrayRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector/CallUserFuncWithArrowFunctionToInlineRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector/CallUserFuncWithArrowFunctionToInlineRectorTest.php index 66d16158af2..436a55a7fbd 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector/CallUserFuncWithArrowFunctionToInlineRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector/CallUserFuncWithArrowFunctionToInlineRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\CallUserFuncWithArrowFunctionToInlineRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP 7.4 - */ final class CallUserFuncWithArrowFunctionToInlineRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector/config/configured_rule.php index 34f80af3205..437fbaf4e05 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector/config/configured_rule.php @@ -3,10 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\CallUserFuncWithArrowFunctionToInlineRector; +use Rector\Config\RectorConfig; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CallUserFuncWithArrowFunctionToInlineRector::class); -}; +return RectorConfig::configure() + ->withRules([CallUserFuncWithArrowFunctionToInlineRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector/ChangeArrayPushToArrayAssignRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector/ChangeArrayPushToArrayAssignRectorTest.php index 2fff1f427dc..15beddfcd82 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector/ChangeArrayPushToArrayAssignRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector/ChangeArrayPushToArrayAssignRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ChangeArrayPushToArrayAssignRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector/Fixture/skip_empty_args.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector/Fixture/skip_empty_args.php.inc new file mode 100644 index 00000000000..9a46848caa7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector/Fixture/skip_empty_args.php.inc @@ -0,0 +1,13 @@ +services(); - $services->set(ChangeArrayPushToArrayAssignRector::class); -}; +return RectorConfig::configure() + ->withRules([ChangeArrayPushToArrayAssignRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/CompactToVariablesRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/CompactToVariablesRectorTest.php index 6b217bbaafb..31f57ee0eef 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/CompactToVariablesRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/CompactToVariablesRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\CompactToVariablesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CompactToVariablesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/array_list_based.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/array_list_based.php.inc index 6b0487771bf..4a554236393 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/array_list_based.php.inc +++ b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/array_list_based.php.inc @@ -28,9 +28,9 @@ final class ArrayListBased $one = 1000; $two = 'hey'; - $names = ['one' => $one, 'two' => $two]; + $names = ['one', 'two']; - return $names; + return ['one' => $one, 'two' => $two]; } } diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/array_list_params.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/array_list_params.php.inc index fc0dcb2974e..c9e7197f428 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/array_list_params.php.inc +++ b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/array_list_params.php.inc @@ -22,9 +22,9 @@ final class ArrayListParams { public function run($one, $two) { - $names = ['one' => $one, 'two' => $two]; + $names = ['one', 'two']; - return $names; + return ['one' => $one, 'two' => $two]; } } diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/compact_with_extract.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/compact_with_extract.php.inc index 6455028a79d..dc0e32ee206 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/compact_with_extract.php.inc +++ b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/compact_with_extract.php.inc @@ -24,10 +24,11 @@ final class CompactWithExtract { public function run() { + $values = ['result']; + $result = 1000; - $values = ['result' => $result]; - return $values; + return ['result' => $result]; } } diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/compact_with_extract_param_override.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/compact_with_extract_param_override.php.inc index 4e940e9cfca..ac13015b3fc 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/compact_with_extract_param_override.php.inc +++ b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/compact_with_extract_param_override.php.inc @@ -24,10 +24,11 @@ final class CompactWithExtractParamOverride { public function run($result = 100) { + $values = ['result']; + $result = 1000; - $values = ['result' => $result]; - return $values; + return ['result' => $result]; } } diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/uncompact_next_uses.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/uncompact_next_uses.php.inc index 640ac7f912e..bb4144ed379 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/uncompact_next_uses.php.inc +++ b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/Fixture/uncompact_next_uses.php.inc @@ -30,11 +30,11 @@ final class UncompactNextUses $one = 1000; $two = 'hey'; - $names = ['one' => $one, 'two' => $two]; + $names = ['one', 'two']; - $misc = $names; + $misc = ['one' => $one, 'two' => $two]; - return $names + $misc; + return ['one' => $one, 'two' => $two] + $misc; } } diff --git a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/config/configured_rule.php index d26d0abdb9c..1ed192c4dec 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/CompactToVariablesRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\CompactToVariablesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CompactToVariablesRector::class); -}; +return RectorConfig::configure() + ->withRules([CompactToVariablesRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/Fixture/fixture.php.inc deleted file mode 100644 index 412986fde3d..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,65 +0,0 @@ - ',', - 1 => ';', - ])); - } - - public function hasMoreThanOneArgument(): bool - { - return in_array('key', array_keys([ - 'key' => ',', - 1 => ';', - ], 'key')); - } - - public function resultIntoAVariable(): void - { - $array = ['foo', 'bar']; - $key = 'key'; - - $result = in_array($key, array_keys($array), true); - } -} - -?> ------ - ',', - 1 => ';', - ]); - } - - public function hasMoreThanOneArgument(): bool - { - return in_array('key', array_keys([ - 'key' => ',', - 1 => ';', - ], 'key')); - } - - public function resultIntoAVariable(): void - { - $array = ['foo', 'bar']; - $key = 'key'; - - $result = array_key_exists($key, $array); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/InArrayAndArrayKeysToArrayKeyExistsRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/InArrayAndArrayKeysToArrayKeyExistsRectorTest.php deleted file mode 100644 index d19f6511640..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/InArrayAndArrayKeysToArrayKeyExistsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/config/configured_rule.php deleted file mode 100644 index cfaa0a16535..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(InArrayAndArrayKeysToArrayKeyExistsRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Fixture/skip_checked_type_string.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Fixture/skip_checked_type_string.php.inc new file mode 100644 index 00000000000..f8f7bf8e8b7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Fixture/skip_checked_type_string.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Fixture/variable_class.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Fixture/variable_class.php.inc new file mode 100644 index 00000000000..4104868cada --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Fixture/variable_class.php.inc @@ -0,0 +1,33 @@ + $type + */ + public function run(object $object, string $type) + { + return is_a($object, $type); + } +} + +?> +----- + $type + */ + public function run(object $object, string $type) + { + return $object instanceof $type; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/InlineIsAInstanceOfRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/InlineIsAInstanceOfRectorTest.php new file mode 100644 index 00000000000..a70a1f82344 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/InlineIsAInstanceOfRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Source/SomeType.php b/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Source/SomeType.php new file mode 100644 index 00000000000..35a55304571 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector/Source/SomeType.php @@ -0,0 +1,9 @@ +withRules([InlineIsAInstanceOfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/Fixture/fixture.php.inc deleted file mode 100644 index 04dbf81b92c..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/IntvalToTypeCastRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/IntvalToTypeCastRectorTest.php deleted file mode 100644 index 3b287acfb89..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/IntvalToTypeCastRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/config/configured_rule.php deleted file mode 100644 index 5ab484aeeb0..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(IntvalToTypeCastRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector/IsAWithStringWithThirdArgumentRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector/IsAWithStringWithThirdArgumentRectorTest.php index 2eafdbd6ef1..58684f472a4 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector/IsAWithStringWithThirdArgumentRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector/IsAWithStringWithThirdArgumentRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\IsAWithStringWithThirdArgumentRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class IsAWithStringWithThirdArgumentRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector/config/configured_rule.php index f004449887e..72cd2bea5b2 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\IsAWithStringWithThirdArgumentRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(IsAWithStringWithThirdArgumentRector::class); -}; +return RectorConfig::configure() + ->withRules([IsAWithStringWithThirdArgumentRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector/Fixture/skip_non_string.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector/Fixture/skip_non_string.php.inc new file mode 100644 index 00000000000..55c33e406f0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector/Fixture/skip_non_string.php.inc @@ -0,0 +1,11 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector/config/configured_rule.php index 00927991d66..c766f314c11 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\RemoveSoleValueSprintfRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveSoleValueSprintfRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveSoleValueSprintfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/fixture.php.inc index a1ab5d1b107..b783006b099 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/fixture.php.inc @@ -13,8 +13,6 @@ class Fixture settype($foo, 'double'); settype($foo, 'bool'); settype($foo, 'boolean'); - - return settype($foo, 'integer'); } public function null() @@ -40,8 +38,6 @@ class Fixture $foo = (double) $foo; $foo = (bool) $foo; $foo = (bool) $foo; - - return (int) $foo; } public function null() diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_args.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_args.php.inc new file mode 100644 index 00000000000..4f0bd9503c0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_args.php.inc @@ -0,0 +1,11 @@ + settype($foo, 'string')]; + } +} diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_assign.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_assign.php.inc deleted file mode 100644 index 0da1c01738c..00000000000 --- a/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_assign.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - settype($foo, 'string')]; - - settype($foo, settype($foo, 'string')); - } -} diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_assign_and_return.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_assign_and_return.php.inc new file mode 100644 index 00000000000..3257de09901 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/Fixture/skip_assign_and_return.php.inc @@ -0,0 +1,20 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/config/configured_rule.php index 0a5610a8cdd..af33d373c26 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SetTypeToCastRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\SetTypeToCastRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SetTypeToCastRector::class); -}; +return RectorConfig::configure() + ->withRules([SetTypeToCastRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector/SimplifyFuncGetArgsCountRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector/SimplifyFuncGetArgsCountRectorTest.php index 99a824327d8..f6520219e5a 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector/SimplifyFuncGetArgsCountRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector/SimplifyFuncGetArgsCountRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\SimplifyFuncGetArgsCountRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyFuncGetArgsCountRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector/config/configured_rule.php index c5fd8f3c6ac..2de4eb2af3d 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\SimplifyFuncGetArgsCountRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyFuncGetArgsCountRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyFuncGetArgsCountRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector/SimplifyInArrayValuesRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector/SimplifyInArrayValuesRectorTest.php index 68bf79475e3..5c60d120410 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector/SimplifyInArrayValuesRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector/SimplifyInArrayValuesRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\SimplifyInArrayValuesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyInArrayValuesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector/config/configured_rule.php index bf929a3ccad..9a0f71853ec 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\SimplifyInArrayValuesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyInArrayValuesRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyInArrayValuesRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/other_delimiter.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/other_delimiter.php.inc new file mode 100644 index 00000000000..72d130079b8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/other_delimiter.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/skip_direct_square_bracket_as_delimiter.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/skip_direct_square_bracket_as_delimiter.php.inc new file mode 100644 index 00000000000..ec48d5b2bda --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/skip_direct_square_bracket_as_delimiter.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/with_square_bracket_as_delimiter.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/with_square_bracket_as_delimiter.php.inc new file mode 100644 index 00000000000..0a4f9014352 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/Fixture/with_square_bracket_as_delimiter.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/SimplifyRegexPatternRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/SimplifyRegexPatternRectorTest.php index c8029da0a35..7ad57569f3e 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/SimplifyRegexPatternRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/SimplifyRegexPatternRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\SimplifyRegexPatternRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyRegexPatternRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/config/configured_rule.php index 3c1d595c906..6bd47f6935d 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\SimplifyRegexPatternRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyRegexPatternRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyRegexPatternRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector/Fixture/skip_dynamic_second_arg.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector/Fixture/skip_dynamic_second_arg.php.inc new file mode 100644 index 00000000000..915922b780c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector/Fixture/skip_dynamic_second_arg.php.inc @@ -0,0 +1,14 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector/config/configured_rule.php index ebbba5ee95b..0f860e40ce9 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\SimplifyStrposLowerRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyStrposLowerRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyStrposLowerRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/fixture.php.inc index 74b37c7d417..af488905505 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/fixture.php.inc @@ -8,8 +8,6 @@ class Fixture { $isIt = in_array(strtolower($type), ['$this'], true); $isIt = in_array(strtolower($type), ['$this']); - - $isIt = in_array(strtolower($type), ['$this', 'two']); } } @@ -25,8 +23,6 @@ class Fixture { $isIt = strtolower($type) === '$this'; $isIt = strtolower($type) == '$this'; - - $isIt = in_array(strtolower($type), ['$this', 'two']); } } diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/negated_in_array.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/negated_in_array.php.inc new file mode 100644 index 00000000000..8f5cbd03258 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/negated_in_array.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/skip_multi_data.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/skip_multi_data.php.inc new file mode 100644 index 00000000000..f75178d3b9a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/Fixture/skip_multi_data.php.inc @@ -0,0 +1,11 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/config/configured_rule.php index ade07b4b95a..acf03decd4e 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\SingleInArrayToCompareRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SingleInArrayToCompareRector::class); -}; +return RectorConfig::configure() + ->withRules([SingleInArrayToCompareRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/array_map_named_params.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/array_map_named_params.php.inc new file mode 100644 index 00000000000..326534eaa8d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/array_map_named_params.php.inc @@ -0,0 +1,17 @@ + $item * 2); + +?> +----- + $item * 2, array: $items); + +?> diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..f7ffce9d893 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/fixture.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/skip_attribute.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/skip_attribute.php.inc new file mode 100644 index 00000000000..e7343bfa676 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/skip_attribute.php.inc @@ -0,0 +1,11 @@ + diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/skip_single_arg.php.inc b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/skip_single_arg.php.inc new file mode 100644 index 00000000000..5697c44e351 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Fixture/skip_single_arg.php.inc @@ -0,0 +1,8 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/SortCallLikeNamedArgsRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/SortCallLikeNamedArgsRectorTest.php new file mode 100644 index 00000000000..d2c7c41d4c8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/SortCallLikeNamedArgsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Source/MyAttribute.php b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Source/MyAttribute.php new file mode 100644 index 00000000000..eb8df9d7ebb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector/Source/MyAttribute.php @@ -0,0 +1,11 @@ +withRules([SortCallLikeNamedArgsRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector/UnwrapSprintfOneArgumentRectorTest.php b/rules-tests/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector/UnwrapSprintfOneArgumentRectorTest.php index c9567d49fae..4c5a575da66 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector/UnwrapSprintfOneArgumentRectorTest.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector/UnwrapSprintfOneArgumentRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\FuncCall\UnwrapSprintfOneArgumentRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class UnwrapSprintfOneArgumentRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector/config/configured_rule.php index 8b29337b385..2f2266a31e5 100644 --- a/rules-tests/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\FuncCall\UnwrapSprintfOneArgumentRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UnwrapSprintfOneArgumentRector::class); -}; +return RectorConfig::configure() + ->withRules([UnwrapSprintfOneArgumentRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/constant_string_with_value.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/constant_string_with_value.php.inc deleted file mode 100644 index f80f4db223c..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/constant_string_with_value.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -value = 'hi'; - } - - public function go() - { - if ($this->value) { - return 'yes'; - } - } -} - -?> ------ -value = 'hi'; - } - - public function go() - { - return 'yes'; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/fix_static_array.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/fix_static_array.php.inc deleted file mode 100644 index db9609d95d6..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/fix_static_array.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -value = [5]; - } - - public function go() - { - if ($this->value) { - $maybe = 'yes'; - return 'she says ' . $maybe; - } - } -} - -?> ------ -value = [5]; - } - - public function go() - { - $maybe = 'yes'; - return 'she says ' . $maybe; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/fixture.php.inc deleted file mode 100644 index 3a440e3370b..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -value = $value; - } - - public function go() - { - if ($this->value) { - return 'yes'; - } - } -} - -?> ------ -value = $value; - } - - public function go() - { - return 'yes'; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines.php.inc deleted file mode 100644 index cbe520e4442..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines.php.inc +++ /dev/null @@ -1,60 +0,0 @@ -value = $value; - } - - public function go() - { - if ($this->value) { - $maybe = 'yes'; - return 'she says ' . $maybe; - } - - foreach ([1, 2, 3] as $number) { - if ($number > 100) { - return $number; - } - } - } -} - -?> ------ -value = $value; - } - - public function go() - { - $maybe = 'yes'; - return 'she says ' . $maybe; - foreach ([1, 2, 3] as $number) { - if ($number > 100) { - return $number; - } - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines_in_callable.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines_in_callable.php.inc deleted file mode 100644 index 20bc5485832..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines_in_callable.php.inc +++ /dev/null @@ -1,53 +0,0 @@ -value = $value; - } - - public function go() - { - $callable = function () { - if ($this->value) { - $maybe = 1; - return 'she said ' . $maybe; - } - }; - } -} - -?> ------ -value = $value; - } - - public function go() - { - $callable = function () { - $maybe = 1; - return 'she said ' . $maybe; - }; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines_removed.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines_removed.php.inc deleted file mode 100644 index 6e59bc64ae4..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/multiple_lines_removed.php.inc +++ /dev/null @@ -1,59 +0,0 @@ -value = $value; - } - - public function go() - { - if ($this->value) { - $maybe = 'yes'; - return 'she says ' . $maybe; - } - $callable = function () { - if ($this->value) { - $maybe = 1; - return 'she said ' . $maybe; - } - }; - } -} - -?> ------ -value = $value; - } - - public function go() - { - $maybe = 'yes'; - return 'she says ' . $maybe; - $callable = function () { - $maybe = 1; - return 'she said ' . $maybe; - }; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/numbers.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/numbers.php.inc deleted file mode 100644 index c280ba653e7..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/numbers.php.inc +++ /dev/null @@ -1,55 +0,0 @@ -value = 15; - $this->smallValue = 0; - } - - public function go() - { - if ($this->value) { - return 'yes'; - } - - if ($this->smallValue) { - return 'no'; - } - } -} - -?> ------ -value = 15; - $this->smallValue = 0; - } - - public function go() - { - return 'yes'; - - if ($this->smallValue) { - return 'no'; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_after_overridden.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_after_overridden.php.inc deleted file mode 100644 index d5e65f6674e..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_after_overridden.php.inc +++ /dev/null @@ -1,26 +0,0 @@ -areListenerClassesLoaded) { - return $this->listenerClassesToEvents; - } - - $this->areListenerClassesLoaded = true; - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_array.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_array.php.inc deleted file mode 100644 index 0860194ae41..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_array.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -value = $value; - } - - public function go() - { - if ($this->value) { - $maybe = 'yes'; - return 'she says ' . $maybe; - } - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_changed_value.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_changed_value.php.inc deleted file mode 100644 index b6b667f14d3..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_changed_value.php.inc +++ /dev/null @@ -1,28 +0,0 @@ -value = $value; - } - - public function go() - { - if ($this->value) { - $maybe = 'yes'; - return 'she says ' . $maybe; - } - } - - public function another() - { - $this->value = null; - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_constant_strings_without_value.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_constant_strings_without_value.php.inc deleted file mode 100644 index d4f821330fa..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_constant_strings_without_value.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -smallValue = ''; - } - - public function go() - { - if ($this->smallValue) { - return 'no'; - } - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_not_yet_used.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_not_yet_used.php.inc deleted file mode 100644 index 860114803b4..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_not_yet_used.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -statieConfiguration = $statieConfiguration; - } - - public function detect(): string - { - if ($this->detectedTemplating) { - return $this->detectedTemplating; - } - - $twigFileCount = 1; - $latteFileCount = 2; - - $this->detectedTemplating = $twigFileCount > $latteFileCount ? 'twig' : 'latte'; - - return $this->detectedTemplating; - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_nullable_set.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_nullable_set.php.inc deleted file mode 100644 index ad197b1c856..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_nullable_set.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -callback = $callback; - } - - public function __toString() - { - $contentItems = []; - - if ($this->callback) { - $contentItems['callback'] = 5; - } - - return '...'; - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_optional_argument_value.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_optional_argument_value.php.inc deleted file mode 100644 index f18a0193226..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_optional_argument_value.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -should = $should; - } - - public function go() - { - if ($this->should) { - $maybe = 'yes'; - return 'she says ' . $maybe; - } - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_public.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_public.php.inc deleted file mode 100644 index 8e8e7c6c96f..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_public.php.inc +++ /dev/null @@ -1,25 +0,0 @@ -value = 10000; - } - - public function go() - { - if ($this->value) { - return true; - } - - return false; - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_scalars.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_scalars.php.inc deleted file mode 100644 index e785d61bed5..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_scalars.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -string = $string; - $this->float = $float; - $this->int = $int; - } - - public function go() - { - if ($this->string) { - return 'yes'; - } - - if ($this->float) { - return 'yes'; - } - - if ($this->int) { - return 'yes'; - } - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_trait.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_trait.php.inc deleted file mode 100644 index f8a8a633a37..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_trait.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -value = $value; - } - - public function go() - { - if ($this->value) { - return 'yes'; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_unknown.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_unknown.php.inc deleted file mode 100644 index 9e8471f7e83..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/skip_unknown.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -yolo = $yolo; - } - - public function go() - { - if ($this->yolo) { - return 'yes'; - } - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/various_types.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/various_types.php.inc deleted file mode 100644 index b3b87a32bb4..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/Fixture/various_types.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -value = 15; - } - - public function go() - { - $this->value = 'hi'; - - if ($this->value) { - return 'yes'; - } - } -} - -?> ------ -value = 15; - } - - public function go() - { - $this->value = 'hi'; - - return 'yes'; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/RemoveAlwaysTrueConditionSetInConstructorRectorTest.php b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/RemoveAlwaysTrueConditionSetInConstructorRectorTest.php deleted file mode 100644 index 1e6c082701a..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/RemoveAlwaysTrueConditionSetInConstructorRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/config/configured_rule.php deleted file mode 100644 index e0664c06398..00000000000 --- a/rules-tests/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveAlwaysTrueConditionSetInConstructorRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/assign_ops.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/assign_ops.php.inc new file mode 100644 index 00000000000..a4723b5de5e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/assign_ops.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/closure_use_not_by_reference.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/closure_use_not_by_reference.php.inc new file mode 100644 index 00000000000..46e926c0c1d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/closure_use_not_by_reference.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..e6714de2720 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/fixture.php.inc @@ -0,0 +1,88 @@ + $a++, + --$b => ++$a, + ]; + return $c; +}; + +?> +----- + $a++, + --$b => ++$a, + ]; +}; + +?> diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/fixture3.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/fixture3.php.inc new file mode 100644 index 00000000000..cbcfc68a429 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/fixture3.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/in_a_function.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/in_a_function.php.inc new file mode 100644 index 00000000000..f021f6e500b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/in_a_function.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/keep_var_doc.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/keep_var_doc.php.inc new file mode 100644 index 00000000000..e8f74679441 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/keep_var_doc.php.inc @@ -0,0 +1,19 @@ +getValue(); + + /** @var string $name */ + return $name; + } + + private function getValue() + { + return 'name'; + } +} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/on_else.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/on_else.php.inc new file mode 100644 index 00000000000..42c82b11948 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/on_else.php.inc @@ -0,0 +1,26 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/skip_closure_use_by_reference.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/skip_closure_use_by_reference.php.inc new file mode 100644 index 00000000000..45b856d1a01 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/skip_closure_use_by_reference.php.inc @@ -0,0 +1,18 @@ +$parameter = 'this'; + } + +} + +$noErrorToo = null; +function ($noError = 'noError') use ($noErrorToo) { + +}; + +$used = true; + +function foo($foo) { + return preg_replace_callback('~~', function ($matches) { + return $matches[0]; + }, $foo); +} + +return $used; + +function ($values) { + $foo = ''; + + foreach ($values as $value) { + echo $foo . $value; + } +}; + +function () { + for ($i = 0; $i < 10; $i++) { + } +}; + +function ($values) { + foreach ($values as $value) { + $foo = 'foo' . $value; + } + echo $foo; +}; + +function ($values) { + list($a, $b) = $values; + return $a + $b; +}; + +function ($values) { + [$c, $d] = $values; + return $c * $d; +}; + +function ($values) { + $current = 'current'; + $next = 'next'; + + while ($next) { + if ($current) { + + } + + $current = false; + + if (true) { + foreach ($values as $value) { + $next = $value; + } + } + + do { + $previous = 'previous'; + } while ($previous); + } +}; + +function (&$parameter) { + $parameter = 'value-by-reference'; +}; + +function () use (&$inheritedVariable) { + $inheritedVariable = 'value-by-reference'; +}; + +function ($interval) { + $j = 0; + for ($i = $j; $i < 10; $i += $interval) { + } +}; + +function () { + static $static = false; + if ($static) { + return; + } + + $static = true; +}; + +function () { + $a = 'a'; + $b = 'b'; + + $this->compact; + + return compact('a', "b"); +}; + +function () { + $a = ''; + echo "$a"; +}; + +function () { + $a = ''; + echo "${a}"; +}; + +function () { + $a = ''; + echo "$a()"; +}; + +function () { + $a = ''; + echo <<a, $this->b) = [$a, $b]; + } + +} + +function () { + $i = 0; + while ($i++ <= 10) { + } +}; + +function () { + $i = 0; + do { + } while (++$i <= 10); +}; + +function () { + $i = 10; + while ($i-- > 0) { + } +}; + +function () { + $i = 10; + do { + } while (--$i > 0); +}; + +function ($data) { + $i = 0; + $c = ''; + foreach ($data as $c) { + $c = $i++; + } + echo $c; +}; + +function ($values) { + $expectedKey = 0; + + foreach ($values as $key => $value) { + if ($key !== $expectedKey++) { + return $value; + } + } + + return null; +}; diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/skip_global_variable.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/skip_global_variable.php.inc new file mode 100644 index 00000000000..ec7ee314f15 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/skip_global_variable.php.inc @@ -0,0 +1,17 @@ +setTime(14, 0, 0); + + return $dateTime; + } +} diff --git a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/special_class_name_with_anonymous_class.php.inc b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/special_class_name_with_anonymous_class.php.inc similarity index 79% rename from rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/special_class_name_with_anonymous_class.php.inc rename to rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/special_class_name_with_anonymous_class.php.inc index 3d720befffd..5f3c985fd7c 100644 --- a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/special_class_name_with_anonymous_class.php.inc +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/Fixture/special_class_name_with_anonymous_class.php.inc @@ -1,6 +1,6 @@ doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/SkipConcatTest.php b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/SkipConcatTest.php new file mode 100644 index 00000000000..bf3f157bf4c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/SkipConcatTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureSkipConcat'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/skip_concat.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/config/configured_rule.php new file mode 100644 index 00000000000..85f617d3ede --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([SimplifyUselessVariableRector::class]); diff --git a/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/config/skip_concat.php b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/config/skip_concat.php new file mode 100644 index 00000000000..b815f89a85f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector/config/skip_concat.php @@ -0,0 +1,13 @@ +ruleWithConfiguration(SimplifyUselessVariableRector::class, [ + SimplifyUselessVariableRector::ONLY_DIRECT_ASSIGN => true, + ]); +}; diff --git a/rules-tests/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector/BooleanNotIdenticalToNotIdenticalRectorTest.php b/rules-tests/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector/BooleanNotIdenticalToNotIdenticalRectorTest.php index 7360af13afe..5bd7f5abb98 100644 --- a/rules-tests/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector/BooleanNotIdenticalToNotIdenticalRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector/BooleanNotIdenticalToNotIdenticalRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Identical\BooleanNotIdenticalToNotIdenticalRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class BooleanNotIdenticalToNotIdenticalRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector/config/configured_rule.php index 15a64a21409..986eba95c9e 100644 --- a/rules-tests/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Identical\BooleanNotIdenticalToNotIdenticalRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(BooleanNotIdenticalToNotIdenticalRector::class); -}; +return RectorConfig::configure() + ->withRules([BooleanNotIdenticalToNotIdenticalRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fixture.php.inc deleted file mode 100644 index bf8e0c67673..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,49 +0,0 @@ -getStdClass(); - if ($stdClass === null) { - return; - } - - /** @var null|stdClass $stdClass */ - $stdClass = $this->getStdClass(); - if ($stdClass === null) { - return; - } - } -} - -?> ------ -getStdClass(); - if (!$stdClass instanceof \stdClass) { - return; - } - - $stdClass = $this->getStdClass(); - if (!$stdClass instanceof \stdClass) { - return; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/flip_instance_nullable_type.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/flip_instance_nullable_type.php.inc new file mode 100644 index 00000000000..6aabdcdea9f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/flip_instance_nullable_type.php.inc @@ -0,0 +1,55 @@ +getStdClass(); + if ($stdClass === null) { + return; + } + } + + private function getStdClass(): ?stdClass + { + if (rand(0, 1)) { + return new stdClass; + } + + return null; + } +} + +?> +----- +getStdClass(); + if (!$stdClass instanceof \stdClass) { + return; + } + } + + private function getStdClass(): ?stdClass + { + if (rand(0, 1)) { + return new stdClass; + } + + return null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fqcn_in_namespace.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fqcn_in_namespace.php.inc index 6e0118bcaba..759cf6c4030 100644 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fqcn_in_namespace.php.inc +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fqcn_in_namespace.php.inc @@ -8,12 +8,16 @@ class FqcnInNamespace { public function run() { - /** @var stdClass|null $stdClass */ $stdClass = $this->getStdClass(); if ($stdClass === null) { return; } } + + public function getStdClass(): ?stdClass + { + return new \stdClass(); + } } ?> @@ -33,6 +37,11 @@ class FqcnInNamespace return; } } + + public function getStdClass(): ?stdClass + { + return new \stdClass(); + } } ?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fqcn_type.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fqcn_type.php.inc deleted file mode 100644 index fcb10b3dddb..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/fqcn_type.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -getStdClass(); - if ($stdClass === null) { - return; - } - } -} - -?> ------ -getStdClass(); - if (!$stdClass instanceof \stdClass) { - return; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/interface_type.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/interface_type.php.inc deleted file mode 100644 index 7b25624cbac..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/interface_type.php.inc +++ /dev/null @@ -1,49 +0,0 @@ -getdateTimeInterface(); - if ($dateTimeInterface === null) { - return; - } - - /** @var null|DateTimeInterface $dateTimeInterface */ - $dateTimeInterface = $this->getdateTimeInterface(); - if ($dateTimeInterface === null) { - return; - } - } -} - -?> ------ -getdateTimeInterface(); - if (!$dateTimeInterface instanceof \DateTimeInterface) { - return; - } - - $dateTimeInterface = $this->getdateTimeInterface(); - if (!$dateTimeInterface instanceof \DateTimeInterface) { - return; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_inverse.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_inverse.php.inc new file mode 100644 index 00000000000..84ff3883565 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_inverse.php.inc @@ -0,0 +1,43 @@ +getNode(); + } + + return null; + } +} + +?> +----- +getNode(); + } + + return null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_type.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_type.php.inc deleted file mode 100644 index b76d13eb434..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_type.php.inc +++ /dev/null @@ -1,55 +0,0 @@ -getStdClass(); - if ($stdClass === null) { - return; - } - } - - private function getStdClass(): ?stdClass - { - if (rand(0, 1)) { - return new stdClass; - } - - return null; - } -} - -?> ------ -getStdClass(); - if (!$stdClass instanceof \stdClass) { - return; - } - } - - private function getStdClass(): ?stdClass - { - if (rand(0, 1)) { - return new stdClass; - } - - return null; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_type_aliased.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_type_aliased.php.inc new file mode 100644 index 00000000000..d8308e98d0f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/nullable_type_aliased.php.inc @@ -0,0 +1,55 @@ +getIntNode(); + if ($intNode === null) { + return; + } + } + + private function getIntNode(): ?SomeInt + { + if (rand(0, 1)) { + return new SomeInt(1); + } + + return null; + } +} + +?> +----- +getIntNode(); + if (!$intNode instanceof \PhpParser\Node\Scalar\Int_) { + return; + } + } + + private function getIntNode(): ?SomeInt + { + if (rand(0, 1)) { + return new SomeInt(1); + } + + return null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/on_assign.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/on_assign.php.inc new file mode 100644 index 00000000000..53d236a7d69 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/on_assign.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_assign.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_assign.php.inc deleted file mode 100644 index fa56213acc6..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_assign.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_doc.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_doc.php.inc index bb0236c0e95..1f91faa8ef9 100644 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_doc.php.inc +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_doc.php.inc @@ -1,10 +1,10 @@ diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_var_tag.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_var_tag.php.inc deleted file mode 100644 index 916570fbcf6..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_no_var_tag.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -getStdClass(); - if ($stdClass === null) { - return; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_not_null_doc.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_not_null_doc.php.inc index c7a1fc5a192..84b8d3746be 100644 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_not_null_doc.php.inc +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_not_null_doc.php.inc @@ -16,5 +16,3 @@ class SkipNotNullDoc } } } - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_not_union.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_not_union.php.inc deleted file mode 100644 index 0d3f3876675..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_not_union.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -getStdClass(); - if ($stdClass === null) { - return; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_phpdoc.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_phpdoc.php.inc new file mode 100644 index 00000000000..ff13aa19a18 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_phpdoc.php.inc @@ -0,0 +1,24 @@ +getStdClass(); + if ($stdClass === null) { + return; + } + } + + public function getStdClass() + { + return new \stdClass(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_same_type.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_same_type.php.inc deleted file mode 100644 index fb47ba7cc6b..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_same_type.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -getString(); - if ($string === null) { - return; - } - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_scalar_type.php.inc b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_scalar_type.php.inc index c6b63b341d8..c93554dee6c 100644 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_scalar_type.php.inc +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/Fixture/skip_scalar_type.php.inc @@ -15,5 +15,3 @@ class SkipScalarType } } } - -?> diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/FlipTypeControlToUseExclusiveTypeRectorTest.php b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/FlipTypeControlToUseExclusiveTypeRectorTest.php index 92dd9f6fc02..b51fce6317a 100644 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/FlipTypeControlToUseExclusiveTypeRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/FlipTypeControlToUseExclusiveTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class FlipTypeControlToUseExclusiveTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/config/configured_rule.php index 99c8ef46cb5..a819819480b 100644 --- a/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(FlipTypeControlToUseExclusiveTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([FlipTypeControlToUseExclusiveTypeRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/fixture.php.inc deleted file mode 100644 index 68a29adcd7b..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/self_should_not_be_namespaced.php.inc b/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/self_should_not_be_namespaced.php.inc deleted file mode 100644 index 4b5a20f97c0..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/self_should_not_be_namespaced.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/static_should_not_be_namespaced.php.inc b/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/static_should_not_be_namespaced.php.inc deleted file mode 100644 index b30d4e9914b..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/Fixture/static_should_not_be_namespaced.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/GetClassToInstanceOfRectorTest.php b/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/GetClassToInstanceOfRectorTest.php deleted file mode 100644 index 416be22a48e..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/GetClassToInstanceOfRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/config/configured_rule.php deleted file mode 100644 index d8989761bf6..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/GetClassToInstanceOfRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(GetClassToInstanceOfRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyArraySearchRector/SimplifyArraySearchRectorTest.php b/rules-tests/CodeQuality/Rector/Identical/SimplifyArraySearchRector/SimplifyArraySearchRectorTest.php index 137ee4cb28c..8e1df487faf 100644 --- a/rules-tests/CodeQuality/Rector/Identical/SimplifyArraySearchRector/SimplifyArraySearchRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyArraySearchRector/SimplifyArraySearchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Identical\SimplifyArraySearchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyArraySearchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyArraySearchRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Identical/SimplifyArraySearchRector/config/configured_rule.php index 8ae6548fb82..fdb16005805 100644 --- a/rules-tests/CodeQuality/Rector/Identical/SimplifyArraySearchRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyArraySearchRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Identical\SimplifyArraySearchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyArraySearchRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyArraySearchRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/directly.php.inc b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/directly.php.inc index bd9e32a38b3..06d15f3bc11 100644 --- a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/directly.php.inc +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/directly.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector\Fixture; -class Directly +final class Directly { public function run(bool $value, string $items) { @@ -20,7 +20,7 @@ class Directly namespace Rector\Tests\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector\Fixture; -class Directly +final class Directly { public function run(bool $value, string $items) { diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/double_negate.php.inc b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/double_negate.php.inc index d1cbc57d13d..2d929d708da 100644 --- a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/double_negate.php.inc +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/double_negate.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector\Fixture; -class DoubleNegate +final class DoubleNegate { public function run() { @@ -18,7 +18,7 @@ class DoubleNegate namespace Rector\Tests\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector\Fixture; -class DoubleNegate +final class DoubleNegate { public function run() { diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/negate.php.inc b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/negate.php.inc index bcd7dd445e7..aebefd0ffc2 100644 --- a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/negate.php.inc +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/negate.php.inc @@ -2,12 +2,11 @@ namespace Rector\Tests\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector\Fixture; -class Negate +final class Negate { public function run($value, array $items) { $isMatch = in_array($value, $items, TRUE) !== TRUE; - $isMatch = in_array($value, $items, TRUE) === FALSE; $isMatch = true !== in_array($value, $items, TRUE); } @@ -19,12 +18,11 @@ class Negate namespace Rector\Tests\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector\Fixture; -class Negate +final class Negate { public function run($value, array $items) { $isMatch = !in_array($value, $items, TRUE); - $isMatch = !in_array($value, $items, TRUE); $isMatch = !in_array($value, $items, TRUE); } diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/skip_docblock_usage.php.inc b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/skip_docblock_usage.php.inc new file mode 100644 index 00000000000..3218b2b3310 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/skip_docblock_usage.php.inc @@ -0,0 +1,14 @@ +prop ? 2 : 0; + } +} diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/skip_equals_false.php.inc b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/skip_equals_false.php.inc new file mode 100644 index 00000000000..25a36b17a3c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/Fixture/skip_equals_false.php.inc @@ -0,0 +1,24 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/config/configured_rule.php index 1123846dc36..ad6a8d7dc62 100644 --- a/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyBoolIdenticalTrueRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyBoolIdenticalTrueRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyConditionsRector/SimplifyConditionsRectorTest.php b/rules-tests/CodeQuality/Rector/Identical/SimplifyConditionsRector/SimplifyConditionsRectorTest.php index 9d199b8481e..c97c3358ff6 100644 --- a/rules-tests/CodeQuality/Rector/Identical/SimplifyConditionsRector/SimplifyConditionsRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyConditionsRector/SimplifyConditionsRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Identical\SimplifyConditionsRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyConditionsRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Identical/SimplifyConditionsRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Identical/SimplifyConditionsRector/config/configured_rule.php index b929c09c799..ed3ca54259d 100644 --- a/rules-tests/CodeQuality/Rector/Identical/SimplifyConditionsRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Identical/SimplifyConditionsRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Identical\SimplifyConditionsRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyConditionsRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyConditionsRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/definitely_string.php.inc b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/definitely_string.php.inc new file mode 100644 index 00000000000..1212ded3897 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/definitely_string.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/fixture.php.inc deleted file mode 100644 index cb938f64057..00000000000 --- a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/greater_and_smaller.php.inc b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/greater_and_smaller.php.inc new file mode 100644 index 00000000000..b937337293a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/greater_and_smaller.php.inc @@ -0,0 +1,37 @@ + 0; + 0 < strlen($string); + + strlen($mixed) > 0; + 0 < strlen($mixed); + + strlen($stringable) > 0; + 0 < strlen($stringable); +}; + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/might_not_be_string.php.inc b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/might_not_be_string.php.inc new file mode 100644 index 00000000000..db0fb2a3b52 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/might_not_be_string.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/non_string_value.php.inc b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/non_string_value.php.inc new file mode 100644 index 00000000000..1c5612d3bed --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/non_string_value.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/skip_less_than_zero.php.inc b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/skip_less_than_zero.php.inc new file mode 100644 index 00000000000..d58cdfbe5eb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/skip_less_than_zero.php.inc @@ -0,0 +1,8 @@ + strlen($string); +}; diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/stringable_object.php.inc b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/stringable_object.php.inc new file mode 100644 index 00000000000..415c2789577 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Fixture/stringable_object.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Source/Stringable.php b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Source/Stringable.php new file mode 100644 index 00000000000..46081d30436 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/Source/Stringable.php @@ -0,0 +1,13 @@ +string; + } +} diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/StrlenZeroToIdenticalEmptyStringRectorTest.php b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/StrlenZeroToIdenticalEmptyStringRectorTest.php index 03f6a52c897..ae62ff71a6e 100644 --- a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/StrlenZeroToIdenticalEmptyStringRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/StrlenZeroToIdenticalEmptyStringRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Identical\StrlenZeroToIdenticalEmptyStringRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StrlenZeroToIdenticalEmptyStringRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/config/configured_rule.php index a39bc2f2e76..4ba40b18fd7 100644 --- a/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Identical\StrlenZeroToIdenticalEmptyStringRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StrlenZeroToIdenticalEmptyStringRector::class); -}; +return RectorConfig::configure() + ->withRules([StrlenZeroToIdenticalEmptyStringRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/CombineIfRectorTest.php b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/CombineIfRectorTest.php index c671ee62830..eee478ed119 100644 --- a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/CombineIfRectorTest.php +++ b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/CombineIfRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\If_\CombineIfRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CombineIfRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/property_fetch_in_condition.php.inc b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/property_fetch_in_condition.php.inc new file mode 100644 index 00000000000..d71f39ff905 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/property_fetch_in_condition.php.inc @@ -0,0 +1,33 @@ +art->netzid > 0 && $artzo_list = $this->artzo_list) { + if ($artzo_list !== []) { + foreach ($artzo_list as $art) { + } + } + } + } +} +?> +----- +art->netzid > 0 && ($artzo_list = $this->artzo_list) && $artzo_list !== []) { + foreach ($artzo_list as $art) { + } + } + } +} +?> diff --git a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/property_fetch_in_condition2.php.inc b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/property_fetch_in_condition2.php.inc new file mode 100644 index 00000000000..f7b54d84f66 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/property_fetch_in_condition2.php.inc @@ -0,0 +1,33 @@ +art->netzid > 0 && $artzo_list = $this->artzo_list) { + foreach ($artzo_list as $art) { + } + } + } + } +} +?> +----- +art->netzid > 0 && $artzo_list = $this->artzo_list)) { + foreach ($artzo_list as $art) { + } + } + } +} +?> diff --git a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/skip_nested_type.php.inc b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/skip_nested_type.php.inc new file mode 100644 index 00000000000..8c7ada706da --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/skip_nested_type.php.inc @@ -0,0 +1,29 @@ +isAssign($expr)) { + /** @var Assign $expr */ + if ($expr->var) { + return true; + } + } + + return false; + } + + private function isAssign($expr) + { + if ($expr instanceof Assign) { + return true; + } + + return false; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/with_assign.php.inc b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/with_assign.php.inc new file mode 100644 index 00000000000..b9733cb238c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/with_assign.php.inc @@ -0,0 +1,33 @@ +getCond2Value()) === null) { + return 'foo'; + } + } + } +} + +?> +----- +getCond2Value()) === null) { + return 'foo'; + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/with_negation_binaryop_previous_if.php.inc b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/with_negation_binaryop_previous_if.php.inc new file mode 100644 index 00000000000..920b5b7795a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/Fixture/with_negation_binaryop_previous_if.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/config/configured_rule.php index 280facec63f..0357f4362ca 100644 --- a/rules-tests/CodeQuality/Rector/If_/CombineIfRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/If_/CombineIfRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\If_\CombineIfRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CombineIfRector::class); -}; +return RectorConfig::configure() + ->withRules([CombineIfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/CompleteMissingIfElseBracketRectorTest.php b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/CompleteMissingIfElseBracketRectorTest.php new file mode 100644 index 00000000000..57e1da3e82f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/CompleteMissingIfElseBracketRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/all_no_bracket.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/all_no_bracket.php.inc new file mode 100644 index 00000000000..d0dedd18356 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/all_no_bracket.php.inc @@ -0,0 +1,38 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/cury_in_cond_no_open_bracket_next.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/cury_in_cond_no_open_bracket_next.php.inc new file mode 100644 index 00000000000..bc4c1219e1b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/cury_in_cond_no_open_bracket_next.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_else.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_else.php.inc new file mode 100644 index 00000000000..40746da0b1d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_else.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_elseif.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_elseif.php.inc new file mode 100644 index 00000000000..e60fcfb0c41 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_elseif.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_elseif_and_else.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_elseif_and_else.php.inc new file mode 100644 index 00000000000..6f06683085d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_elseif_and_else.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_if_only.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_if_only.php.inc new file mode 100644 index 00000000000..30f6ccd9349 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/missing_bracket_on_if_only.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_anonymous_class.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_anonymous_class.php.inc new file mode 100644 index 00000000000..495fef58e7d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_anonymous_class.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_closure.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_closure.php.inc new file mode 100644 index 00000000000..518b35cecd6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_closure.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_method_call.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_method_call.php.inc new file mode 100644 index 00000000000..e5df9de338a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/on_method_call.php.inc @@ -0,0 +1,30 @@ +execute(); + } +} + +?> +----- +execute(); + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_cury_in_cond.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_cury_in_cond.php.inc new file mode 100644 index 00000000000..fd7bf9ba29f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_cury_in_cond.php.inc @@ -0,0 +1,17 @@ + +
Hello
+ + +
Hello
+ + + +
Hello
+ + new \DateTime('now')) { + return; + } + } +} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_spaced_else_stmts.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_spaced_else_stmts.php.inc new file mode 100644 index 00000000000..07714babd8c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_spaced_else_stmts.php.inc @@ -0,0 +1,17 @@ + +
a
+ +
b
+ diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_with_html.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_with_html.php.inc new file mode 100644 index 00000000000..f2057cb2288 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/skip_with_html.php.inc @@ -0,0 +1,6 @@ + +some()) echo ' class="border"' ?>> + + \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/some_class.php.inc b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..bac57f6b257 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/Fixture/some_class.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/config/configured_rule.php new file mode 100644 index 00000000000..4af1d71c4ad --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([CompleteMissingIfElseBracketRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/ConsecutiveNullCompareReturnsToNullCoalesceQueueRectorTest.php b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/ConsecutiveNullCompareReturnsToNullCoalesceQueueRectorTest.php index 05e23daf25a..4ba8bac1cc2 100644 --- a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/ConsecutiveNullCompareReturnsToNullCoalesceQueueRectorTest.php +++ b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/ConsecutiveNullCompareReturnsToNullCoalesceQueueRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\If_\ConsecutiveNullCompareReturnsToNullCoalesceQueueRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ConsecutiveNullCompareReturnsToNullCoalesceQueueRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/return_new_object.php.inc b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/return_new_object.php.inc new file mode 100644 index 00000000000..9376eea4cda --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/return_new_object.php.inc @@ -0,0 +1,39 @@ +prop1 !== null) { + return $this->prop1; + } + if ($this->prop2 !== null) { + return $this->prop2; + } + return new \stdClass(); + } +} + +?> +----- +prop1 ?? $this->prop2) ?? new \stdClass(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_indirect_return.php.inc b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_indirect_return.php.inc new file mode 100644 index 00000000000..07e47b4cb05 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_indirect_return.php.inc @@ -0,0 +1,21 @@ +prop1 !== null) { + return $this->prop1; + } + if ($this->prop2 !== null) { + return $this->prop2; + } + echo 'hi'; + return null; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_no_return.php.inc b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_no_return.php.inc new file mode 100644 index 00000000000..81d4d4a0dcf --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_no_return.php.inc @@ -0,0 +1,20 @@ +prop1 !== null) { + return $this->prop1; + } + if ($this->prop2 !== null) { + return $this->prop2; + } + echo 'hi'; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_no_return2.php.inc b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_no_return2.php.inc new file mode 100644 index 00000000000..ed132bdd5c8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_no_return2.php.inc @@ -0,0 +1,19 @@ +prop1 !== null) { + return $this->prop1; + } + if ($this->prop2 !== null) { + return $this->prop2; + } + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_single_one.php.inc b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_single_one.php.inc new file mode 100644 index 00000000000..f69742e0e40 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/skip_single_one.php.inc @@ -0,0 +1,20 @@ +first) { + return $this->first; + } + + return null; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/throw_after_if.php.inc b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/throw_after_if.php.inc new file mode 100644 index 00000000000..58e2cd453fc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/Fixture/throw_after_if.php.inc @@ -0,0 +1,39 @@ +prop1 !== null) { + return $this->prop1; + } + if ($this->prop2 !== null) { + return $this->prop2; + } + throw new \RuntimeException('No prop is set'); + } +} + +?> +----- +prop1 ?? $this->prop2) ?? throw new \RuntimeException('No prop is set'); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/config/configured_rule.php index 7be0b69e692..0dd9094dee0 100644 --- a/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\If_\ConsecutiveNullCompareReturnsToNullCoalesceQueueRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ConsecutiveNullCompareReturnsToNullCoalesceQueueRector::class); -}; +return RectorConfig::configure() + ->withRules([ConsecutiveNullCompareReturnsToNullCoalesceQueueRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/ExplicitBoolCompareRectorTest.php b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/ExplicitBoolCompareRectorTest.php index c02132138ef..eb5c4fa1ec0 100644 --- a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/ExplicitBoolCompareRectorTest.php +++ b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/ExplicitBoolCompareRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\If_\ExplicitBoolCompareRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ExplicitBoolCompareRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/if_assign_cond.php.inc b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/if_assign_cond.php.inc new file mode 100644 index 00000000000..21fe987bf2f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/if_assign_cond.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/nullable.php.inc b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/nullable.php.inc index 56903192c42..1ae41552cb9 100644 --- a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/nullable.php.inc +++ b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/nullable.php.inc @@ -36,14 +36,14 @@ final class Nullable { public function run(?\stdClass $item) { - if ($item === null) { + if (!$item instanceof \stdClass) { return 'empty'; } } public function go(?\stdClass $item) { - if ($item !== null) { + if ($item instanceof \stdClass) { return 'not empty'; } } diff --git a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/nullable_instance_to_instanceof.php.inc b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/nullable_instance_to_instanceof.php.inc new file mode 100644 index 00000000000..178b3d27862 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/nullable_instance_to_instanceof.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/skip_docblock_nullable.php.inc b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/skip_docblock_nullable.php.inc new file mode 100644 index 00000000000..c8a70d46773 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/skip_docblock_nullable.php.inc @@ -0,0 +1,16 @@ + diff --git a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/with_assign.php.inc b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/with_assign.php.inc new file mode 100644 index 00000000000..70497088f33 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/Fixture/with_assign.php.inc @@ -0,0 +1,41 @@ +getExpiredAt()) { + echo $expired->format('Ymd'); + } + } +} + +?> +----- +getExpiredAt()) instanceof \Datetime) { + echo $expired->format('Ymd'); + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/config/configured_rule.php index 1ef6180fe8c..b33ccbcbfcf 100644 --- a/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/If_/ExplicitBoolCompareRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ExplicitBoolCompareRector::class); -}; +return RectorConfig::configure() + ->withRules([ExplicitBoolCompareRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment.php.inc b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment.php.inc new file mode 100644 index 00000000000..df01205447c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment2.php.inc b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment2.php.inc new file mode 100644 index 00000000000..bd234e0fc59 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment2.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment3.php.inc b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment3.php.inc new file mode 100644 index 00000000000..3df2cbb3961 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment3.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment_deep_comment.php.inc b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment_deep_comment.php.inc new file mode 100644 index 00000000000..665ad1aecbd --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/mirror_comment_deep_comment.php.inc @@ -0,0 +1,59 @@ + 5) { + echo 'a'; + } else { + // above if comment + if ($a === 5) { + // inside if comment + foreach ($b_arg as $element) { + // inside foreach comment + echo $element; + } + echo 'b'; + } else { + // inside else comment + echo 'c'; + } + } + } +} + +?> +----- + 5) { + echo 'a'; + } elseif ($a === 5) { + // above if comment + // inside if comment + foreach ($b_arg as $element) { + // inside foreach comment + echo $element; + } + echo 'b'; + } else { + // inside else comment + echo 'c'; + } + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/skip_with_html.php.inc b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/skip_with_html.php.inc new file mode 100644 index 00000000000..27b76053e92 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/Fixture/skip_with_html.php.inc @@ -0,0 +1,35 @@ + +
+ is_main_item) : ?> + +
+ icon ? $this->Icon->render($menuItem->icon) : '' ?> + +
+
+ is_external_url) { + echo $this->Html->link($title, $menuItem->url, [ + 'menu-icon' => $menuItem->icon ?? false, + 'class' => $aClass, + ]); + } elseif ($menuItem->parsed_url && $menuItem->is_url_valid) { + echo $this->Html->linkFromPath($title, $menuItem->parsed_url, [], [ + 'menu-icon' => $menuItem->icon ?? false, + 'class' => $aClass, + ]); + } else { + // Do something? + } + endif; ?> +
+ doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/config/configured_rule.php index 49193473081..6f875477afc 100644 --- a/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/If_/ShortenElseIfRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\If_\ShortenElseIfRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ShortenElseIfRector::class); -}; +return RectorConfig::configure() + ->withRules([ShortenElseIfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/operator_precedence.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/operator_precedence.php.inc new file mode 100644 index 00000000000..0aa82d52ea6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/operator_precedence.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/skip_with_comment_inside.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/skip_with_comment_inside.php.inc new file mode 100644 index 00000000000..93458b1a3e2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/skip_with_comment_inside.php.inc @@ -0,0 +1,17 @@ +toRawArray(); + } else { + $properties = (array) $data; + } + + return $properties; + } +} + +?> +----- +toRawArray() : (array) $data; + + return $properties; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/with_assign.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/with_assign.php.inc new file mode 100644 index 00000000000..91e2cbf49c2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/Fixture/with_assign.php.inc @@ -0,0 +1,31 @@ +methodX()) { + $this->out = $a; + } else { + $this->out = $this->methodY(); + } + } +} + +?> +----- +out = ($a = $this->methodX()) ? $a : $this->methodY(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/SimplifyIfElseToTernaryRectorTest.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/SimplifyIfElseToTernaryRectorTest.php index c9e246c1307..e488ebb2b31 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/SimplifyIfElseToTernaryRectorTest.php +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/SimplifyIfElseToTernaryRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyIfElseToTernaryRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/config/configured_rule.php index 34cce36e0a9..eba00b24ee1 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyIfElseToTernaryRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyIfElseToTernaryRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/Fixture/fixture.php.inc deleted file mode 100644 index c6f345b87ea..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/SimplifyIfIssetToNullCoalescingRectorTest.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/SimplifyIfIssetToNullCoalescingRectorTest.php deleted file mode 100644 index a18e0af89ef..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/SimplifyIfIssetToNullCoalescingRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/config/configured_rule.php deleted file mode 100644 index c894214aef2..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SimplifyIfIssetToNullCoalescingRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/fixture.php.inc index c21f5dc3318..049e4b734f2 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/fixture.php.inc @@ -8,12 +8,6 @@ function simplifyIfNotNullReturnRector() } return null; - - if (null !== $newNode) { - return $newNode; - } - - return null; } ?> @@ -24,7 +18,6 @@ function simplifyIfNotNullReturnRector() { $newNode = 'something'; return $newNode; - return $newNode; } ?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/fixture2.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/fixture2.php.inc deleted file mode 100644 index 3f105ed8d71..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,53 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip.php.inc index 0884427abb0..6b5b874e432 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip.php.inc +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\CodeQuality\Rector\If_\SimplifyIfNotNullReturnRector\Fixture; -class Skip +final class Skip { public function run() { @@ -13,14 +13,4 @@ class Skip return 5; } - - public function runAgain() - { - $newNode = 'something'; - if ($newNode !== null) { - return $newNode; - } - - return 'another'; - } } diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_nullable_result.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_nullable_result.php.inc new file mode 100644 index 00000000000..11a21a03856 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_nullable_result.php.inc @@ -0,0 +1,25 @@ +resolveMaybe(); + if ($newNode !== null) { + return null; + } + + return 5; + } + + private function resolveMaybe(): ?int + { + if (mt_rand(0, 1)) { + return 100; + } + + return null; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_else.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_else.php.inc new file mode 100644 index 00000000000..66afcdede34 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_else.php.inc @@ -0,0 +1,25 @@ +foo !== null) { + return $this->foo; + } else { + return $this->bar; + } + + // while unreachable, above code is valid and should not be simplified by this rule + // the dead code set can remove unreachable stmt part + // if combined with early return set, it will be cleared up :) + // @see https://getrector.com/demo/ad3358be-5aaf-4aa3-85cf-c239c3eb3fd8 + // @see https://getrector.com/demo/6cbb4273-dff2-45a0-82f0-f6fe932d0188 + return null; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_elseif.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_elseif.php.inc new file mode 100644 index 00000000000..181d336c01f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_elseif.php.inc @@ -0,0 +1,20 @@ +foo !== null) { + return $this->foo; + } elseif ((bool) rand(0, 1)) { + return $this->bar; + } + + return null; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_empty_if.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_empty_if.php.inc new file mode 100644 index 00000000000..f827c854322 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/Fixture/skip_with_empty_if.php.inc @@ -0,0 +1,15 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/config/configured_rule.php index b2e1012844c..3559aa77044 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\If_\SimplifyIfNotNullReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyIfNotNullReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyIfNotNullReturnRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/fixture.php.inc deleted file mode 100644 index 702af226b5a..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -foo->bar(); - if (! $value instanceof \stdClass) { - return null; - } - - return $value; - } -} - -?> ------ -foo->bar(); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/mirror_comment2.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/mirror_comment2.php.inc deleted file mode 100644 index 9e220d3cc7e..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/mirror_comment2.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -bar(); - if (! $value instanceof \stdClass) { - return null; - } - - return $value; - } -} - -?> ------ -bar(); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/skip_doc_block_types.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/skip_doc_block_types.php.inc new file mode 100644 index 00000000000..c5712f6ac06 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/skip_doc_block_types.php.inc @@ -0,0 +1,17 @@ +foo->bar(); + if (! $value instanceof \stdClass) { + return null; + } + + return $value; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/truthy.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/truthy.php.inc deleted file mode 100644 index 8b3032b5530..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/truthy.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -foo->bar(); - if ($value instanceof \stdClass) { - return $value; - } - - return null; - } -} - -?> ------ -foo->bar(); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/union_flip_type.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/union_flip_type.php.inc deleted file mode 100644 index 99e5e1827ee..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/Fixture/union_flip_type.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -foo->bar(); - if (! $value instanceof \stdClass) { - return null; - } - - return $value; - } -} - -?> ------ -foo->bar(); - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/SimplifyIfNullableReturnRectorTest.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/SimplifyIfNullableReturnRectorTest.php index 9828e739dc2..d501eb43f93 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/SimplifyIfNullableReturnRectorTest.php +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/SimplifyIfNullableReturnRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\If_\SimplifyIfNullableReturnRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyIfNullableReturnRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/config/configured_rule.php index b626e30a757..183ef84ee83 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\If_\SimplifyIfNullableReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyIfNullableReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyIfNullableReturnRector::class]); diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/equal_not_equal.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/equal_not_equal.php.inc new file mode 100644 index 00000000000..839177d7c17 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/equal_not_equal.php.inc @@ -0,0 +1,73 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture2.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture2.php.inc deleted file mode 100644 index 6285f26045e..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -getContent(), "\n")) { - return false; - } - - return true; - - if (!strpos($docToken->getContent(), "\n")) { - return true; - } - - return false; - - if (!strpos($docToken->getContent(), "\n")) { - return true; - } - - return 5; -} - -?> ------ -getContent(), "\n"); - return !strpos($docToken->getContent(), "\n"); - if (!strpos($docToken->getContent(), "\n")) { - return true; - } - - return 5; -} - -?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture6.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture6.php.inc deleted file mode 100644 index a070a4e38dc..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture6.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture7.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture7.php.inc index e02f93742d3..2fc21af9030 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture7.php.inc +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture7.php.inc @@ -48,13 +48,12 @@ trait ValidatesAttributes public function function2($value, $secondValue) { - return !(($value === true) || ($secondValue === true)); + return !($value === true || $secondValue === true); } public function function3($attribute, $value, $parameters) { - return !($this->failsBasicDimensionChecks($parameters, $width, $height) || - $this->failsRatioCheck($parameters, $width, $height)); + return !($this->failsBasicDimensionChecks($parameters, $width, $height) || $this->failsRatioCheck($parameters, $width, $height)); } } diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/not_identical_return_false_then_true.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/not_identical_return_false_then_true.php.inc new file mode 100644 index 00000000000..5f8b0aa407a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/not_identical_return_false_then_true.php.inc @@ -0,0 +1,37 @@ +getContent(), "\n") !== false) { + return false; + } + + return true; + } +} + +?> +----- +getContent(), "\n") === false; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return.php.inc new file mode 100644 index 00000000000..d3627c62a4d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return.php.inc @@ -0,0 +1,20 @@ +getChildren())) { + return false; + } else { + $someValue = $this->getSomeValue(); + if (null == $someValue || null == $this->someOtherFunction()) { + return false; + } + } + + return true; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return2.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return2.php.inc new file mode 100644 index 00000000000..651ec0d13de --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return2.php.inc @@ -0,0 +1,20 @@ +getChildren())) { + return false; + } else { + $someValue = $this->getSomeValue(); + if (null == $someValue || null == $this->someOtherFunction()) { + echo 'some statement'; + } + } + + return true; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return3.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return3.php.inc new file mode 100644 index 00000000000..5fbb4572f74 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_indirect_return3.php.inc @@ -0,0 +1,20 @@ +getChildren())) { + return false; + } else { + $someValue = $this->getSomeValue(); + if (null == $someValue || null == $this->someOtherFunction()) { + return; + } + } + + return true; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_not_bool.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_not_bool.php.inc new file mode 100644 index 00000000000..ac4a9877811 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_not_bool.php.inc @@ -0,0 +1,22 @@ +getChildren())) { + return false; + } else { + return '1'; + } + + // while unreachable, above code is valid and should not be simplified by this rule + // the dead code set can remove unreachable stmt part + // if combined with early return set, it will be cleared up :) + // @see https://getrector.com/demo/a4345e26-d168-4fc3-802d-eb813ad09408 + // @see https://getrector.com/demo/74c759c0-3cc7-4ed0-a55d-34fb7d1e3c24 + return true; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_return_no_expr.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_return_no_expr.php.inc new file mode 100644 index 00000000000..d9ed6cdc11c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_else_return_no_expr.php.inc @@ -0,0 +1,17 @@ +getChildren())) { + return false; + } else { + return; + } + + return true; + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_indirect_return.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_indirect_return.php.inc new file mode 100644 index 00000000000..5eeeee89fd2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/skip_indirect_return.php.inc @@ -0,0 +1,21 @@ +getChildren())) { + return false; + } else { + return false; + } + } +} diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/with_parentheses.php.inc b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/with_parentheses.php.inc new file mode 100644 index 00000000000..d9f7a5ae0ca --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/with_parentheses.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/SimplifyIfReturnBoolRectorTest.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/SimplifyIfReturnBoolRectorTest.php index 39e1ab6b2bf..a58e70e4455 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/SimplifyIfReturnBoolRectorTest.php +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/SimplifyIfReturnBoolRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyIfReturnBoolRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/config/configured_rule.php index f06bfff9680..5995215f3c9 100644 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyIfReturnBoolRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyIfReturnBoolRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/AbsolutizeRequireAndIncludePathRectorTest.php b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/AbsolutizeRequireAndIncludePathRectorTest.php index 922a153c5bc..1bfdc82d0fa 100644 --- a/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/AbsolutizeRequireAndIncludePathRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/AbsolutizeRequireAndIncludePathRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Include_\AbsolutizeRequireAndIncludePathRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AbsolutizeRequireAndIncludePathRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/extra_dot.php.inc b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/extra_dot.php.inc new file mode 100644 index 00000000000..70a78fd5ed2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/extra_dot.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/full_list.php.inc b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/full_list.php.inc new file mode 100644 index 00000000000..240dba376f8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/full_list.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/inter_variable.php.inc b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/inter_variable.php.inc new file mode 100644 index 00000000000..da255643ec2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/inter_variable.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/phar.php.inc b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/phar.php.inc new file mode 100644 index 00000000000..36f2fbac8c6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/phar.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/skip_absolute_paths.php.inc b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/skip_absolute_paths.php.inc new file mode 100644 index 00000000000..cc7e267f998 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Fixture/skip_absolute_paths.php.inc @@ -0,0 +1,11 @@ +module = new Module(); + }); + + describe('->getConfig()', function (): void { + + it('returns config', function (): void { + $expected = include 'config/module.config.php'; + expect($this->module->getConfig())->toBe($expected); + }); + + }); + +}); diff --git a/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Source/Module.php b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Source/Module.php new file mode 100644 index 00000000000..d34832c6db9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector/Source/Module.php @@ -0,0 +1,16 @@ +services(); - $services->set(AbsolutizeRequireAndIncludePathRector::class); -}; +return RectorConfig::configure() + ->withRules([AbsolutizeRequireAndIncludePathRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_built_in_class.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_built_in_class.php.inc deleted file mode 100644 index 1a4c095784a..00000000000 --- a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_built_in_class.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -property); - } -} - -?> ------ -property !== null; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_many_properties.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_many_properties.php.inc new file mode 100644 index 00000000000..41c88d51b8c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_many_properties.php.inc @@ -0,0 +1,43 @@ +x, $this->y)) { + return true; + } + + return false; + } +} + +?> +----- +x === null && $this->y === null) { + return true; + } + + return false; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_nullable_typed_property.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_nullable_typed_property.php.inc new file mode 100644 index 00000000000..70e682abc07 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/isset_on_nullable_typed_property.php.inc @@ -0,0 +1,31 @@ +age); + } +} + +?> +----- +age !== null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/negated_isset_property_not_exists.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/negated_isset_property_not_exists.php.inc new file mode 100644 index 00000000000..c76b48fb668 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/negated_isset_property_not_exists.php.inc @@ -0,0 +1,27 @@ +x); + } +} + +?> +----- +x === null; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_direct_std_class.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_direct_std_class.php.inc new file mode 100644 index 00000000000..691602ff03a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_direct_std_class.php.inc @@ -0,0 +1,12 @@ +property); + } +} diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_has_magic_isset.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_has_magic_isset.php.inc new file mode 100644 index 00000000000..bc0500efe99 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_has_magic_isset.php.inc @@ -0,0 +1,12 @@ +foo); // true diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/FixturePhp74/skip_isset_change_if_typed_property.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_isset_change_if_typed_property.php.inc similarity index 88% rename from rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/FixturePhp74/skip_isset_change_if_typed_property.php.inc rename to rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_isset_change_if_typed_property.php.inc index 05fde2a6715..1aa3bffe77d 100644 --- a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/FixturePhp74/skip_isset_change_if_typed_property.php.inc +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_isset_change_if_typed_property.php.inc @@ -1,6 +1,6 @@ {$p}); diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_property_on_array_access.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_property_on_array_access.php.inc new file mode 100644 index 00000000000..01d4afda6e0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_property_on_array_access.php.inc @@ -0,0 +1,21 @@ + $values + */ + private function run(Collection $values): void + { + if (! isset($values['_identity']->value)) { + return; + } + + echo $values['_identity']->value; + } +} diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_std_class_from_json.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_std_class_from_json.php.inc new file mode 100644 index 00000000000..d19800ba4c0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_std_class_from_json.php.inc @@ -0,0 +1,11 @@ +key->value); + } +} diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_typed_property.php.inc b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_typed_property.php.inc new file mode 100644 index 00000000000..2208197f1e5 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Fixture/skip_typed_property.php.inc @@ -0,0 +1,13 @@ +age); + } +} diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/IssetOnPropertyObjectToPropertyExistsRectorTest.php b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/IssetOnPropertyObjectToPropertyExistsRectorTest.php index ec58660124b..a85c62e0ca4 100644 --- a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/IssetOnPropertyObjectToPropertyExistsRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/IssetOnPropertyObjectToPropertyExistsRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class IssetOnPropertyObjectToPropertyExistsRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Php74Test.php b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Php74Test.php deleted file mode 100644 index 51e9469290c..00000000000 --- a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Php74Test.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configued_typed_property.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Source/Collection.php b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Source/Collection.php new file mode 100644 index 00000000000..107f28fea84 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Source/Collection.php @@ -0,0 +1,16 @@ + + */ +class Collection implements ArrayAccess {} \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Source/SomeOtherClass.php b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Source/SomeOtherClass.php new file mode 100644 index 00000000000..debd5aa649f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/Source/SomeOtherClass.php @@ -0,0 +1,10 @@ +parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::TYPED_PROPERTIES); - - $services = $containerConfigurator->services(); - $services->set(IssetOnPropertyObjectToPropertyExistsRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/config/configured_rule.php index 094674eb981..dd1eb0a91dd 100644 --- a/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(IssetOnPropertyObjectToPropertyExistsRector::class); -}; +return RectorConfig::configure() + ->withRules([IssetOnPropertyObjectToPropertyExistsRector::class]); diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector/AndAssignsToSeparateLinesRectorTest.php b/rules-tests/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector/AndAssignsToSeparateLinesRectorTest.php index 8ced9ef4aec..924ded1e8aa 100644 --- a/rules-tests/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector/AndAssignsToSeparateLinesRectorTest.php +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector/AndAssignsToSeparateLinesRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\LogicalAnd\AndAssignsToSeparateLinesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AndAssignsToSeparateLinesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector/config/configured_rule.php index f8966e11261..83ddc949e81 100644 --- a/rules-tests/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\LogicalAnd\AndAssignsToSeparateLinesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(AndAssignsToSeparateLinesRector::class); -}; +return RectorConfig::configure() + ->withRules([AndAssignsToSeparateLinesRector::class]); diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/and.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/and.php.inc index a5f1da1d1b9..adbbd0edbad 100644 --- a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/and.php.inc +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/and.php.inc @@ -4,6 +4,10 @@ if ($f = false and true) { return $f; } +if ($f = true and false) { + return $f; +} + if (false and true) { return $f; } @@ -16,6 +20,10 @@ if (($f = false) && true) { return $f; } +if (($f = true) && false) { + return $f; +} + if (false && true) { return $f; } diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/mixed.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/mixed.php.inc new file mode 100644 index 00000000000..83cb0cc31f9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/mixed.php.inc @@ -0,0 +1,51 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/multi_and.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/multi_and.php.inc new file mode 100644 index 00000000000..e6d91ab02ea --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/multi_and.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/multi_or.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/multi_or.php.inc new file mode 100644 index 00000000000..7ef5dc4470f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/multi_or.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/or.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/or.php.inc index b3c4475fc94..78e78409f6d 100644 --- a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/or.php.inc +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/or.php.inc @@ -4,6 +4,10 @@ if ($f = false or true) { return $f; } +if ($f = true or false) { + return $f; +} + if (false or true) { return $f; } @@ -16,6 +20,10 @@ if (($f = false) || true) { return $f; } +if (($f = true) || false) { + return $f; +} + if (false || true) { return $f; } diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/skip_fopen_or_die_in_assign.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/skip_fopen_or_die_in_assign.php.inc new file mode 100644 index 00000000000..8d5593def5e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/skip_fopen_or_die_in_assign.php.inc @@ -0,0 +1,13 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/config/configured_rule.php index 7f3657793f6..aab11f0d300 100644 --- a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\LogicalAnd\LogicalToBooleanRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(LogicalToBooleanRector::class); -}; +return RectorConfig::configure() + ->withRules([LogicalToBooleanRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/FixClassCaseSensitivityNameRectorTest.php b/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/FixClassCaseSensitivityNameRectorTest.php deleted file mode 100644 index 92fda8a6818..00000000000 --- a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/FixClassCaseSensitivityNameRectorTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/fixture.php.inc deleted file mode 100644 index d90c74091a2..00000000000 --- a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/namespace_and_constants.php.inc b/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/namespace_and_constants.php.inc deleted file mode 100644 index f524e50b876..00000000000 --- a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/namespace_and_constants.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/param_type.php.inc b/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/param_type.php.inc deleted file mode 100644 index 9f428e9125f..00000000000 --- a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/param_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/skip_function_name_exists.php.inc b/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/skip_function_name_exists.php.inc deleted file mode 100644 index 44aae6635cc..00000000000 --- a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/skip_function_name_exists.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/skip_self_parent.php.inc b/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/skip_self_parent.php.inc deleted file mode 100644 index d1efc1792f8..00000000000 --- a/rules-tests/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector/Fixture/skip_self_parent.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(FixClassCaseSensitivityNameRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/New_/NewStaticToNewSelfRector/NewStaticToNewSelfRectorTest.php b/rules-tests/CodeQuality/Rector/New_/NewStaticToNewSelfRector/NewStaticToNewSelfRectorTest.php index f9a9821edf3..4254f5b4aa3 100644 --- a/rules-tests/CodeQuality/Rector/New_/NewStaticToNewSelfRector/NewStaticToNewSelfRectorTest.php +++ b/rules-tests/CodeQuality/Rector/New_/NewStaticToNewSelfRector/NewStaticToNewSelfRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\New_\NewStaticToNewSelfRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class NewStaticToNewSelfRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/New_/NewStaticToNewSelfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/New_/NewStaticToNewSelfRector/config/configured_rule.php index 473aa7e556e..093d719c3c1 100644 --- a/rules-tests/CodeQuality/Rector/New_/NewStaticToNewSelfRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/New_/NewStaticToNewSelfRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\New_\NewStaticToNewSelfRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(NewStaticToNewSelfRector::class); -}; +return RectorConfig::configure() + ->withRules([NewStaticToNewSelfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/CommonNotEqualRectorTest.php b/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/CommonNotEqualRectorTest.php index e358d14025b..60532179e3a 100644 --- a/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/CommonNotEqualRectorTest.php +++ b/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/CommonNotEqualRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\NotEqual\CommonNotEqualRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CommonNotEqualRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/Fixture/multi_line_compare.php.inc b/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/Fixture/multi_line_compare.php.inc new file mode 100644 index 00000000000..6b086f19c9a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/Fixture/multi_line_compare.php.inc @@ -0,0 +1,29 @@ + $two; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/Fixture/skip_already.php.inc b/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/Fixture/skip_already.php.inc new file mode 100644 index 00000000000..407662c6ffa --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NotEqual/CommonNotEqualRector/Fixture/skip_already.php.inc @@ -0,0 +1,11 @@ +services(); - $services->set(CommonNotEqualRector::class); -}; +return RectorConfig::configure() + ->withRules([CommonNotEqualRector::class]); diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.php b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.php new file mode 100644 index 00000000000..ff96658dabd --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc new file mode 100644 index 00000000000..01a7155b5e6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc @@ -0,0 +1,41 @@ +getString(); + +?> +----- +getString(); + +?> diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_on_method_call.php.inc b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_on_method_call.php.inc new file mode 100644 index 00000000000..ed0e4088803 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_on_method_call.php.inc @@ -0,0 +1,47 @@ +get2()?->getString2(); + +?> +----- +get2()->getString2(); + +?> diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc new file mode 100644 index 00000000000..dc5aaef5838 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc @@ -0,0 +1,47 @@ +getString(); + +?> +----- +getString(); + +?> diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip_nullable.php.inc b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip_nullable.php.inc new file mode 100644 index 00000000000..dd4609689b6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip_nullable.php.inc @@ -0,0 +1,18 @@ +getString(); diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php new file mode 100644 index 00000000000..25508edfec7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersionFeature::NULLSAFE_OPERATOR); + + $rectorConfig->rule(CleanupUnneededNullsafeOperatorRector::class); +}; diff --git a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/fixture.php.inc deleted file mode 100644 index 71a16672f7b..00000000000 --- a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,196 +0,0 @@ ->= 1; - return $l; -}; - -function () { - $m .= 1; - return $m; -}; - -function sameVariableInDifferentScope() -{ - $n = array_map(function () { - return $n + 1; - }, []); - - return $n; -} - -function moreVariableOneWithoutAssigment() { - $o++; - $o = 10; - - return $o; -} - -function assigmentAsFunctionParametr() { - doSomething($p = 0); - return $p; -} - -function assigmentAfterAssignment() { - doSomething($qq = $q = 0); - return $q; -} - -function () { - $a = 1; - $b = 1; - $c = [ - $b-- => $a++, - --$b => ++$a, - ]; - return $c; -}; - -?> ------ -> 1; -}; - -function () { - return $m . 1; -}; - -function sameVariableInDifferentScope() -{ - return array_map(function () { - return $n + 1; - }, []); -} - -function moreVariableOneWithoutAssigment() { - $o++; - - return 10; -} - -function assigmentAsFunctionParametr() { - doSomething($p = 0); - return $p; -} - -function assigmentAfterAssignment() { - doSomething($qq = $q = 0); - return $q; -} - -function () { - $a = 1; - $b = 1; - return [ - $b-- => $a++, - --$b => ++$a, - ]; -}; - -?> diff --git a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/fixture3.php.inc b/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/fixture3.php.inc deleted file mode 100644 index e8ab388fa73..00000000000 --- a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/in_a_function.php.inc b/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/in_a_function.php.inc deleted file mode 100644 index f3d9751a52d..00000000000 --- a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/in_a_function.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/keep_var_doc.php.inc b/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/keep_var_doc.php.inc deleted file mode 100644 index deb0414a3b8..00000000000 --- a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/keep_var_doc.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -getValue(); - - /** @var string $name */ - return $name; - } - - private function getValue() - { - return 'name'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/keep_visual.php.inc b/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/keep_visual.php.inc deleted file mode 100644 index fcef7b245df..00000000000 --- a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/keep_visual.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -$parameter = 'this'; - } - -} - -$noErrorToo = null; -function ($noError = 'noError') use ($noErrorToo) { - -}; - -$used = true; - -function foo($foo) { - return preg_replace_callback('~~', function ($matches) { - return $matches[0]; - }, $foo); -} - -return $used; - -function ($values) { - $foo = ''; - - foreach ($values as $value) { - echo $foo . $value; - } -}; - -function () { - for ($i = 0; $i < 10; $i++) { - } -}; - -function ($values) { - foreach ($values as $value) { - $foo = 'foo' . $value; - } - echo $foo; -}; - -function ($values) { - list($a, $b) = $values; - return $a + $b; -}; - -function ($values) { - [$c, $d] = $values; - return $c * $d; -}; - -function ($values) { - $current = 'current'; - $next = 'next'; - - while ($next) { - if ($current) { - - } - - $current = false; - - if (true) { - foreach ($values as $value) { - $next = $value; - } - } - - do { - $previous = 'previous'; - } while ($previous); - } -}; - -function (&$parameter) { - $parameter = 'value-by-reference'; -}; - -function () use (&$inheritedVariable) { - $inheritedVariable = 'value-by-reference'; -}; - -function ($interval) { - $j = 0; - for ($i = $j; $i < 10; $i += $interval) { - } -}; - -function () { - static $static = false; - if ($static) { - return; - } - - $static = true; -}; - -function () { - $a = 'a'; - $b = 'b'; - - $this->compact; - - return compact('a', "b"); -}; - -function () { - $a = ''; - echo "$a"; -}; - -function () { - $a = ''; - echo "${a}"; -}; - -function () { - $a = ''; - echo "$a()"; -}; - -function () { - $a = ''; - echo <<a, $this->b) = [$a, $b]; - } - -} - -function () { - $i = 0; - while ($i++ <= 10) { - } -}; - -function () { - $i = 0; - do { - } while (++$i <= 10); -}; - -function () { - $i = 10; - while ($i-- > 0) { - } -}; - -function () { - $i = 10; - do { - } while (--$i > 0); -}; - -function ($data) { - $i = 0; - $c = ''; - foreach ($data as $c) { - $c = $i++; - } - echo $c; -}; - -function ($values) { - $expectedKey = 0; - - foreach ($values as $key => $value) { - if ($key !== $expectedKey++) { - return $value; - } - } - - return null; -}; diff --git a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/skip_global_variable.php.inc b/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/skip_global_variable.php.inc deleted file mode 100644 index 0775e388ec4..00000000000 --- a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/Fixture/skip_global_variable.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/config/configured_rule.php deleted file mode 100644 index fd5de6efd74..00000000000 --- a/rules-tests/CodeQuality/Rector/Return_/SimplifyUselessVariableRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SimplifyUselessVariableRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/Fixture/default_only_with_a_break.php.inc b/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/Fixture/default_only_with_a_break.php.inc new file mode 100644 index 00000000000..ad2eabfd409 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/Fixture/default_only_with_a_break.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/SingularSwitchToIfRectorTest.php b/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/SingularSwitchToIfRectorTest.php index e4fee3e36a1..7fff5f7ede8 100644 --- a/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/SingularSwitchToIfRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/SingularSwitchToIfRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Switch_\SingularSwitchToIfRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SingularSwitchToIfRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/config/configured_rule.php index eaa38ddd491..703cafff902 100644 --- a/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Switch_/SingularSwitchToIfRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Switch_\SingularSwitchToIfRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SingularSwitchToIfRector::class); -}; +return RectorConfig::configure() + ->withRules([SingularSwitchToIfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/skip_break.php.inc b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/skip_break.php.inc new file mode 100644 index 00000000000..84011f4ac76 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/skip_break.php.inc @@ -0,0 +1,25 @@ + diff --git a/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/skip_empty_cases.php.inc b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/skip_empty_cases.php.inc new file mode 100644 index 00000000000..22cb8cc65be --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/skip_empty_cases.php.inc @@ -0,0 +1,12 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/with_default.php.inc b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/with_default.php.inc new file mode 100644 index 00000000000..0c9370d5266 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/Fixture/with_default.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/SwitchTrueToIfRectorTest.php b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/SwitchTrueToIfRectorTest.php new file mode 100644 index 00000000000..84288f6b88e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/SwitchTrueToIfRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/config/configured_rule.php new file mode 100644 index 00000000000..71414e8644e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Switch_/SwitchTrueToIfRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([SwitchTrueToIfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector/ArrayKeyExistsTernaryThenValueToCoalescingRectorTest.php b/rules-tests/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector/ArrayKeyExistsTernaryThenValueToCoalescingRectorTest.php index 92ac4d88730..ed88bcb45af 100644 --- a/rules-tests/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector/ArrayKeyExistsTernaryThenValueToCoalescingRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector/ArrayKeyExistsTernaryThenValueToCoalescingRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Ternary\ArrayKeyExistsTernaryThenValueToCoalescingRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ArrayKeyExistsTernaryThenValueToCoalescingRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector/config/configured_rule.php index dc5e9d5ad45..f6695847daf 100644 --- a/rules-tests/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Ternary\ArrayKeyExistsTernaryThenValueToCoalescingRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ArrayKeyExistsTernaryThenValueToCoalescingRector::class); -}; +return RectorConfig::configure() + ->withRules([ArrayKeyExistsTernaryThenValueToCoalescingRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/also_the_other_side.php.inc b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/also_the_other_side.php.inc new file mode 100644 index 00000000000..0b4bd7c9031 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/also_the_other_side.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/include_greater_or_equal.php.inc b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/include_greater_or_equal.php.inc new file mode 100644 index 00000000000..52cd9799966 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/include_greater_or_equal.php.inc @@ -0,0 +1,27 @@ += 100 ? $value : 100; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/skip_else_another_value.php.inc b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/skip_else_another_value.php.inc new file mode 100644 index 00000000000..afcee30bb7d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/skip_else_another_value.php.inc @@ -0,0 +1,11 @@ + 100 ? $value : 55; + } +} diff --git a/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/skip_non_number.php.inc b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/skip_non_number.php.inc new file mode 100644 index 00000000000..d0453079c09 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/Fixture/skip_non_number.php.inc @@ -0,0 +1,11 @@ + 100 ? $value : 100; + } +} + +?> +----- + diff --git a/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/NumberCompareToMaxFuncCallRectorTest.php b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/NumberCompareToMaxFuncCallRectorTest.php new file mode 100644 index 00000000000..40915ff9736 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/NumberCompareToMaxFuncCallRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/config/configured_rule.php new file mode 100644 index 00000000000..4cfac194ea9 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([NumberCompareToMaxFuncCallRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/Fixture/fixture.php.inc deleted file mode 100644 index 425b1a206f3..00000000000 --- a/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/SimplifyDuplicatedTernaryRectorTest.php b/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/SimplifyDuplicatedTernaryRectorTest.php deleted file mode 100644 index dad9428a8f1..00000000000 --- a/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/SimplifyDuplicatedTernaryRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/config/configured_rule.php deleted file mode 100644 index 2d91170faa8..00000000000 --- a/rules-tests/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SimplifyDuplicatedTernaryRector::class); -}; diff --git a/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/Fixture/fixture.php.inc index 37062483545..dfa20c0c255 100644 --- a/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/Fixture/fixture.php.inc @@ -11,7 +11,6 @@ function tautologyTernary() $value = ($typeHint !== $fullyQualifiedTypeHint) ? $fullyQualifiedTypeHint : $typeHint; $value = ($typeHint === $fullyQualifiedTypeHint) ? $fullyQualifiedTypeHint : $typeHint; - $value = ($typeHint === $fullyQualifiedTypeHint) ? $typeHint : $fullyQualifiedTypeHint; } @@ -29,9 +28,8 @@ function tautologyTernary() $value = $fullyQualifiedTypeHint; $value = $fullyQualifiedTypeHint; - $value = $fullyQualifiedTypeHint; - $value = $typeHint; + $value = $fullyQualifiedTypeHint; } ?> diff --git a/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/Fixture/identical_compare_left.php.inc b/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/Fixture/identical_compare_left.php.inc new file mode 100644 index 00000000000..7eb7c80a9e7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/Fixture/identical_compare_left.php.inc @@ -0,0 +1,51 @@ +bar($param) === '' ? '' : $this->bar($param); + echo 'asd' . $foo . 'def'; + } + + public function run2(string $param) + { + $foo = '' === $this->bar($param) ? '' : $this->bar($param); + echo 'asd' . $foo . 'def'; + } + + private function bar(string $value) + { + return trim($value); + } +} + +?> +----- +bar($param); + echo 'asd' . $foo . 'def'; + } + + public function run2(string $param) + { + $foo = $this->bar($param); + echo 'asd' . $foo . 'def'; + } + + private function bar(string $value) + { + return trim($value); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/SimplifyTautologyTernaryRectorTest.php b/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/SimplifyTautologyTernaryRectorTest.php index 0eb37246142..dd2b54e9b99 100644 --- a/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/SimplifyTautologyTernaryRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/SimplifyTautologyTernaryRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Ternary\SimplifyTautologyTernaryRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyTautologyTernaryRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/config/configured_rule.php index 0813ed32ce5..369e45bbf7d 100644 --- a/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Ternary\SimplifyTautologyTernaryRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyTautologyTernaryRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyTautologyTernaryRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector/SwitchNegatedTernaryRectorTest.php b/rules-tests/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector/SwitchNegatedTernaryRectorTest.php index 3fdf9665557..64b4f14bcb6 100644 --- a/rules-tests/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector/SwitchNegatedTernaryRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector/SwitchNegatedTernaryRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodeQuality\Rector\Ternary\SwitchNegatedTernaryRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SwitchNegatedTernaryRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector/config/configured_rule.php index 3b957b480c4..29d85602baa 100644 --- a/rules-tests/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Ternary\SwitchNegatedTernaryRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SwitchNegatedTernaryRector::class); -}; +return RectorConfig::configure() + ->withRules([SwitchNegatedTernaryRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/skip_non_array.php.inc b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/skip_non_array.php.inc new file mode 100644 index 00000000000..8333b3252ea --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/skip_non_array.php.inc @@ -0,0 +1,13 @@ +items) ? $this->items[0] : 'default'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/skip_only_empty.php.inc b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/skip_only_empty.php.inc new file mode 100644 index 00000000000..aa8028296a8 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/skip_only_empty.php.inc @@ -0,0 +1,13 @@ +items) ? $this->items[0] : 'default'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/some_class.php.inc b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..18ee2fbd45a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/Fixture/some_class.php.inc @@ -0,0 +1,31 @@ +items) ? $this->items[0] : 'default'; + } +} + +?> +----- +items[0] ?? 'default'; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/TernaryEmptyArrayArrayDimFetchToCoalesceRectorTest.php b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/TernaryEmptyArrayArrayDimFetchToCoalesceRectorTest.php new file mode 100644 index 00000000000..03889931f8b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/TernaryEmptyArrayArrayDimFetchToCoalesceRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/config/configured_rule.php new file mode 100644 index 00000000000..38c9367e302 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([TernaryEmptyArrayArrayDimFetchToCoalesceRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..dc1bb5d1e78 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/Fixture/skip_different_variable.php.inc b/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/Fixture/skip_different_variable.php.inc new file mode 100644 index 00000000000..97860e6ba5a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/Fixture/skip_different_variable.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/config/configured_rule.php new file mode 100644 index 00000000000..0a4573d3a6c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([TernaryImplodeToImplodeRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/fixture_unreachable.php.inc b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/fixture_unreachable.php.inc new file mode 100644 index 00000000000..e97d513cefd --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/fixture_unreachable.php.inc @@ -0,0 +1,59 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/greater_than.php.inc b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/greater_than.php.inc new file mode 100644 index 00000000000..791079306b5 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/greater_than.php.inc @@ -0,0 +1,27 @@ + $second ? true : false; + } +} + +?> +----- + $second; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/simple_example.php.inc b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/simple_example.php.inc new file mode 100644 index 00000000000..e873b29a6cf --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/simple_example.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/skip_fixture2.php.inc b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/skip_fixture2.php.inc deleted file mode 100644 index 115a0e780e4..00000000000 --- a/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/Fixture/skip_fixture2.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/config/configured_rule.php index fd2ed341d2c..ff5d0e7160c 100644 --- a/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/config/configured_rule.php +++ b/rules-tests/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UnnecessaryTernaryExpressionRector::class); -}; +return RectorConfig::configure() + ->withRules([UnnecessaryTernaryExpressionRector::class]); diff --git a/rules-tests/CodingStyle/ClassNameImport/ShortNameResolver/Fixture/also_aliases.php.inc b/rules-tests/CodingStyle/ClassNameImport/ShortNameResolver/Fixture/also_aliases.php.inc new file mode 100644 index 00000000000..af22e421e99 --- /dev/null +++ b/rules-tests/CodingStyle/ClassNameImport/ShortNameResolver/Fixture/also_aliases.php.inc @@ -0,0 +1,15 @@ +shortNameResolver = $this->make(ShortNameResolver::class); + $this->testingParser = $this->make(TestingParser::class); + } + + /** + * @param array|string> $expectedShortNames + */ + #[DataProvider('provideData')] + public function test(string $filePath, array $expectedShortNames): void + { + $file = $this->testingParser->parseFilePathToFile($filePath); + $shortNames = $this->shortNameResolver->resolveFromFile($file); + + $this->assertSame($expectedShortNames, $shortNames); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/various_imports.php.inc', [ + 'VariousImports' => 'Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\Fixture\VariousImports', + 'SomeFile' => 'Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\Source\SomeFile', + ]]; + + yield [__DIR__ . '/Fixture/also_aliases.php.inc', [ + 'AlsoAliases' => 'Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\Fixture\AlsoAliases', + 'AnotherFile' => 'Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\Source\SomeFile', + ]]; + + yield [__DIR__ . '/Fixture/partial_names.php.inc', [ + 'PartialNames' => 'Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\Fixture\PartialNames', + ]]; + + yield [__DIR__ . '/Fixture/union_partial_import.php.inc', [ + 'UnionPartialImport' => 'Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\Fixture\UnionPartialImport', + 'rand' => 'rand', + 'FirstLog' => 'Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\Source\FirstLog', + 'SecondLog' => 'Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\Source\SecondLog', + ]]; + } +} diff --git a/rules-tests/CodingStyle/ClassNameImport/ShortNameResolver/Source/FirstLog.php b/rules-tests/CodingStyle/ClassNameImport/ShortNameResolver/Source/FirstLog.php new file mode 100644 index 00000000000..130e9111548 --- /dev/null +++ b/rules-tests/CodingStyle/ClassNameImport/ShortNameResolver/Source/FirstLog.php @@ -0,0 +1,10 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/array_map.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/array_map.php.inc new file mode 100644 index 00000000000..07810412f6a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/array_map.php.inc @@ -0,0 +1,51 @@ + strlen($x), $items); + } + + public function run(): void + { + $array = [1, 2, 3]; + + array_map( + fn($item) => var_dump( + $item, + ), + $array + ); + } +} + +?> +----- + diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/exists_both_in_method_doc_and_native_method.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/exists_both_in_method_doc_and_native_method.php.inc new file mode 100644 index 00000000000..07b666770f7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/exists_both_in_method_doc_and_native_method.php.inc @@ -0,0 +1,43 @@ + ExistsBothInMethodDocAndNativeMethod::test($value), [1, 2, 3, 4]); + } +} + +?> +----- + diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/multiple_params.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/multiple_params.php.inc new file mode 100644 index 00000000000..fcabc9fe0be --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/multiple_params.php.inc @@ -0,0 +1,27 @@ + strcmp($a, $b)); + } +} + +?> +----- + diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/simple_call.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/simple_call.php.inc new file mode 100644 index 00000000000..63433f45c4a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/simple_call.php.inc @@ -0,0 +1,31 @@ + FooBar::foo($foo); + } +} + +?> +----- + diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_assigned_early.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_assigned_early.php.inc new file mode 100644 index 00000000000..6de70a4eb9f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_assigned_early.php.inc @@ -0,0 +1,15 @@ +name, $this->object); + } +} + +// callback assigned early, use later, keep as is +$formatter = fn(string $label, object $object): string => ucfirst($label); +echo (new SkipAssignedEarly('name', new \stdClass()))->render($formatter); diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_call_dependency_on_arg.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_call_dependency_on_arg.php.inc new file mode 100644 index 00000000000..3e737e0e156 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_call_dependency_on_arg.php.inc @@ -0,0 +1,11 @@ + $foo->foo(); + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_callable_param_assign_with_signature_multi_params.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_callable_param_assign_with_signature_multi_params.php.inc new file mode 100644 index 00000000000..ea5c129f6b0 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_callable_param_assign_with_signature_multi_params.php.inc @@ -0,0 +1,29 @@ +transform = $callable; + return $this; + } + + public function run($value, $optionalData = []) + { + var_dump(($this->transform)($value, $optionalData)); + } +} + +(new SkipCallableParamAssignWithSignatureMultiParams()) + ->withTransform(fn (string $datum): string => trim($datum)) + ->run(' some data '); + + +?> diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_callable_param_union.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_callable_param_union.php.inc new file mode 100644 index 00000000000..95cb3389265 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_callable_param_union.php.inc @@ -0,0 +1,16 @@ +cache->get('bar', fn() => time()); + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_chained_calls.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_chained_calls.php.inc new file mode 100644 index 00000000000..6445ba2db7e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_chained_calls.php.inc @@ -0,0 +1,12 @@ + FooBar::foo()->bar($foo); + fn ($foo) => FooBar::foo()::bar($foo); + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_closure_bind_to.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_closure_bind_to.php.inc new file mode 100644 index 00000000000..f916c7e2496 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_closure_bind_to.php.inc @@ -0,0 +1,25 @@ +bindTo($testInstance, get_class($testInstance)); + } + + $cb(); +} + +testClosureBindTo(fn () => SkipClosureBindTo::close()); diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_with_too_many_statements.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_with_too_many_statements.php.inc new file mode 100644 index 00000000000..e547c2d3f7c --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_with_too_many_statements.php.inc @@ -0,0 +1,21 @@ + FooBar::foo($foo) && true; + +$bar = null; + +function ($foo) use ($bar) +{ + $foo = 1; + return $bar->foo($foo); +}; + +?> diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_mismatched_args_and_params.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_mismatched_args_and_params.php.inc new file mode 100644 index 00000000000..30f127ded6d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_mismatched_args_and_params.php.inc @@ -0,0 +1,21 @@ + FooBar::foo($bar, $foo); + +$bar = null; + +fn ($foo, $bar) => $bar->foo($bar, $foo); + +function ($foo, $barFoo) use ($bar) +{ + return $bar->foo($barFoo, $foo); +}; + +?> diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_multi_param.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_multi_param.php.inc new file mode 100644 index 00000000000..f35270e1bf4 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_multi_param.php.inc @@ -0,0 +1,26 @@ + makeInstance($className), + $items + ); + + // This FAILS - first-class callable receives BOTH value and key + // array_map passes: $className (string) and $key (int) + // But makeInstance expects: $className (string) and $options (array) + $result2 = array_map(makeInstance(...), $items, array_keys($items)); + // TypeError: makeInstance(): Argument #2 ($options) must be of type array, int give + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_no_args_passed.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_no_args_passed.php.inc new file mode 100644 index 00000000000..3a68a124673 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_no_args_passed.php.inc @@ -0,0 +1,17 @@ + fn ($foo) => FooBar::optionalArgs() + ]; + + return $data; + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_null_variable_call.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_null_variable_call.php.inc new file mode 100644 index 00000000000..708b0e9530e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_null_variable_call.php.inc @@ -0,0 +1,18 @@ + $bar->foo($foo); + + function ($foo) use ($bar) + { + return $bar->foo($foo); + }; + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_param_used_as_invokable.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_param_used_as_invokable.php.inc new file mode 100644 index 00000000000..e4308825bed --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_param_used_as_invokable.php.inc @@ -0,0 +1,14 @@ +name, $this->object); + } +} + +$obj = new SkipParamUsedAsInvokable('name', new \stdClass()); +$obj->render(fn(string $label, object $object): string => ucfirst($label)); diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_param_used_as_invokable_from_new.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_param_used_as_invokable_from_new.php.inc new file mode 100644 index 00000000000..c39c00e891e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_param_used_as_invokable_from_new.php.inc @@ -0,0 +1,15 @@ +name, $this->object); + } +} + +$obj = new SkipParamUsedAsInvokableFromNew('name', new \stdClass(), fn(string $label, object $object): string => ucfirst($label)); diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_target_method_from_method_doc.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_target_method_from_method_doc.php.inc new file mode 100644 index 00000000000..930ad816267 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_target_method_from_method_doc.php.inc @@ -0,0 +1,14 @@ + SkipTargetNativeMethodNotExists::test($value), [1, 2, 3, 4]); + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_target_native_method_not_exists.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_target_native_method_not_exists.php.inc new file mode 100644 index 00000000000..dde0fe80575 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_target_native_method_not_exists.php.inc @@ -0,0 +1,11 @@ + SkipTargetNativeMethodNotExists::test($value), [1, 2, 3, 4]); + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_using_this_outside_object.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_using_this_outside_object.php.inc new file mode 100644 index 00000000000..cb25e07f642 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_using_this_outside_object.php.inc @@ -0,0 +1,16 @@ + $this->values()); + self::macro('foo', fn () => $this->values()->foo()); + } + + public static function macro(string $name, callable $callback): void + { + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_variadic_in_array_all.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_variadic_in_array_all.php.inc new file mode 100644 index 00000000000..83c6f716ba5 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_variadic_in_array_all.php.inc @@ -0,0 +1,19 @@ + $data + * + * @phpstan-return ($data is list ? true : false) + */ + private function isListOfArrays(array $data): bool + { + if (!\array_is_list($data)) { + return false; + } + return \array_all($data, static fn(mixed $item): bool => \is_array($item)); + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_with_reset.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_with_reset.php.inc new file mode 100644 index 00000000000..0a027137fe2 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/skip_with_reset.php.inc @@ -0,0 +1,15 @@ + reset($item), $data); + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/supports_variadic_and_unpack.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/supports_variadic_and_unpack.php.inc new file mode 100644 index 00000000000..e3e5766cfc4 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/supports_variadic_and_unpack.php.inc @@ -0,0 +1,19 @@ + FooBar::foo(...$foo); + +?> +----- + diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/using_this_in_instance_method.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/using_this_in_instance_method.php.inc new file mode 100644 index 00000000000..52dc2d966cb --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Fixture/using_this_in_instance_method.php.inc @@ -0,0 +1,37 @@ + $this->values(); + } + + public function values(): array + { + return []; + } +} + +?> +----- +values(...); + } + + public function values(): array + { + return []; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Source/FooBar.php b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Source/FooBar.php new file mode 100644 index 00000000000..7b71b74ed75 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/Source/FooBar.php @@ -0,0 +1,14 @@ + $callback + * @param float|null $beta + * @param array &$metadata + * + * @return T + * + * @throws InvalidArgumentException When $key is not valid or when $beta is negative + */ + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null): mixed; +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/config/configured_rule.php new file mode 100644 index 00000000000..e709859ccb2 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector/config/configured_rule.php @@ -0,0 +1,11 @@ +withRules([ArrowFunctionDelegatingCallToFirstClassCallableRector::class]) + ->withPhpVersion(PhpVersionFeature::FIRST_CLASS_CALLABLE_SYNTAX); diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..6097c31174f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/fixture.php.inc @@ -0,0 +1,15 @@ + 'test'; + +?> +----- + 'test'; + +?> diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_already_static.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_already_static.php.inc new file mode 100644 index 00000000000..4f2f327b9f7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_already_static.php.inc @@ -0,0 +1,5 @@ + 'test'; diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_has_static_call_call_non_static.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_has_static_call_call_non_static.php.inc new file mode 100644 index 00000000000..1d137c5a344 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_has_static_call_call_non_static.php.inc @@ -0,0 +1,19 @@ + self::bar(); + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_with_this.php.inc b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_with_this.php.inc new file mode 100644 index 00000000000..15acc6b456f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/Fixture/skip_with_this.php.inc @@ -0,0 +1,13 @@ + $this->data; + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/StaticArrowFunctionRectorTest.php b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/StaticArrowFunctionRectorTest.php new file mode 100644 index 00000000000..e6a755bb8cf --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/StaticArrowFunctionRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/config/configured_rule.php new file mode 100644 index 00000000000..d37242748c0 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StaticArrowFunctionRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/array_concat.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/array_concat.php.inc deleted file mode 100644 index a55c5e78c73..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/array_concat.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - 14, 'billing_address' => ['name' => 'A', 'address' => 'B', 'city' => 'C', 'zip' => '180 00'], 'administrators' => 5, 'settings' => [3, 4]]; - $json = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/assign_with_concat.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/assign_with_concat.php.inc deleted file mode 100644 index ad84c6746b6..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/assign_with_concat.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - 'admin', 'numberz' => ['id' => 5]]; - $someJsonAsString = \Nette\Utils\Json::encode($jsonData); - } - - public function fun() - { - $jsonData = ['role_name' => 'admin', 'numberz' => ['id' => '5']]; - $someJsonAsString = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/concat_json.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/concat_json.php.inc deleted file mode 100644 index da667d562dd..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/concat_json.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - 'admin', 'numberz' => ['id' => 5]]; - $someJsonAsString = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/fixture.php.inc deleted file mode 100644 index 111ca3060b7..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - 'admin', 'numberz' => ['id' => '10']]; - $someJsonAsString = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/multiline_concat_json.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/multiline_concat_json.php.inc deleted file mode 100644 index 4ca5c0bd0d5..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/multiline_concat_json.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - 'admin', 'numberz' => ['id' => 5]]; - $someJsonAsString = \Nette\Utils\Json::encode($jsonData); - } - - public function fun() - { - $jsonData = ['role_name' => 'admin', 'numberz' => ['id' => '5']]; - $someJsonAsString = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/simple_row_with_spaces.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/simple_row_with_spaces.php.inc deleted file mode 100644 index 42a1db222ee..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/simple_row_with_spaces.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - 'Paulie', 'surname' => 'Garand']; - $json = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/tripleline_multiline_concat_json.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/tripleline_multiline_concat_json.php.inc deleted file mode 100644 index a1f1bbc71d1..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/tripleline_multiline_concat_json.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - 'admin', 'numberz' => ['id' => 5]]; - $someJsonAsString = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/with_implode.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/with_implode.php.inc deleted file mode 100644 index 4a99d9a7d68..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/with_implode.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - [1, 2, 3], 'order_by' => 'random']; - $jsonRequest = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/without_assign.php.inc b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/without_assign.php.inc deleted file mode 100644 index 66a2c190a39..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/Fixture/without_assign.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - ['id' => 1], 'users' => [['id' => 2], ['id' => 3]]]; - $jsonRequest = \Nette\Utils\Json::encode($jsonData); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/ManualJsonStringToJsonEncodeArrayRectorTest.php b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/ManualJsonStringToJsonEncodeArrayRectorTest.php deleted file mode 100644 index 38f55d6c196..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/ManualJsonStringToJsonEncodeArrayRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/config/configured_rule.php deleted file mode 100644 index 253c5c8c872..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ManualJsonStringToJsonEncodeArrayRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/negated_identical.php.inc b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/negated_identical.php.inc new file mode 100644 index 00000000000..1a54b23ec21 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/negated_identical.php.inc @@ -0,0 +1,31 @@ + +----- + 'equal to 10', + $value !== 5 => 'equal to 5', + default => 'other value', + }; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary.php.inc b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary.php.inc new file mode 100644 index 00000000000..afd96295397 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary.php.inc @@ -0,0 +1,31 @@ + 10 ? 'greater than 10' : ($value === 10 ? 'equal to 10' : 'less than 10'); + } +} + +?> +----- + 10 => 'greater than 10', + $value === 10 => 'equal to 10', + default => 'less than 10', + }; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary_with_identical.php.inc b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary_with_identical.php.inc new file mode 100644 index 00000000000..4bf88c20e59 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary_with_identical.php.inc @@ -0,0 +1,31 @@ + +----- + 'equal to 10', + 5 => 'equal to 5', + default => 'other value', + }; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary_with_identical_different_values.php.inc b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary_with_identical_different_values.php.inc new file mode 100644 index 00000000000..7c0eb13788a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/nested_ternary_with_identical_different_values.php.inc @@ -0,0 +1,31 @@ + +----- + 'equal to 10', + $nextValue === 5 => 'equal to 5', + default => 'other value', + }; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/skip_silent_conditions.php.inc b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/skip_silent_conditions.php.inc new file mode 100644 index 00000000000..3c88aefc254 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/Fixture/skip_silent_conditions.php.inc @@ -0,0 +1,15 @@ + 10 ? 'greater than 10' : ($value === 10 ?: 'less than 10'); + } +} diff --git a/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/NestedTernaryToMatchRectorTest.php b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/NestedTernaryToMatchRectorTest.php new file mode 100644 index 00000000000..dd6a3af3fa1 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/NestedTernaryToMatchRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/config/configured_rule.php new file mode 100644 index 00000000000..9e294c27119 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/NestedTernaryToMatchRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([NestedTernaryToMatchRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture.php.inc deleted file mode 100644 index 88563b1fb18..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture3.php.inc b/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture3.php.inc deleted file mode 100644 index 0e89d567d14..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -getSomeType(); - /* @var $value \SomeType */ - - $value2 = $this->getSomeType(); - /* @var \SomeType $value2 */ -} - -?> ------ -getSomeType(); - /** @var \SomeType $value2 */ - $value2 = $this->getSomeType(); -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture4.php.inc b/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture4.php.inc deleted file mode 100644 index 3e1c993dced..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/Fixture/fixture4.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -getContainer()->get('doctrine.orm.entity_manager'); - /* @var $em \Doctrine\ORM\EntityManager */ - $queryBuilder = $em->createQueryBuilder(); - /* @var $queryBuilder \Doctrine\ORM\QueryBuilder */ -} - -?> ------ -getContainer()->get('doctrine.orm.entity_manager'); - /** @var \Doctrine\ORM\QueryBuilder $queryBuilder */ - $queryBuilder = $em->createQueryBuilder(); -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/PHPStormVarAnnotationRectorTest.php b/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/PHPStormVarAnnotationRectorTest.php deleted file mode 100644 index 5367a9fadc8..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/PHPStormVarAnnotationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/config/configured_rule.php deleted file mode 100644 index 0e122dcb7ba..00000000000 --- a/rules-tests/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(PHPStormVarAnnotationRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/Fixture/multiple_assign.php.inc b/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/Fixture/multiple_assign.php.inc new file mode 100644 index 00000000000..c72f8a73a89 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/Fixture/multiple_assign.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/Fixture/skip_var_array_dim_fetch.php.inc b/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/Fixture/skip_var_array_dim_fetch.php.inc new file mode 100644 index 00000000000..b6c776446b5 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/Fixture/skip_var_array_dim_fetch.php.inc @@ -0,0 +1,13 @@ +execute(); - $one = $two = self::execute(); - $one = $two = execute(); - $one = $two = new \stdClass; + $anotherOne = $anotherTwo = new \stdClass; } } @@ -25,12 +23,8 @@ class UsePrevAssignVarCallNew { $one = $this->execute(); $two = $one; - $one = self::execute(); - $two = $one; - $one = execute(); - $two = $one; - $one = new \stdClass; - $two = $one; + $anotherOne = new \stdClass; + $anotherTwo = $anotherOne; } } diff --git a/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/SplitDoubleAssignRectorTest.php b/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/SplitDoubleAssignRectorTest.php index 6cc1151c976..d8a62eadef7 100644 --- a/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/SplitDoubleAssignRectorTest.php +++ b/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/SplitDoubleAssignRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\Assign\SplitDoubleAssignRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SplitDoubleAssignRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/config/configured_rule.php index 53fea39e7f5..ed17be05208 100644 --- a/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/Assign/SplitDoubleAssignRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\Assign\SplitDoubleAssignRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SplitDoubleAssignRector::class); -}; +return RectorConfig::configure() + ->withRules([SplitDoubleAssignRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/CatchExceptionNameMatchingTypeRectorTest.php b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/CatchExceptionNameMatchingTypeRectorTest.php index a1b6363a8c8..9537d649bba 100644 --- a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/CatchExceptionNameMatchingTypeRectorTest.php +++ b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/CatchExceptionNameMatchingTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CatchExceptionNameMatchingTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/change_variable_next_try2.php.inc b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/change_variable_next_try2.php.inc deleted file mode 100644 index aa4c5b900d0..00000000000 --- a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/change_variable_next_try2.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -verify($typoException); - $this->verify2($typoException); - } - } -} - -?> ------ -verify($someException); - $this->verify2($someException); - } - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/change_variable_next_try_reassign_same.php.inc b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/change_variable_next_try_reassign_same.php.inc deleted file mode 100644 index 76f0e9387b9..00000000000 --- a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/change_variable_next_try_reassign_same.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/identical_runs_after_each_other.php.inc b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/identical_runs_after_each_other.php.inc new file mode 100644 index 00000000000..8b9db4ef986 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/identical_runs_after_each_other.php.inc @@ -0,0 +1,53 @@ +exception($e); + } + + try { + } catch (Throwable $e) { + $notification = new Notification(); + $notification->exception($e); + } + } +} + +?> +----- +exception($throwable); + } + + try { + } catch (Throwable $throwable) { + $notification = new Notification(); + $notification->exception($throwable); + } + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/more_then_one_method_contains_try_catch.php.inc b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/more_then_one_method_contains_try_catch.php.inc new file mode 100644 index 00000000000..e87bf30b859 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/more_then_one_method_contains_try_catch.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/more_then_one_try_catch.php.inc b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/more_then_one_try_catch.php.inc new file mode 100644 index 00000000000..5ce9bebec5f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/more_then_one_try_catch.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/skip_change_variable_next_jump_try.php.inc b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/skip_change_variable_next_jump_try.php.inc new file mode 100644 index 00000000000..12d08f85707 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/skip_change_variable_next_jump_try.php.inc @@ -0,0 +1,20 @@ +verify($typoException); + $this->verify2($typoException); + } + } +} \ No newline at end of file diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/use_alias.php.inc b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/use_alias.php.inc new file mode 100644 index 00000000000..57b896e99ef --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/use_alias.php.inc @@ -0,0 +1,39 @@ +getMessage(); + } + } +} + +?> +----- +getMessage(); + } + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/with_interface_suffix.php.inc b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/with_interface_suffix.php.inc new file mode 100644 index 00000000000..f4973c198de --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Fixture/with_interface_suffix.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Source/Notification.php b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Source/Notification.php new file mode 100644 index 00000000000..32449cf442d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector/Source/Notification.php @@ -0,0 +1,7 @@ +services(); - $services->set(CatchExceptionNameMatchingTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([CatchExceptionNameMatchingTypeRector::class]); diff --git a/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/Fixture/skip_non_final_const.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/Fixture/skip_non_final_const.php.inc new file mode 100644 index 00000000000..660add1f8a5 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/Fixture/skip_non_final_const.php.inc @@ -0,0 +1,8 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/RemoveFinalFromConstRectorTest.php b/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/RemoveFinalFromConstRectorTest.php new file mode 100644 index 00000000000..bc8fc175f64 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/RemoveFinalFromConstRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/config/configured_rule.php new file mode 100644 index 00000000000..a82f754285d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector/config/configured_rule.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersionFeature::FINAL_CLASS_CONSTANTS); + + $rectorConfig->rule(RemoveFinalFromConstRector::class); +}; diff --git a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..671ace912b5 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/Fixture/fixture.php.inc @@ -0,0 +1,22 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/Fixture/skip.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/Fixture/skip.php.inc new file mode 100644 index 00000000000..5cb5e35f571 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/Fixture/skip.php.inc @@ -0,0 +1,8 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/config/configured_rule.php new file mode 100644 index 00000000000..be810b88ed9 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([SplitGroupedClassConstantsRector::class]); diff --git a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/Fixture/fixture.php.inc deleted file mode 100644 index a7abc918efa..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/Fixture/skip.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/Fixture/skip.php.inc deleted file mode 100644 index 565f4cd9409..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/Fixture/skip.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/config/configured_rule.php deleted file mode 100644 index 17dd54f5431..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SplitGroupedConstantsAndPropertiesRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/arrays.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/arrays.php.inc deleted file mode 100644 index 9b57244eedf..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/arrays.php.inc +++ /dev/null @@ -1,52 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/correct_invalid.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/correct_invalid.php.inc deleted file mode 100644 index ec0412e5778..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/correct_invalid.php.inc +++ /dev/null @@ -1,89 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/explicit_key_array.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/explicit_key_array.php.inc deleted file mode 100644 index 66e9ca2002a..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/explicit_key_array.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - 'hi' - ]; - - const VALUES_WITHOUT_KEYS = [ - 'hi' - ]; -} - -?> ------ - 'hi' - ]; - - /** - * @var string[] - */ - const VALUES_WITHOUT_KEYS = [ - 'hi' - ]; -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/fixture.php.inc deleted file mode 100644 index cb7c79a6d5d..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/improve_array_type.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/improve_array_type.php.inc deleted file mode 100644 index da71f6217f9..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/improve_array_type.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ -> - */ - private const MODIFYING_NODES = [ - AssignOp::class, - String_::class, - LNumber::class, - ]; -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/keep_class_string_key.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/keep_class_string_key.php.inc deleted file mode 100644 index c1614a1e121..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/keep_class_string_key.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -, string> - */ - private const TAGS_TYPES_TO_NAMES = [ - ReturnTagValueNode::class => '@return', - ParamTagValueNode::class => '@param', - VarTagValueNode::class => '@var', - ]; -} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/misc_type.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/misc_type.php.inc deleted file mode 100644 index 0a3cf9ab561..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/misc_type.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/no_slash.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/no_slash.php.inc deleted file mode 100644 index 050b63cc161..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/no_slash.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/override_bare_string_array.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/override_bare_string_array.php.inc deleted file mode 100644 index 884146e7b7f..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/override_bare_string_array.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - 'Csv', - 'Excel2003XML' => 'Xml', - 'Excel2007' => 'Xlsx', - 'Excel5' => 'Xls', - ]; -} - -?> ------ - - */ - private const OLD_TO_NEW_TYPE = [ - 'CSV' => 'Csv', - 'Excel2003XML' => 'Xml', - 'Excel2007' => 'Xlsx', - 'Excel5' => 'Xls', - ]; -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/override_non_array_mixed.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/override_non_array_mixed.php.inc deleted file mode 100644 index 64d6c352591..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/override_non_array_mixed.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - 'yes' - ]; -} - -?> ------ - - */ - public const NESTED_STRINGS = [ - 'get_it' => 'yes' - ]; -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_already_constant_string.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_already_constant_string.php.inc deleted file mode 100644 index 214664f7348..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_already_constant_string.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - package-name - * - "[aliased-package-name] "Message => aliased-package-name - * - "[Aliased\PackageName] "Message => Aliased\PackageName - */ - public const PACKAGE_NAME_PATTERN = '#\[(?[-\w\\\\]+)\]( ){1,}#'; -} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_class_string_key_value.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_class_string_key_value.php.inc deleted file mode 100644 index 1e3118a8f65..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_class_string_key_value.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -, array>> - */ - public const PRIORITY_TYPES = [ - BinaryOp::class => [BinaryOp::class, Expr::class], - ]; -} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_doule_class_string.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_doule_class_string.php.inc deleted file mode 100644 index 5322d1abb08..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_doule_class_string.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - - */ - private const ALLOWED_CLASS_TYPES = [Strings::class, TrinaryLogic::class]; -} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_mixed.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_mixed.php.inc deleted file mode 100644 index dd5ff07d611..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_mixed.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - [ - 'phpunit/phpunit' => 'v1.0.0', - 'rector/rector' => 'v1.0.0', - ], - 'autoload-dev' => [ - 'psr-4' => [ - 'Symplify\Tests\\' => 'tests', - 'Symplify\SuperTests\\' => 'super-tests', - ], - 'files' => ['src/SomeFile.php', 'src/KeepFile.php'], - ], - ]; -} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_so_many_types.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_so_many_types.php.inc deleted file mode 100644 index e1c6a39e91a..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/skip_so_many_types.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -|string>> - */ - private const CONFLICTING_CHECKER_GROUPS = [ - ['SlevomatCodingStandard\Sniffs\ControlStructures\DisallowYodaComparisonSniff', YodaStyleFixer::class], - [LowerCaseConstantSniff::class, UpperCaseConstantSniff::class], - [LowercaseConstantsFixer::class, UpperCaseConstantSniff::class], - [ConstantCaseFixer::class, UpperCaseConstantSniff::class], - ['SlevomatCodingStandard\Sniffs\TypeHints\DeclareStrictTypesSniff', DeclareEqualNormalizeFixer::class], - ['SlevomatCodingStandard\Sniffs\TypeHints\DeclareStrictTypesSniff', BlankLineAfterOpeningTagFixer::class], - [FileHeaderSniff::class, NoBlankLinesAfterPhpdocFixer::class], - ]; -} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/string_to_string.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/string_to_string.php.inc deleted file mode 100644 index 5affbb0961a..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/string_to_string.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - 'Csv', - 'Excel2003XML' => 'Xml', - 'Excel2007' => 'Xlsx', - 'Excel5' => 'Xls', - ]; -} - -?> ------ - - */ - private const OLD_TO_NEW_TYPE = [ - 'CSV' => 'Csv', - 'Excel2003XML' => 'Xml', - 'Excel2007' => 'Xlsx', - 'Excel5' => 'Xls', - ]; -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/types_on_class_string.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/types_on_class_string.php.inc deleted file mode 100644 index b512bedadba..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/types_on_class_string.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ -> - */ - private const MODIFYING_NODES = [ - 'PhpParser\Node\Expr\AssignOp', - 'PhpParser\Node\Scalar\String_', - ]; -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_const.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_const.php.inc deleted file mode 100644 index b5c55ef4d3a..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_const.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_nested_and_single_level_array_scalars.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_nested_and_single_level_array_scalars.php.inc deleted file mode 100644 index cd83b623a78..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_nested_and_single_level_array_scalars.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ['value2', 1234], - 1 => null, - ]; -} - -?> ------ - - */ - public const ARRAY_CONST = [ - 'key2' => ['value2', 1234], - 1 => null, - ]; -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_nested_array_scalars.php.inc b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_nested_array_scalars.php.inc deleted file mode 100644 index 1adcae1fa43..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/Fixture/union_nested_array_scalars.php.inc +++ /dev/null @@ -1,56 +0,0 @@ - 'value', - ]; - - public const NESTED_STRING_INT = [ - 'key2' => ['value2', 1234], - ]; - - public const STRING_AND_NULL = [ - 'key' => 'value', - 1 => null, - ]; -} - -?> ------ - - */ - public const STRING_ONLY = [ - 'key' => 'value', - ]; - - /** - * @var array> - */ - public const NESTED_STRING_INT = [ - 'key2' => ['value2', 1234], - ]; - - /** - * @var array - */ - public const STRING_AND_NULL = [ - 'key' => 'value', - 1 => null, - ]; -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/VarConstantCommentRectorTest.php b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/VarConstantCommentRectorTest.php deleted file mode 100644 index 631c2fd7064..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/VarConstantCommentRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/config/configured_rule.php deleted file mode 100644 index b9c58b0717e..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassConst/VarConstantCommentRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(VarConstantCommentRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/class_elements_with_comments.php.inc b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/class_elements_with_comments.php.inc new file mode 100644 index 00000000000..232b8451142 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/class_elements_with_comments.php.inc @@ -0,0 +1,62 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/enum_elements.php.inc b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/enum_elements.php.inc new file mode 100644 index 00000000000..9a10b57c710 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/enum_elements.php.inc @@ -0,0 +1,50 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/skip_already_has_newline.php.inc b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/skip_already_has_newline.php.inc new file mode 100644 index 00000000000..6cbde25466f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/skip_already_has_newline.php.inc @@ -0,0 +1,10 @@ + +
+

Hi

+
+ + +
+

Bye

+
+ + + +End of file diff --git a/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/skip_use_trait.php.inc b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/skip_use_trait.php.inc new file mode 100644 index 00000000000..4a513bcc8b3 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/Fixture/skip_use_trait.php.inc @@ -0,0 +1,10 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/NewlineBetweenClassLikeStmtsRectorTest.php b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/NewlineBetweenClassLikeStmtsRectorTest.php new file mode 100644 index 00000000000..d8bd6edf7c5 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/NewlineBetweenClassLikeStmtsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/config/configured_rule.php new file mode 100644 index 00000000000..1f587d70871 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([NewlineBetweenClassLikeStmtsRector::class]); diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/BinaryOpStandaloneAssignsToDirectRectorTest.php b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/BinaryOpStandaloneAssignsToDirectRectorTest.php new file mode 100644 index 00000000000..66dba79b1ac --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/BinaryOpStandaloneAssignsToDirectRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_any_call_like_with_args.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_any_call_like_with_args.php.inc new file mode 100644 index 00000000000..5d481afcc1e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_any_call_like_with_args.php.inc @@ -0,0 +1,16 @@ + $second; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_by_ref_from_param_variable.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_by_ref_from_param_variable.php.inc new file mode 100644 index 00000000000..1362e570fc3 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_by_ref_from_param_variable.php.inc @@ -0,0 +1,14 @@ + $second; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_compare_assign_op.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_compare_assign_op.php.inc new file mode 100644 index 00000000000..5da4308b5e0 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_compare_assign_op.php.inc @@ -0,0 +1,14 @@ + $second; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_compare_binary_op.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_compare_binary_op.php.inc new file mode 100644 index 00000000000..4f9d2a3996a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_compare_binary_op.php.inc @@ -0,0 +1,14 @@ + $second; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_different_order.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_different_order.php.inc new file mode 100644 index 00000000000..063f1eff01b --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_different_order.php.inc @@ -0,0 +1,14 @@ +createLongValue(100); + $second = $this->createLongValue(200); + + return $first <=> $second; + } + + private function createLongValue($value) + { + return $value . ' is long'; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_non_variable_assign.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_non_variable_assign.php.inc new file mode 100644 index 00000000000..f5097685fdd --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/skip_non_variable_assign.php.inc @@ -0,0 +1,16 @@ +number = 200; + + return $first <=> $this->number; + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/some_separated_assign.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/some_separated_assign.php.inc new file mode 100644 index 00000000000..de72da9b732 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/some_separated_assign.php.inc @@ -0,0 +1,30 @@ + $second; + } +} + +?> +----- + 200; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/two_bare_getters.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/two_bare_getters.php.inc new file mode 100644 index 00000000000..3bd4dc89b43 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Fixture/two_bare_getters.php.inc @@ -0,0 +1,34 @@ +getSome(); + $second = $secondSomeGetter->getSome(); + + return $first <=> $second; + } +} + +?> +----- +getSome() <=> $secondSomeGetter->getSome(); + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Source/SomeGetter.php b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Source/SomeGetter.php new file mode 100644 index 00000000000..789f2bd6288 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector/Source/SomeGetter.php @@ -0,0 +1,11 @@ +withRules([BinaryOpStandaloneAssignsToDirectRector::class]); diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/in_method_or_static_call.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/in_method_or_static_call.php.inc deleted file mode 100644 index e6481797b55..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/in_method_or_static_call.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -count($args = func_get_args(), 1); - Assert::count($args = func_get_args(), 1); - } - - private function count($args, $value) - { - Assert::count($args, $value); - } -} - -?> ------ -count($args, 1); - Assert::count($args, 1); - } - - private function count($args, $value) - { - Assert::count($args, $value); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/in_method_or_static_call2.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/in_method_or_static_call2.php.inc deleted file mode 100644 index 6104b3f050f..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/in_method_or_static_call2.php.inc +++ /dev/null @@ -1,61 +0,0 @@ -count($args = func_get_args(), 1); - Assert::count($args = func_get_args(), 1); - }; - } - - public function run2(): void - { - function innerFunc() { - Assert::count($args = func_get_args(), 1); - }; - } - - private function count($args, $value) - { - Assert::count($args, $value); - } -} - -?> ------ -count($args, 1); - Assert::count($args, 1); - }; - } - - public function run2(): void - { - function innerFunc(...$args) { - Assert::count($args, 1); - }; - } - - private function count($args, $value) - { - Assert::count($args, $value); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/inner_closure.php.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/inner_closure.php.php.inc new file mode 100644 index 00000000000..0edd1f8052e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/inner_closure.php.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/skip_inner_calls.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/skip_inner_calls.php.inc new file mode 100644 index 00000000000..f15f908292d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/Fixture/skip_inner_calls.php.inc @@ -0,0 +1,28 @@ +count($args = func_get_args(), 1); + Assert::count($args = func_get_args(), 1); + }; + } + + public function run2(): void + { + function innerFunc() { + Assert::count($args = func_get_args(), 1); + }; + } + + private function count($args, $value) + { + Assert::count($args, $value); + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/FuncGetArgsToVariadicParamRectorTest.php b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/FuncGetArgsToVariadicParamRectorTest.php index 54da08e510d..35c08b52be8 100644 --- a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/FuncGetArgsToVariadicParamRectorTest.php +++ b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/FuncGetArgsToVariadicParamRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class FuncGetArgsToVariadicParamRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/config/configured_rule.php index 93c3a0d0edb..76bdf750df2 100644 --- a/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(FuncGetArgsToVariadicParamRector::class); -}; +return RectorConfig::configure() + ->withRules([FuncGetArgsToVariadicParamRector::class]); diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_anonymous_class.php.inc new file mode 100644 index 00000000000..f4f251b5f9f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_anonymous_class.php.inc @@ -0,0 +1,20 @@ +run() === 1); + } +} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_equal_direct_parent.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_equal_direct_parent.php.inc new file mode 100644 index 00000000000..22c2ac4bedb --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_equal_direct_parent.php.inc @@ -0,0 +1,12 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/SkipParentConstructOverrideInPHP72Test.php b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/SkipParentConstructOverrideInPHP72Test.php index 33b293086b0..c6f5361c216 100644 --- a/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/SkipParentConstructOverrideInPHP72Test.php +++ b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/SkipParentConstructOverrideInPHP72Test.php @@ -5,28 +5,23 @@ namespace Rector\Tests\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SkipParentConstructOverrideInPHP72Test extends AbstractRectorTestCase { /** - * @requires PHP 7.2 * @see https://phpunit.readthedocs.io/en/8.3/incomplete-and-skipped-tests.html#incomplete-and-skipped-tests-requires-tables-api - * - * @dataProvider provideData() */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureForPhp72'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureForPhp72'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Source/Father.php b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Source/Father.php new file mode 100644 index 00000000000..3244ce92cac --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Source/Father.php @@ -0,0 +1,12 @@ +services(); - $services->set(MakeInheritedMethodVisibilitySameAsParentRector::class); -}; +return RectorConfig::configure() + ->withRules([MakeInheritedMethodVisibilitySameAsParentRector::class]); diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/config/php_72.php b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/config/php_72.php index 9b4ccfdc48e..886e7b23e78 100644 --- a/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/config/php_72.php +++ b/rules-tests/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/config/php_72.php @@ -3,14 +3,10 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector; -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersionFeature; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::PARENT_VISIBILITY_OVERRIDE); - - $services = $containerConfigurator->services(); - $services->set(MakeInheritedMethodVisibilitySameAsParentRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersionFeature::PARENT_VISIBILITY_OVERRIDE); + $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class); }; diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/objects.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/objects.php.inc new file mode 100644 index 00000000000..222d894a40a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/objects.php.inc @@ -0,0 +1,38 @@ +foo = new Value; + $this->foo->bar = 1; + $this->bar = new Value; + $this->bar->bar = 1; + } +} + +?> +----- +foo = new Value; + $this->foo->bar = 1; + + $this->bar = new Value; + $this->bar->bar = 1; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/properties.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/properties.php.inc new file mode 100644 index 00000000000..af4390e732c --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/properties.php.inc @@ -0,0 +1,36 @@ +bar = new Value; + $foo->bar->bar = 5; + $bar = new Value; + $bar->foo = 1; + } +} + +?> +----- +bar = new Value; + $foo->bar->bar = 5; + + $bar = new Value; + $bar->foo = 1; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/statics.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/statics.php.inc new file mode 100644 index 00000000000..4b05113a94e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/Fixture/statics.php.inc @@ -0,0 +1,38 @@ +bar = 1; + self::$barFoo = new Value; + self::$barFoo->bar = 1; + } +} + +?> +----- +bar = 1; + + self::$barFoo = new Value; + self::$barFoo->bar = 1; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/NewlineBeforeNewAssignSetRectorTest.php b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/NewlineBeforeNewAssignSetRectorTest.php index 5fce4038488..482082aab59 100644 --- a/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/NewlineBeforeNewAssignSetRectorTest.php +++ b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/NewlineBeforeNewAssignSetRectorTest.php @@ -5,29 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class NewlineBeforeNewAssignSetRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - if ($this->isWindows()) { - $this->markTestSkipped('doesnt work on windows, see https://github.com/rectorphp/rector/issues/6572'); - } - - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/config/configured_rule.php index 7536ed562af..27a0659d24c 100644 --- a/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(NewlineBeforeNewAssignSetRector::class); -}; +return RectorConfig::configure() + ->withRules([NewlineBeforeNewAssignSetRector::class]); diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/first_from_multiple_attr_in_attr_groups.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/first_from_multiple_attr_in_attr_groups.php.inc deleted file mode 100644 index e1cb4f3e7ac..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/first_from_multiple_attr_in_attr_groups.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/one_around.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/one_around.php.inc deleted file mode 100644 index 8e719de484d..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/one_around.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/skip_expected_order.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/skip_expected_order.php.inc deleted file mode 100644 index b91448d585f..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Fixture/skip_expected_order.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/OrderAttributesRectorTest.php b/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/OrderAttributesRectorTest.php deleted file mode 100644 index 26233de5f23..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/OrderAttributesRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Source/FirstAttribute.php b/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Source/FirstAttribute.php deleted file mode 100644 index edaa2bbe9e6..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/OrderAttributesRector/Source/FirstAttribute.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(OrderAttributesRector::class) - ->call('configure', [[ - OrderAttributesRector::ATTRIBUTES_ORDER => [FirstAttribute::class, SecondAttribute::class], - ]]); -}; diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/class_method_declaration.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/class_method_declaration.php.inc deleted file mode 100644 index 691cd2aa22f..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/class_method_declaration.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/methods_calling.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/methods_calling.php.inc deleted file mode 100644 index 87aa73000f9..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/methods_calling.php.inc +++ /dev/null @@ -1,61 +0,0 @@ -__doSomething(); - - $myClass->__construct(); - $myClass->__destruct(); - $myClass->__call(); - $myClass->__callStatic(); - $myClass->__get(); - $myClass->__set(); - $myClass->__isset(); - $myClass->__unset(); - $myClass->__sleep(); - $myClass->__wakeup(); - $myClass->__serialize(); - $myClass->__unserialize(); - $myClass->__toString(); - $myClass->__invoke(); - $myClass->__set_state(); - $myClass->__debugInfo(); - } -} - -?> ------ -doSomething(); - - $myClass->__construct(); - $myClass->__destruct(); - $myClass->__call(); - $myClass->__callStatic(); - $myClass->__get(); - $myClass->__set(); - $myClass->__isset(); - $myClass->__unset(); - $myClass->__sleep(); - $myClass->__wakeup(); - $myClass->__serialize(); - $myClass->__unserialize(); - $myClass->__toString(); - $myClass->__invoke(); - $myClass->__set_state(); - $myClass->__debugInfo(); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/skip_native_php_methods.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/skip_native_php_methods.php.inc deleted file mode 100644 index 0d21f42f088..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/skip_native_php_methods.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/skip_only_double_underscore.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/skip_only_double_underscore.php.inc deleted file mode 100644 index 4ccf6184d38..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/Fixture/skip_only_double_underscore.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/RemoveDoubleUnderscoreInMethodNameRectorTest.php b/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/RemoveDoubleUnderscoreInMethodNameRectorTest.php deleted file mode 100644 index 45e103c8589..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/RemoveDoubleUnderscoreInMethodNameRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/config/configured_rule.php deleted file mode 100644 index 4c0cef73016..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveDoubleUnderscoreInMethodNameRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/data_provider.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/data_provider.php.inc deleted file mode 100644 index 95549e18a1f..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/data_provider.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture.php.inc deleted file mode 100644 index 84b1bd4103e..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - 'callback']; - } -} - -?> ------ - 'callback'; - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture2.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture2.php.inc deleted file mode 100644 index bdaef8371fb..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture3.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture3.php.inc deleted file mode 100644 index a38a58f5ddb..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture4.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture4.php.inc deleted file mode 100644 index 771f87f0b49..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/fixture4.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/provide_data_multi_items.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/provide_data_multi_items.php.inc deleted file mode 100644 index 543e8f860f0..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/provide_data_multi_items.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/should_not_remove_comments.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/should_not_remove_comments.php.inc deleted file mode 100644 index 6a7334d2644..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/should_not_remove_comments.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/should_not_remove_doc_blocks.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/should_not_remove_doc_blocks.php.inc deleted file mode 100644 index b6a363cd945..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/should_not_remove_doc_blocks.php.inc +++ /dev/null @@ -1,57 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/skip_should_not_apply_when_class_name_differs.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/skip_should_not_apply_when_class_name_differs.php.inc deleted file mode 100644 index bca7516418f..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/skip_should_not_apply_when_class_name_differs.php.inc +++ /dev/null @@ -1,16 +0,0 @@ - 'event']; - } - - public function provideSomeData(): array - { - return ['some' => 'data']; - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/skip_should_not_apply_when_method_name_differs.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/skip_should_not_apply_when_method_name_differs.php.inc deleted file mode 100644 index b1932e55901..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Fixture/skip_should_not_apply_when_method_name_differs.php.inc +++ /dev/null @@ -1,18 +0,0 @@ - 'public']; - } - - private function getSomePrivateData(): array - { - return ['visibility' => 'private']; - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/ReturnArrayClassMethodToYieldRectorTest.php b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/ReturnArrayClassMethodToYieldRectorTest.php deleted file mode 100644 index d7b4a521afd..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/ReturnArrayClassMethodToYieldRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Source/EventSubscriberInterface.php b/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Source/EventSubscriberInterface.php deleted file mode 100644 index 6073744b05c..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector/Source/EventSubscriberInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(ReturnArrayClassMethodToYieldRector::class) - ->call('configure', [[ - ReturnArrayClassMethodToYieldRector::METHODS_TO_YIELDS => ValueObjectInliner::inline([ - new ReturnArrayClassMethodToYield(EventSubscriberInterface::class, 'getSubscribedEvents'), - new ReturnArrayClassMethodToYield(ParentTestCase::class, 'provide*'), - new ReturnArrayClassMethodToYield(ParentTestCase::class, 'dataProvider*'), - new ReturnArrayClassMethodToYield(TestCase::class, 'provideData'), - ]), - ]]); -}; diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/fixture.php.inc deleted file mode 100644 index dcc1ec6c91e..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -run(...$data); - } - - private function run(...$args) - { - } -} - -?> ------ -run($data); - } - - private function run(array $args = []) - { - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/handle_multiple_items_to_array.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/handle_multiple_items_to_array.php.inc deleted file mode 100644 index a03c38c5b3b..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/handle_multiple_items_to_array.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -run('John', 1, 2, 3, 4, 5); - } - - public function run(string $name, ...$items) - { - } -} - -?> ------ -run('John', [1, 2, 3, 4, 5]); - } - - public function run(string $name, array $items = []) - { - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/not_pass_arg.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/not_pass_arg.php.inc deleted file mode 100644 index 38e29a9de0d..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/not_pass_arg.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -run(); - } -} - -?> ------ -run(); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_dynamic_args.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_dynamic_args.php.inc deleted file mode 100644 index c6d6bc01ff8..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_dynamic_args.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -{$methodName}(...$this->getParameters()); - } - - private function getParameters(): array{} -} -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_no_params_or_args.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_no_params_or_args.php.inc deleted file mode 100644 index 05490f3f01a..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_no_params_or_args.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -execute(); - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_no_spread_variables.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_no_spread_variables.php.inc deleted file mode 100644 index 4ec2ba4c702..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_no_spread_variables.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -execute($data); - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_third_party.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_third_party.php.inc deleted file mode 100644 index 1c875b2102c..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_third_party.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -addDirectory(...$directories); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/typed_param_variadic.php.inc b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/typed_param_variadic.php.inc deleted file mode 100644 index 7d48ce3d6ed..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/typed_param_variadic.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/UnSpreadOperatorRectorTest.php b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/UnSpreadOperatorRectorTest.php deleted file mode 100644 index 3682745b3d8..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/UnSpreadOperatorRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/config/configured_rule.php deleted file mode 100644 index 991959f48c1..00000000000 --- a/rules-tests/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(UnSpreadOperatorRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/AddArrayDefaultToArrayPropertyRectorTest.php b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/AddArrayDefaultToArrayPropertyRectorTest.php deleted file mode 100644 index d42f565f1c4..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/AddArrayDefaultToArrayPropertyRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/array_and_type.php.inc b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/array_and_type.php.inc deleted file mode 100644 index fc75fb74974..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/array_and_type.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/count_on_null.php.inc b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/count_on_null.php.inc deleted file mode 100644 index 61ce721f76b..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/count_on_null.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -values !== null && count($this->values) > 0; - } - - public function isEmptyOtherSide() - { - return null !== $this->values && count($this->values); - } -} - -?> ------ -values) > 0; - } - - public function isEmptyOtherSide() - { - return count($this->values); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/fixture.php.inc deleted file mode 100644 index a04a4c3919c..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -values === null; - } -} - -?> ------ -values === []; - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/scalar_integer.php.inc b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/scalar_integer.php.inc deleted file mode 100644 index 3ab8513c29f..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/scalar_integer.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/skip.php.inc b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/skip.php.inc deleted file mode 100644 index c4fd89ecb67..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/skip.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -values === null; - } -} diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/static_property.php.inc b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/static_property.php.inc deleted file mode 100644 index 24a1c055c77..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/static_property.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/two_types.php.inc b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/two_types.php.inc deleted file mode 100644 index 6ec35ab5978..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/Fixture/two_types.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/config/configured_rule.php deleted file mode 100644 index d11a313ad24..00000000000 --- a/rules-tests/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(AddArrayDefaultToArrayPropertyRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/ClosureDelegatingCallToFirstClassCallableRectorTest.php b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/ClosureDelegatingCallToFirstClassCallableRectorTest.php new file mode 100644 index 00000000000..c8a265b7096 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/ClosureDelegatingCallToFirstClassCallableRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/another_simple_call.php.inc b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/another_simple_call.php.inc new file mode 100644 index 00000000000..3550bf78a6d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/another_simple_call.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_already_first_class_callable.php.inc b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_already_first_class_callable.php.inc new file mode 100644 index 00000000000..5f42b9d9daf --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_already_first_class_callable.php.inc @@ -0,0 +1,12 @@ +foo(); + }; + + function ($foo) + { + return (new $foo)->foo(); + }; + + function ($foo) + { + return $foo::foo(); + }; + + function ($foo) + { + return ($foo . '\\Foo')::foo(); + }; + } +} diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_with_no_return.php.inc b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_with_no_return.php.inc new file mode 100644 index 00000000000..2374638f3a2 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_with_no_return.php.inc @@ -0,0 +1,21 @@ +foo($foo); + }; + } +} diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_without_calls.php.inc b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_without_calls.php.inc new file mode 100644 index 00000000000..247d883b4ba --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_functions_without_calls.php.inc @@ -0,0 +1,8 @@ +foo($barFoo, $foo); + }; + } +} diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_named_args.php.inc b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_named_args.php.inc new file mode 100644 index 00000000000..66131f4f959 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_named_args.php.inc @@ -0,0 +1,19 @@ +foo(foo: $foo); + }; + + function ($foo) + { + return Foo::foo(foo: $foo); + }; + } +} diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_null_variable_call.php.inc b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_null_variable_call.php.inc new file mode 100644 index 00000000000..f454eba7cdb --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_null_variable_call.php.inc @@ -0,0 +1,16 @@ +foo($foo); + }; + } +} diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_used_by_caller_variable.php.inc b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_used_by_caller_variable.php.inc new file mode 100644 index 00000000000..c93b29f0f08 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Fixture/skip_used_by_caller_variable.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Source/SomeClassWithArgs.php b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Source/SomeClassWithArgs.php new file mode 100644 index 00000000000..2ec4634fafb --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector/Source/SomeClassWithArgs.php @@ -0,0 +1,14 @@ +withRules([ClosureDelegatingCallToFirstClassCallableRector::class]) + ->withPhpVersion(PhpVersionFeature::FIRST_CLASS_CALLABLE_SYNTAX); diff --git a/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..de3d36c58b7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/fixture.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/fixture_with_named_this_binding.php.inc b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/fixture_with_named_this_binding.php.inc new file mode 100644 index 00000000000..3d35c44b0f3 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/fixture_with_named_this_binding.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/skip_already_static.php.inc b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/skip_already_static.php.inc new file mode 100644 index 00000000000..3c0e340c3ce --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/skip_already_static.php.inc @@ -0,0 +1,11 @@ +callOnObject(function () { + echo 'method call'; +}); + +bind_on_object(function () { + echo 'closure func call 0'; +}, null, function () { + echo 'closure func call 0'; +}); + +?> diff --git a/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/skip_with_this.php.inc b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/skip_with_this.php.inc new file mode 100644 index 00000000000..56e15fda82c --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Fixture/skip_with_this.php.inc @@ -0,0 +1,19 @@ +data; + } + + return strtoupper($this->data); + }; + } +} diff --git a/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Source/BindObject.php b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Source/BindObject.php new file mode 100644 index 00000000000..cd4881bf0ed --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/Source/BindObject.php @@ -0,0 +1,22 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/config/configured_rule.php new file mode 100644 index 00000000000..7194947ac2e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Closure/StaticClosureRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StaticClosureRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/AlwaysSprintfTest.php b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/AlwaysSprintfTest.php new file mode 100644 index 00000000000..6bbb14afd98 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/AlwaysSprintfTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureAlwaysSprintf'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/always_sprintf.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/EncapsedStringsToSprintfRectorTest.php b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/EncapsedStringsToSprintfRectorTest.php index 81fae8a812f..803c39e6d24 100644 --- a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/EncapsedStringsToSprintfRectorTest.php +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/EncapsedStringsToSprintfRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class EncapsedStringsToSprintfRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo, false); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/encapsed_strings_to_sprintf_should_escape_percent.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/encapsed_strings_to_sprintf_should_escape_percent.php.inc index 538bbd51a8f..5f8a0c13b42 100644 --- a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/encapsed_strings_to_sprintf_should_escape_percent.php.inc +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/encapsed_strings_to_sprintf_should_escape_percent.php.inc @@ -9,6 +9,7 @@ final class EncapsedStringsToSprintfShouldEscapePercent return "$value%"; } } + ?> ----- diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/fixture.php.inc deleted file mode 100644 index 6559919ce3b..00000000000 --- a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/fixture2.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/fixture2.php.inc deleted file mode 100644 index 036910602ac..00000000000 --- a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -format}"; - } -} - -?> ------ -format); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/include_numbers.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/include_numbers.php.inc new file mode 100644 index 00000000000..a9513793bc3 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/include_numbers.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/just_value.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/just_value.php.inc index 22f9d9fd2f1..81bde398110 100644 --- a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/just_value.php.inc +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/just_value.php.inc @@ -22,7 +22,7 @@ final class JustValue public function testCommand(): void { $flag = 'hey'; - $expected = [sprintf('--%s', $flag)]; + $expected = ['--' . $flag]; } } diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/newline.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/newline.php.inc index b2b4e1be88b..16fd8582ee1 100644 --- a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/newline.php.inc +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/newline.php.inc @@ -6,7 +6,9 @@ final class Newline { public function run(string $format) { - return "${format}\n"; + $result = "${format}\n"; + + $nextResult = "\n${format}"; } } @@ -20,7 +22,9 @@ final class Newline { public function run(string $format) { - return $format . PHP_EOL; + $result = $format . PHP_EOL; + + $nextResult = PHP_EOL . $format; } } diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/newline2.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/newline2.php.inc deleted file mode 100644 index 8362fd71cbd..00000000000 --- a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/newline2.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/only_three_concat.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/only_three_concat.php.inc new file mode 100644 index 00000000000..7ef090eb72d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/only_three_concat.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/single_encapsed.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/single_encapsed.php.inc new file mode 100644 index 00000000000..f4d8035924f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/single_encapsed.php.inc @@ -0,0 +1,31 @@ +format}"; + } +} + +?> +----- +format; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/skip-hexa-chars.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/skip-hexa-chars.php.inc new file mode 100644 index 00000000000..51b1af58fb8 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/skip-hexa-chars.php.inc @@ -0,0 +1,5 @@ + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/translation_function.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/translation_function.php.inc new file mode 100644 index 00000000000..7b606970abe --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/Fixture/translation_function.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/encapsed_strings_to_sprintf_should_escape_percent.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/encapsed_strings_to_sprintf_should_escape_percent.php.inc new file mode 100644 index 00000000000..7c037508439 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/encapsed_strings_to_sprintf_should_escape_percent.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/include_numbers.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/include_numbers.php.inc new file mode 100644 index 00000000000..b898862884f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/include_numbers.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/just_value.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/just_value.php.inc new file mode 100644 index 00000000000..c979c449f3d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/just_value.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/newline.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/newline.php.inc new file mode 100644 index 00000000000..36ba17f9110 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/newline.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/numberz.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/numberz.php.inc new file mode 100644 index 00000000000..bf8c1dcc52f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/numberz.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/only_three_concat.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/only_three_concat.php.inc new file mode 100644 index 00000000000..33d5c721a1f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/only_three_concat.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/prefixed_eol.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/prefixed_eol.php.inc new file mode 100644 index 00000000000..7ac3fd60568 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/prefixed_eol.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/single_encapsed.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/single_encapsed.php.inc new file mode 100644 index 00000000000..bad0396ab51 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/single_encapsed.php.inc @@ -0,0 +1,31 @@ +format}"; + } +} + +?> +----- +format; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/skip_in_heredoc.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/skip_in_heredoc.php.inc new file mode 100644 index 00000000000..afd560059c8 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/FixtureAlwaysSprintf/skip_in_heredoc.php.inc @@ -0,0 +1,16 @@ +ruleWithConfiguration(EncapsedStringsToSprintfRector::class, [ + EncapsedStringsToSprintfRector::ALWAYS => true, + ]); +}; diff --git a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/config/configured_rule.php index 99d70bcbe62..0df51bedde7 100644 --- a/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(EncapsedStringsToSprintfRector::class); -}; +return RectorConfig::configure() + ->withRules([EncapsedStringsToSprintfRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/Fixture/some_variables_with_braces.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/Fixture/some_variables_with_braces.php.inc index 75861907bef..0efc49728c4 100644 --- a/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/Fixture/some_variables_with_braces.php.inc +++ b/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/Fixture/some_variables_with_braces.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\CodingStyle\Rector\Encapsed\WrapEncapsedVariableInCurlyBracesRector\Fixture; -function some_variales_with_braces($hello, $world) +function some_variables_with_braces($hello, $world) { return "$hello {$world}!"; } @@ -13,7 +13,7 @@ function some_variales_with_braces($hello, $world) namespace Rector\Tests\CodingStyle\Rector\Encapsed\WrapEncapsedVariableInCurlyBracesRector\Fixture; -function some_variales_with_braces($hello, $world) +function some_variables_with_braces($hello, $world) { return "{$hello} {$world}!"; } diff --git a/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/Fixture/with_middle_variable.php.inc b/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/Fixture/with_middle_variable.php.inc new file mode 100644 index 00000000000..8dba78d37a4 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/Fixture/with_middle_variable.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/WrapEncapsedVariableInCurlyBracesRectorTest.php b/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/WrapEncapsedVariableInCurlyBracesRectorTest.php index ae27f5646bb..b37724d02f9 100644 --- a/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/WrapEncapsedVariableInCurlyBracesRectorTest.php +++ b/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/WrapEncapsedVariableInCurlyBracesRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\Encapsed\WrapEncapsedVariableInCurlyBracesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class WrapEncapsedVariableInCurlyBracesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/config/configured_rule.php index f915b34ad44..bb80081d3f4 100644 --- a/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\Encapsed\WrapEncapsedVariableInCurlyBracesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(WrapEncapsedVariableInCurlyBracesRector::class); -}; +return RectorConfig::configure() + ->withRules([WrapEncapsedVariableInCurlyBracesRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/EnumCaseToPascalCaseRectorTest.php b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/EnumCaseToPascalCaseRectorTest.php new file mode 100644 index 00000000000..df26473efb0 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/EnumCaseToPascalCaseRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath, true); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/case_on_self.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/case_on_self.php.inc new file mode 100644 index 00000000000..e07d8d31f14 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/case_on_self.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used.php.inc new file mode 100644 index 00000000000..4d909765650 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used_alias.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used_alias.php.inc new file mode 100644 index 00000000000..033a08bfa62 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used_alias.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_enum_const.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_enum_const.php.inc new file mode 100644 index 00000000000..cebfb93ff2c --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_enum_const.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_existing_pascal.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_existing_pascal.php.inc new file mode 100644 index 00000000000..075ae7d5801 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_existing_pascal.php.inc @@ -0,0 +1,11 @@ + diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/WithAutoloadPathsTest.php b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/WithAutoloadPathsTest.php new file mode 100644 index 00000000000..1b791d6c8f4 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/WithAutoloadPathsTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath, true); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/with_autoload_configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/configured_rule.php new file mode 100644 index 00000000000..372d2669825 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([EnumCaseToPascalCaseRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/with_autoload_configured_rule.php b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/with_autoload_configured_rule.php new file mode 100644 index 00000000000..8a4fd8fdc29 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/with_autoload_configured_rule.php @@ -0,0 +1,10 @@ +withRules([EnumCaseToPascalCaseRector::class]) + ->withAutoloadPaths([__DIR__ . '/../Source']); diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_explicit_array_and_explicit_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_explicit_array_and_explicit_array.php.inc new file mode 100644 index 00000000000..53bc84db9a3 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_explicit_array_and_explicit_array.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_explicit_array_and_variable.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_explicit_array_and_variable.php.inc new file mode 100644 index 00000000000..fbfe131c566 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_explicit_array_and_variable.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_variable_and_explicit_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_variable_and_explicit_array.php.inc new file mode 100644 index 00000000000..9939c19ccd7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/flatten_variable_and_explicit_array.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/integer_keys.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/integer_keys.php.inc new file mode 100644 index 00000000000..7247b1847e2 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/integer_keys.php.inc @@ -0,0 +1,33 @@ + 'two', 3 => 'four']; + $iter2 = [5 => 'six', 7 => 'eight']; + + return array_merge($iter1, $iter2); + } +} + +?> +----- + 'two', 3 => 'four']; + $iter2 = [5 => 'six', 7 => 'eight']; + + return [...$iter1, ...$iter2]; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_doblock_based_type_array_merge.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_doblock_based_type_array_merge.php.inc new file mode 100644 index 00000000000..f70373dfaf8 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_doblock_based_type_array_merge.php.inc @@ -0,0 +1,15 @@ + $iter1 + * @param array $iter2 + */ + public function run($iter1, $iter2) + { + $values = array_merge($iter1, $iter2); + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_get_iterator.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_get_iterator.php.inc new file mode 100644 index 00000000000..6832db354a1 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_get_iterator.php.inc @@ -0,0 +1,16 @@ +files() + ->in(__DIR__ . '/Source'); + + $files = iterator_to_array($finder->getIterator()); + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_iterator_to_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_iterator_to_array.php.inc new file mode 100644 index 00000000000..e1b83677e7a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_iterator_to_array.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_parse_url.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_parse_url.php.inc similarity index 77% rename from rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_parse_url.php.inc rename to rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_parse_url.php.inc index 7166ecea8cd..3b2190acd04 100644 --- a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_parse_url.php.inc +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_parse_url.php.inc @@ -1,6 +1,6 @@ 'two', 'three' => 'four']; + $iter2 = ['five' => 'six', 'seven' => 'eight']; + + return array_merge($iter1, $iter2); + } + + public function go() + { + $iter1 = [1 => 'two', 3 => 'four']; + $iter2 = ['five' => 'six', 'seven' => 'eight']; + + return array_merge($iter1, $iter2); + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_string_keys_from_functions.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_string_keys_from_functions.php.inc new file mode 100644 index 00000000000..ec11183a35e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp74/skip_string_keys_from_functions.php.inc @@ -0,0 +1,20 @@ +1]; +} +function y(): array +{ + return ['a'=>1]; +} + +class SkipStringKeysFromFunctions +{ + public function run() + { + return array_merge(y(), x()); + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/any_key.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/any_key.php.inc new file mode 100644 index 00000000000..bd9f366fa44 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/any_key.php.inc @@ -0,0 +1,65 @@ + + */ + public function getA(): array + { + return [1, 2, 3]; + } + + /** + * @return array + */ + public function getB(): array + { + return [4, 5, 6]; + } + + public function run() + { + $a = $this->getA(); + $b = $this->getB(); + + return array_merge($a, $b); + } +} + +?> +----- + + */ + public function getA(): array + { + return [1, 2, 3]; + } + + /** + * @return array + */ + public function getB(): array + { + return [4, 5, 6]; + } + + public function run() + { + $a = $this->getA(); + $b = $this->getB(); + + return [...$a, ...$b]; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_explicit_array_and_explicit_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_explicit_array_and_explicit_array.php.inc new file mode 100644 index 00000000000..83c865bf1b3 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_explicit_array_and_explicit_array.php.inc @@ -0,0 +1,27 @@ + 'bar'], ['baz' => 'xxx']); + } +} + +?> +----- + 'bar', 'baz' => 'xxx']; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_explicit_array_and_variable.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_explicit_array_and_variable.php.inc new file mode 100644 index 00000000000..d53dd08a44b --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_explicit_array_and_variable.php.inc @@ -0,0 +1,31 @@ + 'bar']; + + return array_merge(['baz' => 'xxx'], $array); + } +} + +?> +----- + 'bar']; + + return ['baz' => 'xxx', ...$array]; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_variable_and_explicit_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_variable_and_explicit_array.php.inc new file mode 100644 index 00000000000..6530ce73db0 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/flatten_variable_and_explicit_array.php.inc @@ -0,0 +1,31 @@ + 'bar']; + + return array_merge($array, ['baz' => 'xxx']); + } +} + +?> +----- + 'bar']; + + return [...$array, 'baz' => 'xxx']; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/integer_keys.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/integer_keys.php.inc new file mode 100644 index 00000000000..fa5dc5e62db --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/integer_keys.php.inc @@ -0,0 +1,33 @@ + 'two', 3 => 'four']; + $iter2 = [5 => 'six', 7 => 'eight']; + + return array_merge($iter1, $iter2); + } +} + +?> +----- + 'two', 3 => 'four']; + $iter2 = [5 => 'six', 7 => 'eight']; + + return [...$iter1, ...$iter2]; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/parse_url.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/parse_url.php.inc new file mode 100644 index 00000000000..189a49be610 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/parse_url.php.inc @@ -0,0 +1,45 @@ +parseUrl($url), $this->parseUrl($redirectLocation)); + } + + /** + * @return array + */ + private function parseUrl(string $url): array + { + $urlParts = parse_url($url); + + return array_filter($urlParts); + } +} +?> +----- +parseUrl($url), ...$this->parseUrl($redirectLocation)]; + } + + /** + * @return array + */ + private function parseUrl(string $url): array + { + $urlParts = parse_url($url); + + return array_filter($urlParts); + } +} +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/simple_array_merge.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/simple_array_merge.php.inc new file mode 100644 index 00000000000..82c80916eb9 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/simple_array_merge.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_first_class_callable.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..cc09041409d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_first_class_callable.php.inc @@ -0,0 +1,11 @@ +files() + ->in(__DIR__ . '/Source'); + + $files = iterator_to_array($finder->getIterator()); + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_iterator_to_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_iterator_to_array.php.inc new file mode 100644 index 00000000000..81e624e5da7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_iterator_to_array.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_spread_array_merge.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_spread_array_merge.php.inc new file mode 100644 index 00000000000..b19b82b23d7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/skip_spread_array_merge.php.inc @@ -0,0 +1,20 @@ + 'two', 'three' => 'four']; + $iter2 = ['five' => 'six', 'seven' => 'eight']; + + return array_merge($iter1, $iter2); + } + + public function go() + { + $iter1 = [1 => 'two', 3 => 'four']; + $iter2 = ['five' => 'six', 'seven' => 'eight']; + + return array_merge($iter1, $iter2); + } +} +?> +----- + 'two', 'three' => 'four']; + $iter2 = ['five' => 'six', 'seven' => 'eight']; + + return [...$iter1, ...$iter2]; + } + + public function go() + { + $iter1 = [1 => 'two', 3 => 'four']; + $iter2 = ['five' => 'six', 'seven' => 'eight']; + + return [...$iter1, ...$iter2]; + } +} +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/string_keys_from_functions.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/string_keys_from_functions.php.inc new file mode 100644 index 00000000000..36252c49c5c --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/FixturePhp81/string_keys_from_functions.php.inc @@ -0,0 +1,45 @@ +1]; +} +function y(): array +{ + return ['a'=>1]; +} + +class StringKeysFromFunctions +{ + public function run() + { + return array_merge(y(), x()); + } +} + +?> +----- +1]; +} +function y(): array +{ + return ['a'=>1]; +} + +class StringKeysFromFunctions +{ + public function run() + { + return [...y(), ...x()]; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Php74ArraySpreadInsteadOfArrayMergeRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Php74ArraySpreadInsteadOfArrayMergeRectorTest.php new file mode 100644 index 00000000000..484e79ef123 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Php74ArraySpreadInsteadOfArrayMergeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php74.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Php81ArraySpreadInsteadOfArrayMergeRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Php81ArraySpreadInsteadOfArrayMergeRectorTest.php new file mode 100644 index 00000000000..6da9ce56a18 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Php81ArraySpreadInsteadOfArrayMergeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp81'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php81.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule_php74.php b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule_php74.php new file mode 100644 index 00000000000..c7dc0c2f534 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule_php74.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersion::PHP_74); + $rectorConfig->rule(ArraySpreadInsteadOfArrayMergeRector::class); +}; diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule_php81.php b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule_php81.php new file mode 100644 index 00000000000..77290a8ebce --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule_php81.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersion::PHP_81); + $rectorConfig->rule(ArraySpreadInsteadOfArrayMergeRector::class); +}; diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/CallUserFuncArrayToVariadicRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/CallUserFuncArrayToVariadicRectorTest.php index fc1e0790115..6a49d250b22 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/CallUserFuncArrayToVariadicRectorTest.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/CallUserFuncArrayToVariadicRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CallUserFuncArrayToVariadicRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/first_class_callable.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/first_class_callable.php.inc new file mode 100644 index 00000000000..19e228d8c0a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/first_class_callable.php.inc @@ -0,0 +1,47 @@ +redirector = new Redirector(); + } + + public function run() + { + $args = \func_get_args(); + call_user_func_array($this->redirector->redirect(...), $args); + } +} + +?> +----- +redirector = new Redirector(); + } + + public function run() + { + $args = \func_get_args(); + $this->redirector->redirect(...$args); + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/config/configured_rule.php index 75e88d54088..3e6371d19e6 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/config/configured_rule.php @@ -3,9 +3,11 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CallUserFuncArrayToVariadicRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersion::PHP_80); + + $rectorConfig->rule(CallUserFuncArrayToVariadicRector::class); }; diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/CallUserFuncToMethodCallRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/CallUserFuncToMethodCallRectorTest.php index 003814fc8c3..7806cdb95be 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/CallUserFuncToMethodCallRectorTest.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/CallUserFuncToMethodCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CallUserFuncToMethodCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/pass_arg.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/pass_arg.php.inc new file mode 100644 index 00000000000..11ddcdc6933 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/pass_arg.php.inc @@ -0,0 +1,35 @@ +args = call_user_func([$this, 'args'], $args); + } + } +} + +?> +----- +args = $this->args($args); + } + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/config/configured_rule.php index 8a1f439fab4..89f6394e67c 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/config/configured_rule.php @@ -3,10 +3,9 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector; +use Rector\Config\RectorConfig; +use Rector\ValueObject\PhpVersion; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CallUserFuncToMethodCallRector::class); -}; +return RectorConfig::configure() + ->withPhpVersion(PhpVersion::PHP_81) + ->withRules([CallUserFuncToMethodCallRector::class]); diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/ClosureFromCallableToFirstClassCallableRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/ClosureFromCallableToFirstClassCallableRectorTest.php new file mode 100644 index 00000000000..969de8623c7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/ClosureFromCallableToFirstClassCallableRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/class_callable_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/class_callable_array.php.inc new file mode 100644 index 00000000000..6d2c7cfee7c --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/class_callable_array.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/function_callable_string.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/function_callable_string.php.inc new file mode 100644 index 00000000000..715136b2681 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/function_callable_string.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/object_callable_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/object_callable_array.php.inc new file mode 100644 index 00000000000..af66c20bc76 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/object_callable_array.php.inc @@ -0,0 +1,19 @@ + +----- +method(...); + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/supports_static_self_call.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/supports_static_self_call.php.inc new file mode 100644 index 00000000000..7013d15173b --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/supports_static_self_call.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/config/configured_rule.php new file mode 100644 index 00000000000..065c7f645d7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ClosureFromCallableToFirstClassCallableRector::class]); diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/ConsistentImplodeRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/ConsistentImplodeRectorTest.php index 4338accf46c..b2a30d22fd4 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/ConsistentImplodeRectorTest.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/ConsistentImplodeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\FuncCall\ConsistentImplodeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ConsistentImplodeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/Fixture/on_join.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/Fixture/on_join.php.inc new file mode 100644 index 00000000000..f93fbeb66e0 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/Fixture/on_join.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/Fixture/skip_concat_on_first_arg.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/Fixture/skip_concat_on_first_arg.php.inc new file mode 100644 index 00000000000..c74bc2a8af3 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/Fixture/skip_concat_on_first_arg.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/config/configured_rule.php index 13fff397680..bbbf0360be6 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentImplodeRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ConsistentImplodeRector::class); -}; +return RectorConfig::configure() + ->withRules([ConsistentImplodeRector::class]); diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/ConsistentPregDelimiterRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/ConsistentPregDelimiterRectorTest.php deleted file mode 100644 index 6d0cc06c298..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/ConsistentPregDelimiterRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_content_delimiter_in_pattern.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_content_delimiter_in_pattern.php.inc deleted file mode 100644 index 856e1d24944..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_content_delimiter_in_pattern.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_content_delimiter_in_pattern_multi.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_content_delimiter_in_pattern_multi.php.inc deleted file mode 100644 index 41c6c30ec3c..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_content_delimiter_in_pattern_multi.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_nette_static_call.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_nette_static_call.php.inc deleted file mode 100644 index 7946ab79703..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/escape_nette_static_call.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/fixture.php.inc deleted file mode 100644 index ba6bc523bb8..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/skip_concat.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/skip_concat.php.inc deleted file mode 100644 index 29123449f73..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/Fixture/skip_concat.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/config/configured_rule.php deleted file mode 100644 index 86ceb5ff1f7..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ConsistentPregDelimiterRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/CountArrayToEmptyArrayComparisonRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/CountArrayToEmptyArrayComparisonRectorTest.php index 8f7266dcb0c..892e52ed46d 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/CountArrayToEmptyArrayComparisonRectorTest.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/CountArrayToEmptyArrayComparisonRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CountArrayToEmptyArrayComparisonRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/Fixture/fixture_not_identical.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/Fixture/fixture_not_identical.php.inc index aa1e3d8463c..15a75d2c3b0 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/Fixture/fixture_not_identical.php.inc +++ b/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/Fixture/fixture_not_identical.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector\Fixture; -class FixturNotIdentical +class FixtureNotIdentical { public function run() { @@ -25,7 +25,7 @@ class FixturNotIdentical namespace Rector\Tests\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector\Fixture; -class FixturNotIdentical +class FixtureNotIdentical { public function run() { diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/Fixture/skip_countable_instance.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/Fixture/skip_countable_instance.php.inc new file mode 100644 index 00000000000..2418a5d7337 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/Fixture/skip_countable_instance.php.inc @@ -0,0 +1,16 @@ + 0) { + +} + + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/config/configured_rule.php index 015a8b7ab11..8d03684bbdc 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CountArrayToEmptyArrayComparisonRector::class); -}; +return RectorConfig::configure() + ->withRules([CountArrayToEmptyArrayComparisonRector::class]); diff --git a/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_filter.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_filter.php.inc new file mode 100644 index 00000000000..990fc64cfa7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_filter.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_function_named_argument.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_function_named_argument.php.inc new file mode 100644 index 00000000000..daacdae8216 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_function_named_argument.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_walk.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_walk.php.inc new file mode 100644 index 00000000000..26cd6d72ddf --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/array_walk.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/nested_array_call.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/nested_array_call.php.inc new file mode 100644 index 00000000000..c4068c8e3fb --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/nested_array_call.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/skip_already_callable.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/skip_already_callable.php.inc new file mode 100644 index 00000000000..1f9c1d49be0 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/Fixture/skip_already_callable.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/config/configured_rule.php new file mode 100644 index 00000000000..5f2de914a7c --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(FunctionFirstClassCallableRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_81); +}; diff --git a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/AutoImportTest.php b/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/AutoImportTest.php deleted file mode 100644 index c6387ca8553..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/AutoImportTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureAutoImport'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/auto_import.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/fixture.php.inc deleted file mode 100644 index 0acc95c01cb..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/skip_defined_previous_call.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/skip_defined_previous_call.php.inc deleted file mode 100644 index 34826e54072..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/skip_defined_previous_call.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/skip_variable_name.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/skip_variable_name.php.inc deleted file mode 100644 index fb204c7c4e5..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/Fixture/skip_variable_name.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/FixtureAutoImport/do_not_auto_import_names.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/FixtureAutoImport/do_not_auto_import_names.php.inc deleted file mode 100644 index 6d129c65cf2..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/FixtureAutoImport/do_not_auto_import_names.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/PreslashSimpleFunctionRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/PreslashSimpleFunctionRectorTest.php deleted file mode 100644 index 38e784677c9..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/PreslashSimpleFunctionRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/config/auto_import.php b/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/config/auto_import.php deleted file mode 100644 index 666870f1a70..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/config/auto_import.php +++ /dev/null @@ -1,15 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(PreslashSimpleFunctionRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/config/configured_rule.php deleted file mode 100644 index fd0fbaf6f6e..00000000000 --- a/rules-tests/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(PreslashSimpleFunctionRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/FuncCall/StrictArraySearchRector/StrictArraySearchRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/StrictArraySearchRector/StrictArraySearchRectorTest.php index a630f0b1813..7608865d9bf 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/StrictArraySearchRector/StrictArraySearchRectorTest.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/StrictArraySearchRector/StrictArraySearchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\FuncCall\StrictArraySearchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StrictArraySearchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/FuncCall/StrictArraySearchRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/StrictArraySearchRector/config/configured_rule.php index 24427d4a46c..8d775fbedf1 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/StrictArraySearchRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/StrictArraySearchRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\FuncCall\StrictArraySearchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StrictArraySearchRector::class); -}; +return RectorConfig::configure() + ->withRules([StrictArraySearchRector::class]); diff --git a/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/both_integers.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/both_integers.php.inc new file mode 100644 index 00000000000..08bd7af21a9 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/both_integers.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/both_strings.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/both_strings.php.inc new file mode 100644 index 00000000000..473f297fc56 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/both_strings.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/skip_both_mixed_type.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/skip_both_mixed_type.php.inc new file mode 100644 index 00000000000..e00a59754b0 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/Fixture/skip_both_mixed_type.php.inc @@ -0,0 +1,14 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/config/configured_rule.php new file mode 100644 index 00000000000..40d23d5523a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/StrictInArrayRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StrictInArrayRector::class]); diff --git a/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/Fixture/skip_indirect_string_number.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/Fixture/skip_indirect_string_number.php.inc index b767c278258..0c2d57b107a 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/Fixture/skip_indirect_string_number.php.inc +++ b/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/Fixture/skip_indirect_string_number.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConst class SkipIndirectStringNumber { - private const MIN_PHP_VERSION = '7.3'; + private const MIN_PHP_VERSION = '7.2'; public function run() { diff --git a/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/VersionCompareFuncCallToConstantRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/VersionCompareFuncCallToConstantRectorTest.php index 87b9258d58d..f636da2b012 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/VersionCompareFuncCallToConstantRectorTest.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/VersionCompareFuncCallToConstantRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class VersionCompareFuncCallToConstantRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/config/configured_rule.php index 0ad45ccaffb..1b9249ce984 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(VersionCompareFuncCallToConstantRector::class); -}; +return RectorConfig::configure() + ->withRules([VersionCompareFuncCallToConstantRector::class]); diff --git a/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/Fixture/fixture.php.inc index 59f7f413d89..cec254913f9 100644 --- a/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/Fixture/fixture.php.inc +++ b/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/Fixture/fixture.php.inc @@ -1,23 +1,27 @@ ----- diff --git a/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/Fixture/skip_mixed.php.inc b/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/Fixture/skip_mixed.php.inc new file mode 100644 index 00000000000..773e8fc2332 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/Fixture/skip_mixed.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/NullableCompareToNullRectorTest.php b/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/NullableCompareToNullRectorTest.php index c327dad1a65..c1ae38972d7 100644 --- a/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/NullableCompareToNullRectorTest.php +++ b/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/NullableCompareToNullRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\If_\NullableCompareToNullRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class NullableCompareToNullRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/config/configured_rule.php index b330790b361..b009ae86232 100644 --- a/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/If_/NullableCompareToNullRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\If_\NullableCompareToNullRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(NullableCompareToNullRector::class); -}; +return RectorConfig::configure() + ->withRules([NullableCompareToNullRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/extra_dot.php.inc b/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/extra_dot.php.inc deleted file mode 100644 index fc7fdad2d88..00000000000 --- a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/extra_dot.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/fixture.php.inc deleted file mode 100644 index 8febda4b3fc..00000000000 --- a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/phar.php.inc b/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/phar.php.inc deleted file mode 100644 index fb84c11f02b..00000000000 --- a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/Fixture/phar.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/FollowRequireByDirRectorTest.php b/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/FollowRequireByDirRectorTest.php deleted file mode 100644 index 972aeba3d1a..00000000000 --- a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/FollowRequireByDirRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/config/configured_rule.php deleted file mode 100644 index cf099d3c2cd..00000000000 --- a/rules-tests/CodingStyle/Rector/Include_/FollowRequireByDirRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(FollowRequireByDirRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/skip_create_mock.php.inc b/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/skip_create_mock.php.inc deleted file mode 100644 index 4aabf59f680..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/skip_create_mock.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -createMock(\Foo::class); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/static_to_this.php.inc b/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/static_to_this.php.inc deleted file mode 100644 index 6b5fdf7ada2..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/static_to_this.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ -assertThis(); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self.php.inc b/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self.php.inc deleted file mode 100644 index 94db2c60ddc..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -assertThis(); - self::assertThis(); - static::assertThis(); - parent::assertThis(); - } -} - -?> ------ - diff --git a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self_phpunit_assert.php.inc b/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self_phpunit_assert.php.inc deleted file mode 100644 index eda665b70ff..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self_phpunit_assert.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -assertEquals('a', 'a'); - } -} - -?> ------ - diff --git a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_this.php.inc b/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_this.php.inc deleted file mode 100644 index ec32125dab3..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_this.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -assertThis(); - self::assertThis(); - parent::assertThis(); - self::assertThisAndThat(1, 2); - } -} - -?> ------ -assertThis(); - $this->assertThis(); - parent::assertThis(); - $this->assertThisAndThat(1, 2); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/PreferThisOrSelfMethodCallRectorTest.php b/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/PreferThisOrSelfMethodCallRectorTest.php deleted file mode 100644 index 55d8da50547..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/PreferThisOrSelfMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Source/AbstractTestCase.php b/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Source/AbstractTestCase.php deleted file mode 100644 index d4b36b8ceb6..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Source/AbstractTestCase.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(PreferThisOrSelfMethodCallRector::class) - ->call('configure', [[ - PreferThisOrSelfMethodCallRector::TYPE_TO_PREFERENCE => [ - AbstractTestCase::class => ValueObjectInliner::inline(PreferenceSelfThis::PREFER_SELF()), - BeLocalClass::class => ValueObjectInliner::inline(PreferenceSelfThis::PREFER_THIS()), - TestCase::class => ValueObjectInliner::inline(PreferenceSelfThis::PREFER_SELF()), - ], - ]]); -}; diff --git a/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/Fixture/fixture.php.inc deleted file mode 100644 index 8dcbd642a96..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -info(sprintf('Hi %s', 'Tom')); - } -} - -?> ------ -info($message); - } -} - -?> diff --git a/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/UseMessageVariableForSprintfInSymfonyStyleRectorTest.php b/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/UseMessageVariableForSprintfInSymfonyStyleRectorTest.php deleted file mode 100644 index 1698f0e60e6..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/UseMessageVariableForSprintfInSymfonyStyleRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/config/configured_rule.php deleted file mode 100644 index bf2cae8b74b..00000000000 --- a/rules-tests/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(UseMessageVariableForSprintfInSymfonyStyleRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/DocBlockRectorTest.php b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/DocBlockRectorTest.php deleted file mode 100644 index 87ba8d12bb8..00000000000 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/DocBlockRectorTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureDocBlock'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/import_doc_block_config.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/attribute_already_with_use.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/attribute_already_with_use.php.inc new file mode 100644 index 00000000000..7b6e173816b --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/attribute_already_with_use.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/bootstrap_names.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/bootstrap_names.php.inc deleted file mode 100644 index ac13a084ab3..00000000000 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/bootstrap_names.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/fixture.php.inc index f6b26cfbfcc..63e3e455d6b 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/fixture.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/fixture.php.inc @@ -17,6 +17,7 @@ class SomeClass namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; use SomeAnother\AnotherClass; + class SomeClass { public function create() diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/global_namespace.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/global_namespace.php.inc new file mode 100644 index 00000000000..dfa6f8f18fa --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/global_namespace.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_function.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_function.php.inc index 74a6997701c..e9fbeb301dd 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_function.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_function.php.inc @@ -23,6 +23,7 @@ function someFunctionWithNoEffect() namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; use function Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Function_\count; + class ImportFunction { public function run() diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_param_doc.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_param_doc.php.inc index 242f92ef431..fd8b10b0f82 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_param_doc.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_param_doc.php.inc @@ -20,6 +20,7 @@ class ImportParamDoc namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\NormalParamClass; + class ImportParamDoc { /** diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_root_namespace_classes_enabled.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_root_namespace_classes_enabled.php.inc index c5bccad01bc..c16db2d2a6b 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_root_namespace_classes_enabled.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/import_root_namespace_classes_enabled.php.inc @@ -34,6 +34,7 @@ final class ImportRootNamespaceClassesEnabled namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; use DateTime; + final class ImportRootNamespaceClassesEnabled { /** diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/keep_static_method.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/keep_static_method.php.inc index 89eca7e5fe9..91d516ff7a4 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/keep_static_method.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/keep_static_method.php.inc @@ -4,6 +4,9 @@ declare(strict_types=1); namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; +use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Contract\Token; +use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Contract; + abstract class KeepStaticMethod { public static function decode(string $token): Contract\Token @@ -13,3 +16,26 @@ abstract class KeepStaticMethod return Token::create($parts[0] ?? '', $parts[1] ?? ''); } } + +?> +----- + diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/no_class.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/no_class.php.inc index 245c074fdf9..3ac860742fa 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/no_class.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/no_class.php.inc @@ -14,6 +14,7 @@ function noClass() namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; use SomeAnother\AnotherClass; + function noClass() { return new AnotherClass; diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/same_namespaced_class.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/same_namespaced_class.php.inc index 4ca8d8118c9..cd9b9a7d4be 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/same_namespaced_class.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/same_namespaced_class.php.inc @@ -19,6 +19,7 @@ class SameNamespacedClass namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\SharedShortName; + class SameNamespacedClass { /** diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/short.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/short.php.inc index 5084484e4a8..bd3c1a23d4b 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/short.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/short.php.inc @@ -13,6 +13,7 @@ class SomeException extends \Exception namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; use Exception; + class SomeException extends Exception { } diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_aliased_names.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_aliased_names.php.inc index 6ae697743e4..227980bb95f 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_aliased_names.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_aliased_names.php.inc @@ -10,4 +10,3 @@ class SkipAliasedNames $constraint = Assert\Blank::class; } } -?> diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureDocBlock/skip_already_class_name_in_inlined_var_doc.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_already_class_name_in_inlined_var_doc.php.inc similarity index 91% rename from rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureDocBlock/skip_already_class_name_in_inlined_var_doc.php.inc rename to rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_already_class_name_in_inlined_var_doc.php.inc index 4288866ef47..801adf83305 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureDocBlock/skip_already_class_name_in_inlined_var_doc.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_already_class_name_in_inlined_var_doc.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\FixtureDocBlock; +namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; class SkipAlreadyClassNameInInlinedVarDoc { diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureDocBlock/skip_import_var_property_lowercased_typo.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_import_var_property_lowercased_typo.php.inc similarity index 88% rename from rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureDocBlock/skip_import_var_property_lowercased_typo.php.inc rename to rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_import_var_property_lowercased_typo.php.inc index 18796fc778e..04466940a62 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureDocBlock/skip_import_var_property_lowercased_typo.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_import_var_property_lowercased_typo.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\FixtureDocBlock; +namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; class SkipalreadyclassnameinvarDoc { diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_multiple_namespaces.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_multiple_namespaces.php.inc new file mode 100644 index 00000000000..9f022497957 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/skip_multiple_namespaces.php.inc @@ -0,0 +1,18 @@ + ------ - diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureConstant/double_import_constant.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureConstant/double_import_constant.php.inc new file mode 100644 index 00000000000..0b3dd8db924 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureConstant/double_import_constant.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureFunction/double_import_function.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureFunction/double_import_function.php.inc index cce0780c261..12569264f11 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureFunction/double_import_function.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureFunction/double_import_function.php.inc @@ -22,6 +22,7 @@ class DoubleImportFunction namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\FixtureFunction; use function Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Function_\substr; + class DoubleImportFunction { public function create() diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureGeneric/repository.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureGeneric/repository.php.inc index 39c0d2a1c27..4ec97ec1817 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureGeneric/repository.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureGeneric/repository.php.inc @@ -17,6 +17,7 @@ class Repository namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\FixtureGeneric; use Doctrine\ORM\EntityManagerInterface; + class Repository { /** diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureGeneric/repository_generic.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureGeneric/repository_generic.php.inc index d14dcf59a8c..3b35d282734 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureGeneric/repository_generic.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureGeneric/repository_generic.php.inc @@ -18,6 +18,7 @@ namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRe use Doctrine\ORM\EntityRepository; use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Generic\SomeGeneral; + final class RepositoryGeneric { /** diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureNonNamespaced/constant_import.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureNonNamespaced/constant_import.php.inc new file mode 100644 index 00000000000..123f14898ad --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureNonNamespaced/constant_import.php.inc @@ -0,0 +1,23 @@ + +----- + +----- + diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureNonNamespaced/simple_with_strict_types_with_other_import_multiple.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureNonNamespaced/simple_with_strict_types_with_other_import_multiple.php.inc new file mode 100644 index 00000000000..c9d9551d170 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureNonNamespaced/simple_with_strict_types_with_other_import_multiple.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureRoot/import_root_namespace_classes_disabled.php.inc b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureRoot/import_root_namespace_classes_disabled.php.inc index 3695e2cc01a..1027fa86c75 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureRoot/import_root_namespace_classes_disabled.php.inc +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/FixtureRoot/import_root_namespace_classes_disabled.php.inc @@ -53,6 +53,7 @@ final class ImportRootNamespaceClassesDisabled namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture; use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Response; + final class ImportRootNamespaceClassesDisabled { /** diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/ImportFullyQualifiedNamesRectorTest.php b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/ImportFullyQualifiedNamesRectorTest.php index 810ca489fcc..3f9683918e5 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/ImportFullyQualifiedNamesRectorTest.php +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/ImportFullyQualifiedNamesRectorTest.php @@ -5,46 +5,41 @@ namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * @see \Rector\PostRector\Rector\NameImportingPostRector */ final class ImportFullyQualifiedNamesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - * @dataProvider provideDataFunction() - * @dataProvider provideDataGeneric() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + #[DataProvider('provideDataConstant')] + #[DataProvider('provideDataFunction')] + #[DataProvider('provideDataGeneric')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } - /** - * @return Iterator - */ - public function provideDataFunction(): Iterator + public static function provideDataConstant(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureFunction'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureConstant'); } - /** - * @return Iterator - */ - public function provideDataGeneric(): Iterator + public static function provideDataFunction(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureGeneric'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureFunction'); + } + + public static function provideDataGeneric(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureGeneric'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/ImportRootNamespaceClassesDisabledTest.php b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/ImportRootNamespaceClassesDisabledTest.php index 58d98e2ad4b..622a08270eb 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/ImportRootNamespaceClassesDisabledTest.php +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/ImportRootNamespaceClassesDisabledTest.php @@ -5,28 +5,26 @@ namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * @see \Rector\PostRector\Rector\NameImportingPostRector */ final class ImportRootNamespaceClassesDisabledTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } /** * @return Iterator */ - public function provideData(): iterable + public static function provideData(): iterable { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureRoot'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureRoot'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/NonNamespacedTest.php b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/NonNamespacedTest.php index ae2d49ba765..60bbc21bc7a 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/NonNamespacedTest.php +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/NonNamespacedTest.php @@ -5,28 +5,23 @@ namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * @see \Rector\PostRector\Rector\NameImportingPostRector */ final class NonNamespacedTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureNonNamespaced'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureNonNamespaced'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Php80Test.php b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Php80Test.php deleted file mode 100644 index 60965ec12c4..00000000000 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Php80Test.php +++ /dev/null @@ -1,37 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureAttributes'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/import_config.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/Constant_/constant.php b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/Constant_/constant.php new file mode 100644 index 00000000000..e5df75aba9f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/Constant_/constant.php @@ -0,0 +1,7 @@ +parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->importNames(); + $rectorConfig->importShortClasses(); - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class); + $rectorConfig->rule(RenameClassRector::class); }; diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/config/import_doc_block_config.php b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/config/import_doc_block_config.php deleted file mode 100644 index d0180bcfa94..00000000000 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/config/import_doc_block_config.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - $parameters->set(Option::IMPORT_DOC_BLOCKS, true); - - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/config/not_import_short_classes.php b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/config/not_import_short_classes.php index 94f2fe00187..0e364b09dea 100644 --- a/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/config/not_import_short_classes.php +++ b/rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/config/not_import_short_classes.php @@ -2,15 +2,12 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - $parameters->set(Option::IMPORT_SHORT_CLASSES, false); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->importNames(); + $rectorConfig->importShortClasses(false); - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class); + $rectorConfig->rule(RenameClassRector::class); }; diff --git a/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/Fixture/fixture.php.inc deleted file mode 100644 index eeb859f8978..00000000000 --- a/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/UseIncrementAssignRectorTest.php b/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/UseIncrementAssignRectorTest.php deleted file mode 100644 index 6149cf20ccb..00000000000 --- a/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/UseIncrementAssignRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/config/configured_rule.php deleted file mode 100644 index 022274ca08d..00000000000 --- a/rules-tests/CodingStyle/Rector/Plus/UseIncrementAssignRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(UseIncrementAssignRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/Fixture/as_array_key.php.inc b/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/Fixture/as_array_key.php.inc deleted file mode 100644 index 69c61666dbf..00000000000 --- a/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/Fixture/as_array_key.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/PostIncDecToPreIncDecRectorTest.php b/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/PostIncDecToPreIncDecRectorTest.php index f5badc220eb..8e08ad27677 100644 --- a/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/PostIncDecToPreIncDecRectorTest.php +++ b/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/PostIncDecToPreIncDecRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\PostInc\PostIncDecToPreIncDecRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class PostIncDecToPreIncDecRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/config/configured_rule.php index 3795924c3b3..feae54dc2cc 100644 --- a/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\PostInc\PostIncDecToPreIncDecRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(PostIncDecToPreIncDecRector::class); -}; +return RectorConfig::configure() + ->withRules([PostIncDecToPreIncDecRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/AddFalseDefaultToBoolPropertyRectorTest.php b/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/AddFalseDefaultToBoolPropertyRectorTest.php deleted file mode 100644 index 36c3f3c5935..00000000000 --- a/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/AddFalseDefaultToBoolPropertyRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/Fixture/fixture.php.inc deleted file mode 100644 index 8aaf1e13a08..00000000000 --- a/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/Fixture/skip_default.php.inc b/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/Fixture/skip_default.php.inc deleted file mode 100644 index e9b3624fd16..00000000000 --- a/rules-tests/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector/Fixture/skip_default.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(AddFalseDefaultToBoolPropertyRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..eec11e63bd2 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/fixture.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/skip.php.inc b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/skip.php.inc new file mode 100644 index 00000000000..a06def41375 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/skip.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/with_values.php.inc b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/with_values.php.inc new file mode 100644 index 00000000000..7a183cbd41a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/Fixture/with_values.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/SplitGroupedPropertiesRectorTest.php b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/SplitGroupedPropertiesRectorTest.php new file mode 100644 index 00000000000..d720d0c007f --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/SplitGroupedPropertiesRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/config/configured_rule.php new file mode 100644 index 00000000000..680337e656a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Property/SplitGroupedPropertiesRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([SplitGroupedPropertiesRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/complex_if.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/complex_if.php.inc new file mode 100644 index 00000000000..ce5289247af --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/complex_if.php.inc @@ -0,0 +1,78 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/complex_try_catch.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/complex_try_catch.php.inc new file mode 100644 index 00000000000..c143070a35c --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/complex_try_catch.php.inc @@ -0,0 +1,50 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line.php.inc new file mode 100644 index 00000000000..1ab28344fff --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line.php.inc @@ -0,0 +1,45 @@ + $value) { + # code... + } + return true; + } +} + +?> +----- + $value) { + # code... + } + + return true; + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_classlike.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_classlike.php.inc new file mode 100644 index 00000000000..01b470d03f3 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_classlike.php.inc @@ -0,0 +1,56 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_function.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_function.php.inc new file mode 100644 index 00000000000..994a7057a99 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_function.php.inc @@ -0,0 +1,26 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_switch.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_switch.php.inc new file mode 100644 index 00000000000..9f650fa918a --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_switch.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_try_catch.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_try_catch.php.inc new file mode 100644 index 00000000000..e74652b0403 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/no_new_line_try_catch.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_already_has_newline.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_already_has_newline.php.inc new file mode 100644 index 00000000000..bd26fe3cd84 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_already_has_newline.php.inc @@ -0,0 +1,21 @@ + $value) { + # code... + } + + return true; + } +} diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_class_like.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_class_like.php.inc new file mode 100644 index 00000000000..86f3b823a50 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_class_like.php.inc @@ -0,0 +1,13 @@ + +
+

Hi

+
+ + +
+

Bye

+
+ + + +End of file diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_if_inside_do_while_next_not_stmt.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_if_inside_do_while_next_not_stmt.php.inc new file mode 100644 index 00000000000..1f016ab9509 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_if_inside_do_while_next_not_stmt.php.inc @@ -0,0 +1,15 @@ +foo); + } +} diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_inside_return_statement.php.inc b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_inside_return_statement.php.inc new file mode 100644 index 00000000000..761261f781d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/Fixture/skip_inside_return_statement.php.inc @@ -0,0 +1,16 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/config/configured_rule.php new file mode 100644 index 00000000000..414e8d43049 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/NewlineAfterStatementRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([NewlineAfterStatementRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/no_namespace_useless_alias.php.inc b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/no_namespace_useless_alias.php.inc new file mode 100644 index 00000000000..2ea20e974c7 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/no_namespace_useless_alias.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/remove_useless_alias.php.inc b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/remove_useless_alias.php.inc new file mode 100644 index 00000000000..f957c0621c6 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/remove_useless_alias.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/remove_useless_alias2.php.inc b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/remove_useless_alias2.php.inc new file mode 100644 index 00000000000..bb03375062e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/remove_useless_alias2.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/skip_alias_different_with_lastname.php.inc b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/skip_alias_different_with_lastname.php.inc new file mode 100644 index 00000000000..532dcfe4a2d --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/Fixture/skip_alias_different_with_lastname.php.inc @@ -0,0 +1,5 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/config/configured_rule.php new file mode 100644 index 00000000000..540691651f1 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveUselessAliasInUseStatementRector::class]); diff --git a/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..5f3dcf65c94 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/fixture.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/skip.php.inc b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/skip.php.inc new file mode 100644 index 00000000000..497848558b9 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/skip.php.inc @@ -0,0 +1,12 @@ +boundary."\"\n\t"; + } +} diff --git a/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/skip_non_printable_chars.php.inc b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/skip_non_printable_chars.php.inc new file mode 100644 index 00000000000..6963f6f9115 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/Fixture/skip_non_printable_chars.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/SimplifyQuoteEscapeRectorTest.php b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/SimplifyQuoteEscapeRectorTest.php new file mode 100644 index 00000000000..bb79887c960 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/SimplifyQuoteEscapeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/config/configured_rule.php new file mode 100644 index 00000000000..6c3561098f4 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([SimplifyQuoteEscapeRector::class]); diff --git a/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/Fixture/fixture.php.inc deleted file mode 100644 index 668aabd6f88..00000000000 --- a/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/SplitStringClassConstantToClassConstFetchRectorTest.php b/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/SplitStringClassConstantToClassConstFetchRectorTest.php deleted file mode 100644 index 97d51a9104a..00000000000 --- a/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/SplitStringClassConstantToClassConstFetchRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/config/configured_rule.php deleted file mode 100644 index 933c80582d7..00000000000 --- a/rules-tests/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SplitStringClassConstantToClassConstFetchRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/Fixture/fixture.php.inc deleted file mode 100644 index 4e52d37ef6c..00000000000 --- a/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/Fixture/skip.php.inc b/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/Fixture/skip.php.inc deleted file mode 100644 index a6255c8698c..00000000000 --- a/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/Fixture/skip.php.inc +++ /dev/null @@ -1,12 +0,0 @@ -boundary."\"\n\t"; - } -} diff --git a/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/SymplifyQuoteEscapeRectorTest.php b/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/SymplifyQuoteEscapeRectorTest.php deleted file mode 100644 index 257bc994b96..00000000000 --- a/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/SymplifyQuoteEscapeRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/config/configured_rule.php deleted file mode 100644 index 88734abfbcf..00000000000 --- a/rules-tests/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SymplifyQuoteEscapeRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/multiple_occurrences_in_one_string.php.inc b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/multiple_occurrences_in_one_string.php.inc new file mode 100644 index 00000000000..57f4b00619e --- /dev/null +++ b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/multiple_occurrences_in_one_string.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/mutiple_occurrences_in_one_string.php.inc b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/mutiple_occurrences_in_one_string.php.inc deleted file mode 100644 index 3c920faa36e..00000000000 --- a/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/mutiple_occurrences_in_one_string.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/skip_a_closure.php.inc b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/skip_a_closure.php.inc new file mode 100644 index 00000000000..1b6e9098839 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/skip_a_closure.php.inc @@ -0,0 +1,11 @@ + +CODE_SAMPLE; + } +} diff --git a/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Source/ConstantReferenceClass.php b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Source/ConstantReferenceClass.php new file mode 100644 index 00000000000..175d9bf68ef --- /dev/null +++ b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/Source/ConstantReferenceClass.php @@ -0,0 +1,10 @@ +doTestFileInfo($file); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/config/configured_rule.php index 7ffacc9aeab..bbb7f7ab83e 100644 --- a/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\String_\UseClassKeywordForClassNameResolutionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UseClassKeywordForClassNameResolutionRector::class); -}; +return RectorConfig::configure() + ->withRules([UseClassKeywordForClassNameResolutionRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/BinarySwitchToIfElseRectorTest.php b/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/BinarySwitchToIfElseRectorTest.php deleted file mode 100644 index eb8695de451..00000000000 --- a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/BinarySwitchToIfElseRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/deep_nested_break.php.inc b/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/deep_nested_break.php.inc deleted file mode 100644 index a63b989e995..00000000000 --- a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/deep_nested_break.php.inc +++ /dev/null @@ -1,46 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/extra_break.php.inc b/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/extra_break.php.inc deleted file mode 100644 index 986f85ca76c..00000000000 --- a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/extra_break.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/fixture.php.inc deleted file mode 100644 index 69e0c7d8e75..00000000000 --- a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,75 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/if_or.php.inc b/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/if_or.php.inc deleted file mode 100644 index dcde4893faf..00000000000 --- a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/if_or.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/in_class.php.inc b/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/in_class.php.inc deleted file mode 100644 index d8e6c15ab6a..00000000000 --- a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/in_class.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/nested_break.php.inc b/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/nested_break.php.inc deleted file mode 100644 index 5a4f8c9127f..00000000000 --- a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/Fixture/nested_break.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/config/configured_rule.php deleted file mode 100644 index 03a91b2ef9c..00000000000 --- a/rules-tests/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(BinarySwitchToIfElseRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector/TernaryConditionVariableAssignmentRectorTest.php b/rules-tests/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector/TernaryConditionVariableAssignmentRectorTest.php index 12e98b935b7..95381a6caa5 100644 --- a/rules-tests/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector/TernaryConditionVariableAssignmentRectorTest.php +++ b/rules-tests/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector/TernaryConditionVariableAssignmentRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\Ternary\TernaryConditionVariableAssignmentRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class TernaryConditionVariableAssignmentRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector/config/configured_rule.php index 302125c7071..23d2bb14492 100644 --- a/rules-tests/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\Ternary\TernaryConditionVariableAssignmentRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(TernaryConditionVariableAssignmentRector::class); -}; +return RectorConfig::configure() + ->withRules([TernaryConditionVariableAssignmentRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/fixture.php.inc deleted file mode 100644 index 58c7ff66581..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/handle_static_cal.php.inc b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/handle_static_cal.php.inc deleted file mode 100644 index 6695608dc60..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/handle_static_cal.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/interace_extending.php.inc b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/interace_extending.php.inc deleted file mode 100644 index 0e2c897ff7c..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/interace_extending.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/keep_nette_symfony_finder.php.inc b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/keep_nette_symfony_finder.php.inc deleted file mode 100644 index fe7f0a28d17..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/keep_nette_symfony_finder.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_class_name.php.inc b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_class_name.php.inc deleted file mode 100644 index 0e72c346160..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_class_name.php.inc +++ /dev/null @@ -1,9 +0,0 @@ -filesystem = $filesystem; - } - - public function fixComposerJson(string $composerJsonFile): void - { - $fileContent = NetteFileSystem::read($composerJsonFile); - $this->filesystem->dumpFile($composerJsonFile, '...'); - } -} diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_no_namespace_class_name.php.inc b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_no_namespace_class_name.php.inc deleted file mode 100644 index 57610242799..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_no_namespace_class_name.php.inc +++ /dev/null @@ -1,7 +0,0 @@ - 'bar', - ]; -} diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_vich_annotation.php.inc b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_vich_annotation.php.inc deleted file mode 100644 index 2577ccae0c1..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/skip_vich_annotation.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/used_union_type.php.inc b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/used_union_type.php.inc deleted file mode 100644 index c9309fc6956..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Fixture/used_union_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/RemoveUnusedAliasRectorTest.php b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/RemoveUnusedAliasRectorTest.php deleted file mode 100644 index 10cab2e24f7..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/RemoveUnusedAliasRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Source/AbstractInterface.php b/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Source/AbstractInterface.php deleted file mode 100644 index e68a52d1ea7..00000000000 --- a/rules-tests/CodingStyle/Rector/Use_/RemoveUnusedAliasRector/Source/AbstractInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(RemoveUnusedAliasRector::class); -}; diff --git a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Fixture/with_trait_use_adaptation.php.inc b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Fixture/with_trait_use_adaptation.php.inc new file mode 100644 index 00000000000..ee280d612e8 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Fixture/with_trait_use_adaptation.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Fixture/with_trait_use_adaptation_flipped.php.inc b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Fixture/with_trait_use_adaptation_flipped.php.inc new file mode 100644 index 00000000000..422b4b62172 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Fixture/with_trait_use_adaptation_flipped.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/SeparateMultiUseImportsRectorTest.php b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/SeparateMultiUseImportsRectorTest.php index 88a7292355a..f4058b6b8fa 100644 --- a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/SeparateMultiUseImportsRectorTest.php +++ b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/SeparateMultiUseImportsRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\CodingStyle\Rector\Use_\SeparateMultiUseImportsRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SeparateMultiUseImportsRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Source/FirstTrait.php b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Source/FirstTrait.php index 308d9eedc65..e207bdb656c 100644 --- a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Source/FirstTrait.php +++ b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Source/FirstTrait.php @@ -6,5 +6,8 @@ trait FirstTrait { - + public function exec(): string + { + return 'foo'; + } } diff --git a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Source/SecondTrait.php b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Source/SecondTrait.php index 1db9cf410b4..2d1f0990224 100644 --- a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Source/SecondTrait.php +++ b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/Source/SecondTrait.php @@ -6,5 +6,8 @@ trait SecondTrait { - + public function exec(): string + { + return 'bar'; + } } diff --git a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/config/configured_rule.php index 70bd9d8b0f8..09933c95987 100644 --- a/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector/config/configured_rule.php @@ -3,9 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\Use_\SeparateMultiUseImportsRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SeparateMultiUseImportsRector::class); -}; +return RectorConfig::configure() + ->withRules([SeparateMultiUseImportsRector::class]); diff --git a/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/AddPackageToRequireComposerRectorTest.php b/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/AddPackageToRequireComposerRectorTest.php deleted file mode 100644 index 57d68a53709..00000000000 --- a/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/AddPackageToRequireComposerRectorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -doTestFileInfo($fileInfo); - } - - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.json'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/Fixture/added_package.json b/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/Fixture/added_package.json deleted file mode 100644 index 15293682ac2..00000000000 --- a/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/Fixture/added_package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "require-dev": { - "vendor1/package1": "^1.0", - "vendor1/package2": "^2.0" - } -} ------ -{ - "require": { - "vendor1/package3": "^3.0" - }, - "require-dev": { - "vendor1/package1": "^1.0", - "vendor1/package2": "^2.0" - } -} diff --git a/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/config/some_config.php b/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/config/some_config.php deleted file mode 100644 index b608c06814b..00000000000 --- a/rules-tests/Composer/Rector/AddPackageToRequireComposerRector/config/some_config.php +++ /dev/null @@ -1,18 +0,0 @@ -services(); - $services->set(AddPackageToRequireComposerRector::class) - ->call('configure', [[ - AddPackageToRequireComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('vendor1/package3', '^3.0'), - ]), - ]]); -}; diff --git a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/AddPackageToRequireDevComposerRectorTest.php b/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/AddPackageToRequireDevComposerRectorTest.php deleted file mode 100644 index a8109a767b0..00000000000 --- a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/AddPackageToRequireDevComposerRectorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -doTestFileInfo($fileInfo); - } - - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.json'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/missing_package.json b/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/missing_package.json deleted file mode 100644 index b080093c605..00000000000 --- a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/missing_package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "require": { - "vendor1/package1": "^1.0", - "vendor1/package2": "^2.0" - } -} ------ -{ - "require": { - "vendor1/package1": "^1.0", - "vendor1/package2": "^2.0" - }, - "require-dev": { - "vendor1/package3": "^3.0" - } -} diff --git a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/skip_existing_package.json b/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/skip_existing_package.json deleted file mode 100644 index 21e98eae411..00000000000 --- a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/skip_existing_package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "require": { - "vendor1/package1": "^1.0" - }, - "require-dev": { - "vendor1/package2": "^2.0", - "vendor1/package3": "^3.0" - } -} diff --git a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/skip_existing_versions.json b/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/skip_existing_versions.json deleted file mode 100644 index 58309d00252..00000000000 --- a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/Fixture/skip_existing_versions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "require": { - "vendor1/package1": "^1.0", - "vendor1/package2": "^2.0" - }, - "require-dev": { - "vendor1/package3": "^3.0" - } -} diff --git a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/config/some_config.php b/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/config/some_config.php deleted file mode 100644 index 47f6505c864..00000000000 --- a/rules-tests/Composer/Rector/AddPackageToRequireDevComposerRector/config/some_config.php +++ /dev/null @@ -1,20 +0,0 @@ -services(); - $services->set(AddPackageToRequireDevComposerRector::class) - ->call('configure', [[ - AddPackageToRequireDevComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('vendor1/package3', '^3.0'), - new PackageAndVersion('vendor1/package1', '^3.0'), - new PackageAndVersion('vendor1/package2', '^3.0'), - ]), - ]]); -}; diff --git a/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/ChangePackageVersionComposerRectorTest.php b/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/ChangePackageVersionComposerRectorTest.php deleted file mode 100644 index 718b7b393fa..00000000000 --- a/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/ChangePackageVersionComposerRectorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -doTestFileInfo($fileInfo); - } - - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.json'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/Fixture/change_package.json b/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/Fixture/change_package.json deleted file mode 100644 index bded7fe0755..00000000000 --- a/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/Fixture/change_package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "require-dev": { - "vendor1/package1": "^1.0", - "vendor1/package3": "^2.0" - } -} ------ -{ - "require-dev": { - "vendor1/package1": "^1.0", - "vendor1/package3": "^15.0" - } -} diff --git a/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/config/some_config.php b/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/config/some_config.php deleted file mode 100644 index 926e9fa8838..00000000000 --- a/rules-tests/Composer/Rector/ChangePackageVersionComposerRector/config/some_config.php +++ /dev/null @@ -1,18 +0,0 @@ -services(); - $services->set(ChangePackageVersionComposerRector::class) - ->call('configure', [[ - ChangePackageVersionComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('vendor1/package3', '^15.0'), - ]), - ]]); -}; diff --git a/rules-tests/Composer/Rector/CombinationComposerRector/CombinationComposerRectorTest.php b/rules-tests/Composer/Rector/CombinationComposerRector/CombinationComposerRectorTest.php deleted file mode 100644 index de1f178df34..00000000000 --- a/rules-tests/Composer/Rector/CombinationComposerRector/CombinationComposerRectorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -doTestFileInfo($fileInfo); - } - - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.json'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/Composer/Rector/CombinationComposerRector/Fixture/basic_package.json b/rules-tests/Composer/Rector/CombinationComposerRector/Fixture/basic_package.json deleted file mode 100644 index ddb6e38680f..00000000000 --- a/rules-tests/Composer/Rector/CombinationComposerRector/Fixture/basic_package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "require": { - "vendor1/package1": "^1.0", - "vendor1/package2": "^2.0", - "vendor1/package3": "^3.0" - } -} ------ -{ - "require": { - "vendor1/package1": "^1.0", - "vendor1/package3": "~3.0.0", - "vendor2/package1": "^3.0" - } -} diff --git a/rules-tests/Composer/Rector/CombinationComposerRector/config/some_config.php b/rules-tests/Composer/Rector/CombinationComposerRector/config/some_config.php deleted file mode 100644 index 5268610ce71..00000000000 --- a/rules-tests/Composer/Rector/CombinationComposerRector/config/some_config.php +++ /dev/null @@ -1,27 +0,0 @@ -services(); - $services->set(ReplacePackageAndVersionComposerRector::class) - ->call('configure', [[ - ReplacePackageAndVersionComposerRector::REPLACE_PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new ReplacePackageAndVersion('vendor1/package2', 'vendor2/package1', '^3.0'), - ]), - ]]); - - $services->set(ChangePackageVersionComposerRector::class) - ->call('configure', [[ - ChangePackageVersionComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new PackageAndVersion('vendor1/package3', '~3.0.0'), - ]), - ]]); -}; diff --git a/rules-tests/Composer/Rector/RemovePackageComposerRector/Fixture/remove_package.json b/rules-tests/Composer/Rector/RemovePackageComposerRector/Fixture/remove_package.json deleted file mode 100644 index 92b6704907c..00000000000 --- a/rules-tests/Composer/Rector/RemovePackageComposerRector/Fixture/remove_package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "require": { - "vendor1/package1": "^1.0", - "vendor1/package2": "^2.0" - }, - "require-dev": { - "vendor1/package3": "^3.0" - } -} ------ -[] diff --git a/rules-tests/Composer/Rector/RemovePackageComposerRector/RemovePackageComposerRectorTest.php b/rules-tests/Composer/Rector/RemovePackageComposerRector/RemovePackageComposerRectorTest.php deleted file mode 100644 index 03230288cd1..00000000000 --- a/rules-tests/Composer/Rector/RemovePackageComposerRector/RemovePackageComposerRectorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -doTestFileInfo($fileInfo); - } - - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.json'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/Composer/Rector/RemovePackageComposerRector/config/some_config.php b/rules-tests/Composer/Rector/RemovePackageComposerRector/config/some_config.php deleted file mode 100644 index 4ed27860d15..00000000000 --- a/rules-tests/Composer/Rector/RemovePackageComposerRector/config/some_config.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(RemovePackageComposerRector::class) - ->call('configure', [[ - RemovePackageComposerRector::PACKAGE_NAMES => ['vendor1/package3', 'vendor1/package1', 'vendor1/package2'], - ]]); -}; diff --git a/rules-tests/Composer/Rector/RenamePackageComposerRector/Fixture/remove_package.json b/rules-tests/Composer/Rector/RenamePackageComposerRector/Fixture/remove_package.json deleted file mode 100644 index 331ddebaa94..00000000000 --- a/rules-tests/Composer/Rector/RenamePackageComposerRector/Fixture/remove_package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "require": { - "foo/bar": "dev-main" - }, - "require-dev": { - "foo/baz": "dev-main" - } -} ------ -{ - "require": { - "baz/bar": "dev-main" - }, - "require-dev": { - "baz/baz": "dev-main" - } -} diff --git a/rules-tests/Composer/Rector/RenamePackageComposerRector/RenamePackageComposerRectorTest.php b/rules-tests/Composer/Rector/RenamePackageComposerRector/RenamePackageComposerRectorTest.php deleted file mode 100644 index 70be1ca171e..00000000000 --- a/rules-tests/Composer/Rector/RenamePackageComposerRector/RenamePackageComposerRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.json'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/Composer/Rector/RenamePackageComposerRector/config/some_config.php b/rules-tests/Composer/Rector/RenamePackageComposerRector/config/some_config.php deleted file mode 100644 index 4eda7d1db4a..00000000000 --- a/rules-tests/Composer/Rector/RenamePackageComposerRector/config/some_config.php +++ /dev/null @@ -1,21 +0,0 @@ -services(); - $services->set(RenamePackageComposerRector::class) - ->call('configure', [ - [ - RenamePackageComposerRector::RENAME_PACKAGES => - ValueObjectInliner::inline( - [new RenamePackage('foo/bar', 'baz/bar'), new RenamePackage('foo/baz', 'baz/baz')] - ), - ], - ]); -}; diff --git a/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/Fixture/moved_package.json b/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/Fixture/moved_package.json deleted file mode 100644 index 0ecdaddbeb0..00000000000 --- a/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/Fixture/moved_package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "require": { - "vendor1/package1": "^1.0" - } -} ------ -{ - "require": { - "vendor1/package3": "^4.0" - } -} diff --git a/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/Fixture/skip_missing_package.json b/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/Fixture/skip_missing_package.json deleted file mode 100644 index 9115894dcc2..00000000000 --- a/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/Fixture/skip_missing_package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "require": { - "vendor1/package4": "^1.0" - } -} diff --git a/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/ReplacePackageAndVersionComposerRectorTest.php b/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/ReplacePackageAndVersionComposerRectorTest.php deleted file mode 100644 index f76c4ac706f..00000000000 --- a/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/ReplacePackageAndVersionComposerRectorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -doTestFileInfo($fileInfo); - } - - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*.json'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/config/some_config.php b/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/config/some_config.php deleted file mode 100644 index 0e1942656ba..00000000000 --- a/rules-tests/Composer/Rector/ReplacePackageAndVersionComposerRector/config/some_config.php +++ /dev/null @@ -1,18 +0,0 @@ -services(); - $services->set(ReplacePackageAndVersionComposerRector::class) - ->call('configure', [[ - ReplacePackageAndVersionComposerRector::REPLACE_PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([ - new ReplacePackageAndVersion('vendor1/package1', 'vendor1/package3', '^4.0'), - ]), - ]]); -}; diff --git a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/fixture.php.inc index 1c02b34eccb..c5920ad0c95 100644 --- a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/fixture.php.inc +++ b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/fixture.php.inc @@ -11,12 +11,6 @@ class Fixture 1 => 'B', 1 => 'A' ]; - - $key = 1; - $items = [ - $key => 'A', - $key => 'A', - ]; } } @@ -33,11 +27,6 @@ class Fixture $items = [ 1 => 'A' ]; - - $key = 1; - $items = [ - $key => 'A', - ]; } } diff --git a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_call_like_keys.php.inc b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_call_like_keys.php.inc new file mode 100644 index 00000000000..e15885fb601 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_call_like_keys.php.inc @@ -0,0 +1,16 @@ + 'A', + rand() => 'A', + ]; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_duplicate_pre_inc_decrement.php.inc b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_duplicate_pre_inc_decrement.php.inc new file mode 100644 index 00000000000..e68be939015 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_duplicate_pre_inc_decrement.php.inc @@ -0,0 +1,23 @@ + 'Max', + ++$index => 'Max Ϙ', + ++$index => 'Last', + ]; + + [ + $index => 'Max', + --$index => 'Max Ϙ', + --$index => 'Last', + ]; + } +} diff --git a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_property_fetch.php.inc b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_property_fetch.php.inc new file mode 100644 index 00000000000..1cf3a246e2f --- /dev/null +++ b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_property_fetch.php.inc @@ -0,0 +1,21 @@ +foo => 'A', + $this->foo => 'A', + ]; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_static_property_fetch.php.inc b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_static_property_fetch.php.inc new file mode 100644 index 00000000000..624b24d053c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/skip_static_property_fetch.php.inc @@ -0,0 +1,25 @@ + $this->run(), + self::$foo => $this->run(), + ]; + } + + private function run() + { + static::$foo = rand(); + + return 'A'; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/variable_key.php.inc b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/variable_key.php.inc new file mode 100644 index 00000000000..84ba7d9ba0b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/Fixture/variable_key.php.inc @@ -0,0 +1,36 @@ + 'A', + $key => 'A', + ]; + } +} + +?> +----- + 'A', + ]; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/RemoveDuplicatedArrayKeyRectorTest.php b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/RemoveDuplicatedArrayKeyRectorTest.php index 19f2a540fcc..7fb7e601014 100644 --- a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/RemoveDuplicatedArrayKeyRectorTest.php +++ b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/RemoveDuplicatedArrayKeyRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Array_\RemoveDuplicatedArrayKeyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDuplicatedArrayKeyRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/config/configured_rule.php index 3ef5ad5eb49..7b77060fc80 100644 --- a/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Array_\RemoveDuplicatedArrayKeyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDuplicatedArrayKeyRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDuplicatedArrayKeyRector::class]); diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/fixture.php.inc deleted file mode 100644 index 7cbbec908a1..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -getOne(); - } - - private function getOne(): void - { - } -} - -?> ------ -getOne(); - } - - private function getOne(): void - { - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_assign_to_property_fetch.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_assign_to_property_fetch.php.inc deleted file mode 100644 index 623a3b2a44b..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_assign_to_property_fetch.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -value = $this->getOne(); - } - - private function getOne(): void - { - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_returned_value.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_returned_value.php.inc deleted file mode 100644 index 6024509361c..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_returned_value.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -getOne(); - } - - private function getOne(): int - { - return 1; - } -} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_variable_used.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_variable_used.php.inc deleted file mode 100644 index 9146ded2ccd..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/skip_variable_used.php.inc +++ /dev/null @@ -1,52 +0,0 @@ -getOne(); - } else { - $value = 1; - } - - return $value; - } - - public function run2() - { - if (rand(0, 1)) { - $this->value = $this->getOne(); - } else { - $this->value = 1; - } - - return $this->value; - } - - public function run3($value = 1) - { - if (rand(0, 1)) { - $value = $this->getOne(); - } - - return compact('value'); - } - - public function run4($value = 1) - { - if (rand(0, 1)) { - $value = $this->getOne(); - } - - include 'anotherFile.php'; - } - - private function getOne(): void - { - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/some_static_call.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/some_static_call.php.inc deleted file mode 100644 index 43c10d79ca5..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/Fixture/some_static_call.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/RemoveAssignOfVoidReturnFunctionRectorTest.php b/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/RemoveAssignOfVoidReturnFunctionRectorTest.php deleted file mode 100644 index 0816937da7c..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/RemoveAssignOfVoidReturnFunctionRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/config/configured_rule.php deleted file mode 100644 index ce27f2212f3..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveAssignOfVoidReturnFunctionRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/compare_interpolated_string.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/compare_interpolated_string.php.inc new file mode 100644 index 00000000000..135894f06a2 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/compare_interpolated_string.php.inc @@ -0,0 +1,22 @@ +{$match}"; + }, + (string)$markdown + ); + + } +} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/next_reassign_with_call_on_variable.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/next_reassign_with_call_on_variable.php.inc new file mode 100644 index 00000000000..e91abff584c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/next_reassign_with_call_on_variable.php.inc @@ -0,0 +1,42 @@ +getItems(); + } + + public function getItems() + { + return sort($this->items); + } +} + +?> +----- +getItems(); + } + + public function getItems() + { + return sort($this->items); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/skip_next_coalesce_assign.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/skip_next_coalesce_assign.php.inc new file mode 100644 index 00000000000..fd8df7d30e3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/skip_next_coalesce_assign.php.inc @@ -0,0 +1,15 @@ +items = $input; + + $this->items = $this->getItems(); + } + + public function getItems() + { + return sort($this->items); + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/skip_next_ternary_assign.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/skip_next_ternary_assign.php.inc new file mode 100644 index 00000000000..1c40c1783e0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/Fixture/skip_next_ternary_assign.php.inc @@ -0,0 +1,17 @@ + diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/RemoveDoubleAssignRectorTest.php b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/RemoveDoubleAssignRectorTest.php index e867af8ed75..282379cce0d 100644 --- a/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/RemoveDoubleAssignRectorTest.php +++ b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/RemoveDoubleAssignRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Assign\RemoveDoubleAssignRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDoubleAssignRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/config/configured_rule.php index 18bc48f3eeb..743fa95ee70 100644 --- a/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Assign/RemoveDoubleAssignRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDoubleAssignRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDoubleAssignRector::class]); diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_concat.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_concat.php.inc deleted file mode 100644 index 2d4c3971f33..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_concat.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_double.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_double.php.inc deleted file mode 100644 index 40dd0f74e0c..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_double.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - 'one', 'two']; - - $days = ['something_else']; - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_func_call_internal.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_func_call_internal.php.inc deleted file mode 100644 index 55a508fb17b..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_func_call_internal.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_multi_first_to_go.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_multi_first_to_go.php.inc deleted file mode 100644 index 0cb3ae728b7..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_multi_first_to_go.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_multi_same_line.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_multi_same_line.php.inc deleted file mode 100644 index fc5838f52b6..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_multi_same_line.php.inc +++ /dev/null @@ -1,49 +0,0 @@ -createMe(); - $me = $this->createMe(); - $me = $this->createMe(); - - return $me; - } - - public function createMe() - { - return new self(); - } -} - -?> ------ -createMe(); - $this->createMe(); - $me = $this->createMe(); - - return $me; - } - - public function createMe() - { - return new self(); - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_new_instance.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_new_instance.php.inc deleted file mode 100644 index 2ba0c1b9467..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_new_instance.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_property.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_property.php.inc deleted file mode 100644 index c339294d744..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_property.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -days; - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_scalar.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_scalar.php.inc deleted file mode 100644 index 58428a03bba..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_scalar.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - 'one', 'two']; - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_session.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_session.php.inc deleted file mode 100644 index 6047615c5da..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_session.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_string_long.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_string_long.php.inc deleted file mode 100644 index 284293512fa..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/assign_string_long.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - -EOF; - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/fixture.php.inc deleted file mode 100644 index 3caf26a75c7..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -process(); - } - - public function process() - { - // something going on - return 5; - } -} - -?> ------ -process(); - } - - public function process() - { - // something going on - return 5; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_assign_embed_html.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_assign_embed_html.php.inc deleted file mode 100644 index c1872927d85..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_assign_embed_html.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -get($token))) { - pg_query_params('SELECT ... =$1', [ - $id, - ]); - } - } -} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_in_compact.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_in_compact.php.inc deleted file mode 100644 index 4d61df02076..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_in_compact.php.inc +++ /dev/null @@ -1,12 +0,0 @@ -days = [1 => 'one', 'two']; - } - - public function useMe() - { - return $this->days; - } -} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_use_and.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_use_and.php.inc deleted file mode 100644 index 97fef8f73cc..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_use_and.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -getImageFormat()) && $format !== false) { - return 'png'; - } - - return 'non_png'; - } -} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_used_after_if_else.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_used_after_if_else.php.inc deleted file mode 100644 index 10ee74ea4bc..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_used_after_if_else.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_var_override_and_re_use.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_var_override_and_re_use.php.inc deleted file mode 100644 index 794ed2dcc4e..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/Fixture/skip_var_override_and_re_use.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/config/configured_rule.php deleted file mode 100644 index f0681cd2e2a..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveUnusedAssignVariableRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/assign_expr_has_call_like.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/assign_expr_has_call_like.php.inc new file mode 100644 index 00000000000..823597c8586 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/assign_expr_has_call_like.php.inc @@ -0,0 +1,33 @@ +validate($params['test']) : null; + } +} +?> +----- +validate($params['test']) : null; + } +} +?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/clean_cast_assign_expr_call_like.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/clean_cast_assign_expr_call_like.php.inc new file mode 100644 index 00000000000..e98707f9ab3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/clean_cast_assign_expr_call_like.php.inc @@ -0,0 +1,31 @@ +api_post("whatever", []); + + return 5; + } +} + +?> +----- +api_post("whatever", []); + + return 5; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/create_mock_in_test.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/create_mock_in_test.php.inc new file mode 100644 index 00000000000..f0580db30c3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/create_mock_in_test.php.inc @@ -0,0 +1,30 @@ +createMock('SomeClass'); + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/function.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/function.php.inc new file mode 100644 index 00000000000..7fd57c7d69b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/function.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/mirror_comment_assign_method_call.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/mirror_comment_assign_method_call.php.inc new file mode 100644 index 00000000000..8b9290dc5a3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/mirror_comment_assign_method_call.php.inc @@ -0,0 +1,29 @@ +query(); + } +} + +?> +----- +query(); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/on_new_instance.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/on_new_instance.php.inc deleted file mode 100644 index 790f8d2a5b2..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/on_new_instance.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/shadowed_local_variable.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/shadowed_local_variable.php.inc index 65ca1cae9b3..3b181fe187c 100644 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/shadowed_local_variable.php.inc +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/shadowed_local_variable.php.inc @@ -2,7 +2,8 @@ namespace Rector\Tests\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector\Fixture; -class ShadowedLocalVariable { +final class SkipShadowedLocalVariable +{ public function run(array $params) { $toDate = null; @@ -17,24 +18,3 @@ class ShadowedLocalVariable { } } -?> ------ -setTimestamp(strtotime('now - 3 month')); - } - - return $toDate; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_assign_closure_bind_reference.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_assign_closure_bind_reference.php.inc new file mode 100644 index 00000000000..9d872d078f5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_assign_closure_bind_reference.php.inc @@ -0,0 +1,12 @@ + $container->locked, null, $container)($container); + $containerLocked = false; + } +} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_assign_no_discard_method_call.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_assign_no_discard_method_call.php.inc new file mode 100644 index 00000000000..754972eff93 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_assign_no_discard_method_call.php.inc @@ -0,0 +1,17 @@ +some_call(); + } + + #[\NoDiscard] + private function some_call() + { + + } +} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_dynamic_variables.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_dynamic_variables.php.inc new file mode 100644 index 00000000000..99332345401 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_dynamic_variables.php.inc @@ -0,0 +1,14 @@ + $mapped) + { + ${$key . "_key"} = $mapped; + } + } +} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_file_get_contents.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_file_get_contents.php.inc new file mode 100644 index 00000000000..112fe37a05a --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_file_get_contents.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_in_get_defined_vars.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_in_get_defined_vars.php.inc new file mode 100644 index 00000000000..fb0a75267f8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_in_get_defined_vars.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_next_include_another_file.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_next_include_another_file.php.inc index de7fc1f337e..b65cc5a0810 100644 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_next_include_another_file.php.inc +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_next_include_another_file.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector\Fixture; -class SkipNextIncludeAnotherFile +final class SkipNextIncludeAnotherFile { public function run() { @@ -11,17 +11,5 @@ class SkipNextIncludeAnotherFile if (rand(0, 1)) { include 'test.php'; } - - if (rand(0, 1)) { - include_once 'test.php'; - } - - if (rand(0, 1)) { - require 'test.php'; - } - - if (rand(0, 1)) { - require_once 'test.php'; - } } -} \ No newline at end of file +} diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_object_with_destruct_method.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_object_with_destruct_method.php.inc new file mode 100644 index 00000000000..64b52e44c59 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_object_with_destruct_method.php.inc @@ -0,0 +1,20 @@ +createLock(); + + echo 'foobar'; + } + + private function createLock(): SomeLock + { + return new SomeLock(); + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_remove_variable_on_next_return_after_break_in_case.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_remove_variable_on_next_return_after_break_in_case.php.inc new file mode 100644 index 00000000000..a96b103216b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_remove_variable_on_next_return_after_break_in_case.php.inc @@ -0,0 +1,19 @@ + diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_from_property_fetch_reference.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_from_property_fetch_reference.php.inc new file mode 100644 index 00000000000..0e540013678 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_from_property_fetch_reference.php.inc @@ -0,0 +1,13 @@ +{$key}; + } + $current = $value; +} + +?> diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_function.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_function.php.inc new file mode 100644 index 00000000000..7585f80ba80 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_function.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_in_dynamic_variable.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_in_dynamic_variable.php.inc new file mode 100644 index 00000000000..5309fc7d76e --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_used_in_dynamic_variable.php.inc @@ -0,0 +1,28 @@ + $johnSmith, + ]; + }; + + $johnSmith = [ + 'id' => 123, + 'name' => 'John Smith', + 'recentArticle' => $article('1'), + ]; + + return $article($id); + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_variable_name.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_variable_name.php.inc new file mode 100644 index 00000000000..b2e3418334d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/skip_variable_name.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/variable_variable_not_has_next_variable.php.inc b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/variable_variable_not_has_next_variable.php.inc deleted file mode 100644 index ab8ccd322b3..00000000000 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Fixture/variable_variable_not_has_next_variable.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/RemoveUnusedVariableAssignRectorTest.php b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/RemoveUnusedVariableAssignRectorTest.php index 2e64476a44c..6d63382731e 100644 --- a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/RemoveUnusedVariableAssignRectorTest.php +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/RemoveUnusedVariableAssignRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedVariableAssignRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Source/SomeLock.php b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Source/SomeLock.php new file mode 100644 index 00000000000..4b80744d84d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector/Source/SomeLock.php @@ -0,0 +1,13 @@ +services(); - $services->set(RemoveUnusedVariableAssignRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnusedVariableAssignRector::class]); diff --git a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/fixture.php.inc deleted file mode 100644 index a7c2479f38a..00000000000 --- a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/jump_around_one.php.inc b/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/jump_around_one.php.inc deleted file mode 100644 index feb280dc2bc..00000000000 --- a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/jump_around_one.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/nested.php.inc b/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/nested.php.inc deleted file mode 100644 index 9e2125fd793..00000000000 --- a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/nested.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/skip_multiple_variables.php.inc b/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/skip_multiple_variables.php.inc deleted file mode 100644 index c5059ab9573..00000000000 --- a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/Fixture/skip_multiple_variables.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/config/configured_rule.php deleted file mode 100644 index 914f4e7b5c7..00000000000 --- a/rules-tests/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveDuplicatedInstanceOfRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..0ebbbc5e1ef --- /dev/null +++ b/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/fixture.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/no_stmts.php.inc b/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/no_stmts.php.inc new file mode 100644 index 00000000000..de2d23354ad --- /dev/null +++ b/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/no_stmts.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/skip_normal_statements.php.inc b/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/skip_normal_statements.php.inc new file mode 100644 index 00000000000..c055d38056b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/Fixture/skip_normal_statements.php.inc @@ -0,0 +1,10 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/config/configured_rule.php new file mode 100644 index 00000000000..84615393477 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReplaceBlockToItsStmtsRector::class]); diff --git a/rules-tests/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector/RemoveAndTrueRectorTest.php b/rules-tests/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector/RemoveAndTrueRectorTest.php index f2033af14ce..d0ae5e97629 100644 --- a/rules-tests/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector/RemoveAndTrueRectorTest.php +++ b/rules-tests/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector/RemoveAndTrueRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\BooleanAnd\RemoveAndTrueRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveAndTrueRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector/config/configured_rule.php index c256b94c7a1..81172a20f04 100644 --- a/rules-tests/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\BooleanAnd\RemoveAndTrueRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveAndTrueRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveAndTrueRector::class]); diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/fixture.php.inc index 1d72a80a08e..bf21b3bb489 100644 --- a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/fixture.php.inc +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/fixture.php.inc @@ -1,23 +1,39 @@ ----- diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/fixture2.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/fixture2.php.inc index aad418cf537..5018c9cfb2b 100644 --- a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/fixture2.php.inc +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/fixture2.php.inc @@ -1,23 +1,39 @@ ----- diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/method_call_return_string_type.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/method_call_return_string_type.php.inc new file mode 100644 index 00000000000..501d60bdf58 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/method_call_return_string_type.php.inc @@ -0,0 +1,37 @@ +getResult(); + } + + private function getResult(): string + { + return 'test'; + } +} + +?> +----- +getResult(); + } + + private function getResult(): string + { + return 'test'; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/method_call_return_string_type_external.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/method_call_return_string_type_external.php.inc new file mode 100644 index 00000000000..3d580e5d77c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/method_call_return_string_type_external.php.inc @@ -0,0 +1,33 @@ +getResult(); + } +} + +?> +----- +getResult(); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_method_call_return_docblock_string.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_method_call_return_docblock_string.php.inc new file mode 100644 index 00000000000..65e22dc0d92 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_method_call_return_docblock_string.php.inc @@ -0,0 +1,19 @@ +getResult(); + } + + /** + * @return string + */ + private function getResult() + { + return 1; + } +} diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_non_typed_param.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_non_typed_param.php.inc index 25ca2c5158d..6f5f9f1b1d2 100644 --- a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_non_typed_param.php.inc +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_non_typed_param.php.inc @@ -1,6 +1,6 @@ property; } } - -?> diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_non_typed_property.php2.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_non_typed_property.php2.php.inc index e315215b369..0f284c8c902 100644 --- a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_non_typed_property.php2.php.inc +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_non_typed_property.php2.php.inc @@ -1,19 +1,13 @@ property; + $value = (int) $externalNonStrictTypedProperty->property; } } - -?> diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_phpdoc.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_phpdoc.php.inc new file mode 100644 index 00000000000..c4b68353e59 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_phpdoc.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_union_falsy_mixed_on_trait.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_union_falsy_mixed_on_trait.php.inc new file mode 100644 index 00000000000..adfb471f776 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_union_falsy_mixed_on_trait.php.inc @@ -0,0 +1,9 @@ +config['allow_404'] ?? false); + } +} diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_with_set_get_magic.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_with_set_get_magic.php.inc new file mode 100644 index 00000000000..f4a80b43d65 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/skip_with_set_get_magic.php.inc @@ -0,0 +1,31 @@ +data[$name])) { + return $this->data[$name]; + } + } + + public function __set($name, $value) + { + $this->data[$name] = (object) $value; + } +} + +class SkipWithSetGetMagic +{ + public function run(AConfig $config) + { + $config->property = []; + var_dump([] === (array) $config->property); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/some_typed_property.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/some_typed_property.php.inc new file mode 100644 index 00000000000..bdbf40e960c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/some_typed_property.php.inc @@ -0,0 +1,31 @@ +property; + } +} + +?> +----- +property; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/typed_param.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/typed_param.php.inc index c1fe523e883..44d05e47983 100644 --- a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/typed_param.php.inc +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/typed_param.php.inc @@ -1,8 +1,8 @@ property; - } -} - -?> ------ -property; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/with_substr.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/with_substr.php.inc new file mode 100644 index 00000000000..4e28363cc08 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Fixture/with_substr.php.inc @@ -0,0 +1,29 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/FixturePhp74/skip_substr.php.inc b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/FixturePhp74/skip_substr.php.inc new file mode 100644 index 00000000000..31aa57c0bc7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/FixturePhp74/skip_substr.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php74.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/RecastingRemovalRectorTest.php b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/RecastingRemovalRectorTest.php index 8887bab6a05..fb0292d9e25 100644 --- a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/RecastingRemovalRectorTest.php +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/RecastingRemovalRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Cast\RecastingRemovalRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RecastingRemovalRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Source/ExternalNonStrictTypedProperty.php b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Source/ExternalNonStrictTypedProperty.php new file mode 100644 index 00000000000..c20cf30d688 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/Source/ExternalNonStrictTypedProperty.php @@ -0,0 +1,11 @@ +services(); - $services->set(RecastingRemovalRector::class); -}; +return RectorConfig::configure() + ->withRules([RecastingRemovalRector::class]) + ->withPhpVersion(PhpVersion::PHP_80); diff --git a/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/config/configured_rule_php74.php b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/config/configured_rule_php74.php new file mode 100644 index 00000000000..c86fc9d7fe5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Cast/RecastingRemovalRector/config/configured_rule_php74.php @@ -0,0 +1,11 @@ +withRules([RecastingRemovalRector::class]) + ->withPhpVersion(PhpVersion::PHP_74); diff --git a/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/Fixture/skip_called_with_classname.php.inc b/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/Fixture/skip_called_with_classname.php.inc new file mode 100644 index 00000000000..24940d484af --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/Fixture/skip_called_with_classname.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/Fixture/skip_class_constant_used_in_enum.php.inc b/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/Fixture/skip_class_constant_used_in_enum.php.inc new file mode 100644 index 00000000000..48d3fa671eb --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/Fixture/skip_class_constant_used_in_enum.php.inc @@ -0,0 +1,19 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/config/configured_rule.php index 7f2a11a57d6..34a753d44f3 100644 --- a/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassConst\RemoveUnusedPrivateClassConstantRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnusedPrivateClassConstantRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnusedPrivateClassConstantRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassLike/RemoveAnnotationRector/RemoveAnnotationRectorTest.php b/rules-tests/DeadCode/Rector/ClassLike/RemoveAnnotationRector/RemoveAnnotationRectorTest.php index 6731a7e54b4..3eb8efe4831 100644 --- a/rules-tests/DeadCode/Rector/ClassLike/RemoveAnnotationRector/RemoveAnnotationRectorTest.php +++ b/rules-tests/DeadCode/Rector/ClassLike/RemoveAnnotationRector/RemoveAnnotationRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ClassLike\RemoveAnnotationRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveAnnotationRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassLike/RemoveAnnotationRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassLike/RemoveAnnotationRector/config/configured_rule.php index 2c58220db0f..6ac5e717858 100644 --- a/rules-tests/DeadCode/Rector/ClassLike/RemoveAnnotationRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/ClassLike/RemoveAnnotationRector/config/configured_rule.php @@ -2,16 +2,10 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassLike\RemoveAnnotationRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveAnnotationRector::class) - ->call('configure', [[ - RemoveAnnotationRector::ANNOTATIONS_TO_REMOVE => [ - 'method', - 'JMS\DiExtraBundle\Annotation\InjectParams', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RemoveAnnotationRector::class, ['method', 'JMS\DiExtraBundle\Annotation\InjectParams']); }; diff --git a/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..c483de715a1 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/fixture.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/skip_array_doc.php.inc b/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/skip_array_doc.php.inc new file mode 100644 index 00000000000..da57dca6cc5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/skip_array_doc.php.inc @@ -0,0 +1,29 @@ +structure = ['test']; + } else { + $this->structure = $this->some_mixed_function(); + } + } + + private function some_mixed_function(): mixed + { + return null; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/skip_missing_property_type.php.inc b/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/skip_missing_property_type.php.inc new file mode 100644 index 00000000000..86de7e0178b --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Fixture/skip_missing_property_type.php.inc @@ -0,0 +1,15 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Source/RealType.php b/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Source/RealType.php new file mode 100644 index 00000000000..b9a59add843 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector/Source/RealType.php @@ -0,0 +1,8 @@ +rule(RemoveTypedPropertyNonMockDocblockRector::class); +}; diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..f2c87e96ce4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/multiple_default.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/multiple_default.php.inc new file mode 100644 index 00000000000..9b1d4dff4fc --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/multiple_default.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/skip_different_value.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/skip_different_value.php.inc new file mode 100644 index 00000000000..7ed91fa0333 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Fixture/skip_different_value.php.inc @@ -0,0 +1,15 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Source/SomeParentClass.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Source/SomeParentClass.php new file mode 100644 index 00000000000..c4068044768 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector/Source/SomeParentClass.php @@ -0,0 +1,12 @@ +withRules([RemoveArgumentFromDefaultParentCallRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/Fixture/fixture.php.inc deleted file mode 100644 index 7b51e1f2cff..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,22 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/Fixture/skip.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/Fixture/skip.php.inc deleted file mode 100644 index fdc708f6652..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/Fixture/skip.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/property_promotion.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/RemoveDeadConstructorRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/RemoveDeadConstructorRectorTest.php deleted file mode 100644 index 046dffcd8e7..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/RemoveDeadConstructorRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/config/configured_rule.php deleted file mode 100644 index 7f582f019b8..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveDeadConstructorRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/config/property_promotion.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/config/property_promotion.php deleted file mode 100644 index 7c1a550acd4..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector/config/property_promotion.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::PROPERTY_PROMOTION); - - $services = $containerConfigurator->services(); - $services->set(RemoveDeadConstructorRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/default_param.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/default_param.php.inc deleted file mode 100644 index ca3603983c1..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/default_param.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/fixture.php.inc deleted file mode 100644 index a72a51deed4..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/identical_defaults.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/identical_defaults.php.inc deleted file mode 100644 index 6fd79bd0c83..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/identical_defaults.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/skip_access_override.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/skip_access_override.php.inc deleted file mode 100644 index 4166805435f..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Fixture/skip_access_override.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Source/ChildOfToBeImplementedInterface.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Source/ChildOfToBeImplementedInterface.php deleted file mode 100644 index 51a3d0311b3..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector/Source/ChildOfToBeImplementedInterface.php +++ /dev/null @@ -1,9 +0,0 @@ -services(); - $services->set(RemoveDelegatingParentCallRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/first_fixture.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/first_fixture.php.inc new file mode 100644 index 00000000000..60c5142644b --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/first_fixture.php.inc @@ -0,0 +1,22 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/keep_deprecated.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/keep_deprecated.php.inc new file mode 100644 index 00000000000..24ce07ac99d --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/keep_deprecated.php.inc @@ -0,0 +1,13 @@ +gaz(); + } + + private function gaz() { + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/non_constructor_attribute.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/non_constructor_attribute.php.inc new file mode 100644 index 00000000000..a49416fede9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/non_constructor_attribute.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/removed_empty_construct_when_no_parent_construct.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/removed_empty_construct_when_no_parent_construct.php.inc new file mode 100644 index 00000000000..f7f875fca50 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/removed_empty_construct_when_no_parent_construct.php.inc @@ -0,0 +1,26 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/skip.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/skip.php.inc new file mode 100644 index 00000000000..899f42465fe --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Fixture/skip.php.inc @@ -0,0 +1,19 @@ +call([$this, 'doFoo']); + } + + public function doFoo() { + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/FixturePhp80/skip_property_promotion.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/FixturePhp80/skip_property_promotion.php.inc deleted file mode 100644 index b673e530fd3..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/FixturePhp80/skip_property_promotion.php.inc +++ /dev/null @@ -1,10 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/RemoveEmptyClassMethodRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/RemoveEmptyClassMethodRectorTest.php index 9084b02b801..1abfbbfe633 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/RemoveEmptyClassMethodRectorTest.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/RemoveEmptyClassMethodRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveEmptyClassMethodRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveEmptyClassMethodRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Source/MyDIContainer.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Source/MyDIContainer.php new file mode 100644 index 00000000000..5775232d1b2 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector/Source/MyDIContainer.php @@ -0,0 +1,12 @@ +services(); - $services->set(RemoveEmptyClassMethodRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveEmptyClassMethodRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/Fixture/skip_loop.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/Fixture/skip_loop.php.inc deleted file mode 100644 index 73f2ea75dec..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/Fixture/skip_loop.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -process(); - return; - } - } - } - - private function process() - { - } -} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/Fixture/skip_not_last_return.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/Fixture/skip_not_last_return.php.inc deleted file mode 100644 index efa7c6d04fa..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/Fixture/skip_not_last_return.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/RemoveLastReturnRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/RemoveLastReturnRectorTest.php deleted file mode 100644 index aa617903581..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/RemoveLastReturnRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/config/configured_rule.php deleted file mode 100644 index 78d74d585b7..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveLastReturnRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveLastReturnRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_param.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_param.php.inc new file mode 100644 index 00000000000..ca9d474d56a --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_param.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_return.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_return.php.inc new file mode 100644 index 00000000000..5279eb21411 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_return.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_var.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_var.php.inc new file mode 100644 index 00000000000..f681f440623 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_var.php.inc @@ -0,0 +1,24 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_var2.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_var2.php.inc new file mode 100644 index 00000000000..b92bfa15a05 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/remove_null_var2.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/skip_different_tag_value.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/skip_different_tag_value.php.inc new file mode 100644 index 00000000000..1c3e49073c8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/Fixture/skip_different_tag_value.php.inc @@ -0,0 +1,14 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/config/configured_rule.php new file mode 100644 index 00000000000..f4a7668ccf2 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveNullTagValueNodeRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/skip_attribute_decorated_args.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/skip_attribute_decorated_args.php.inc new file mode 100644 index 00000000000..4e192c6932c --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/skip_attribute_decorated_args.php.inc @@ -0,0 +1,14 @@ +d->format('Y-m-d'); + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/some_class.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..e12790aaadc --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/some_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/with_equal_default_param_value.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/with_equal_default_param_value.php.inc new file mode 100644 index 00000000000..f904501bfab --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Fixture/with_equal_default_param_value.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/RemoveParentDelegatingConstructorRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/RemoveParentDelegatingConstructorRectorTest.php new file mode 100644 index 00000000000..e65a7af8078 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/RemoveParentDelegatingConstructorRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Source/SomeParentWithPrivatePropertyPromotion.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Source/SomeParentWithPrivatePropertyPromotion.php new file mode 100644 index 00000000000..731284c25ee --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector/Source/SomeParentWithPrivatePropertyPromotion.php @@ -0,0 +1,12 @@ +withRules([RemoveParentDelegatingConstructorRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/has_first_class_callable.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/has_first_class_callable.php.inc new file mode 100644 index 00000000000..99178f2bdfd --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/has_first_class_callable.php.inc @@ -0,0 +1,43 @@ +hey = $hey; + + $this->execute(...); + } + + private function execute() + { + } +} + +?> +----- +hey = $hey; + + $this->execute(...); + } + + private function execute() + { + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/mix_property_promotion_with_normal_param.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/mix_property_promotion_with_normal_param.php.inc new file mode 100644 index 00000000000..a952d94bbed --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/mix_property_promotion_with_normal_param.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/multiple_unused_params.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/multiple_unused_params.php.inc new file mode 100644 index 00000000000..26c91f4a054 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/multiple_unused_params.php.inc @@ -0,0 +1,31 @@ +hey = $hey; + } +} + +?> +----- +hey = $hey; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_attribute_marker.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_attribute_marker.php.inc new file mode 100644 index 00000000000..37573cbe5cd --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_attribute_marker.php.inc @@ -0,0 +1,16 @@ +name = $name; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_normal_param_between_two_property_promotion.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_normal_param_between_two_property_promotion.php.inc new file mode 100644 index 00000000000..10c378e8a1a --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_normal_param_between_two_property_promotion.php.inc @@ -0,0 +1,12 @@ +demofile->getName(); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_by_exec_first_class_callable.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_by_exec_first_class_callable.php.inc new file mode 100644 index 00000000000..1d90a1c5bb5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_by_exec_first_class_callable.php.inc @@ -0,0 +1,20 @@ +hey = $hey; + + $this->execute(...)($man); + } + + private function execute($man) + { + echo $man; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_by_get_func_args.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_by_get_func_args.php.inc new file mode 100644 index 00000000000..2bb7cdbeecd --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_by_get_func_args.php.inc @@ -0,0 +1,13 @@ +data = func_get_args(); + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_in_closure_use.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_in_closure_use.php.inc new file mode 100644 index 00000000000..2108a490e7a --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/skip_used_in_closure_use.php.inc @@ -0,0 +1,15 @@ +data = compact('hey', 'man'); + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/used_in_inner_class.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/used_in_inner_class.php.inc new file mode 100644 index 00000000000..bca8a5f460e --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Fixture/used_in_inner_class.php.inc @@ -0,0 +1,44 @@ +hey = $hey; + + new class { + public function __construct($man) + { + + } + }; + } +} + +?> +----- +hey = $hey; + + new class { + public function __construct() + { + } + }; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/FixturePhp80/skip_property_promotion.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/FixturePhp80/skip_property_promotion.php.inc deleted file mode 100644 index 5313c9b9e89..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/FixturePhp80/skip_property_promotion.php.inc +++ /dev/null @@ -1,22 +0,0 @@ -demofile->getName(); - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Php80Test.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Php80Test.php deleted file mode 100644 index dc017e5270f..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Php80Test.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/RemoveUnusedConstructorParamRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/RemoveUnusedConstructorParamRectorTest.php index 9eb1969c5ad..b8a307d289b 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/RemoveUnusedConstructorParamRectorTest.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/RemoveUnusedConstructorParamRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveUnusedConstructorParamRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedConstructorParamRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Source/InterfaceWithConstruct.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Source/InterfaceWithConstruct.php new file mode 100644 index 00000000000..05298ebadf3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector/Source/InterfaceWithConstruct.php @@ -0,0 +1,8 @@ +services(); - $services->set(RemoveUnusedConstructorParamRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnusedConstructorParamRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/in_between_parameter.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/in_between_parameter.php.inc index bfb81431b40..05fbd34e106 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/in_between_parameter.php.inc +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/in_between_parameter.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodParameterRector\Fixture; -final class SkipInBetweenParameter +final class InBetweenParameter { private $value; private $value3; @@ -12,4 +12,34 @@ final class SkipInBetweenParameter $this->value = $value; $this->value3 = $value3; } + + public function execute() + { + $this->run('a', 'b', 'c'); + } } + +?> +----- +value = $value; + $this->value3 = $value3; + } + + public function execute() + { + $this->run('a', 'c'); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/named_argument_same_position.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/named_argument_same_position.php.inc new file mode 100644 index 00000000000..10d87afed22 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/named_argument_same_position.php.inc @@ -0,0 +1,37 @@ +isTrue(text: 'test', value: $param); + } + + private function isTrue(string $text, $value = true): bool + { + return $text === 'test'; + } +} + +?> +---- +isTrue(text: 'test'); + } + + private function isTrue(string $text): bool + { + return $text === 'test'; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/remove_last.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/remove_last.php.inc index 4f7392696ce..3a853bf307a 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/remove_last.php.inc +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/remove_last.php.inc @@ -1,5 +1,7 @@ +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_anonymous_class_constructor.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_anonymous_class_constructor.php.inc new file mode 100644 index 00000000000..1cf32cac233 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_anonymous_class_constructor.php.inc @@ -0,0 +1,24 @@ +run(5); + $this->run(); + } + + private function run($value = 100) + { + $class = new class($value) { + private $value; + + public function __construct($value) + { + $this->value = $value; + } + }; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_get_defined_vars.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_get_defined_vars.php.inc new file mode 100644 index 00000000000..96ff2bda894 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_get_defined_vars.php.inc @@ -0,0 +1,11 @@ +isTrue(value: $param, text: 'test'); + } + + private function isTrue(string $text, bool $value): bool + { + return $value === true; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_unpack_argument.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_unpack_argument.php.inc new file mode 100644 index 00000000000..22f0e5f67f3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_unpack_argument.php.inc @@ -0,0 +1,16 @@ +isTrue(...['test', $param]); + } + + private function isTrue(string $text, bool $value): bool + { + return $value === true; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_with_first_class_callable.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_with_first_class_callable.php.inc new file mode 100644 index 00000000000..4e249b486e1 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/Fixture/skip_with_first_class_callable.php.inc @@ -0,0 +1,16 @@ +validate(...); + } + + private function validate(string $attribute, mixed $value): bool + { + return $value === true; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/RemoveUnusedPrivateMethodParameterRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/RemoveUnusedPrivateMethodParameterRectorTest.php index 9214784390a..acc9e9a1af5 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/RemoveUnusedPrivateMethodParameterRectorTest.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/RemoveUnusedPrivateMethodParameterRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodParameterRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedPrivateMethodParameterRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/config/configured_rule.php index 96858d4dece..849838b5149 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodParameterRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnusedPrivateMethodParameterRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnusedPrivateMethodParameterRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/keep_nullsafe_called_method.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/keep_nullsafe_called_method.php.inc new file mode 100644 index 00000000000..4863d70271d --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/keep_nullsafe_called_method.php.inc @@ -0,0 +1,16 @@ +getX()?->privateMethod(); + } + + public function getX(): KeepNullSafeCalledMethod|null { + return rand(0,1) ? new KeepNullSafeCalledMethod() : null; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/keep_with_call_user_func.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/keep_with_call_user_func.php.inc new file mode 100644 index 00000000000..c2b9d7c93bb --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/keep_with_call_user_func.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array2.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array2.php.inc new file mode 100644 index 00000000000..16411fbe455 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array2.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array3.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array3.php.inc new file mode 100644 index 00000000000..ef59afaae7f --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array3.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array4.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array4.php.inc new file mode 100644 index 00000000000..2b49a939902 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array4.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array5.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array5.php.inc new file mode 100644 index 00000000000..80b0c56b7e7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/normal_array5.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_all_methods_public.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_all_methods_public.php.inc new file mode 100644 index 00000000000..f8dc6b027ae --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_all_methods_public.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_anonymous_class.php.inc index f4288be0f66..b8042397f3b 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_anonymous_class.php.inc +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_anonymous_class.php.inc @@ -6,7 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PhpParser\NodeVisitor; use PhpParser\NodeVisitorAbstract; -use Rector\Core\NodeAnalyzer\ClassAnalyzer; +use Rector\NodeAnalyzer\ClassAnalyzer; use Rector\PhpParser\Node\Manipulator\ClassManipulator; use Rector\PhpParser\Node\VariableInfo; @@ -51,7 +51,7 @@ class SkipAnonymousClass private function processClassNode(Class_ $classNode): Class_ { - $variableInfos = $this->propertiesByClass[spl_object_hash($classNode)] ?? []; + $variableInfos = $this->propertiesByClass[spl_object_id($classNode)] ?? []; foreach ($variableInfos as $propertyInfo) { $this->classManipulator->addConstructorDependency($classNode, $propertyInfo); } diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_fqn_with_constructor.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_fqn_with_constructor.php.inc new file mode 100644 index 00000000000..a6648668ee7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_fqn_with_constructor.php.inc @@ -0,0 +1,27 @@ +args = $args; + } + + public function run() + { + $array = [3, 2, 1]; + + usort($array, [SkipArrayCallablesFqnWithConstructor::class, 'sort']); + + return $array; + } + + private function sort($a, $b) + { + return $a <=> $b; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_magic_constant_with_constructor.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_magic_constant_with_constructor.php.inc new file mode 100644 index 00000000000..757997e730c --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_magic_constant_with_constructor.php.inc @@ -0,0 +1,27 @@ +args = $args; + } + + public function run() + { + $array = [3, 2, 1]; + + usort($array, [__CLASS__, 'sort']); + + return $array; + } + + private function sort($a, $b) + { + return $a <=> $b; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_self_with_constructor.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_self_with_constructor.php.inc new file mode 100644 index 00000000000..432df9164ed --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_self_with_constructor.php.inc @@ -0,0 +1,27 @@ +args = $args; + } + + public function run() + { + $array = [3, 2, 1]; + + usort($array, [self::class, 'sort']); + + return $array; + } + + private function sort($a, $b) + { + return $a <=> $b; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_static_with_constructor.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_static_with_constructor.php.inc new file mode 100644 index 00000000000..ae13ef9ccee --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_array_callables_static_with_constructor.php.inc @@ -0,0 +1,27 @@ +args = $args; + } + + public function run() + { + $array = [3, 2, 1]; + + usort($array, [static::class, 'sort']); + + return $array; + } + + private function sort($a, $b) + { + return $a <=> $b; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_data_provider_method.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_data_provider_method.php.inc new file mode 100644 index 00000000000..a054e84a272 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_data_provider_method.php.inc @@ -0,0 +1,24 @@ +addArgument('state', InputArgument::REQUIRED, 'active or deactivate the maintenance mode'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + Assert::inArray($input->getArgument('state'), ['disable', 'active']); + $state = $input->getArgument('state'); + + return $this->{$state}($io); + } + + private function disable(SymfonyStyle $io): int + { + $io->caution('/!\\ Maintenance Mode is disable /!\\'); + + return Command::SUCCESS; + } + + private function active(SymfonyStyle $io): int + { + $io->caution('/!\\ Maintenance Mode is active /!\\'); + + return Command::SUCCESS; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_fluent_no_return_type.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_fluent_no_return_type.php.inc new file mode 100644 index 00000000000..d8e63e39a2e --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_fluent_no_return_type.php.inc @@ -0,0 +1,31 @@ +bar() + ->baz() + ->qux(); + } + + private function bar() + { + return $this; + } + + private function baz() + { + return $this; + } + + public function qux() + { + return $this; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_fluent_no_return_type2.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_fluent_no_return_type2.php.inc new file mode 100644 index 00000000000..6cae49e0421 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_fluent_no_return_type2.php.inc @@ -0,0 +1,25 @@ +bar() + ->baz(); + } + + private function bar() + { + return $this; + } + + private function baz() + { + return $this; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_not_identifier_name.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_not_identifier_name.php.inc new file mode 100644 index 00000000000..1851998549d --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_not_identifier_name.php.inc @@ -0,0 +1,21 @@ +{'do' . $name}(); + } + + private function doFoo() + { + return 5; + } + + private function doBar() + { + return 4; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_used_as_closure.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_used_as_closure.php.inc new file mode 100644 index 00000000000..5a1e03b1834 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_used_as_closure.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_used_by_closure_trait.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_used_by_closure_trait.php.inc new file mode 100644 index 00000000000..44b63bb25c8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Fixture/skip_used_by_closure_trait.php.inc @@ -0,0 +1,14 @@ +bar() + ->bat(); + } + + private function bar() + { + return $this; + } + + private function baz() + { + return $this; + } + + private function bat() + { + return $this; + } +} + +?> +----- +bar() + ->bat(); + } + + private function bar() + { + return $this; + } + + private function bat() + { + return $this; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/RemoveUnusedPrivateMethodRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/RemoveUnusedPrivateMethodRectorTest.php index 79b6bce6dd4..a56cacf4d6c 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/RemoveUnusedPrivateMethodRectorTest.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/RemoveUnusedPrivateMethodRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedPrivateMethodRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Source/TraitConsumer.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Source/TraitConsumer.php new file mode 100644 index 00000000000..130cef62b59 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/Source/TraitConsumer.php @@ -0,0 +1,25 @@ +run(); + } + + public static function helloExecute() + { + self::hello(); + } + + public function some() + { + function () { + $this->world(); + }; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/config/configured_rule.php index 6fc2ca527db..0f4bf63e864 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnusedPrivateMethodRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnusedPrivateMethodRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/remove_also_param_doc.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/remove_also_param_doc.php.inc new file mode 100644 index 00000000000..0e265f8d8ad --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/remove_also_param_doc.php.inc @@ -0,0 +1,41 @@ +usedDependency; + } +} + +?> +----- +usedDependency; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_attribute.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_attribute.php.inc new file mode 100644 index 00000000000..3fb26757826 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_attribute.php.inc @@ -0,0 +1,12 @@ + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_public_some_property_readonly.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_public_some_property_readonly.php.inc new file mode 100644 index 00000000000..cc17bc37988 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_public_some_property_readonly.php.inc @@ -0,0 +1,12 @@ + $this->a + $this->b; + } + + public function __construct( + private readonly int $a, + private readonly int $b, + ) {} + + public function getSum(): int + { + return $this->sum; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_with_magic_get.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_with_magic_get.php.inc new file mode 100644 index 00000000000..c3b3608deae --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/skip_with_magic_get.php.inc @@ -0,0 +1,25 @@ +{$name}; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/some_private_property_readonly.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/some_private_property_readonly.php.inc new file mode 100644 index 00000000000..68c76c55e97 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Fixture/some_private_property_readonly.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/RemoveUnusedPromotedPropertyRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/RemoveUnusedPromotedPropertyRectorTest.php index bfc98e6863f..00adf057582 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/RemoveUnusedPromotedPropertyRectorTest.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/RemoveUnusedPromotedPropertyRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedPromotedPropertyRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Source/SomeInterfaceWithConstruct.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Source/SomeInterfaceWithConstruct.php new file mode 100644 index 00000000000..631e2b410f4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector/Source/SomeInterfaceWithConstruct.php @@ -0,0 +1,8 @@ +phpVersion(PhpVersionFeature::PROPERTY_PROMOTION); -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnusedPromotedPropertyRector::class); + $rectorConfig->rule(RemoveUnusedPromotedPropertyRector::class); }; diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_command_handler.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_command_handler.php.inc new file mode 100644 index 00000000000..54f98ff667f --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_command_handler.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_no_params.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_no_params.php.inc new file mode 100644 index 00000000000..8300721d9dc --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_no_params.php.inc @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_private.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_private.php.inc new file mode 100644 index 00000000000..3bc58564783 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/skip_private.php.inc @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/some_class.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..2a8c3b2cf99 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/some_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/with_param_doc.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/with_param_doc.php.inc new file mode 100644 index 00000000000..01132debeaf --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Fixture/with_param_doc.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/FixtureTreatClassesAsFinal/skip_abstract_method.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/FixtureTreatClassesAsFinal/skip_abstract_method.php.inc new file mode 100644 index 00000000000..ddd6836efd0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/FixtureTreatClassesAsFinal/skip_abstract_method.php.inc @@ -0,0 +1,8 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Source/InterfaceWithMethod.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Source/InterfaceWithMethod.php new file mode 100644 index 00000000000..5b8083be9a9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/Source/InterfaceWithMethod.php @@ -0,0 +1,8 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureTreatClassesAsFinal'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_with_treat_classes_as_final.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/config/configured_rule.php new file mode 100644 index 00000000000..6f0b7732356 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveUnusedPublicMethodParameterRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/config/configured_rule_with_treat_classes_as_final.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/config/configured_rule_with_treat_classes_as_final.php new file mode 100644 index 00000000000..769f6559af4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector/config/configured_rule_with_treat_classes_as_final.php @@ -0,0 +1,10 @@ +withRules([RemoveUnusedPublicMethodParameterRector::class]) + ->withTreatClassesAsFinal(); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/simple.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/simple.php.inc new file mode 100644 index 00000000000..e2f5abd4194 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/simple.php.inc @@ -0,0 +1,26 @@ +std = $std; + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_complex_statement.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_complex_statement.php.inc new file mode 100644 index 00000000000..bcc8a192bf2 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_complex_statement.php.inc @@ -0,0 +1,14 @@ +std = ''; + } + } +} + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_different_variable.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_different_variable.php.inc new file mode 100644 index 00000000000..bb5f767a42c --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_different_variable.php.inc @@ -0,0 +1,14 @@ +std = $std2; + } +} + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_normal_param.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_normal_param.php.inc new file mode 100644 index 00000000000..50acf711b31 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_normal_param.php.inc @@ -0,0 +1,14 @@ +std = $std; + } +} + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_readonly_promotion.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_readonly_promotion.php.inc new file mode 100644 index 00000000000..15cbee4e501 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/Fixture/skip_readonly_promotion.php.inc @@ -0,0 +1,14 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/config/configured_rule.php new file mode 100644 index 00000000000..1d74a54bc80 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveUselessAssignFromPropertyPromotionRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/do_not_duplicate_annotations.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/do_not_duplicate_annotations.php.inc new file mode 100644 index 00000000000..50edb248db6 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/do_not_duplicate_annotations.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/keep_const_float_type.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/keep_const_float_type.php.inc new file mode 100644 index 00000000000..bf3cf8a266b --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/keep_const_float_type.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/remove_nullable_type.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/remove_nullable_type.php.inc new file mode 100644 index 00000000000..f4f10961606 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/remove_nullable_type.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/same_namespace_param_type.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/same_namespace_param_type.php.inc new file mode 100644 index 00000000000..08052668ae8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/same_namespace_param_type.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/same_namespace_param_type_deep.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/same_namespace_param_type_deep.php.inc new file mode 100644 index 00000000000..64c4864983d --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/same_namespace_param_type_deep.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/skip_integer_range_type.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/skip_integer_range_type.php.inc new file mode 100644 index 00000000000..970bc9448c8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/skip_integer_range_type.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/skip_multiline_comment_for_userland_class_type_hint.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/skip_multiline_comment_for_userland_class_type_hint.php.inc new file mode 100644 index 00000000000..517153cfde0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/skip_multiline_comment_for_userland_class_type_hint.php.inc @@ -0,0 +1,22 @@ +run(); + } + } +} + +?> +----- +run(); + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/union_of_interface_with_comment.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/union_of_interface_with_comment.php.inc new file mode 100644 index 00000000000..521f4dc1247 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/union_of_interface_with_comment.php.inc @@ -0,0 +1,47 @@ +run(); + } + } +} + +?> +----- +run(); + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/union_unsorted.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/union_unsorted.php.inc new file mode 100644 index 00000000000..d2c357a3667 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Fixture/union_unsorted.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/RemoveUselessParamTagRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/RemoveUselessParamTagRectorTest.php index f5f9c718cc4..05bf5ef4fd9 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/RemoveUselessParamTagRectorTest.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/RemoveUselessParamTagRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use SplFileInfo; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUselessParamTagRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Source/Deep/SomeClass.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Source/Deep/SomeClass.php new file mode 100644 index 00000000000..65a88560edb --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector/Source/Deep/SomeClass.php @@ -0,0 +1,12 @@ +services(); - $services->set(RemoveUselessParamTagRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUselessParamTagRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/return_dynamic_expr_in_construct.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/return_dynamic_expr_in_construct.php.inc new file mode 100644 index 00000000000..44ff691472c --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/return_dynamic_expr_in_construct.php.inc @@ -0,0 +1,38 @@ +execute(); + } +} + +?> +----- +execute(); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/return_expr_in_construct.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/return_expr_in_construct.php.inc new file mode 100644 index 00000000000..a60465e6526 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/return_expr_in_construct.php.inc @@ -0,0 +1,37 @@ +init(); + return true; + } + + $this->execute(); + } +} + +?> +----- +init(); + return; + } + + $this->execute(); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/skip_abstract_method.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/skip_abstract_method.php.inc new file mode 100644 index 00000000000..3b645207310 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/Fixture/skip_abstract_method.php.inc @@ -0,0 +1,8 @@ +init(); + return; + } + + $this->execute(); + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/RemoveUselessReturnExprInConstructRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/RemoveUselessReturnExprInConstructRectorTest.php new file mode 100644 index 00000000000..1773e8229e0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/RemoveUselessReturnExprInConstructRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/config/configured_rule.php new file mode 100644 index 00000000000..84141d45fde --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveUselessReturnExprInConstructRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/keep_const_float_type.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/keep_const_float_type.php.inc new file mode 100644 index 00000000000..bb35d63d27e --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/keep_const_float_type.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/phpstan-return.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/phpstan-return.php.inc new file mode 100644 index 00000000000..9bf39155182 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/phpstan-return.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/psalm-return.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/psalm-return.php.inc new file mode 100644 index 00000000000..c61d304c264 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/psalm-return.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_null.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_null.php.inc new file mode 100644 index 00000000000..63ef7571e42 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_null.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_return_mixed.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_return_mixed.php.inc new file mode 100644 index 00000000000..ffa78cd34ee --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_return_mixed.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_return_mixed2.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_return_mixed2.php.inc new file mode 100644 index 00000000000..b670d7cc3e7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/remove_return_mixed2.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_static_on_return_self_final_class.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_static_on_return_self_final_class.php.inc new file mode 100644 index 00000000000..053a4e13f06 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_static_on_return_self_final_class.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_union_doc_and_void.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_union_doc_and_void.php.inc new file mode 100644 index 00000000000..07b41d29ac7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_union_doc_and_void.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_void_on_fluent.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_void_on_fluent.php.inc new file mode 100644 index 00000000000..2360399c304 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_void_on_fluent.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_void_on_void.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_void_on_void.php.inc new file mode 100644 index 00000000000..eb519737e21 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/return_void_on_void.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/skip_integer_range_type.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/skip_integer_range_type.php.inc new file mode 100644 index 00000000000..9c7c5056ade --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/skip_integer_range_type.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/skip_nullable_template_tag.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/skip_nullable_template_tag.php.inc new file mode 100644 index 00000000000..822b7768814 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/skip_nullable_template_tag.php.inc @@ -0,0 +1,13 @@ + (string) $ticket->getFile()->getId(), + * ); + * + * @template TValue of mixed + * @template TCallbackValue of mixed + * @template TKey of array-key + * @param array $input + * @param callable(TValue): TKey $keyCallback + * @param null|callable(TValue): TCallbackValue $valueCallback + * + * @return ($valueCallback is null ? array> : array>) + */ + public static function groupBy(array $input, callable $keyCallback, ?callable $valueCallback = null) : array + { + $output = []; + foreach ($input as $result) { + $output[$keyCallback($result)][] = $valueCallback !== null ? $valueCallback($result) : $result; + } + + return $output; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/something_and_never.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/something_and_never.php.inc new file mode 100644 index 00000000000..b74ad90aac5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/something_and_never.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/something_and_void.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/something_and_void.php.inc new file mode 100644 index 00000000000..59e36a53bcf --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/something_and_void.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/useless_nullable_doc.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/useless_nullable_doc.php.inc new file mode 100644 index 00000000000..bcb4b59c133 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Fixture/useless_nullable_doc.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/RemoveUselessReturnTagRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/RemoveUselessReturnTagRectorTest.php index b5c7105de8d..0c424914828 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/RemoveUselessReturnTagRectorTest.php +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/RemoveUselessReturnTagRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveUselessReturnTagRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use SplFileInfo; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUselessReturnTagRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Source/Performance.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Source/Performance.php new file mode 100644 index 00000000000..e2dcc7be376 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector/Source/Performance.php @@ -0,0 +1,5 @@ +services(); - $services->set(RemoveUselessReturnTagRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUselessReturnTagRector::class]); diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_clone.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_clone.php.inc new file mode 100644 index 00000000000..b8aaca18b6b --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_clone.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_construct.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_construct.php.inc new file mode 100644 index 00000000000..1cc072c192c --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_construct.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_destruct.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_destruct.php.inc new file mode 100644 index 00000000000..f3585f36c43 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/Fixture/remove_return_destruct.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/RemoveVoidDocblockFromMagicMethodRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/RemoveVoidDocblockFromMagicMethodRectorTest.php new file mode 100644 index 00000000000..55965fa2cbe --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/RemoveVoidDocblockFromMagicMethodRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/config/configured_rule.php new file mode 100644 index 00000000000..8aeda0cf9a1 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveVoidDocblockFromMagicMethodRector::class]); diff --git a/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..00127e80452 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/Fixture/fixture.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/Fixture/skip_used_variable.php.inc b/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/Fixture/skip_used_variable.php.inc new file mode 100644 index 00000000000..d0f80a1abdd --- /dev/null +++ b/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/Fixture/skip_used_variable.php.inc @@ -0,0 +1,15 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/config/configured_rule.php new file mode 100644 index 00000000000..6530e562750 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveUnusedClosureVariableUseRector::class]); diff --git a/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/Fixture/do_not_remove_parentheses.php.inc b/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/Fixture/do_not_remove_parentheses.php.inc new file mode 100644 index 00000000000..126248703ec --- /dev/null +++ b/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/Fixture/do_not_remove_parentheses.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/RemoveConcatAutocastRectorTest.php b/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/RemoveConcatAutocastRectorTest.php index 2033967c737..3a72c6660db 100644 --- a/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/RemoveConcatAutocastRectorTest.php +++ b/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/RemoveConcatAutocastRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Concat\RemoveConcatAutocastRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveConcatAutocastRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/config/configured_rule.php index e7e50a642d0..eac9ec4a7f3 100644 --- a/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Concat/RemoveConcatAutocastRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Concat\RemoveConcatAutocastRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveConcatAutocastRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveConcatAutocastRector::class]); diff --git a/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/Fixture/no_stmts.php.inc b/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/Fixture/no_stmts.php.inc new file mode 100644 index 00000000000..47954777d4c --- /dev/null +++ b/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/Fixture/no_stmts.php.inc @@ -0,0 +1,27 @@ + 80000) { + } + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/Fixture/no_stmts2.php.inc b/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/Fixture/no_stmts2.php.inc new file mode 100644 index 00000000000..b0f8f132278 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/Fixture/no_stmts2.php.inc @@ -0,0 +1,31 @@ + PHP_VERSION_ID) { + + } + + echo 'do something'; + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/RemovePhpVersionIdCheckRectorTest.php b/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/RemovePhpVersionIdCheckRectorTest.php index 42625574d67..3ac149adb0d 100644 --- a/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/RemovePhpVersionIdCheckRectorTest.php +++ b/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/RemovePhpVersionIdCheckRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\ConstFetch\RemovePhpVersionIdCheckRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemovePhpVersionIdCheckRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/config/configured_rule.php index 247b1a454d0..c0019155024 100644 --- a/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector/config/configured_rule.php @@ -2,14 +2,11 @@ declare(strict_types=1); -use Rector\Core\ValueObject\PhpVersion; +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ConstFetch\RemovePhpVersionIdCheckRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemovePhpVersionIdCheckRector::class) - ->call('configure', [[ - RemovePhpVersionIdCheckRector::PHP_VERSION_CONSTRAINT => PhpVersion::PHP_80, - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(RemovePhpVersionIdCheckRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_80); }; diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/binary_op.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/binary_op.php.inc new file mode 100644 index 00000000000..49ce128a63c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/binary_op.php.inc @@ -0,0 +1,70 @@ + 2; +1 >= 2; +1 <= 2; +1 <=> 2; +1 & 2; +1 | 2; +1 << 2; +1 >> 2; +1 !== 2; +1 != 2; +1 == 2; +1 === 2; +1 xor 2; + +1 and 2; +1 or 2; +1 ?? 2; +1 && 2; +1 || 2; + +function wrapToPreventPhpStanCallingMethods () +{ + 1 + foo(); + foo() + 1; + foo2() + foo3(); +} +?> +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/cast.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/cast.php.inc similarity index 100% rename from rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/cast.php.inc rename to rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/cast.php.inc diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/class_constant_fetch.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/class_constant_fetch.php.inc new file mode 100644 index 00000000000..42b76d2aac6 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/class_constant_fetch.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/clone.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/clone.php.inc new file mode 100644 index 00000000000..618f2b860c7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/clone.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/closure.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/closure.php.inc new file mode 100644 index 00000000000..f1ec4fb10b8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/closure.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/constant_fetch.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/constant_fetch.php.inc similarity index 100% rename from rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/constant_fetch.php.inc rename to rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/constant_fetch.php.inc diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/instanceOf.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/instanceOf.php.inc new file mode 100644 index 00000000000..e405e2dcb90 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/instanceOf.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/isset.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/isset.php.inc new file mode 100644 index 00000000000..ec5a1277a41 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/isset.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/scalar.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/scalar.php.inc similarity index 100% rename from rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/scalar.php.inc rename to rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/scalar.php.inc diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/skip_pipe_operator.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/skip_pipe_operator.php.inc new file mode 100644 index 00000000000..0ccd0076db9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/skip_pipe_operator.php.inc @@ -0,0 +1,11 @@ + assert(...); + +sprintf('echo "%s"', 'foo') + |> passthru(...); + +?> diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/static_property_fetch.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/static_property_fetch.php.inc new file mode 100644 index 00000000000..c1ab9f96e65 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/static_property_fetch.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/unairy.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/unairy.php.inc new file mode 100644 index 00000000000..0ed212865a3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/unairy.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/variable.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/variable.php.inc new file mode 100644 index 00000000000..ac43377daa7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/variable.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/FixtureRemovedComments/array_dim_fetch.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/FixtureRemovedComments/array_dim_fetch.php.inc new file mode 100644 index 00000000000..62738d04017 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/FixtureRemovedComments/array_dim_fetch.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/FixtureRemovedComments/property_fetch.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/FixtureRemovedComments/property_fetch.php.inc new file mode 100644 index 00000000000..270d7c4de9d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/FixtureRemovedComments/property_fetch.php.inc @@ -0,0 +1,20 @@ +prop = 1; + + $var->prop; + + //comment + $var->{methodCall()}; + + //comment + ${methodCall()}->prop; + + //comment + ${methodCall1()}->{methodCall2()}; +} +?> diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/RemoveDeadStmtRectorTest.php b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/RemoveDeadStmtRectorTest.php index 131b1647ad9..e0bf0f5a88f 100644 --- a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/RemoveDeadStmtRectorTest.php +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/RemoveDeadStmtRectorTest.php @@ -5,41 +5,35 @@ namespace Rector\Tests\DeadCode\Rector\Expression\RemoveDeadStmtRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDeadStmtRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + if (str_ends_with($filePath, 'skip_pipe_operator.php.inc') && PHP_VERSION_ID < 80500) { + $this->markTestSkipped('test contains php 8.5 syntax early before transformation'); + } + + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } - /** - * @dataProvider provideDataForTestKeepComments() - */ - public function testKeepComments(SmartFileInfo $fileInfo): void + #[DataProvider('provideDataForTestKeepComments')] + public function testKeepComments(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideDataForTestKeepComments(): Iterator + public static function provideDataForTestKeepComments(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureRemovedComments'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureRemovedComments'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/config/configured_rule.php index 4174c31610d..1aaff6e4c97 100644 --- a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Expression\RemoveDeadStmtRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDeadStmtRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadStmtRector::class]); diff --git a/rules-tests/DeadCode/Rector/Expression/SimplifyMirrorAssignRector/SimplifyMirrorAssignRectorTest.php b/rules-tests/DeadCode/Rector/Expression/SimplifyMirrorAssignRector/SimplifyMirrorAssignRectorTest.php index 091eafb647c..28d77bc1594 100644 --- a/rules-tests/DeadCode/Rector/Expression/SimplifyMirrorAssignRector/SimplifyMirrorAssignRectorTest.php +++ b/rules-tests/DeadCode/Rector/Expression/SimplifyMirrorAssignRector/SimplifyMirrorAssignRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Expression\SimplifyMirrorAssignRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyMirrorAssignRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Expression/SimplifyMirrorAssignRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Expression/SimplifyMirrorAssignRector/config/configured_rule.php index 97908453b34..5d5a17554e3 100644 --- a/rules-tests/DeadCode/Rector/Expression/SimplifyMirrorAssignRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Expression/SimplifyMirrorAssignRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Expression\SimplifyMirrorAssignRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyMirrorAssignRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyMirrorAssignRector::class]); diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/continue_inside_do.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/continue_inside_do.php.inc new file mode 100644 index 00000000000..a0bf3cc8bc9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/continue_inside_do.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/continue_inside_foreach.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/continue_inside_foreach.php.inc new file mode 100644 index 00000000000..df4791318d3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/continue_inside_foreach.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/for_.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/for_.php.inc new file mode 100644 index 00000000000..5013cb44966 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/for_.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/for_multiple_continue.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/for_multiple_continue.php.inc new file mode 100644 index 00000000000..d9c5b2bcca6 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/for_multiple_continue.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/inside_while.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/inside_while.php.inc new file mode 100644 index 00000000000..255e34c7f0a --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/inside_while.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/skip_has_num.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/skip_has_num.php.inc new file mode 100644 index 00000000000..8837a012173 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/skip_has_num.php.inc @@ -0,0 +1,19 @@ + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/skip_not_last_stmt.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/skip_not_last_stmt.php.inc new file mode 100644 index 00000000000..36ab7a359b5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/Fixture/skip_not_last_stmt.php.inc @@ -0,0 +1,17 @@ + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/RemoveDeadContinueRectorTest.php b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/RemoveDeadContinueRectorTest.php new file mode 100644 index 00000000000..55b8167b3f7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/RemoveDeadContinueRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/config/configured_rule.php new file mode 100644 index 00000000000..d8d1aa8c92a --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadContinueRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveDeadContinueRector::class]); diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/fixture.php.inc index aab9645a275..97333af079d 100644 --- a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/fixture.php.inc +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/fixture.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\DeadCode\Rector\For_\RemoveDeadIfForeachForRector\Fixture class Fixture { - public function run($someObject) + public function run($someObject, $differentValue) { $value = 5; if ($value) { @@ -13,11 +13,11 @@ class Fixture if ($someObject->run()) { } - $values = []; + $values = ['a', 'b', 'c']; foreach ($values as $value) { } - return $value; + return $differentValue; } public function forMe() @@ -35,16 +35,16 @@ namespace Rector\Tests\DeadCode\Rector\For_\RemoveDeadIfForeachForRector\Fixture class Fixture { - public function run($someObject) + public function run($someObject, $differentValue) { $value = 5; if ($someObject->run()) { } - $values = []; + $values = ['a', 'b', 'c']; - return $value; + return $differentValue; } public function forMe() diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/side_effect_checks.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/side_effect_checks.php.inc index b4d9d2e5eec..0671fd00833 100644 --- a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/side_effect_checks.php.inc +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/side_effect_checks.php.inc @@ -27,8 +27,6 @@ class SideEffectChecks { public function run() { - if (5 + 10) { - } } } diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/skip_used_in_next_stmt.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/skip_used_in_next_stmt.php.inc new file mode 100644 index 00000000000..459f38e21cb --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/skip_used_in_next_stmt.php.inc @@ -0,0 +1,19 @@ += 0 && $result[$i] === '0'; $i--) { + ; + } + + if ($i >= 0 && $result[$i] === $decimalSep) { + $i--; + } + + $result = substr($result, 0, $i + 1); + } +} diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/skip_used_in_next_stmt2.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/skip_used_in_next_stmt2.php.inc new file mode 100644 index 00000000000..5e073c9a0ca --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/skip_used_in_next_stmt2.php.inc @@ -0,0 +1,19 @@ + $value) { + ; + } + + if ($i >= 0 && $result[$i] === $decimalSep) { + $i--; + } + + $result = substr($result, 0, $i + 1); + } +} diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/unused_value.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/unused_value.php.inc new file mode 100644 index 00000000000..85ba8769296 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/unused_value.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/with_used_value.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/with_used_value.php.inc new file mode 100644 index 00000000000..86fe75ed9eb --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/Fixture/with_used_value.php.inc @@ -0,0 +1,47 @@ +run()) { + } + + $values = ['a', 'b', 'c']; + foreach ($values as $value) { + } + + return $value; + } +} + +?> +----- +run()) { + } + + $values = ['a', 'b', 'c']; + foreach ($values as $value) { + } + + return $value; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/RemoveDeadIfForeachForRectorTest.php b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/RemoveDeadIfForeachForRectorTest.php index 7785a9a84a8..0f53c3eb7fc 100644 --- a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/RemoveDeadIfForeachForRectorTest.php +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/RemoveDeadIfForeachForRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\For_\RemoveDeadIfForeachForRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDeadIfForeachForRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/config/configured_rule.php index 00c41026fde..0bacdd2640c 100644 --- a/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadIfForeachForRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\For_\RemoveDeadIfForeachForRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDeadIfForeachForRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadIfForeachForRector::class]); diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/do_.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/do_.php.inc deleted file mode 100644 index e5a48e2ad47..00000000000 --- a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/do_.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_do.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_do.php.inc new file mode 100644 index 00000000000..83b9234e8b7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_do.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_foreach.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_foreach.php.inc new file mode 100644 index 00000000000..7074f6c608c --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_foreach.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_while.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_while.php.inc new file mode 100644 index 00000000000..4874ca4b38d --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/empty_while.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/foreach_.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/foreach_.php.inc deleted file mode 100644 index 31c1bed9c0e..00000000000 --- a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/foreach_.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/skip_with_side_effect.php.inc b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/skip_with_side_effect.php.inc new file mode 100644 index 00000000000..e2a8cf224f9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/Fixture/skip_with_side_effect.php.inc @@ -0,0 +1,24 @@ + ------ - diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/RemoveDeadLoopRectorTest.php b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/RemoveDeadLoopRectorTest.php index e67d454c560..9522f9a33b5 100644 --- a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/RemoveDeadLoopRectorTest.php +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/RemoveDeadLoopRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\For_\RemoveDeadLoopRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDeadLoopRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/config/configured_rule.php index 5f7a6bb0adb..446555e60da 100644 --- a/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/For_/RemoveDeadLoopRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\For_\RemoveDeadLoopRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDeadLoopRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadLoopRector::class]); diff --git a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/fixture.php.inc index dd427f5067a..3a4965bb47a 100644 --- a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/fixture.php.inc +++ b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/fixture.php.inc @@ -10,10 +10,6 @@ class Fixture foreach ($items as $key => $value) { $result = $value; } - - foreach ($items as $key => $value) { - $result = $key; - } } } @@ -31,10 +27,6 @@ class Fixture foreach ($items as $value) { $result = $value; } - - foreach ($items as $key => $value) { - $result = $key; - } } } diff --git a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/remove_unused_var_docblock_key.php.inc b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/remove_unused_var_docblock_key.php.inc new file mode 100644 index 00000000000..a1d24cfcc15 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/remove_unused_var_docblock_key.php.inc @@ -0,0 +1,40 @@ + $value) { + $result = $value; + } + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_key_used_in_next_stmt.php.inc b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_key_used_in_next_stmt.php.inc new file mode 100644 index 00000000000..d473d4dbb52 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_key_used_in_next_stmt.php.inc @@ -0,0 +1,16 @@ + $value) { + // next + } + + return $result; + } +} diff --git a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_in_compact.php.inc b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_in_compact.php.inc new file mode 100644 index 00000000000..3b1d9f127fc --- /dev/null +++ b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_in_compact.php.inc @@ -0,0 +1,47 @@ + $rename) { + foreach ($rename as $old_key => $new_key) { + DB::statement(<<<'SQL' + WITH cte AS ( + SELECT id, + CASE + WHEN new_values ?? :old_key THEN + jsonb_set( + new_values - :old_key, + ARRAY[:new_key], + new_values->:old_key, + true + ) + ELSE new_values + END AS new_values, + CASE + WHEN old_values ?? :old_key THEN + jsonb_set( + old_values - :old_key, + ARRAY[:new_key], + old_values->:old_key, + true + ) + ELSE old_values + END AS old_values + FROM audits + WHERE auditable_type = :morph + AND (new_values ?? :old_key OR old_values ?? :old_key) + ) + UPDATE audits + SET new_values = cte.new_values, + old_values = cte.old_values + FROM cte + WHERE audits.id = cte.id + SQL, compact('morph', 'old_key', 'new_key')); + } + } + } +} diff --git a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_in_throw_stmts_in_catch.php.inc b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_in_throw_stmts_in_catch.php.inc new file mode 100644 index 00000000000..4cfd3bd9064 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_in_throw_stmts_in_catch.php.inc @@ -0,0 +1,20 @@ + $val) { + throw new \Exception('test'); + } + } catch (\Throwable) { + echo 'Failed at index '.$key; + } + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_key.php.inc b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_key.php.inc new file mode 100644 index 00000000000..e25572931dc --- /dev/null +++ b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/Fixture/skip_used_key.php.inc @@ -0,0 +1,14 @@ + $value) { + $result = $key; + } + } +} diff --git a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/RemoveUnusedForeachKeyRectorTest.php b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/RemoveUnusedForeachKeyRectorTest.php index c94bad539a0..1b32da682e2 100644 --- a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/RemoveUnusedForeachKeyRectorTest.php +++ b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/RemoveUnusedForeachKeyRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Foreach_\RemoveUnusedForeachKeyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedForeachKeyRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/config/configured_rule.php index f332d93259f..21085dade03 100644 --- a/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Foreach_\RemoveUnusedForeachKeyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnusedForeachKeyRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnusedForeachKeyRector::class]); diff --git a/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/remove_filter_var_on_constnant_fetch.php.inc b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/remove_filter_var_on_constnant_fetch.php.inc new file mode 100644 index 00000000000..958331a5fbe --- /dev/null +++ b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/remove_filter_var_on_constnant_fetch.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/remove_filter_var_on_exact_type.php.inc b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/remove_filter_var_on_exact_type.php.inc new file mode 100644 index 00000000000..105cea7bef4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/remove_filter_var_on_exact_type.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/skip_different_filter_const.php.inc b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/skip_different_filter_const.php.inc new file mode 100644 index 00000000000..655ee497bf3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/skip_different_filter_const.php.inc @@ -0,0 +1,11 @@ + [ + 'min_range' => 10, + ], + 'flags' => FILTER_FLAG_ALLOW_OCTAL, + ]; + + return filter_var($value, FILTER_VALIDATE_INT, $options); + } +} diff --git a/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/skip_value_to_be_modified.php.inc b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/skip_value_to_be_modified.php.inc new file mode 100644 index 00000000000..1ad13d540b6 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/Fixture/skip_value_to_be_modified.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..bce81e1ca66 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveFilterVarOnExactTypeRector::class]); diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/arrow_function.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/arrow_function.php.inc new file mode 100644 index 00000000000..79f14c048ff --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/arrow_function.php.inc @@ -0,0 +1,25 @@ + $x > 5 ? 'high' : 10; + +$ternary = fn($value): string|int|float|null => + $value === null ? null : ($value > 0 ? 'positive' : -1); + +$cast = fn($input): int|string|array => (int) $input; + +?> +----- + $x > 5 ? 'high' : 10; + +$ternary = fn($value): string|int|null => + $value === null ? null : ($value > 0 ? 'positive' : -1); + +$cast = fn($input): int => (int) $input; + +?> diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/closure.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/closure.php.inc new file mode 100644 index 00000000000..a95e7f9ea9d --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/closure.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/edge_cases.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/edge_cases.php.inc new file mode 100644 index 00000000000..699ec6effa3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/edge_cases.php.inc @@ -0,0 +1,59 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_class.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_class.php.inc new file mode 100644 index 00000000000..e48682d17ad --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_class.php.inc @@ -0,0 +1,87 @@ + 'value']; + } +} + +?> +----- + 'value']; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_inheritance.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_inheritance.php.inc new file mode 100644 index 00000000000..6c96b7fedde --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_inheritance.php.inc @@ -0,0 +1,59 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_methods.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_methods.php.inc new file mode 100644 index 00000000000..8b5675bc527 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/final_methods.php.inc @@ -0,0 +1,57 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/function.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/function.php.inc new file mode 100644 index 00000000000..c135730ed2d --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/function.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/phpdocs.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/phpdocs.php.inc new file mode 100644 index 00000000000..f355f0fbf9a --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/phpdocs.php.inc @@ -0,0 +1,129 @@ + $class + * @return class-string|int + */ + public function bar(string $class): string|int + { + return $class; + } + + /** @return class-string|int */ + public function baz(string $class): string|int + { + return SomeInterface::class; + } + + /** @return \Iterator|string */ + function qux(): \Iterator|string + { + return new \ArrayIterator([1]); + } + + /** @return \Iterator|string */ + function qax(): \Iterator|string + { + return 'text'; + } + + /** + * @param int $a + */ + function quux(int $a): int|string + { + return $a; + } + + /** + * @param int $a + * @return int|string + */ + function mixedReturn(int $a): int|string + { + return $a; + } +} + +?> +----- + $class + * @return class-string<\Rector\Tests\DeadCode\Rector\FunctionLike\NarrowWideUnionReturnTypeRector\Source\SomeInterface> + */ + public function bar(string $class): string + { + return $class; + } + + /** @return class-string<\Rector\Tests\DeadCode\Rector\FunctionLike\NarrowWideUnionReturnTypeRector\Source\SomeInterface> */ + public function baz(string $class): string + { + return SomeInterface::class; + } + + /** @return \Iterator */ + function qux(): \Iterator + { + return new \ArrayIterator([1]); + } + + /** @return string */ + function qax(): string + { + return 'text'; + } + + /** + * @param int $a + */ + function quux(int $a): int + { + return $a; + } + + /** + * @param int $a + * @return int + */ + function mixedReturn(int $a): int + { + return $a; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_abstracts.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_abstracts.php.inc new file mode 100644 index 00000000000..eac40931cdc --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_abstracts.php.inc @@ -0,0 +1,28 @@ + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_constant_wildcard.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_constant_wildcard.php.inc new file mode 100644 index 00000000000..7d0c581963d --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_constant_wildcard.php.inc @@ -0,0 +1,24 @@ + $this->execute(); + } + + /** + * @return string + */ + private function execute() + { + return 1; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_exact_types.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_exact_types.php.inc new file mode 100644 index 00000000000..c07ea1e5931 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_exact_types.php.inc @@ -0,0 +1,26 @@ + yield 1; + +fn(string $a): \Iterator|array|null => match ($a) { + 'a' => yield 1, + 'b' => yield 2, + default => 'test', +}; + +fn(): \Iterator|array|null => yield from [1, 2, 3]; + +?> diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_native_mixed_returns.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_native_mixed_returns.php.inc new file mode 100644 index 00000000000..13486609782 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_native_mixed_returns.php.inc @@ -0,0 +1,29 @@ + $class + * @return class-string|int + */ + public function bar($class): string|int + { + return $class; + } + + /** + * @return class-string<\stdClass>|int + */ + public function skipMixedByDoc(): string|int + { + /** + * @var class-string<\stdClass> $class + */ + $class = get(); + return $class; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_non_final_classes.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_non_final_classes.php.inc new file mode 100644 index 00000000000..56cf1ecb4aa --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_non_final_classes.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_non_final_inheritance.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_non_final_inheritance.php.inc new file mode 100644 index 00000000000..c92203bfc30 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_non_final_inheritance.php.inc @@ -0,0 +1,29 @@ + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_null.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_null.php.inc new file mode 100644 index 00000000000..0649b58f15e --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_null.php.inc @@ -0,0 +1,11 @@ + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_standalone_null.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_standalone_null.php.inc new file mode 100644 index 00000000000..93058a219d0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_standalone_null.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_true_false_to_become_bool.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_true_false_to_become_bool.php.inc new file mode 100644 index 00000000000..28288710887 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/skip_true_false_to_become_bool.php.inc @@ -0,0 +1,16 @@ + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/terminating_methods.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/terminating_methods.php.inc new file mode 100644 index 00000000000..501e66a76be --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Fixture/terminating_methods.php.inc @@ -0,0 +1,123 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/NarrowWideUnionReturnTypeRectorTest.php b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/NarrowWideUnionReturnTypeRectorTest.php new file mode 100644 index 00000000000..0b90526ee08 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/NarrowWideUnionReturnTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Source/SomeAbstractClass.php b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Source/SomeAbstractClass.php new file mode 100644 index 00000000000..218bdf90cc6 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector/Source/SomeAbstractClass.php @@ -0,0 +1,8 @@ +rule(NarrowWideUnionReturnTypeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES); +}; diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/Fixture/fixture.php.inc deleted file mode 100644 index ad06d9da2c9..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/Fixture/keep_comment.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/Fixture/keep_comment.php.inc deleted file mode 100644 index 94211a9cfa2..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/Fixture/keep_comment.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - 5) { - return $a; - } else - - $a++; - - return $a; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/RemoveCodeAfterReturnRectorTest.php b/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/RemoveCodeAfterReturnRectorTest.php deleted file mode 100644 index 26652582703..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/RemoveCodeAfterReturnRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/config/configured_rule.php deleted file mode 100644 index cbbf503a51c..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveCodeAfterReturnRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/Fixture/if_in_class_method.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/Fixture/if_in_class_method.php.inc new file mode 100644 index 00000000000..504996207b3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/Fixture/if_in_class_method.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/Fixture/skip_return_value.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/Fixture/skip_return_value.php.inc new file mode 100644 index 00000000000..9a7b1646af4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/Fixture/skip_return_value.php.inc @@ -0,0 +1,17 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/config/configured_rule.php index b36e8584b2c..74f1e40ed59 100644 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\FunctionLike\RemoveDeadReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDeadReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadReturnRector::class]); diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/fixture.php.inc deleted file mode 100644 index c201f96735f..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_array_shifted_value.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_array_shifted_value.php.inc deleted file mode 100644 index 70da80f492d..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_array_shifted_value.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -data)) { - return $this->data; - } - - $this->data = $data; - if (! empty($this->data)) { - return $this->data; - } - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_referenced.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_referenced.php.inc deleted file mode 100644 index 977b5a6a55b..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_referenced.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -_render(array_shift($rows), $widths, ['style' => $config['headerStyle']]); - } - - if (empty($rows)) { - return; - } - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_this.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_this.php.inc deleted file mode 100644 index 93ec9010bdf..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/Fixture/skip_this.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/config/configured_rule.php deleted file mode 100644 index 4e67f59b2cf..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveDuplicatedIfReturnRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/fixture.php.inc deleted file mode 100644 index 5f40af7cce3..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/function.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/function.php.inc deleted file mode 100644 index b818e8c806b..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/function.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep.php.inc deleted file mode 100644 index 37aa47d70da..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -getContents(); - $expectedContent = $originalContent; - - return $expectedContent; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_conditional_override.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_conditional_override.php.inc deleted file mode 100644 index 47a1eeb0b11..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_conditional_override.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -stmts[0]; - $innerNode = $innerNode instanceof Expression ? $innerNode->expr : $innerNode; - - return $innerNode; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_presenter_name.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_presenter_name.php.inc deleted file mode 100644 index fb1d85a1c84..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_presenter_name.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -getName($classNode); - - /** @var string $presenterPart */ - $presenterPart = Strings::after($presenterName, '\\', -1); - - /** @var string $presenterPart */ - $presenterPart = Strings::substring($presenterPart, 0, -Strings::length('Presenter')); - $presenterPart = StaticRectorStrings::anything($presenterPart); - - $match = (array) Strings::match($this->getName($classMethod), '#^(action|render)(?.*?$)#sm'); - $actionPart = lcfirst($match['short_action_name']); - - return $presenterPart . '/' . $actionPart; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_re_use.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_re_use.php.inc deleted file mode 100644 index a6ac7a9cd3e..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_re_use.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -getAttribute(AttributeKey::SCOPE); - if ($scope === null) { - return []; - } - - /** @var Scope $nodeScope */ - $scope = $node->getAttribute(AttributeKey::SCOPE); - - return $scope->getType($node); - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_re_use_2.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_re_use_2.php.inc deleted file mode 100644 index 3a792d3191b..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_re_use_2.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -diff($old, $new); - - // remove first line, just meta info added by UnifiedDiffOutputBuilder - $diff = Strings::replace($diff, '#^(.*\n){1}#'); - - return $diff; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_same_level_but_different_condition_scope.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_same_level_but_different_condition_scope.php.inc deleted file mode 100644 index 353558cf585..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/keep_same_level_but_different_condition_scope.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_assign_method_call.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_assign_method_call.php.inc deleted file mode 100644 index 5d3250fc9b8..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_assign_method_call.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -get(); - $value = 1; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_issue_1093.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_issue_1093.php.inc deleted file mode 100644 index b8503f2ad1a..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_issue_1093.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - $dependency) { - if (!empty($themes[$themename]->status)) { - $theme_uninstallable = FALSE; - } - } - - if ($theme_uninstallable) { - return true; - } - - return false; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_issue_1286.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_issue_1286.php.inc deleted file mode 100644 index d4b89e01b63..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_issue_1286.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - $api_key]); - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_native_variable.php.inc b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_native_variable.php.inc deleted file mode 100644 index 30cc8bd5d9a..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/Fixture/skip_native_variable.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -store($directories); - $anotherDirectories = 2; - $directories = 3; - $anotherDirectories = 3; - $directories = 4; - $directories = 5; - return $directories + $anotherDirectories; - } - - public function store(int $directories) - { - } -} - -?> ------ -store($directories); - $anotherDirectories = 3; - $directories = 5; - return $directories + $anotherDirectories; - } - - public function store(int $directories) - { - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/RemoveOverriddenValuesRectorTest.php b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/RemoveOverriddenValuesRectorTest.php deleted file mode 100644 index 2e469bf8de1..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/RemoveOverriddenValuesRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/config/configured_rule.php deleted file mode 100644 index 21b0741dd09..00000000000 --- a/rules-tests/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveOverriddenValuesRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/from_call_with_native_return.php.inc b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/from_call_with_native_return.php.inc new file mode 100644 index 00000000000..86030d12d94 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/from_call_with_native_return.php.inc @@ -0,0 +1,51 @@ +get() || $number > 50) { + return 'yes'; + } + + return 'no'; + } +} + +?> +----- + 50) { + return 'yes'; + } + + return 'no'; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/skip_from_call_with_return_docblock.php.inc b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/skip_from_call_with_return_docblock.php.inc new file mode 100644 index 00000000000..f1da9251d7d --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/skip_from_call_with_return_docblock.php.inc @@ -0,0 +1,23 @@ +get() || $number > 50) { + return 'yes'; + } + + return 'no'; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/skip_from_docblock.php.inc b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/skip_from_docblock.php.inc new file mode 100644 index 00000000000..d70e37a992f --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/skip_from_docblock.php.inc @@ -0,0 +1,18 @@ + 50) { + return 'yes'; + } + + return 'no'; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/some_class.php.inc b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..583691319c7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/Fixture/some_class.php.inc @@ -0,0 +1,35 @@ + 50) { + return 'yes'; + } + + return 'no'; + } +} + +?> +----- + 50) { + return 'yes'; + } + + return 'no'; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/ReduceAlwaysFalseIfOrRectorTest.php b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/ReduceAlwaysFalseIfOrRectorTest.php new file mode 100644 index 00000000000..b3350c9a97e --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/ReduceAlwaysFalseIfOrRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/config/configured_rule.php new file mode 100644 index 00000000000..52cfb81d996 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ReduceAlwaysFalseIfOrRector::class); +}; diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and.php.inc new file mode 100644 index 00000000000..fe0dec6a1f6 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/empty_stmts.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/empty_stmts.php.inc new file mode 100644 index 00000000000..a384ddcd2b9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/empty_stmts.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/merge_comment.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/merge_comment.php.inc new file mode 100644 index 00000000000..a476acd339b --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/merge_comment.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/multi_stmts.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/multi_stmts.php.inc new file mode 100644 index 00000000000..e676c3c8f19 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/multi_stmts.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/nop_stmt.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/nop_stmt.php.inc new file mode 100644 index 00000000000..ede36fd54cc --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/nop_stmt.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_array_dim_fetch.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_array_dim_fetch.php.inc new file mode 100644 index 00000000000..be4b6f231b5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_array_dim_fetch.php.inc @@ -0,0 +1,51 @@ + [ + 'id' => 1, + ], + ]; +} + +/** + * @return array{array{id: int}}|array{} + */ +function toRem(): array +{ + if (rand(0,1)) { + return []; + } + + return [ + 0 => [ + 'id' => 1, + ] + ]; +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_assign_in_if_cond.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_assign_in_if_cond.php.inc new file mode 100644 index 00000000000..9281b1b756b --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_assign_in_if_cond.php.inc @@ -0,0 +1,13 @@ + */ + protected array $dispatchesEvents = []; + + protected function fireResourceEvent(string $event, SomeModel $model, mixed ...$args): void + { + $class = $this->dispatchesEvents[$event] ?? null; + + if ($class === null) { + return; + } + + some_event(new $class($model, ...$args)); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_by_docblock_param_definition.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_by_docblock_param_definition.php.inc new file mode 100644 index 00000000000..2d537efddd6 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_by_docblock_param_definition.php.inc @@ -0,0 +1,28 @@ +|string $fields + */ + public function run($fields) + { + $data = ['init']; + + if (is_array($fields)) { + foreach ($fields as $name => $attributes) { + if (is_string($attributes)) { + continue; + } + + if (is_array($attributes)) { + $data = (array_merge($data, [$name => $attributes])); + } + } + } + + return $this; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_by_param_doc.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_by_param_doc.php.inc new file mode 100644 index 00000000000..a5234fff230 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_by_param_doc.php.inc @@ -0,0 +1,18 @@ +\n"; + foreach ($criticalErrors as $criticalError) { + call_user_func_array("StatusMessage", $criticalError); + echo "

"; + } + } + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_method_call.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_method_call.php.inc new file mode 100644 index 00000000000..f2f276613f0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_method_call.php.inc @@ -0,0 +1,40 @@ +code = $code; + } +} + +class SkipMethodCall { + private const WEBSITE_ALREADY_EXISTS = 'website_already_exists'; + + public function run() + { + try { + return $this->someApiAction(); + } catch (ApiException $ex) { + if ($ex->getCode() !== self::WEBSITE_ALREADY_EXISTS) { + throw $ex; + } + } + } + + private function someApiAction() + { + if (rand() % 2) { + throw new ApiException('API Action failed', 'website_already_exists'); + } + + return 1; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_mixed_fallback_null_on_trait.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_mixed_fallback_null_on_trait.php.inc new file mode 100644 index 00000000000..a46f037c35f --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_mixed_fallback_null_on_trait.php.inc @@ -0,0 +1,26 @@ + + */ + private $dispatchesEvents; + + protected function fireResourceEvent(string $event, Model $model, mixed ...$args): void + { + if ($this->silent) { + return; + } + + $event = $this->dispatchesEvents[$event] ?? null; + + if (is_null($event)) { + return; + } + + event(new $event($model, ...$args)); + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_override_by_next_method_call.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_override_by_next_method_call.php.inc new file mode 100644 index 00000000000..c61105b1cfa --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_override_by_next_method_call.php.inc @@ -0,0 +1,26 @@ +executed = false; + + $this->execute(); + + if (!$this->executed) { + echo 'not run'; + } + } + + protected function execute() + { + if (rand(0, 1)) { + $this->executed = true; + } + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc.php.inc new file mode 100644 index 00000000000..740997bbb4d --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc.php.inc @@ -0,0 +1,25 @@ +property = new \DateTime('now'); + } + } + + public function verify() + { + if ($this->property instanceof \DateTime) { + return true; + } + + return false; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc2.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc2.php.inc new file mode 100644 index 00000000000..0ef8f7719f8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc2.php.inc @@ -0,0 +1,18 @@ +property instanceof \DateTime) { + return true; + } + + return false; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc_in_trait.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc_in_trait.php.inc new file mode 100644 index 00000000000..b6975dbeef8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc_in_trait.php.inc @@ -0,0 +1,20 @@ +formatter)) { + // if no formatter, use the default + $this->formatter = new Formatter(); + } + + $this->formatter->format(); + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_fetch_doc.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_fetch_doc.php.inc new file mode 100644 index 00000000000..66e84b7aee7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_fetch_doc.php.inc @@ -0,0 +1,22 @@ +bodyFormat) && $this->bodyFormat !== '') { + } + } + + public function reset() + { + $this->bodyFormat = null; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_fetch_doc2.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_fetch_doc2.php.inc new file mode 100644 index 00000000000..4f5d60be83f --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_fetch_doc2.php.inc @@ -0,0 +1,17 @@ + + */ + public $data = []; + + public function run($key) + { + if (is_int($this->data[$key]) && $this->data[$key] > 0) { + } + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_static_method_call.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_static_method_call.php.inc new file mode 100644 index 00000000000..e1ca399d25b --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_static_method_call.php.inc @@ -0,0 +1,40 @@ +code = $code; + } +} + +class SkipMethodCall { + private const WEBSITE_ALREADY_EXISTS = 'website_already_exists'; + + public function run() + { + try { + return self::someApiAction(); + } catch (ApiException $ex) { + if ($ex->getCode() !== self::WEBSITE_ALREADY_EXISTS) { + throw $ex; + } + } + } + + public static function someApiAction() + { + if (rand() % 2) { + throw new ApiException('API Action failed', 'website_already_exists'); + } + + return 1; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_trait_this.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_trait_this.php.inc new file mode 100644 index 00000000000..403c4e633c1 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_trait_this.php.inc @@ -0,0 +1,17 @@ +getExample() === false) { + $this->setExample('demo'); + } + } + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_union_docblock_param_is_object.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_union_docblock_param_is_object.php.inc new file mode 100644 index 00000000000..39e13949f21 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_union_docblock_param_is_object.php.inc @@ -0,0 +1,29 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Source/SomeClassWithPropertyUseVarDocblock.php b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Source/SomeClassWithPropertyUseVarDocblock.php new file mode 100644 index 00000000000..4c1f74d4ca3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Source/SomeClassWithPropertyUseVarDocblock.php @@ -0,0 +1,9 @@ +services(); - $services->set(RemoveAlwaysTrueIfConditionRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveAlwaysTrueIfConditionRector::class]); diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..866d950a398 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/fixture.php.inc @@ -0,0 +1,52 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_else_no_stmt.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_else_no_stmt.php.inc new file mode 100644 index 00000000000..fd98c086a0a --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_else_no_stmt.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_elseif_no_stmt.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_elseif_no_stmt.php.inc new file mode 100644 index 00000000000..bc7334cbb66 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_elseif_no_stmt.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_no_stmt_with_else.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_no_stmt_with_else.php.inc new file mode 100644 index 00000000000..41f562f1c9e --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_no_stmt_with_else.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_no_stmt_with_else_no_stmt.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_no_stmt_with_else_no_stmt.php.inc new file mode 100644 index 00000000000..3969df58f78 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/if_no_stmt_with_else_no_stmt.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/multi_elseif_with_binaryop.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/multi_elseif_with_binaryop.php.inc new file mode 100644 index 00000000000..675d2959501 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/multi_elseif_with_binaryop.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/side_effect_checks.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/side_effect_checks.php.inc new file mode 100644 index 00000000000..77c6e2e881c --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/side_effect_checks.php.inc @@ -0,0 +1,45 @@ +someCall()) { + } + + if (false) { + } elseif ($this->anotherCall()) { + } + } +} + +?> +----- +someCall()) { + } + + if (!false && $this->anotherCall()) { + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_if_comment.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_if_comment.php.inc new file mode 100644 index 00000000000..7932e5a16bf --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_if_comment.php.inc @@ -0,0 +1,19 @@ + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_property_fetch.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_property_fetch.php.inc new file mode 100644 index 00000000000..b5ac0366184 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_property_fetch.php.inc @@ -0,0 +1,17 @@ +v) { + echo '2'; + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_used_in_next_stmt.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_used_in_next_stmt.php.inc new file mode 100644 index 00000000000..83fefcf31ed --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_used_in_next_stmt.php.inc @@ -0,0 +1,19 @@ += 0 && $result[$i] === '0'; $i--) { + ; + } + + if ($i >= 0 && $result[$i] === $decimalSep) { + $i--; + } + + $result = substr($result, 0, $i + 1); + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_used_in_next_stmt2.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_used_in_next_stmt2.php.inc new file mode 100644 index 00000000000..7479fe30f59 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/Fixture/skip_used_in_next_stmt2.php.inc @@ -0,0 +1,19 @@ + $value) { + ; + } + + if ($i >= 0 && $result[$i] === $decimalSep) { + $i--; + } + + $result = substr($result, 0, $i + 1); + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/RemoveDeadIfBlockRectorTest.php b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/RemoveDeadIfBlockRectorTest.php new file mode 100644 index 00000000000..60d139a16de --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/RemoveDeadIfBlockRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/config/configured_rule.php new file mode 100644 index 00000000000..5cd1fadda4c --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadIfBlockRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveDeadIfBlockRector::class]); diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/include_and.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/include_and.php.inc new file mode 100644 index 00000000000..483fb23e07b --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/include_and.php.inc @@ -0,0 +1,33 @@ +getLine() === 100) { + } + } +} + +?> +----- +getLine() === 100) { + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/no_stmts.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/no_stmts.php.inc new file mode 100644 index 00000000000..88454b4ae25 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/no_stmts.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/reassign_on_not_typed_param.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/reassign_on_not_typed_param.php.inc new file mode 100644 index 00000000000..e1a7c1fcc78 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/reassign_on_not_typed_param.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_asssign_union.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_assign_union.php.inc similarity index 100% rename from rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_asssign_union.php.inc rename to rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_assign_union.php.inc diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_direct_method_call.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_direct_method_call.php.inc index 2de6dfef75c..6d73e4f4c96 100644 --- a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_direct_method_call.php.inc +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_direct_method_call.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\DeadCode\Rector\If_\RemoveDeadInstanceOfRector\Fixture; use stdClass; -class SkipDirectMethodCall +final class SkipDirectMethodCall { private function get(): stdClass { @@ -22,5 +22,3 @@ class SkipDirectMethodCall return true; } } - -?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_include_and_from_docblock.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_include_and_from_docblock.php.inc new file mode 100644 index 00000000000..1b5ccdb643d --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_include_and_from_docblock.php.inc @@ -0,0 +1,18 @@ +getLine() === 100) { + } + } +} + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_property_fetch.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_property_fetch.php.inc new file mode 100644 index 00000000000..8bb2e782fa8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_property_fetch.php.inc @@ -0,0 +1,26 @@ +var = new stdClass; + } + + public function go() + { + if (! $this->var instanceof stdClass) { + echo 'you need to run init() first' . PHP_EOL; + return; + } + + echo 'success' . PHP_EOL; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_trait.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_trait.php.inc index 684e5cbf440..f7ad4265066 100644 --- a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_trait.php.inc +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_trait.php.inc @@ -2,10 +2,7 @@ namespace Rector\Tests\DeadCode\Rector\If_\RemoveDeadInstanceOfRector\Fixture; -interface HasA -{ - public function getA(); -} +use Rector\Tests\DeadCode\Rector\If_\RemoveDeadInstanceOfRector\Source\HasAInterface; trait A { @@ -19,13 +16,13 @@ trait B { public function getB() { - if ($this instanceof HasA) { + if ($this instanceof HasAInterface) { return $this->getA(); } } } -class C implements HasA +class C implements HasAInterface { use A; use B; diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_trait_this.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_trait_this.php.inc new file mode 100644 index 00000000000..ab89ae8ef6f --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_trait_this.php.inc @@ -0,0 +1,17 @@ +getExample() === false) { + $this->setExample('demo'); + } + } + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_union_not_typed_param.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_union_not_typed_param.php.inc new file mode 100644 index 00000000000..8c79711c1d2 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/skip_union_not_typed_param.php.inc @@ -0,0 +1,24 @@ + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property.php.inc deleted file mode 100644 index 3854b893a53..00000000000 --- a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property.php.inc +++ /dev/null @@ -1,50 +0,0 @@ -var = new stdClass; - } - - public function go() - { - if (! $this->var instanceof stdClass) { - echo 'var is not an stdClass'; - return; - } - - echo 'success' . PHP_EOL; - } -} - -?> ------ -var = new stdClass; - } - - public function go() - { - echo 'success' . PHP_EOL; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property_via_property_promotion.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property_via_property_promotion.php.inc deleted file mode 100644 index 889a1fa3e2a..00000000000 --- a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property_via_property_promotion.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -var instanceof stdClass) { - echo 'var is not an stdClass'; - return; - } - - echo 'success' . PHP_EOL; - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/union_typed_param.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/union_typed_param.php.inc new file mode 100644 index 00000000000..57e738d73a7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/union_typed_param.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/RemoveDeadInstanceOfRectorTest.php b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/RemoveDeadInstanceOfRectorTest.php index 9c9a33ab183..1d146b8c332 100644 --- a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/RemoveDeadInstanceOfRectorTest.php +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/RemoveDeadInstanceOfRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\If_\RemoveDeadInstanceOfRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDeadInstanceOfRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Source/HasAInterface.php b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Source/HasAInterface.php new file mode 100644 index 00000000000..ae65d2da9dc --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Source/HasAInterface.php @@ -0,0 +1,9 @@ +services(); - $services->set(RemoveDeadInstanceOfRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadInstanceOfRector::class]); diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/non_typed_property_filled_by_construct.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/non_typed_property_filled_by_construct.php.inc similarity index 78% rename from rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/non_typed_property_filled_by_construct.php.inc rename to rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/non_typed_property_filled_by_construct.php.inc index 37d41212e98..db26461e316 100644 --- a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/non_typed_property_filled_by_construct.php.inc +++ b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/non_typed_property_filled_by_construct.php.inc @@ -1,6 +1,6 @@ target instanceof Document) { + return 'document'; + } + + if ($this->target instanceof Course) { + return 'course'; + } + + if ($this->target instanceof Book) { + return 'book'; + } + + return 'unknown'; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/typed_property.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/typed_property.php.inc new file mode 100644 index 00000000000..77adafa21dc --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/typed_property.php.inc @@ -0,0 +1,50 @@ +var = new stdClass; + } + + public function go() + { + if (! $this->var instanceof stdClass) { + echo 'var is not an stdClass'; + return; + } + + echo 'success' . PHP_EOL; + } +} + +?> +----- +var = new stdClass; + } + + public function go() + { + echo 'success' . PHP_EOL; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property_filled_by_construct_hardcode.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/typed_property_filled_by_construct_hardcode.php.inc similarity index 77% rename from rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property_filled_by_construct_hardcode.php.inc rename to rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/typed_property_filled_by_construct_hardcode.php.inc index ad499db365a..944c70fb8bd 100644 --- a/rules-tests/DeadCode/Rector/If_/RemoveDeadInstanceOfRector/Fixture/typed_property_filled_by_construct_hardcode.php.inc +++ b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Fixture/typed_property_filled_by_construct_hardcode.php.inc @@ -1,6 +1,6 @@ var instanceof stdClass) { + echo 'var is not an stdClass'; + return; + } + + echo 'success' . PHP_EOL; + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/RemoveTypedPropertyDeadInstanceOfRectorTest.php b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/RemoveTypedPropertyDeadInstanceOfRectorTest.php new file mode 100644 index 00000000000..3230886cea3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/RemoveTypedPropertyDeadInstanceOfRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Source/Book.php b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Source/Book.php new file mode 100644 index 00000000000..f26649509b6 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector/Source/Book.php @@ -0,0 +1,9 @@ +withRules([RemoveTypedPropertyDeadInstanceOfRector::class]); diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_check.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_check.php.inc new file mode 100644 index 00000000000..bce0a0eafb9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_check.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_check_with_empty.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_check_with_empty.php.inc new file mode 100644 index 00000000000..86c384b5da0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_check_with_empty.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_non_empty_and_bigger_than_zero.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_non_empty_and_bigger_than_zero.php.inc new file mode 100644 index 00000000000..583c4009924 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_non_empty_and_bigger_than_zero.php.inc @@ -0,0 +1,33 @@ + 0) { + foreach ($items as $item) { + echo $item; + } + } + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_with_return_value_inside_foreach.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_with_return_value_inside_foreach.php.inc new file mode 100644 index 00000000000..39392e83de1 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/if_with_return_value_inside_foreach.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/multi_if_check_with_empty.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/multi_if_check_with_empty.php.inc new file mode 100644 index 00000000000..8f9e6415cd0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/multi_if_check_with_empty.php.inc @@ -0,0 +1,51 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/not_equal.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/not_equal.php.inc deleted file mode 100644 index 7ffd0e62f1a..00000000000 --- a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/not_equal.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/not_equal_condition.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/not_equal_condition.php.inc new file mode 100644 index 00000000000..56a37c2dfc5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/not_equal_condition.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/offset_exists_dim_fetch.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/offset_exists_dim_fetch.php.inc new file mode 100644 index 00000000000..0dd67d1648d --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/offset_exists_dim_fetch.php.inc @@ -0,0 +1,59 @@ +> + */ + private static $groups = []; + + public static function knownOffset(): array + { + $group = 'default'; + + self::$groups[$group] = ["foo"]; + + if (! empty(self::$groups[$group])) { + foreach (self::$groups[$group] as $group) { + echo "hello"; + } + } + + return []; + } +} + +?> +----- +> + */ + private static $groups = []; + + public static function knownOffset(): array + { + $group = 'default'; + + self::$groups[$group] = ["foo"]; + + foreach (self::$groups[$group] as $group) { + echo "hello"; + } + + return []; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/property_foreach.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/property_foreach.php.inc new file mode 100644 index 00000000000..37bb447f57d --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/property_foreach.php.inc @@ -0,0 +1,35 @@ +items) { + foreach ($this->items as $item) { + } + } + } +} + +?> +----- +items as $item) { + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_assign_if_cond.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_assign_if_cond.php.inc new file mode 100644 index 00000000000..c353ce83515 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_assign_if_cond.php.inc @@ -0,0 +1,9 @@ + 0) { + foreach ($vars2 as $key => $val) { + echo "hello"; + } + } + + return []; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_count_mixed.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_count_mixed.php.inc new file mode 100644 index 00000000000..e0dfea9b018 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_count_mixed.php.inc @@ -0,0 +1,17 @@ + 0) { + foreach ($vars as $key => $val) { + echo "hello"; + } + } + + return []; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_if_check_throw_with_empty.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_if_check_throw_with_empty.php.inc new file mode 100644 index 00000000000..f5c7da14c2e --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_if_check_throw_with_empty.php.inc @@ -0,0 +1,19 @@ +allowedValues) { + foreach ($values as $index => $value) { + if (!isset($this->allowedValues[$value])) { + unset($values[$index]); + } + } + } + + return $values; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_if_non_empty_and_bigger_than_zero_phpdoc.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_if_non_empty_and_bigger_than_zero_phpdoc.php.inc new file mode 100644 index 00000000000..653016ca816 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_if_non_empty_and_bigger_than_zero_phpdoc.php.inc @@ -0,0 +1,20 @@ + 0) { + foreach ($items as $item) { + echo $item; + } + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_nullable_array.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_nullable_array.php.inc new file mode 100644 index 00000000000..35661488e15 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_nullable_array.php.inc @@ -0,0 +1,19 @@ + + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_nullable_array_on_not_empty.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_nullable_array_on_not_empty.php.inc new file mode 100644 index 00000000000..1c306edab89 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_nullable_array_on_not_empty.php.inc @@ -0,0 +1,21 @@ += 0) { + return $tierPrice; + } + } + } + + return 0.0; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_phpdoc_offset_dim_fetch.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_phpdoc_offset_dim_fetch.php.inc new file mode 100644 index 00000000000..e3d1921c90a --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_phpdoc_offset_dim_fetch.php.inc @@ -0,0 +1,24 @@ + diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_property_dim_fetch.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_property_dim_fetch.php.inc new file mode 100644 index 00000000000..72c5a88e688 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_property_dim_fetch.php.inc @@ -0,0 +1,24 @@ +> + */ + private static $groups = []; + + public static function getFootnotes($group = self::DEFAULT_GROUP): array + { + if (! empty(self::$groups[$group])) { + foreach (self::$groups[$group] as $note) { + echo "hello"; + } + } + + return []; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_value_can_null.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_value_can_null.php.inc new file mode 100644 index 00000000000..35a8b90e5f4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture/skip_value_can_null.php.inc @@ -0,0 +1,17 @@ +items) { + foreach ($this->items as $value) { + echo $value; + } + } + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/RemoveUnusedNonEmptyArrayBeforeForeachRectorTest.php b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/RemoveUnusedNonEmptyArrayBeforeForeachRectorTest.php index 6591c64b5f9..4754d0740dd 100644 --- a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/RemoveUnusedNonEmptyArrayBeforeForeachRectorTest.php +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/RemoveUnusedNonEmptyArrayBeforeForeachRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedNonEmptyArrayBeforeForeachRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/config/configured_rule.php index f3da86749f8..43c8ed520e8 100644 --- a/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnusedNonEmptyArrayBeforeForeachRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnusedNonEmptyArrayBeforeForeachRector::class]); diff --git a/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/Fixture/empty_content.php.inc b/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/Fixture/empty_content.php.inc new file mode 100644 index 00000000000..99e74c0fb86 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/Fixture/empty_content.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/SimplifyIfElseWithSameContentRectorTest.php b/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/SimplifyIfElseWithSameContentRectorTest.php index 65edda63389..329573de29a 100644 --- a/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/SimplifyIfElseWithSameContentRectorTest.php +++ b/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/SimplifyIfElseWithSameContentRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\If_\SimplifyIfElseWithSameContentRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SimplifyIfElseWithSameContentRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/config/configured_rule.php index 14f54cbd11b..dfe4be4d072 100644 --- a/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\If_\SimplifyIfElseWithSameContentRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SimplifyIfElseWithSameContentRector::class); -}; +return RectorConfig::configure() + ->withRules([SimplifyIfElseWithSameContentRector::class]); diff --git a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/Fixture/fixture.php.inc deleted file mode 100644 index bb06e0ca770..00000000000 --- a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/UnwrapFutureCompatibleIfFunctionExistsRectorTest.php b/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/UnwrapFutureCompatibleIfFunctionExistsRectorTest.php deleted file mode 100644 index 8a30e7bb37c..00000000000 --- a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/UnwrapFutureCompatibleIfFunctionExistsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/config/configured_rule.php deleted file mode 100644 index ef27e4184f9..00000000000 --- a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(UnwrapFutureCompatibleIfFunctionExistsRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/Fixture/skip_elsefis.php.inc b/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/Fixture/skip_elsefis.php.inc deleted file mode 100644 index 07a6dc234f5..00000000000 --- a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/Fixture/skip_elsefis.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/config/configured_rule.php index 91a5aa64917..5be32b91a43 100644 --- a/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UnwrapFutureCompatibleIfPhpVersionRector::class); -}; +return RectorConfig::configure() + ->withRules([UnwrapFutureCompatibleIfPhpVersionRector::class]); diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/empty_method_call_as_part_of_assign.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/empty_method_call_as_part_of_assign.php.inc deleted file mode 100644 index 083c505b8c2..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/empty_method_call_as_part_of_assign.php.inc +++ /dev/null @@ -1,55 +0,0 @@ -emptyMethod = $emptyMethod; - } - - public function run() - { - $var = $this->emptyMethod->run(); - - return false; - } -} - -?> ------ -emptyMethod = $emptyMethod; - } - - public function run() - { - $var = false; - - return false; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/empty_method_call_as_part_of_if.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/empty_method_call_as_part_of_if.php.inc deleted file mode 100644 index a9ecc28076e..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/empty_method_call_as_part_of_if.php.inc +++ /dev/null @@ -1,59 +0,0 @@ -emptyMethod = $emptyMethod; - } - - public function run() - { - if ($this->emptyMethod->run()) { - return true; - } - - return false; - } -} - -?> ------ -emptyMethod = $emptyMethod; - } - - public function run() - { - if (false) { - return true; - } - - return false; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index 5e094a95af8..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -run(); - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_call_object_from_return.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_call_object_from_return.php.inc deleted file mode 100644 index d54f058932d..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_call_object_from_return.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -getObj(); - $obj->run(); - } -} - -?> ------ -getObj(); - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_remove_method_call_on_this.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_remove_method_call_on_this.php.inc deleted file mode 100644 index 9fd15ea49ff..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_remove_method_call_on_this.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -validateLineLengths(); - } - - protected function validateLineLengths(): void - { - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_remove_method_call_on_this_use_abstract.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_remove_method_call_on_this_use_abstract.php.inc deleted file mode 100644 index de9732c89fd..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/fixture_remove_method_call_on_this_use_abstract.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -validateLineLengths(); - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_abstract_empty_method.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_abstract_empty_method.php.inc deleted file mode 100644 index d96df3889b2..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_abstract_empty_method.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -abstractEmptyMethod = $abstractEmptyMethod; - } - - public function run() - { - $this->abstractEmptyMethod->veryEmpty(); - } -} diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_current_this_object.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_current_this_object.php.inc deleted file mode 100644 index af51af9cc34..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_current_this_object.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -init(); - - echo 'run'; - } - - // can be overridden by child - protected function init() - { - } -} diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_property_fetch.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_property_fetch.php.inc deleted file mode 100644 index 231263b08a6..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_property_fetch.php.inc +++ /dev/null @@ -1,25 +0,0 @@ -obj = $obj; - } -} - -class A extends ParentClass -{ - public function execute() - { - $this->obj->run(); - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_trait.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_trait.php.inc deleted file mode 100644 index 1deb19ae85f..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_call_from_trait.php.inc +++ /dev/null @@ -1,22 +0,0 @@ -validateLineLengths(); - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_fluent_return_this.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_fluent_return_this.php.inc deleted file mode 100644 index 4a9a34c673f..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_fluent_return_this.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -prepare()->run(); - } -} - -class Extended extends SkipFluentReturnThis -{ - public function run() - { - echo 'hello'; - } -} - -(new SkipFluentReturnThis())->call(Extended::class); - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_fluent_return_this2.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_fluent_return_this2.php.inc deleted file mode 100644 index 0e68ef18c36..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_fluent_return_this2.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -run(); - } -} - -class Extended extends SkipFluentReturnThis2 -{ - public function run() - { - echo 'hello'; - } -} - -(new SkipFluentReturnThis2())->call(Extended::class); - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_in_other_method_call.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_in_other_method_call.php.inc deleted file mode 100644 index 50ea6141da3..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_in_other_method_call.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -assertNull($obj->run()); - } - - private function assertNull($value) - { - return $value === null; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_magic_name_call.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_magic_name_call.php.inc deleted file mode 100644 index 9c1e025afc3..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_magic_name_call.php.inc +++ /dev/null @@ -1,25 +0,0 @@ -emptyMethod = $emptyMethod; - } - - public function execute($magic) - { - $magic = 'one' ? 'two' : 'three'; - - $this->emptyMethod->{$magic}(); - } -} diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_non_empty_method_call.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_non_empty_method_call.php.inc deleted file mode 100644 index df22a790c05..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_non_empty_method_call.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -run(); - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_using_interface.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_using_interface.php.inc deleted file mode 100644 index f96c3921921..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Fixture/skip_using_interface.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -run(); - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/FixturePhp74/void_return_type_in_arrow_function.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/FixturePhp74/void_return_type_in_arrow_function.php.inc deleted file mode 100644 index c27273ab523..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/FixturePhp74/void_return_type_in_arrow_function.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - (new VoidReturnTypeInArrowFunction())->test(); - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/FixturePhp74/void_return_type_in_arrow_function2.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/FixturePhp74/void_return_type_in_arrow_function2.php.inc deleted file mode 100644 index ebe2eba428b..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/FixturePhp74/void_return_type_in_arrow_function2.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - (new VoidReturnTypeInArrowFunction2())->test()) { - -} - -?> ------ - false) { - -} - -?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Php74Test.php b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Php74Test.php deleted file mode 100644 index d16e3e8b86c..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Php74Test.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/RemoveEmptyMethodCallRectorTest.php b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/RemoveEmptyMethodCallRectorTest.php deleted file mode 100644 index 76955489fd4..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/RemoveEmptyMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Source/AbstractEmptyMethod.php b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Source/AbstractEmptyMethod.php deleted file mode 100644 index 32ecbfadb6a..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Source/AbstractEmptyMethod.php +++ /dev/null @@ -1,10 +0,0 @@ -d = $d; - } - - public function run() - { - return $this->d->format('Y-m-d H:i:s'); - } -} diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Source/SomeInterface.php b/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Source/SomeInterface.php deleted file mode 100644 index a3e78dd6f92..00000000000 --- a/rules-tests/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector/Source/SomeInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(RemoveEmptyMethodCallRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..7e44cab4706 --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ +callWithDefaultNull(null); + } +} + +?> +----- +callWithDefaultNull(); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_middle_default_not_null.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_middle_default_not_null.php.inc new file mode 100644 index 00000000000..6fe117f241d --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_middle_default_not_null.php.inc @@ -0,0 +1,35 @@ +withMiddleNotNullDefault(null, 1, null, null); + } +} + +?> +----- +withMiddleNotNullDefault(null, 1); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_new_ctor.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_new_ctor.php.inc new file mode 100644 index 00000000000..d2d9ebfef02 --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_new_ctor.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_static_call.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_static_call.php.inc new file mode 100644 index 00000000000..5b3ca4b379d --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/handle_static_call.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/skip_ds_map_get.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/skip_ds_map_get.php.inc new file mode 100644 index 00000000000..941dfc20f63 --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/skip_ds_map_get.php.inc @@ -0,0 +1,18 @@ +get('foo', null); + if ($value) { + //... + } + } +} diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/skip_named_argument_position_not_match.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/skip_named_argument_position_not_match.php.inc new file mode 100644 index 00000000000..e01aef962e8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/skip_named_argument_position_not_match.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/with_named_argument_position_match.php.inc b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/with_named_argument_position_match.php.inc new file mode 100644 index 00000000000..bf75090152a --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Fixture/with_named_argument_position_match.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/RemoveNullArgOnNullDefaultParamRectorTest.php b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/RemoveNullArgOnNullDefaultParamRectorTest.php new file mode 100644 index 00000000000..dc07e73c85d --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/RemoveNullArgOnNullDefaultParamRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Source/SomeExternalClass.php b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Source/SomeExternalClass.php new file mode 100644 index 00000000000..d20426eed31 --- /dev/null +++ b/rules-tests/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector/Source/SomeExternalClass.php @@ -0,0 +1,26 @@ +withRules([RemoveNullArgOnNullDefaultParamRector::class]); diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/keep_object_shape.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/keep_object_shape.php.inc new file mode 100644 index 00000000000..dd540033f9e --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/keep_object_shape.php.inc @@ -0,0 +1,12 @@ + $sickDays + */ +foreach ($sickDays as $vacation) { + +} + +?> +----- + $sickDays + */ +foreach ($sickDays as $vacation) { + +} + +?> diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/never_used_next_statement.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/never_used_next_statement.php.inc new file mode 100644 index 00000000000..81135a711a7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/never_used_next_statement.php.inc @@ -0,0 +1,50 @@ + 'value a', + 'b' => 'value b', + 'c' => 'value c', + ]; + + extract($options); + /** + * @var string $a + * @var string $b + * @var string $c + */ + $callback = function ($var1, $var2, $var3) { + return $var1 . $var2 . $var3; + }; + } +} + +?> +----- + 'value a', + 'b' => 'value b', + 'c' => 'value c', + ]; + + extract($options); + $callback = function ($var1, $var2, $var3) { + return $var1 . $var2 . $var3; + }; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/other_comment_before_var.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/other_comment_before_var.php.inc index 5686c1ef99d..ec729a9e51b 100644 --- a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/other_comment_before_var.php.inc +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/other_comment_before_var.php.inc @@ -2,29 +2,12 @@ namespace Rector\Tests\DeadCode\Rector\Node\RemoveNonExistingVarAnnotationRector\Fixture; -class OtherCommentBeforeVar +final class SkipOtherCommentBeforeVar { public function get() { /** @var \stdClass $nonExisting */ - // Load data also with projekt... + // Load data also with project... $return[] = $this->getReturnData(); } } - -?> ------ -getReturnData(); - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/return_annotation_invalid.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/return_annotation_invalid.php.inc new file mode 100644 index 00000000000..b1c385cbc17 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/return_annotation_invalid.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/return_annotation_unwanted.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/return_annotation_unwanted.php.inc new file mode 100644 index 00000000000..3fdd8fa12e0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/return_annotation_unwanted.php.inc @@ -0,0 +1,28 @@ + */ + return new stdClass; + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_direct_return_func_call.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_direct_return_func_call.php.inc new file mode 100644 index 00000000000..ab88a4fdafd --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_direct_return_func_call.php.inc @@ -0,0 +1,12 @@ +foo = 'foo') { + $foundstd = true; + break; + } + } + } +} diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_indirect_variable_definition.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_indirect_variable_definition.php.inc new file mode 100644 index 00000000000..af902c8e29d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_indirect_variable_definition.php.inc @@ -0,0 +1,12 @@ +*/ + $x = new MyClass; + } +} diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_next_inline_html.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_next_inline_html.php.inc new file mode 100644 index 00000000000..471dd0bf30a --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_next_inline_html.php.inc @@ -0,0 +1,9 @@ + + +
\ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_other_comment_before_var.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_other_comment_before_var.php.inc new file mode 100644 index 00000000000..ec729a9e51b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_other_comment_before_var.php.inc @@ -0,0 +1,13 @@ +getReturnData(); + } +} diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_property.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_property.php.inc new file mode 100644 index 00000000000..9c1bc8a7d54 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_property.php.inc @@ -0,0 +1,11 @@ + + */ + static $cached_result = []; + + if (!isset($cached_result[$param])) { + $cached_result[$param] = doExpensiveCalculation($param); + } + + return $cached_result[$param]; + } +} diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_used_after_next_statement.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_used_after_next_statement.php.inc new file mode 100644 index 00000000000..47f103d3bec --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_used_after_next_statement.php.inc @@ -0,0 +1,29 @@ + 'value a', + 'b' => 'value b', + 'c' => 'value c', + ]; + + extract($options); + /** + * @var string $a + * @var string $b + * @var string $c + */ + $callback = function ($var1, $var2, $var3) { + return $var1 . $var2 . $var3; + }; + + $callback($a, $b, $c); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_used_after_next_statement2.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_used_after_next_statement2.php.inc new file mode 100644 index 00000000000..7ddb8d486a3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/skip_used_after_next_statement2.php.inc @@ -0,0 +1,25 @@ + diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/subcontent.php.inc b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/subcontent.php.inc deleted file mode 100644 index d4363012f76..00000000000 --- a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/Fixture/subcontent.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -getAnotherData(); - - return $trainings; - } -} - -?> ------ -getAnotherData(); - - return $trainings; - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/RemoveNonExistingVarAnnotationRectorTest.php b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/RemoveNonExistingVarAnnotationRectorTest.php index a75142fa4d6..9583b507823 100644 --- a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/RemoveNonExistingVarAnnotationRectorTest.php +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/RemoveNonExistingVarAnnotationRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Node\RemoveNonExistingVarAnnotationRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveNonExistingVarAnnotationRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/config/configured_rule.php index 1e2a61750d5..db25000bfc0 100644 --- a/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Node\RemoveNonExistingVarAnnotationRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveNonExistingVarAnnotationRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveNonExistingVarAnnotationRector::class]); diff --git a/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/multiply_function_call.php.inc b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/multiply_function_call.php.inc new file mode 100644 index 00000000000..9756a8a7367 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/multiply_function_call.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/skip_constants.php.inc b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/skip_constants.php.inc new file mode 100644 index 00000000000..1fafe179417 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/skip_constants.php.inc @@ -0,0 +1,16 @@ + 0, + 'data' => [], + ]; + + foreach ($results as $item) { + if ($item->value > $stats->maxRange) { + $stats->maxRange = $item->value; + } + } + + $stats->maxRange = (intval(($stats->maxRange + 1) / 500) + 1) * 500; + + return $stats->maxRange; +} diff --git a/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/with_parentheses.php.inc b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/with_parentheses.php.inc new file mode 100644 index 00000000000..1198a8601ae --- /dev/null +++ b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Fixture/with_parentheses.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/RemoveDeadZeroAndOneOperationRectorTest.php b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/RemoveDeadZeroAndOneOperationRectorTest.php index 3d8b6ddb3ae..cbc85eea808 100644 --- a/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/RemoveDeadZeroAndOneOperationRectorTest.php +++ b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/RemoveDeadZeroAndOneOperationRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Plus\RemoveDeadZeroAndOneOperationRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDeadZeroAndOneOperationRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Source/SomeParentClass.php b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Source/SomeParentClass.php new file mode 100644 index 00000000000..cd72af76746 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector/Source/SomeParentClass.php @@ -0,0 +1,8 @@ +services(); - $services->set(RemoveDeadZeroAndOneOperationRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadZeroAndOneOperationRector::class]); diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/array_dim_fetch_use.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/array_dim_fetch_use.php.inc deleted file mode 100644 index 7c67798d491..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/array_dim_fetch_use.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -events[$this->currentProvider] = 1000; - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/constructor_only.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/constructor_only.php.inc index 77b2d632cc7..8e2c3289847 100644 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/constructor_only.php.inc +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/constructor_only.php.inc @@ -23,7 +23,7 @@ namespace Rector\Tests\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRecto class ConstructorOnly { - public function __construct() + public function __construct(int $contentFinder) { } } diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/constructor_only_static.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/constructor_only_static.php.inc new file mode 100644 index 00000000000..c20bc6c5c66 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/constructor_only_static.php.inc @@ -0,0 +1,16 @@ +contentFinder = $contentFinder; + $this->init($stdClass); + } + + private function init(\stdClass $stdClass) + { + var_dump($stdClass); + } +} + +?> +----- +init($stdClass); + } + + private function init(\stdClass $stdClass) + { + var_dump($stdClass); + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/do_not_remove_used_parameter_in_method_call.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/do_not_remove_used_parameter_in_method_call.php.inc new file mode 100644 index 00000000000..17a08fa52d4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/do_not_remove_used_parameter_in_method_call.php.inc @@ -0,0 +1,51 @@ +page = $page; + $this->section = $section; + + parent::__construct($page, $section); + } + + public function getPage() + { + return $this->page; + } +} + +?> +----- +page = $page; + + parent::__construct($page, $section); + } + + public function getPage() + { + return $this->page; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/doctrine_entity_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/doctrine_entity_property.php.inc index 9dd57b0da4a..691f5ca4569 100644 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/doctrine_entity_property.php.inc +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/doctrine_entity_property.php.inc @@ -3,6 +3,8 @@ namespace Rector\Tests\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector\Fixture; use Doctrine\ORM\Mapping as ORM; +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; /** * @ORM\Entity @@ -15,6 +17,9 @@ class DoctrineEntityProperty * @ORM\Column */ private $unusedAnnotatedProperty; + + #[Id, Column(type: "integer")] + private $id = null; } ?> @@ -24,6 +29,8 @@ class DoctrineEntityProperty namespace Rector\Tests\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector\Fixture; use Doctrine\ORM\Mapping as ORM; +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; /** * @ORM\Entity @@ -34,6 +41,9 @@ class DoctrineEntityProperty * @ORM\Column */ private $unusedAnnotatedProperty; + + #[Id, Column(type: "integer")] + private $id = null; } ?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/fixture.php.inc deleted file mode 100644 index 6759d9d1127..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/function_call.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/function_call.php.inc deleted file mode 100644 index 847390bdf9f..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/function_call.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -prop = foo(); - - $this->prop[bar()] = foo(); - - return $this->prop[bar()] = foo(); - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/including_assign.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/including_assign.php.inc deleted file mode 100644 index 0b9d7088ecf..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/including_assign.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -someService = $this->getService('Some'); - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/magically_assigned_fetch.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/magically_assigned_fetch.php.inc index ef923092769..b4bdcf8297c 100644 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/magically_assigned_fetch.php.inc +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/magically_assigned_fetch.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector\Fixture; -class MagicallyAssignedFetch +final class SkipMagicallyAssignedFetch { private $seemsUnused; @@ -14,20 +14,3 @@ class MagicallyAssignedFetch $this->{$this->anotherName} = 'foo'; } } -?> ------ -{$this->anotherName} = 'foo'; - } -} -?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/no_usage_outside_anonymous_class.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/no_usage_outside_anonymous_class.php.inc deleted file mode 100644 index efa7d17c8a7..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/no_usage_outside_anonymous_class.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -var; - } - }; - } -} -?> ------ -var; - } - }; - } -} -?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/null-coalescing-assignment.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/null-coalescing-assignment.php.inc new file mode 100644 index 00000000000..4cd4e73ea56 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/null-coalescing-assignment.php.inc @@ -0,0 +1,11 @@ +bar ??= 3; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/on_arg.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/on_arg.php.inc new file mode 100644 index 00000000000..e505a36e7fa --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/on_arg.php.inc @@ -0,0 +1,39 @@ +now = nowEagleTz(), + ); + } +} + +?> +----- + \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/remove_magic_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/remove_magic_property.php.inc deleted file mode 100644 index 4f5674fceb5..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/remove_magic_property.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -mails_to_send[$recipient->email] = ""; - - foreach ($this->mails_to_send as $email) { - } - } -} - -?> ------ -mails_to_send[$recipient->email] = ""; - - foreach ($this->mails_to_send as $email) { - } - } -} - -?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/remove_only_private_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/remove_only_private_property.php.inc new file mode 100644 index 00000000000..b31be2787d9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/remove_only_private_property.php.inc @@ -0,0 +1,29 @@ +dataProperty->unusedPropertyName = 'some data'; + } +} +?> +----- +dataProperty->unusedPropertyName = 'some data'; + } +} +?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/sip_new_same_classname_usage.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/sip_new_same_classname_usage.php.inc new file mode 100644 index 00000000000..2fa0987cd0b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/sip_new_same_classname_usage.php.inc @@ -0,0 +1,19 @@ +var = $var; + } + + public function run($var) + { + $obj = new SkipNewSameClassNameUsage($var); + echo $obj->var; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_anonymous_class.php.inc deleted file mode 100644 index c95192599f9..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_anonymous_class.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -callable = $callable; - } - - /** - * @return int|Node|null - */ - public function enterNode(Node $node) - { - $callable = $this->callable; - return $callable($node); - } - }; - } -} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_anonymous_class_as_risky.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_anonymous_class_as_risky.php.inc new file mode 100644 index 00000000000..505eed784f5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_anonymous_class_as_risky.php.inc @@ -0,0 +1,20 @@ +var; + } + }; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_append_assignment.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_append_assignment.php.inc new file mode 100644 index 00000000000..951a5da1c16 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_append_assignment.php.inc @@ -0,0 +1,11 @@ +bar += 3; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_appender_assignment.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_appender_assignment.php.inc new file mode 100644 index 00000000000..5b60aa442f2 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_appender_assignment.php.inc @@ -0,0 +1,14 @@ +bar; + + return $a; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_array_pop.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_array_pop.php.inc deleted file mode 100644 index 2ba819380c5..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_array_pop.php.inc +++ /dev/null @@ -1,28 +0,0 @@ -classStack); - $node->setAttribute(AttributeKey::CLASS_NODE, $classLike); - } - - public function processClass(Node $node): void - { - $this->classStack[] = $this->classLike; - } -} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_clone_usage.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_clone_usage.php.inc new file mode 100644 index 00000000000..1b218745935 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_clone_usage.php.inc @@ -0,0 +1,19 @@ +var = $var; + } + + public function run() + { + $obj = clone $this; + echo $obj->var; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_dim_fetch_write_only.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_dim_fetch_write_only.php.inc new file mode 100644 index 00000000000..5ed45edeaec --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_dim_fetch_write_only.php.inc @@ -0,0 +1,15 @@ +seemsUnused[] = 'foo'; + } + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_dim_fetched_used.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_dim_fetched_used.php.inc new file mode 100644 index 00000000000..5e2b7d8e548 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_dim_fetched_used.php.inc @@ -0,0 +1,22 @@ +itemId = $itemId; + } + + public function isActive() + { + return $this->items[$this->itemId] ?? 'yes'; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_in_middle_assign.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_in_middle_assign.php.inc new file mode 100644 index 00000000000..239df7f26e0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_in_middle_assign.php.inc @@ -0,0 +1,14 @@ +a = 0; + return $b; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_magically_accessed_fetch.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_magically_accessed_fetch.php.inc deleted file mode 100644 index ee159692ac5..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_magically_accessed_fetch.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -{$this->anotherName}; - } -} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_multi_assign.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_multi_assign.php.inc new file mode 100644 index 00000000000..2ea548fe4d9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_multi_assign.php.inc @@ -0,0 +1,15 @@ +content_type = $content_type = "Something"; + + return $content_type; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_new_self_usage.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_new_self_usage.php.inc new file mode 100644 index 00000000000..fb0e36b97d1 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_new_self_usage.php.inc @@ -0,0 +1,22 @@ +var = $var; + self::$var2 = $var2; + } + + public function run() + { + $obj = new self('a', 'b'); + echo $obj->var; + echo $obj::$var2; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_nullsafe_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_nullsafe_property.php.inc new file mode 100644 index 00000000000..5f09c36c722 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_nullsafe_property.php.inc @@ -0,0 +1,16 @@ +getX()?->myProp; + } + + public function getX(): SkipNullSafePropertyFetch|null { + return rand(0,1) ? new SkipNullSafePropertyFetch() : null; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_propertyfetch_used_in_assign_expr.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_propertyfetch_used_in_assign_expr.php.inc new file mode 100644 index 00000000000..eb41f1c3512 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_propertyfetch_used_in_assign_expr.php.inc @@ -0,0 +1,23 @@ +foo = $foo; + $this->bar = $bar; + } + + protected function calculateMac(): string + { + $foobar = $this->foo; + $foobar .= $this->bar; + + return md5($foobar); + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_read_dim_fetch_use.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_read_dim_fetch_use.php.inc deleted file mode 100644 index d358556c90a..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_read_dim_fetch_use.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -key = $key; - } - - public function buildData(): array - { - $data[$this->key] = 100000000; - - return $data; - } -} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_self_nullsafe_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_self_nullsafe_property.php.inc new file mode 100644 index 00000000000..6a0a25893d7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_self_nullsafe_property.php.inc @@ -0,0 +1,16 @@ +getX()?->myProp; + } + + public function getX(): self|null { + return rand(0,1) ? new self() : null; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_static_property_fetch_public.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_static_property_fetch_public.php.inc new file mode 100644 index 00000000000..626c01f656d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_static_property_fetch_public.php.inc @@ -0,0 +1,23 @@ +entityId = $dto->getId(); + $this->className = $dto::class; + + self::$pendingList[$this->className][$this->entityId] = true; + } +} + + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_trait_in_trait_user.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_trait_in_trait_user.php.inc deleted file mode 100644 index 830fe4cf3ec..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_trait_in_trait_user.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -property; - } -} - -trait SkipTraitInTraitUser -{ - use TraitA; - - private $property = 10; -} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_used_under_isset.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_used_under_isset.php.inc deleted file mode 100644 index 5d1e499b6c8..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_used_under_isset.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -oldPackageName = $oldPackageName; - $this->newPackageName = $newPackageName; - $this->targetVersion = $targetVersion; - } - - public function modify(array $composerData): array - { - foreach (['require', 'require-dev'] as $section) { - if (isset($composerData[$section][$this->oldPackageName])) { - unset($composerData[$section][$this->oldPackageName]); - $composerData[$section][$this->newPackageName] = $this->targetVersion; - } - } - - return $composerData; - } -} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_user.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_user.php.inc deleted file mode 100644 index 9593cc93fa7..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_user.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -count); - } - - return $key; - } -} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_using_coalesce_assign_operator.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_using_coalesce_assign_operator.php.inc new file mode 100644 index 00000000000..8b46483e440 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_using_coalesce_assign_operator.php.inc @@ -0,0 +1,20 @@ +someService = $someService; + } + + public function get(string $key) + { + return $this->types[$key] ??= $this->someService->resolve(); + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_with_trait.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_with_trait.php.inc deleted file mode 100644 index a09b3c57646..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/skip_with_trait.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -a; - } -} -class SkipWithTrait -{ - use SomeTrait; - private $a; -} - -?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/static_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/static_property.php.inc index 8fb705d910e..9b49c802aa7 100644 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/static_property.php.inc +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/static_property.php.inc @@ -6,31 +6,9 @@ class StaticProperty { public static $publicProperty; protected static $protectedProperty; - private static $privateProperty; - private static $unusedPrivateProperty; public function foo() { return $this::$privateProperty; } } - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/used_in_assign_return.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/used_in_assign_return.php.inc new file mode 100644 index 00000000000..e91eeb3856b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/used_in_assign_return.php.inc @@ -0,0 +1,29 @@ +count = 2; + } +} + +?> +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only.php.inc deleted file mode 100644 index 71457df5115..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -seemsUnused[] = 'foo'; - } - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only_dim_fetch_use.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only_dim_fetch_use.php.inc deleted file mode 100644 index 0cfba66dbbf..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only_dim_fetch_use.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -key = $key; - } - - public function buildData(): array - { - $data[$this->key] = 10000; - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only_dim_fetch_use_variable.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only_dim_fetch_use_variable.php.inc deleted file mode 100644 index c6e53bb8b7d..00000000000 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Fixture/write_only_dim_fetch_use_variable.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -key = $key; - } - - public function buildData(): array - { - $this->writeOnly[$this->key] = 10000; - } -} - -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/RemoveUnusedPrivatePropertyRectorTest.php b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/RemoveUnusedPrivatePropertyRectorTest.php index 80a0c4aa33f..8b67b0c9bc0 100644 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/RemoveUnusedPrivatePropertyRectorTest.php +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/RemoveUnusedPrivatePropertyRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedPrivatePropertyRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Source/AbstractSetting.php b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Source/AbstractSetting.php new file mode 100644 index 00000000000..37c4f843e60 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/Source/AbstractSetting.php @@ -0,0 +1,15 @@ +runSomething(); + $section->runSomething(); + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/config/configured_rule.php index d93bbeabaec..280710251a2 100644 --- a/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnusedPrivatePropertyRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnusedPrivatePropertyRector::class]); diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_class.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_class.php.inc new file mode 100644 index 00000000000..df38cce3edb --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_class.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_param_construct.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_param_construct.php.inc new file mode 100644 index 00000000000..1b9b8366114 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_param_construct.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_property.php.inc new file mode 100644 index 00000000000..6d7f7b3d7ab --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_property.php.inc @@ -0,0 +1,34 @@ +name = $name; + } +} + +?> +----- +name = $name; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_no_readonly_doc.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_no_readonly_doc.php.inc new file mode 100644 index 00000000000..f2047395390 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_no_readonly_doc.php.inc @@ -0,0 +1,13 @@ +name = $name; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_with_description.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_with_description.php.inc new file mode 100644 index 00000000000..eb9dd784d86 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_with_description.php.inc @@ -0,0 +1,16 @@ +name = $name; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/RemoveUselessReadOnlyTagRectorTest.php b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/RemoveUselessReadOnlyTagRectorTest.php new file mode 100644 index 00000000000..90b3a2dc676 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/RemoveUselessReadOnlyTagRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/config/configured_rule.php new file mode 100644 index 00000000000..653369471a9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/config/configured_rule.php @@ -0,0 +1,11 @@ +withRules([RemoveUselessReadOnlyTagRector::class]) + ->withPhpVersion(PhpVersion::PHP_81); diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/keep_const_float_type.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/keep_const_float_type.php.inc new file mode 100644 index 00000000000..95af5b22408 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/keep_const_float_type.php.inc @@ -0,0 +1,13 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_expression.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_expression.php.inc new file mode 100644 index 00000000000..ed6084a7e7b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_expression.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_multi_var_remove_one_var.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_multi_var_remove_one_var.php.inc new file mode 100644 index 00000000000..addd937fb7d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_multi_var_remove_one_var.php.inc @@ -0,0 +1,40 @@ +bulkConsumerRegistration ?? "abc"; + +?> +----- +bulkConsumerRegistration ?? "abc"; + +?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_equal_type.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_equal_type.php.inc new file mode 100644 index 00000000000..b8a11e082de --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_equal_type.php.inc @@ -0,0 +1,22 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_null.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_null.php.inc new file mode 100644 index 00000000000..adbcb993329 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_null.php.inc @@ -0,0 +1,25 @@ + +---- + + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_specific_typed_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_specific_typed_property.php.inc new file mode 100644 index 00000000000..7031854b8bc --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_specific_typed_property.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_super_type.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_super_type.php.inc new file mode 100644 index 00000000000..b6a7f3ea707 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/remove_super_type.php.inc @@ -0,0 +1,22 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_different_typed_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_different_typed_property.php.inc new file mode 100644 index 00000000000..e6df7c76be8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_different_typed_property.php.inc @@ -0,0 +1,14 @@ +get(); diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_sub_type.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_sub_type.php.inc new file mode 100644 index 00000000000..92b75857b43 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_sub_type.php.inc @@ -0,0 +1,9 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/RemoveUselessVarTagRectorTest.php b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/RemoveUselessVarTagRectorTest.php index 62a55c4703d..84575d8feb9 100644 --- a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/RemoveUselessVarTagRectorTest.php +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/RemoveUselessVarTagRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Property\RemoveUselessVarTagRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUselessVarTagRectorTest extends AbstractRectorTestCase { - /** - * @requires PHP 7.4 - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Source/DifferentTypeProperty.php b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Source/DifferentTypeProperty.php new file mode 100644 index 00000000000..01da3e27063 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Source/DifferentTypeProperty.php @@ -0,0 +1,8 @@ +services(); - $services->set(RemoveUselessVarTagRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUselessVarTagRector::class]); diff --git a/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/FixtureTypedProperties/skip_typed_properties.php.inc b/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/FixtureTypedProperties/skip_typed_properties.php.inc index 7060e2df3a5..caaf6efb75a 100644 --- a/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/FixtureTypedProperties/skip_typed_properties.php.inc +++ b/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/FixtureTypedProperties/skip_typed_properties.php.inc @@ -1,6 +1,6 @@ doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/TypedPropertiesRemoveNullPropertyInitializationRectorTest.php b/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/TypedPropertiesRemoveNullPropertyInitializationRectorTest.php index c8c937d3731..f4f5a5f4f87 100644 --- a/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/TypedPropertiesRemoveNullPropertyInitializationRectorTest.php +++ b/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/TypedPropertiesRemoveNullPropertyInitializationRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\PropertyProperty\RemoveNullPropertyInitializationRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class TypedPropertiesRemoveNullPropertyInitializationRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - * @requires PHP 7.4 - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureTypedProperties'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureTypedProperties'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/config/configured_rule.php index b70f3ca4f37..0d8c1cb4e92 100644 --- a/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\PropertyProperty\RemoveNullPropertyInitializationRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveNullPropertyInitializationRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveNullPropertyInitializationRector::class]); diff --git a/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/config/typed_properties.php b/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/config/typed_properties.php index d7dfcaf20cb..70c1c5b5c0f 100644 --- a/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/config/typed_properties.php +++ b/rules-tests/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector/config/typed_properties.php @@ -2,15 +2,11 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\PropertyProperty\RemoveNullPropertyInitializationRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::TYPED_PROPERTIES); - - $services = $containerConfigurator->services(); - $services->set(RemoveNullPropertyInitializationRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersionFeature::TYPED_PROPERTIES); + $rectorConfig->rule(RemoveNullPropertyInitializationRector::class); }; diff --git a/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/Fixture/skip_method_call_in_condition.php.inc b/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/Fixture/skip_method_call_in_condition.php.inc new file mode 100644 index 00000000000..5af81869a1c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/Fixture/skip_method_call_in_condition.php.inc @@ -0,0 +1,15 @@ +save()) { + return true; + } + + return true; + } +} diff --git a/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/Fixture/skip_method_call_in_condition_with_concat.php.inc b/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/Fixture/skip_method_call_in_condition_with_concat.php.inc new file mode 100644 index 00000000000..28070bc1d5b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/Fixture/skip_method_call_in_condition_with_concat.php.inc @@ -0,0 +1,15 @@ +save()) { + return true; + } + + return true; + } +} diff --git a/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/RemoveDeadConditionAboveReturnRectorTest.php b/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/RemoveDeadConditionAboveReturnRectorTest.php index e1875492443..bd9a18c045e 100644 --- a/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/RemoveDeadConditionAboveReturnRectorTest.php +++ b/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/RemoveDeadConditionAboveReturnRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Return_\RemoveDeadConditionAboveReturnRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDeadConditionAboveReturnRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/config/configured_rule.php index 5e54cccbbfb..648f6556d5b 100644 --- a/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Return_\RemoveDeadConditionAboveReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDeadConditionAboveReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadConditionAboveReturnRector::class]); diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/anonymous_class_without_parent.php.inc b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/anonymous_class_without_parent.php.inc deleted file mode 100644 index 62bfcd23804..00000000000 --- a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/anonymous_class_without_parent.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/parent_but_no_method.php.inc b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/parent_but_no_method.php.inc deleted file mode 100644 index d874bb82e7b..00000000000 --- a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/parent_but_no_method.php.inc +++ /dev/null @@ -1,52 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/FixturePhp74/parent_with_return_and_used_in_mixin.php.inc b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/parent_with_return_and_used_in_mixin.php.inc similarity index 100% rename from rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/FixturePhp74/parent_with_return_and_used_in_mixin.php.inc rename to rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/parent_with_return_and_used_in_mixin.php.inc diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/skip_another_static_call.php.inc b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/skip_another_static_call.php.inc new file mode 100644 index 00000000000..de337264a9c --- /dev/null +++ b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/skip_another_static_call.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/skip_with_assign_parent_not_loaded.php.inc b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/skip_with_assign_parent_not_loaded.php.inc new file mode 100644 index 00000000000..ed1fa8a21c4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Fixture/skip_with_assign_parent_not_loaded.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Php74Test.php b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Php74Test.php deleted file mode 100644 index 7dbbe74fd24..00000000000 --- a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Php74Test.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php74.php'; - } -} diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/RemoveParentCallWithoutParentRectorTest.php b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/RemoveParentCallWithoutParentRectorTest.php index c7eb2832d6e..e341f9ac06b 100644 --- a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/RemoveParentCallWithoutParentRectorTest.php +++ b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/RemoveParentCallWithoutParentRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveParentCallWithoutParentRectorTest extends AbstractRectorTestCase { - /** - * @requires PHP < 8.0 - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Source/SomeParentMethod.php b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Source/SomeParentMethod.php new file mode 100644 index 00000000000..da27069ed42 --- /dev/null +++ b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/Source/SomeParentMethod.php @@ -0,0 +1,10 @@ +services(); - $services->set(RemoveParentCallWithoutParentRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveParentCallWithoutParentRector::class]); diff --git a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/config/php74.php b/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/config/php74.php deleted file mode 100644 index 4ee6ecce394..00000000000 --- a/rules-tests/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector/config/php74.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_74); - - $services = $containerConfigurator->services(); - $services->set(RemoveParentCallWithoutParentRector::class); -}; diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/allow_any_type.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/allow_any_type.php.inc new file mode 100644 index 00000000000..addcf8cdd51 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/allow_any_type.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/allow_equal.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/allow_equal.php.inc new file mode 100644 index 00000000000..4c80d441029 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/allow_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/any_expr_compare.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/any_expr_compare.php.inc new file mode 100644 index 00000000000..0483501edb8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/any_expr_compare.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/skip_different_return.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/skip_different_return.php.inc new file mode 100644 index 00000000000..c9c02faae87 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/skip_different_return.php.inc @@ -0,0 +1,15 @@ +isMissing(100)) { + return $this->isMissing(100); + } + + return $result; + } + + private function isMissing(int $int): int + { + return mt_rand(0, 1) * $int; + } +} diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/skip_side_effect_native.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/skip_side_effect_native.php.inc new file mode 100644 index 00000000000..a1f23a3d273 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/Fixture/skip_side_effect_native.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/RemoveConditionExactReturnRectorTest.php b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/RemoveConditionExactReturnRectorTest.php new file mode 100644 index 00000000000..53f37383059 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/RemoveConditionExactReturnRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/config/configured_rule.php new file mode 100644 index 00000000000..38fa889998d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveConditionExactReturnRector::class]); diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/binairy_op.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/binairy_op.php.inc deleted file mode 100644 index cdf5141bfa4..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/binairy_op.php.inc +++ /dev/null @@ -1,70 +0,0 @@ - 2; -1 >= 2; -1 <= 2; -1 <=> 2; -1 & 2; -1 | 2; -1 << 2; -1 >> 2; -1 !== 2; -1 != 2; -1 == 2; -1 === 2; -1 xor 2; - -1 and 2; -1 or 2; -1 ?? 2; -1 && 2; -1 || 2; - -function wrapToPreventPhpStanCallingMethods () -{ - 1 + foo(); - foo() + 1; - foo2() + foo3(); -} -?> ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/class_constant_fetch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/class_constant_fetch.php.inc deleted file mode 100644 index ab267e944bb..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/class_constant_fetch.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/clone.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/clone.php.inc deleted file mode 100644 index 79447a836c6..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/clone.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/closure.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/closure.php.inc deleted file mode 100644 index 630e841aa7d..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/closure.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/empty.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/empty.php.inc deleted file mode 100644 index 2a09b9b296b..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/empty.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/instanceOf.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/instanceOf.php.inc deleted file mode 100644 index b1bb60288cf..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/instanceOf.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/isset.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/isset.php.inc deleted file mode 100644 index 9044b3ba4f3..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/isset.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/static_property_fetch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/static_property_fetch.php.inc deleted file mode 100644 index d18db941a02..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/static_property_fetch.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/unairy.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/unairy.php.inc deleted file mode 100644 index ce294f7cfcf..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/unairy.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/variable.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/variable.php.inc deleted file mode 100644 index 23cacfccc99..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/Fixture/variable.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/array_dim_fetch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/array_dim_fetch.php.inc deleted file mode 100644 index 9ad5840b1d7..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/array_dim_fetch.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/comment_unwrap_keep.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/comment_unwrap_keep.php.inc deleted file mode 100644 index 1b5a15d4c11..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/comment_unwrap_keep.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/property_fetch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/property_fetch.php.inc deleted file mode 100644 index 2ee32638804..00000000000 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveDeadStmtRector/FixtureRemovedComments/property_fetch.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -prop = 1; - - $var->prop; - - //comment - $var->{methodCall()}; - - //comment - ${methodCall()}->prop; - - //comment - ${methodCall1()}->{methodCall2()}; -} -?> ------ -prop = 1; - - //comment - methodCall(); - - //comment - methodCall(); - - //comment - methodCall1(); - methodCall2(); -} -?> diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/Fixture/merge_comment.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/Fixture/merge_comment.php.inc new file mode 100644 index 00000000000..7a1ea0e9f10 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/Fixture/merge_comment.php.inc @@ -0,0 +1,44 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/Fixture/skip_if_body_used.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/Fixture/skip_if_body_used.php.inc new file mode 100644 index 00000000000..df2ba164d0d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/Fixture/skip_if_body_used.php.inc @@ -0,0 +1,18 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/RemoveNextSameValueConditionRectorTest.php b/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/RemoveNextSameValueConditionRectorTest.php new file mode 100644 index 00000000000..27cb5054a9c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/RemoveNextSameValueConditionRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/config/configured_rule.php new file mode 100644 index 00000000000..d4543f5b089 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveNextSameValueConditionRector::class]); diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_if_else.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_if_else.php.inc new file mode 100644 index 00000000000..efaeeb9707e --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_if_else.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_if_elseif_else.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_if_elseif_else.php.inc new file mode 100644 index 00000000000..bb896f4ae49 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_if_elseif_else.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_switch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_switch.php.inc new file mode 100644 index 00000000000..5a3b7b22e7e --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_switch.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_switch_case_collection.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_switch_case_collection.php.inc new file mode 100644 index 00000000000..df217bf4126 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_switch_case_collection.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_try_catch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_try_catch.php.inc new file mode 100644 index 00000000000..451646bcf04 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/always_terminated_try_catch.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/keep_function_after_return_no_namespace.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/keep_function_after_return_no_namespace.php.inc new file mode 100644 index 00000000000..c52488769a7 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/keep_function_after_return_no_namespace.php.inc @@ -0,0 +1,9 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/side_effect_return_early.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/side_effect_return_early.php.inc new file mode 100644 index 00000000000..49533df8098 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/side_effect_return_early.php.inc @@ -0,0 +1,31 @@ +sideEffect(); + return 5; + + $removeMe = 10; + } +} + +?> +----- +sideEffect(); + return 5; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_arrow_fn.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_arrow_fn.php.inc new file mode 100644 index 00000000000..fe7bc62f312 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_arrow_fn.php.inc @@ -0,0 +1,12 @@ + true; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_assign_only_in_switch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_assign_only_in_switch.php.inc new file mode 100644 index 00000000000..bfdab37d31d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_assign_only_in_switch.php.inc @@ -0,0 +1,17 @@ + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_goto_direct.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_goto_direct.php.inc new file mode 100644 index 00000000000..7c17f5b229a --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_goto_direct.php.inc @@ -0,0 +1,17 @@ + + Please disconnect + + + We couldn't connect you + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_if_elseif.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_if_elseif.php.inc new file mode 100644 index 00000000000..7ab89fb3c86 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_if_elseif.php.inc @@ -0,0 +1,17 @@ +assertTrue('...'); + } +} diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_no_case_in_switch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_no_case_in_switch.php.inc new file mode 100644 index 00000000000..4d8769db243 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_no_case_in_switch.php.inc @@ -0,0 +1,14 @@ +value = 1; + + if ($this->sideEffect()) { + + } + + if ($this->value === 1) { + return 'a'; + } + + return 'b'; + } +} diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_switch_break_not_unreachable.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_switch_break_not_unreachable.php.inc new file mode 100644 index 00000000000..abe8b8c4bf4 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/skip_switch_break_not_unreachable.php.inc @@ -0,0 +1,19 @@ + $__outputLevel__) { + ob_end_clean(); + } + } + + return false; + } +} \ No newline at end of file diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_break.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_break.php.inc new file mode 100644 index 00000000000..80f76a6f8e0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_break.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_case.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_case.php.inc new file mode 100644 index 00000000000..efa39ebf2cd --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_case.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_catch.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_catch.php.inc new file mode 100644 index 00000000000..5f064d378bf --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_catch.php.inc @@ -0,0 +1,42 @@ +getMessage()); + echo 'test'; + } + } +} + +?> +----- +getMessage()); + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_continue.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_continue.php.inc new file mode 100644 index 00000000000..5308016ea7c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_continue.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_do.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_do.php.inc new file mode 100644 index 00000000000..8ba086e4c63 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_do.php.inc @@ -0,0 +1,38 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_else_if.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_else_if.php.inc new file mode 100644 index 00000000000..ebc6ab154bf --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_else_if.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_finally.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_finally.php.inc new file mode 100644 index 00000000000..e8410fcdd34 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_finally.php.inc @@ -0,0 +1,42 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_for.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_for.php.inc new file mode 100644 index 00000000000..b33462c4b5a --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_for.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_goto_indirect.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_goto_indirect.php.inc new file mode 100644 index 00000000000..8a6c3abe404 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_goto_indirect.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_try.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_try.php.inc new file mode 100644 index 00000000000..866d55b6705 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_try.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_while.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_while.php.inc new file mode 100644 index 00000000000..085b2b213f8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/some_while.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/switch_return_after_break.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/switch_return_after_break.php.inc new file mode 100644 index 00000000000..19bdbbeb029 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/switch_return_after_break.php.inc @@ -0,0 +1,42 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/try_finally.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/try_finally.php.inc index 3ffa157b714..05f7f760c25 100644 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/try_finally.php.inc +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/try_finally.php.inc @@ -8,7 +8,7 @@ class TryFinally public function setMultiple($values, $ttl = null): bool { try { - return true; + throw new Exception(); } finally { echo 3; } @@ -29,7 +29,7 @@ class TryFinally public function setMultiple($values, $ttl = null): bool { try { - return true; + throw new Exception(); } finally { echo 3; } diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/try_return_finally_comment.php.inc b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/try_return_finally_comment.php.inc new file mode 100644 index 00000000000..6f49e14b32a --- /dev/null +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/Fixture/try_return_finally_comment.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/RemoveUnreachableStatementRectorTest.php b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/RemoveUnreachableStatementRectorTest.php index 9572bd8ab23..9ceb4b43be5 100644 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/RemoveUnreachableStatementRectorTest.php +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/RemoveUnreachableStatementRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnreachableStatementRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/config/configured_rule.php index 884fdea7665..1046d99ac15 100644 --- a/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnreachableStatementRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveUnreachableStatementRector::class]); diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts.php.inc new file mode 100644 index 00000000000..1e6d00b131b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts2.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts2.php.inc new file mode 100644 index 00000000000..ee50b4d6441 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts2.php.inc @@ -0,0 +1,60 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts3.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts3.php.inc new file mode 100644 index 00000000000..6ea444d0917 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts3.php.inc @@ -0,0 +1,61 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts4.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts4.php.inc new file mode 100644 index 00000000000..ebd6941e59b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts4.php.inc @@ -0,0 +1,46 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts5.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts5.php.inc new file mode 100644 index 00000000000..9ccca6f0712 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/consecutive_equal_case_stmts5.php.inc @@ -0,0 +1,46 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated.php.inc new file mode 100644 index 00000000000..1880f002192 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated2.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated2.php.inc new file mode 100644 index 00000000000..a6c9201086f --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated2.php.inc @@ -0,0 +1,50 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated3.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated3.php.inc new file mode 100644 index 00000000000..54e7753195d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated3.php.inc @@ -0,0 +1,59 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated4.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated4.php.inc new file mode 100644 index 00000000000..a0ed8bc0e08 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated4.php.inc @@ -0,0 +1,56 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated5.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated5.php.inc new file mode 100644 index 00000000000..bf33e1be122 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/different_indirect_duplicated5.php.inc @@ -0,0 +1,53 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/indirect_duplicated.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/indirect_duplicated.php.inc new file mode 100644 index 00000000000..37cc0dbfd07 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/indirect_duplicated.php.inc @@ -0,0 +1,44 @@ +modifyHeader($node, 'replace'); + case 'clearHeader': + return $this->modifyHeader($node, 'remove'); + case 'clearRawHeaders': + return $this->modifyHeader($node, 'replace'); + case '...': + return 5; + } + } +} + +?> +----- +modifyHeader($node, 'replace'); + case 'clearHeader': + return $this->modifyHeader($node, 'remove'); + case '...': + return 5; + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/multi_indirect_duplicated.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/multi_indirect_duplicated.php.inc new file mode 100644 index 00000000000..6c72922a51c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/multi_indirect_duplicated.php.inc @@ -0,0 +1,47 @@ +modifyHeader($node, 'replace'); + case 'clearHeader': + return $this->modifyHeader($node, 'remove'); + case 'clearRawHeaders': + return $this->modifyHeader($node, 'replace'); + case 'clearRawHeaders2': + return $this->modifyHeader($node, 'replace'); + case '...': + return 5; + } + } +} + +?> +----- +modifyHeader($node, 'replace'); + case 'clearHeader': + return $this->modifyHeader($node, 'remove'); + case '...': + return 5; + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/no_stmts_with_equal_comments.php.inc b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/no_stmts_with_equal_comments.php.inc new file mode 100644 index 00000000000..0ee4d1fd19e --- /dev/null +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/no_stmts_with_equal_comments.php.inc @@ -0,0 +1,53 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/config/configured_rule.php index dde8b92e96f..21632048f17 100644 --- a/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Switch_\RemoveDuplicatedCaseInSwitchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDuplicatedCaseInSwitchRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDuplicatedCaseInSwitchRector::class]); diff --git a/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..eb25989d9ed --- /dev/null +++ b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/skip_different_type.php.inc b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/skip_different_type.php.inc new file mode 100644 index 00000000000..823854898ab --- /dev/null +++ b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/skip_different_type.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_array_empty.php.inc b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_array_empty.php.inc new file mode 100644 index 00000000000..ef60dd833cb --- /dev/null +++ b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_array_empty.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_if_equal.php.inc b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_if_equal.php.inc new file mode 100644 index 00000000000..f313c151152 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_if_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_integer_zero.php.inc b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_integer_zero.php.inc new file mode 100644 index 00000000000..13f2160d9d9 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/Fixture/with_integer_zero.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/RemoveUselessTernaryRectorTest.php b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/RemoveUselessTernaryRectorTest.php new file mode 100644 index 00000000000..c0d2f7b0d16 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/RemoveUselessTernaryRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/config/configured_rule.php new file mode 100644 index 00000000000..abc80c166c5 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Ternary/RemoveUselessTernaryRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveUselessTernaryRector::class]); diff --git a/rules-tests/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector/TernaryToBooleanOrFalseToBooleanAndRectorTest.php b/rules-tests/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector/TernaryToBooleanOrFalseToBooleanAndRectorTest.php index 14adedb1059..265ae7e2a4a 100644 --- a/rules-tests/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector/TernaryToBooleanOrFalseToBooleanAndRectorTest.php +++ b/rules-tests/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector/TernaryToBooleanOrFalseToBooleanAndRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\Ternary\TernaryToBooleanOrFalseToBooleanAndRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class TernaryToBooleanOrFalseToBooleanAndRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector/config/configured_rule.php index bb2e0544bcf..2e6cd638dc7 100644 --- a/rules-tests/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Ternary\TernaryToBooleanOrFalseToBooleanAndRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(TernaryToBooleanOrFalseToBooleanAndRector::class); -}; +return RectorConfig::configure() + ->withRules([TernaryToBooleanOrFalseToBooleanAndRector::class]); diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/drop_with_finally.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/drop_with_finally.php.inc new file mode 100644 index 00000000000..cb47a710320 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/drop_with_finally.php.inc @@ -0,0 +1,39 @@ +getMessage()); + } catch (\Throwable $throwable) { + throw $throwable; + } finally { + } + } +} + +?> +----- +getMessage()); + } finally { + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/drop_with_many_dead_catches.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/drop_with_many_dead_catches.php.inc new file mode 100644 index 00000000000..ff76fd20508 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/drop_with_many_dead_catches.php.inc @@ -0,0 +1,43 @@ +getMessage()); + } catch (DeadException $exception) { + throw $exception; + } catch (RuntimeException $exception) { + throw new InvalidArgumentException($exception->getMessage()); + } catch (\Throwable $throwable) { + throw $throwable; + } + } +} + +?> +----- +getMessage()); + } catch (RuntimeException $exception) { + throw new InvalidArgumentException($exception->getMessage()); + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..6582dc7c0bb --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/fixture.php.inc @@ -0,0 +1,37 @@ +getMessage()); + } catch (\Throwable $throwable) { + throw $throwable; + } + } +} + +?> +----- +getMessage()); + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_dead_try_catch.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_dead_try_catch.php.inc new file mode 100644 index 00000000000..7245d41e65f --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_dead_try_catch.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_has_stmt_empty_catch_throw.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_has_stmt_empty_catch_throw.php.inc new file mode 100644 index 00000000000..fac157b5093 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_has_stmt_empty_catch_throw.php.inc @@ -0,0 +1,16 @@ +getMessage()); + } catch (Throwable $throwable) { + } + } +} diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_has_stmt_raise_exception_with_code_before.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_has_stmt_raise_exception_with_code_before.php.inc new file mode 100644 index 00000000000..3b5d4d4be05 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_has_stmt_raise_exception_with_code_before.php.inc @@ -0,0 +1,24 @@ +getMessage()); + } catch (Throwable $throwable) { + $this->resetState(); + throw $throwable; + } + } + + private function resetState() + { + } +} diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_next_throwable_has_special_condition.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_next_throwable_has_special_condition.php.inc new file mode 100644 index 00000000000..aa8cb819a78 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_next_throwable_has_special_condition.php.inc @@ -0,0 +1,27 @@ +getMessage() === 'Special condition') { + return; + } + + throw $throwable; + } + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_with_different_throw_variable.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_with_different_throw_variable.php.inc new file mode 100644 index 00000000000..2d9748cb6ad --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/Fixture/skip_with_different_throw_variable.php.inc @@ -0,0 +1,17 @@ +getMessage()); + } catch (Throwable $throwable) { + throw $variable; + } + } +} diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/RemoveDeadCatchRectorTest.php b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/RemoveDeadCatchRectorTest.php new file mode 100644 index 00000000000..0300b630958 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/RemoveDeadCatchRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/config/configured_rule.php new file mode 100644 index 00000000000..266775aee15 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadCatchRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveDeadCatchRector::class]); diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/drop_with_empty_finally.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/drop_with_empty_finally.php.inc index c777fb66cff..dcd60e6a53c 100644 --- a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/drop_with_empty_finally.php.inc +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/drop_with_empty_finally.php.inc @@ -25,7 +25,6 @@ class DropWithEmptyFinally { public function run() { - } } diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/empty_try_and_catch.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/empty_try_and_catch.php.inc new file mode 100644 index 00000000000..330c22bb803 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/empty_try_and_catch.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/fixture.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/fixture.php.inc index 2f39b095123..f1a094f2b9c 100644 --- a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/fixture.php.inc +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/fixture.php.inc @@ -25,7 +25,6 @@ class Fixture { public function run() { - } } diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/multi_lines_different_throw_variable.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/multi_lines_different_throw_variable.php.inc new file mode 100644 index 00000000000..8f228d7609e --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/multi_lines_different_throw_variable.php.inc @@ -0,0 +1,17 @@ +doSomething(); + $this->doSomethingElse(); + } + catch (Throwable $throwable) { + throw $variable; + } + } +} diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/skip_has_stmt_empty_catch_throw.php.inc b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/skip_has_stmt_empty_catch_throw.php.inc new file mode 100644 index 00000000000..ceb085fe917 --- /dev/null +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/Fixture/skip_has_stmt_empty_catch_throw.php.inc @@ -0,0 +1,15 @@ +call(); + } + catch (Throwable $throwable) { + } + } +} diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/RemoveDeadTryCatchRectorTest.php b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/RemoveDeadTryCatchRectorTest.php index 4af7bb4ba9a..a8118887fc3 100644 --- a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/RemoveDeadTryCatchRectorTest.php +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/RemoveDeadTryCatchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\DeadCode\Rector\TryCatch\RemoveDeadTryCatchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveDeadTryCatchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/config/configured_rule.php index 274ac6e4b2f..2bd6382306d 100644 --- a/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\TryCatch\RemoveDeadTryCatchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDeadTryCatchRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadTryCatchRector::class]); diff --git a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_is_factory_variable_double_method_call.php.inc b/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_is_factory_variable_double_method_call.php.inc deleted file mode 100644 index 8d6d09501ec..00000000000 --- a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_is_factory_variable_double_method_call.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -createSomeClassWithFluentMethods()->one(); - } -} diff --git a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_non_fluent_nette_container_builder.php.inc b/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_non_fluent_nette_container_builder.php.inc deleted file mode 100644 index b872a6b9e2c..00000000000 --- a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_non_fluent_nette_container_builder.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -addDefinition('someClass'); - } -} diff --git a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_return_new_double_method_call.php.inc b/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_return_new_double_method_call.php.inc deleted file mode 100644 index 4354c48e74b..00000000000 --- a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_return_new_double_method_call.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -one()->two(); - } -} diff --git a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_variable_double_method_call.php.inc b/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_variable_double_method_call.php.inc deleted file mode 100644 index 5a4a8eb03ff..00000000000 --- a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Fixture/skip_variable_double_method_call.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -one()->two(); - } -} diff --git a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/FluentChainMethodCallRootExtractorTest.php b/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/FluentChainMethodCallRootExtractorTest.php deleted file mode 100644 index c3ab0a6cd1c..00000000000 --- a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/FluentChainMethodCallRootExtractorTest.php +++ /dev/null @@ -1,95 +0,0 @@ -boot(); - $this->fluentChainMethodCallRootExtractor = $this->getService(FluentChainMethodCallRootExtractor::class); - $this->testingParser = $this->getService(TestingParser::class); - } - - public function test(): void - { - $assignAndRootExpr = $this->parseFileAndCreateAssignAndRootExprForSure( - __DIR__ . '/Fixture/skip_variable_double_method_call.php.inc' - ); - - $this->assertFalse($assignAndRootExpr->isFirstCallFactory()); - - $this->assertNull($assignAndRootExpr->getSilentVariable()); - } - - public function testFactory(): void - { - $assignAndRootExpr = $this->parseFileAndCreateAssignAndRootExprForSure( - __DIR__ . '/Fixture/skip_is_factory_variable_double_method_call.php.inc' - ); - - $this->assertTrue($assignAndRootExpr->isFirstCallFactory()); - } - - public function testNew(): void - { - $assignAndRootExpr = $this->parseFileAndCreateAssignAndRootExprForSure( - __DIR__ . '/Fixture/skip_return_new_double_method_call.php.inc' - ); - - $this->assertFalse($assignAndRootExpr->isFirstCallFactory()); - - /** @var Variable $silentVariable */ - $silentVariable = $assignAndRootExpr->getSilentVariable(); - $this->assertInstanceOf(Variable::class, $silentVariable); - - $this->assertIsString($silentVariable->name); - $this->assertSame('someClassWithFluentMethods', $silentVariable->name); - } - - public function testSingleMethodCallNull(): void - { - $assignAndRootExpr = $this->parseFileAndCreateAssignAndRootExpr( - __DIR__ . '/Fixture/skip_non_fluent_nette_container_builder.php.inc' - ); - - $this->assertNull($assignAndRootExpr); - } - - private function parseFileAndCreateAssignAndRootExprForSure(string $filePath): AssignAndRootExpr - { - $assignAndRootExpr = $this->parseFileAndCreateAssignAndRootExpr($filePath); - $this->assertInstanceOf(AssignAndRootExpr::class, $assignAndRootExpr); - - /** @var AssignAndRootExpr $assignAndRootExpr */ - return $assignAndRootExpr; - } - - private function parseFileAndCreateAssignAndRootExpr(string $filePath): ?AssignAndRootExpr - { - /** @var MethodCall[] $methodCalls */ - $methodCalls = $this->testingParser->parseFileToDecoratedNodesAndFindNodesByType( - $filePath, - MethodCall::class - ); - - return $this->fluentChainMethodCallRootExtractor->extractFromMethodCalls( - $methodCalls, - FluentCallsKind::NORMAL - ); - } -} diff --git a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Source/AnotherTypeFactory.php b/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Source/AnotherTypeFactory.php deleted file mode 100644 index 5d7bbf21e39..00000000000 --- a/rules-tests/Defluent/NodeFactory/FluentChainMethodCallRootExtractor/Source/AnotherTypeFactory.php +++ /dev/null @@ -1,16 +0,0 @@ -someFunction(); - $valueObject->otherFunction(); - } -} - -?> ------ -someFunction()->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture2.php.inc b/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture2.php.inc deleted file mode 100644 index b971add8ccc..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -someFunction(1); - $valueObject->otherFunction(2); - } -} - -?> ------ -someFunction(1)->otherFunction(2); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture3.php.inc b/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture3.php.inc deleted file mode 100644 index b840c5f8b9c..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -someFunction(); - $valueObject->otherFunction(); - $valueObject->keepAsItIs(); - } -} - -?> ------ -someFunction()->otherFunction(); - $valueObject->keepAsItIs(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture4.php.inc b/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture4.php.inc deleted file mode 100644 index 93b6255c538..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Fixture/fixture4.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -someFunction(); - $valueObject->otherFunction(); - $valueObject->joinThisAsWell(); - } -} - -?> ------ -someFunction()->otherFunction()->joinThisAsWell(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/NormalToFluentRectorTest.php b/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/NormalToFluentRectorTest.php deleted file mode 100644 index a3c28a3be2f..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/NormalToFluentRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Source/FluentInterfaceClass.php b/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Source/FluentInterfaceClass.php deleted file mode 100644 index 0a631367d84..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/NormalToFluentRector/Source/FluentInterfaceClass.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(NormalToFluentRector::class) - ->call('configure', [[ - NormalToFluentRector::CALLS_TO_FLUENT => ValueObjectInliner::inline( - [ - - new NormalToFluent(FluentInterfaceClass::class, [ - 'someFunction', - 'otherFunction', - 'joinThisAsWell', - ]), - ] - ), - ]]); -}; diff --git a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/change_parent_local.php.inc b/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/change_parent_local.php.inc deleted file mode 100644 index 419487d2bc9..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/change_parent_local.php.inc +++ /dev/null @@ -1,51 +0,0 @@ - ------ - diff --git a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/fixture.php.inc b/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/fixture.php.inc deleted file mode 100644 index 6b18d3522fc..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ - diff --git a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/skip_magic_call.php.inc b/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/skip_magic_call.php.inc deleted file mode 100644 index 610b3338d16..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/skip_magic_call.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -{strtolower(substr($name, 3))} = $args[0]; - } - - return $this; - } -} - -$obj = new SkipMagicCall(); -$obj->setAuthor('author') - ->setTitle('title'); - -?> diff --git a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/skip_multi_nested.php.inc b/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/skip_multi_nested.php.inc deleted file mode 100644 index cef6f322f97..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Fixture/skip_multi_nested.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -isWindows()) { - $this->markTestSkipped('minor differences on windows, see https://github.com/rectorphp/rector/issues/6571'); - } - - $this->doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Source/vendor/ParentInVendor.php b/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Source/vendor/ParentInVendor.php deleted file mode 100644 index 2781a4d6a94..00000000000 --- a/rules-tests/Defluent/Rector/ClassMethod/ReturnThisRemoveRector/Source/vendor/ParentInVendor.php +++ /dev/null @@ -1,17 +0,0 @@ -services(); - $services->set(ReturnThisRemoveRector::class); -}; diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/casted_return_value.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/casted_return_value.php.inc deleted file mode 100644 index dd5c5fa7c18..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/casted_return_value.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -someFunction()->otherFunction(); - } -} - -?> ------ -someFunction(); - return (int) $differentReturnValues->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/default_parent_try_catch_this.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/default_parent_try_catch_this.php.inc deleted file mode 100644 index ce6bb2223c8..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/default_parent_try_catch_this.php.inc +++ /dev/null @@ -1,68 +0,0 @@ -pDisallowAjax()->pRedirect($path); - } -} - -class SomeAjaxPresenter -{ - public function pDisallowAjax(): self - { - try { - $value = 100; - } catch (Throwable $throwable) { - } - - return $this; - } - - public function pRedirect($path) - { - - } -} - -?> ------ -pDisallowAjax(); - $this->pRedirect($path); - } -} - -class SomeAjaxPresenter -{ - public function pDisallowAjax(): self - { - try { - $value = 100; - } catch (Throwable $throwable) { - } - - return $this; - } - - public function pRedirect($path) - { - - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index ecad112aa75..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -someClass = new FluentInterfaceClass(); - - $this->someClass->someFunction() - ->otherFunction(); - } -} - -?> ------ -someClass = new FluentInterfaceClass(); - $this->someClass->someFunction(); - $this->someClass->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/last_different_type.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/last_different_type.php.inc deleted file mode 100644 index 5ddea7bacd7..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/last_different_type.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -someFunction() - ->otherFunction(); - } -} - -?> ------ -someFunction(); - $differentReturnValues->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/last_normal_call_on_fluent.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/last_normal_call_on_fluent.php.inc deleted file mode 100644 index 0052bf66b87..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/last_normal_call_on_fluent.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -someFunction() - ->voidReturningMethod(); - } -} - -?> ------ -someFunction(); - $fluentInterfaceClass->voidReturningMethod(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/multi_casted_return_value.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/multi_casted_return_value.php.inc deleted file mode 100644 index b21971b84bf..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/multi_casted_return_value.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -someFunction()->otherFunction(); - } -} - -?> ------ -someFunction(); - return (int) $differentReturnValues->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/multiple_some_command.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/multiple_some_command.php.inc deleted file mode 100644 index 532092f47cd..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/multiple_some_command.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -setName('mail-queue:send') - ->setDescription('Consume the mail.') - ->setHelp('This command allows you to consume mail from another module.'); - } -} - -?> ------ -setName('mail-queue:send'); - $this->setDescription('Consume the mail.'); - $this->setHelp('This command allows you to consume mail from another module.'); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_container_configurator.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_container_configurator.php.inc deleted file mode 100644 index ec2d1b4c06a..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_container_configurator.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -services(); - - $services->set(ReturnThisRemoveRector::class) - ->arg('key', 'value'); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time.php.inc deleted file mode 100644 index 2eefdad1c51..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -modify(1)->format('Y-m-D'); - } - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time_modify_command.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time_modify_command.php.inc deleted file mode 100644 index c30d6e79337..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time_modify_command.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -getBillingDate()->modify('00:00'); - } - - public function getBillingDate(): DateTime - { - return new DateTime('now'); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_different_type.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_different_type.php.inc deleted file mode 100644 index 8ac090cc83f..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_different_type.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -otherFunction() - ->someFunction(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_factory_call.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_factory_call.php.inc deleted file mode 100644 index 564473b76c3..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_factory_call.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -getApplication() - ->setAutoExit(true); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_fluent_in_if_cond.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_fluent_in_if_cond.php.inc deleted file mode 100644 index fb1cf3c2298..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_fluent_in_if_cond.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -someFunction()->otherFunction()) { - - } - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_getter_response_header.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_getter_response_header.php.inc deleted file mode 100644 index 213bfb6b57b..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_getter_response_header.php.inc +++ /dev/null @@ -1,26 +0,0 @@ -getHttpResponse()->setHeader('Access-Control-Allow-Origin', '*'); - } - - /** - * @return SomeResponse - */ - public function getHttpResponse() - { - return $this->httpResponse; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_inside_ternary_else.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_inside_ternary_else.php.inc deleted file mode 100644 index 0e9052f5d88..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_inside_ternary_else.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -someFunction()->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_inside_ternary_else2_casted.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_inside_ternary_else2_casted.php.inc deleted file mode 100644 index 02c7900abda..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_inside_ternary_else2_casted.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -someFunction()->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin.php.inc deleted file mode 100644 index 60b4ae83d34..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -obj = $this; - $this->obj->run()->execute(); - } - - public function run() - { - return $this; - } - - public function execute() - { - return $this; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin2.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin2.php.inc deleted file mode 100644 index 0092f1cae19..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin2.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -obj = $this; - $this->obj->run()->execute(); - } - - public function run() - { - return $this; - } - - public function execute() - { - return $this; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin_in_source.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin_in_source.php.inc deleted file mode 100644 index 1be57a9cf79..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_mixin_in_source.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -addQuery() - ->select(); - } - - public function someFunction2(MixinClass $mixinClass) - { - $mixinClass->addQuery() - ->select(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_base_control_in_forms.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_base_control_in_forms.php.inc deleted file mode 100644 index 3200f071da2..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_base_control_in_forms.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -addText('textInput'); - $someControl->setRequired(1) - ->addRule(1); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_di_builder.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_di_builder.php.inc deleted file mode 100644 index 02aff485711..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_di_builder.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -getContainerBuilder(); - $containerBuilder->addDefinition('one') - ->setFactory('two'); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_new.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_new.php.inc deleted file mode 100644 index b4f20f87a73..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_new.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -someFunction()->otherFunction(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_phpstan_trinary.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_phpstan_trinary.php.inc deleted file mode 100644 index 10a2ad90c93..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_phpstan_trinary.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -isSuperTypeOf($variableType)->yes(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_query_builder.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_query_builder.php.inc deleted file mode 100644 index 9c317927a48..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_query_builder.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -addQuery() - ->select(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_return.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_return.php.inc deleted file mode 100644 index 5d3c2d6985c..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_return.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -services(); - return $services->set(ReturnThisRemoveRector::class) - ->arg('key', 'value'); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_used_as_parameter.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_used_as_parameter.php.inc deleted file mode 100644 index a2802e6d81c..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/skip_used_as_parameter.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -processMoreFluentInterface($someClass->someFunction()->otherFunction()); - } - - public function processMoreFluentInterface(FluentInterfaceClass $someClass) - { - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/some_command.php.inc b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/some_command.php.inc deleted file mode 100644 index d8d7cf260f0..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Fixture/some_command.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -setName('push-notification-queue:compose') - ->setDescription('Compose push notifications to raw format to be send.'); - } -} - -?> ------ -setName('push-notification-queue:compose'); - $this->setDescription('Compose push notifications to raw format to be send.'); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/FluentChainMethodCallToNormalMethodCallRectorTest.php b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/FluentChainMethodCallToNormalMethodCallRectorTest.php deleted file mode 100644 index d80debf1219..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/FluentChainMethodCallToNormalMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Source/Cell.php b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Source/Cell.php deleted file mode 100644 index 37bad82dc2f..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Source/Cell.php +++ /dev/null @@ -1,13 +0,0 @@ -value = 100; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Source/FluentInterfaceClassInterface.php b/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Source/FluentInterfaceClassInterface.php deleted file mode 100644 index b109092ec8d..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector/Source/FluentInterfaceClassInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(FluentChainMethodCallToNormalMethodCallRector::class); -}; diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/method_call_before_arg.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/method_call_before_arg.php.inc deleted file mode 100644 index 69ff30e06c2..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/method_call_before_arg.php.inc +++ /dev/null @@ -1,56 +0,0 @@ -getDummyUser(); - - $this->someMethod( - (new DummyUserProfile()) - ->setOldDescription($oldDescription) - ->setUserId($user->id) - ); - } - - public function someMethod(DummyUserProfile $dummyUserProfile) - { - } -} - -?> ------ -getDummyUser(); - $dummyUserProfile = new DummyUserProfile(); - ($dummyUserProfile) - ->setOldDescription($oldDescription); - $dummyUserProfile - ->setUserId($user->id); - - $this->someMethod( - $dummyUserProfile - ); - } - - public function someMethod(DummyUserProfile $dummyUserProfile) - { - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/new_in_arg.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/new_in_arg.php.inc deleted file mode 100644 index 55e79b4224f..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/new_in_arg.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -processFluentClass((new FluentClass())->someFunction()); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} - -?> ------ -someFunction(); - $this->processFluentClass($fluentClass); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_casted.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_casted.php.inc deleted file mode 100644 index 0132a69cf58..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_casted.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -processFluentClass((new FluentClass())->someFunction()->otherFunction2()); - } - - public function processFluentClass($value) - { - return $value; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_new_in_arg_not_name.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_new_in_arg_not_name.php.inc deleted file mode 100644 index 64136b3f5da..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_new_in_arg_not_name.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -processFluentClass( - (new $valueObject->class)->someFunction()->otherFunction() - ); - } - - public function someFunction2() - { - $this->processFluentClass( - (new $this->getFluentClass())->someFunction()->otherFunction() - ); - } - - private function getFluentClass() - { - return ValueObject::A_CLASS; - } - - public function processFluentClass($arg) - { - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_non_arg.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_non_arg.php.inc deleted file mode 100644 index 89d4c4428c5..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_non_arg.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -otherFunction() - ->someFunction(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_non_fluent_new_in_arg.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_non_fluent_new_in_arg.php.inc deleted file mode 100644 index 7f9d6823750..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_non_fluent_new_in_arg.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -processFluentClass((new NonFluentClass())->number()); - } - - public function processFluentClass(int $number) - { - - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_self_self.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_self_self.php.inc deleted file mode 100644 index 3ea0de08cac..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_self_self.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -getDateMin()) && $setGetDateTime->getDateMin() instanceof DateTime) { - $setGetDateTime->setDateMin($setGetDateTime->getDateMin()->setTimezone($dateTimeZone)); - } - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_single_call.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_single_call.php.inc deleted file mode 100644 index 43046194d54..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_single_call.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -processFluentClass($someClass->someFunction()); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_use_return.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_use_return.php.inc deleted file mode 100644 index 4a8bdd8e3d9..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/skip_use_return.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -processFluentClass((new FluentClass())->someFunction()); - } - - public function processFluentClass($value) - { - return $this; - } -} - -?> \ No newline at end of file diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/used_as_parameter.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/used_as_parameter.php.inc deleted file mode 100644 index 0056250a92a..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/used_as_parameter.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -processFluentClass($someClass->someFunction()->otherFunction()); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} - -?> ------ -someFunction(); - $someClass->otherFunction(); - $this->processFluentClass($someClass); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/with_args.php.inc b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/with_args.php.inc deleted file mode 100644 index fd64f8dcf15..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Fixture/with_args.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -processFluentClass($someClass->someFunction(100)->otherFunction([1, 2, 3])); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} - -?> ------ -someFunction(100); - $someClass->otherFunction([1, 2, 3]); - $this->processFluentClass($someClass); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/InArgFluentChainMethodCallToStandaloneMethodCallRectorTest.php b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/InArgFluentChainMethodCallToStandaloneMethodCallRectorTest.php deleted file mode 100644 index 99c88e8d9c5..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/InArgFluentChainMethodCallToStandaloneMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/DummyUser.php b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/DummyUser.php deleted file mode 100644 index 6933e652f84..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/DummyUser.php +++ /dev/null @@ -1,10 +0,0 @@ -userId = $id; - return $this; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/DummyUserProvider.php b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/DummyUserProvider.php deleted file mode 100644 index fb70e74df1e..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/DummyUserProvider.php +++ /dev/null @@ -1,13 +0,0 @@ -dateMin = $dateTime; - } - - public function getDateMin(): ?DateTime - { - return $this->dateMin; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/ValueObject.php b/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/ValueObject.php deleted file mode 100644 index 57c66647de4..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector/Source/ValueObject.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(InArgFluentChainMethodCallToStandaloneMethodCallRector::class); -}; diff --git a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/fixture.php.inc b/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/fixture.php.inc deleted file mode 100644 index bca5925d2fe..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -anotherMethod(new AnotherClass()) - ->someFunction(); - } - - public function anotherMethod(AnotherClass $anotherClass): AnotherClass - { - return $anotherClass; - } -} - -?> ------ -someFunction(); - $this->anotherMethod($anotherClass); - } - - public function anotherMethod(AnotherClass $anotherClass): AnotherClass - { - return $anotherClass; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/multi_chain.php.inc b/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/multi_chain.php.inc deleted file mode 100644 index 2d2292a46a0..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/multi_chain.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -anotherMethod(new AnotherClass()) - ->someFunction() - ->anotherFunction(); - } - - public function anotherMethod(AnotherClass $anotherClass): AnotherClass - { - return $anotherClass; - } -} - -?> ------ -someFunction(); - $anotherClass->anotherFunction(); - $this->anotherMethod($anotherClass); - } - - public function anotherMethod(AnotherClass $anotherClass): AnotherClass - { - return $anotherClass; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/with_arguments.php.inc b/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/with_arguments.php.inc deleted file mode 100644 index f99e14252b7..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Fixture/with_arguments.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -anotherMethod(new AnotherClass([1, 2, 3])) - ->someFunction(4) - ->anotherFunction(5); - } - - public function anotherMethod(AnotherClass $anotherClass): AnotherClass - { - return $anotherClass; - } -} - -?> ------ -someFunction(4); - $anotherClass->anotherFunction(5); - $this->anotherMethod($anotherClass); - } - - public function anotherMethod(AnotherClass $anotherClass): AnotherClass - { - return $anotherClass; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/MethodCallOnSetterMethodCallToStandaloneAssignRectorTest.php b/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/MethodCallOnSetterMethodCallToStandaloneAssignRectorTest.php deleted file mode 100644 index 8ac6d400b8d..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/MethodCallOnSetterMethodCallToStandaloneAssignRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Source/AnotherClass.php b/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Source/AnotherClass.php deleted file mode 100644 index 694079386c9..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector/Source/AnotherClass.php +++ /dev/null @@ -1,18 +0,0 @@ -services(); - $services->set(MethodCallOnSetterMethodCallToStandaloneAssignRector::class); -}; diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_finder.php.inc b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_finder.php.inc deleted file mode 100644 index fa810cc99a8..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_finder.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -files() - ->in(__DIR__) - ->getIterator(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_getter_on_new.php.inc b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_getter_on_new.php.inc deleted file mode 100644 index 7020eff0604..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_getter_on_new.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -otherFunction(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_in_arg_with_new.php.inc b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_in_arg_with_new.php.inc deleted file mode 100644 index 31081bfd025..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_in_arg_with_new.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -someFunction()->otherFunction()); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_in_assign_same_variable.php.inc b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_in_assign_same_variable.php.inc deleted file mode 100644 index f645740a36d..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_in_assign_same_variable.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -someFunction2($obj)->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_inside_if_cond.php.inc b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_inside_if_cond.php.inc deleted file mode 100644 index 908697f675a..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_inside_if_cond.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -someFunction()->otherFunction()) { - return $instance; - } - } -} - -?> \ No newline at end of file diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_not_in_assign.php.inc b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_not_in_assign.php.inc deleted file mode 100644 index 9985417c03b..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_not_in_assign.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -someFunction2($stdClass)->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_return_with_new.php.inc b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_return_with_new.php.inc deleted file mode 100644 index f18c356af17..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/skip_return_with_new.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -someFunction()->otherFunction(); - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/with_new.php.inc b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/with_new.php.inc deleted file mode 100644 index cf75b6f7d8a..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Fixture/with_new.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -someClass = (new FluentInterfaceClass())->someFunction()->otherFunction(); - } -} - -?> ------ -someClass = new FluentInterfaceClass(); - $this->someClass->someFunction(); - $this->someClass->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/NewFluentChainMethodCallToNonFluentRectorTest.php b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/NewFluentChainMethodCallToNonFluentRectorTest.php deleted file mode 100644 index 0852be67ac4..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/NewFluentChainMethodCallToNonFluentRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Source/DifferentReturnValues.php b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Source/DifferentReturnValues.php deleted file mode 100644 index 7ca666c7797..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Source/DifferentReturnValues.php +++ /dev/null @@ -1,18 +0,0 @@ -value = 100; - } -} diff --git a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Source/FluentInterfaceClassInterface.php b/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Source/FluentInterfaceClassInterface.php deleted file mode 100644 index 9c3d91c434f..00000000000 --- a/rules-tests/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector/Source/FluentInterfaceClassInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(NewFluentChainMethodCallToNonFluentRector::class); -}; diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/DefluentReturnMethodCallRectorTest.php b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/DefluentReturnMethodCallRectorTest.php deleted file mode 100644 index a32d25e5b7c..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/DefluentReturnMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index 8ab538842aa..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -someFunction(); - } -} - -?> ------ -someFunction(); - return $someClass; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_assign.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_assign.php.inc deleted file mode 100644 index 96c07fd0d33..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_assign.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -withStatus(500); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_assign_use_trait.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_assign_use_trait.php.inc deleted file mode 100644 index b4943f6b0dd..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_assign_use_trait.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -withStatus(500); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_direct.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_direct.php.inc deleted file mode 100644 index 22542ddc267..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_clone_object_direct.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -duplicate(); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_date_time.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_date_time.php.inc deleted file mode 100644 index d2eae2dd415..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_date_time.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -modify('+1 month')->format('Y-m-d'); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_interface.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_interface.php.inc deleted file mode 100644 index ab5afc998e7..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_interface.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -withStatus(500); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_new_object.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_new_object.php.inc deleted file mode 100644 index 655b33f7385..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_new_object.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -duplicateSelf(); - } - - public function run2(SelfStaticThisButNewVersion $selfButNewVersion) - { - return $selfButNewVersion->duplicateStatic(); - } - - public function run3(SelfStaticThisButNewVersion $selfButNewVersion) - { - return $selfButNewVersion->duplicateThis(); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_new_object_assign.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_new_object_assign.php.inc deleted file mode 100644 index 15c65cba5be..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_new_object_assign.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -withStatus(500); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_part_of_fluent_call.php.inc b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_part_of_fluent_call.php.inc deleted file mode 100644 index 09d70829752..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Fixture/skip_part_of_fluent_call.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -someFunction() - ->anotherFunction(); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/MessageInterface.php b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/MessageInterface.php deleted file mode 100644 index c26f8876418..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/MessageInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -status = $status; - - return $self; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfButCloneAssignVersionTrait.php b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfButCloneAssignVersionTrait.php deleted file mode 100644 index 61f278601f0..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfButCloneAssignVersionTrait.php +++ /dev/null @@ -1,18 +0,0 @@ -status = $status; - - return $self; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfButCloneVersion.php b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfButCloneVersion.php deleted file mode 100644 index 3b729d581ae..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfButCloneVersion.php +++ /dev/null @@ -1,13 +0,0 @@ -status = $status; - - return $self; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfStaticThisButNewVersion.php b/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfStaticThisButNewVersion.php deleted file mode 100644 index 1fced3ed813..00000000000 --- a/rules-tests/Defluent/Rector/Return_/DefluentReturnMethodCallRector/Source/SelfStaticThisButNewVersion.php +++ /dev/null @@ -1,23 +0,0 @@ -services(); - $services->set(DefluentReturnMethodCallRector::class); -}; diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/return_basic_double_fluent.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/return_basic_double_fluent.php.inc deleted file mode 100644 index f9c4cdd5133..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/return_basic_double_fluent.php.inc +++ /dev/null @@ -1,51 +0,0 @@ -someClass = new FluentInterfaceClass(); - - return $this->someClass->someFunction() - ->otherFunction(); - } -} - -?> ------ -someClass = new FluentInterfaceClass(); - $this->someClass->someFunction(); - $this->someClass->otherFunction(); - return $this->someClass; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_assign_of_fluent.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_assign_of_fluent.php.inc deleted file mode 100644 index 4b77e629844..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_assign_of_fluent.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -addCell()->setName(2); - return $cell; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_container_configurator.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_container_configurator.php.inc deleted file mode 100644 index 878c241f8ee..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_container_configurator.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -services(); - return $services->set(ReturnThisRemoveRector::class) - ->arg('key', 'value'); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time_modify_command.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time_modify_command.php.inc deleted file mode 100644 index fc25a9981a5..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_date_time_modify_command.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -getBillingDate()->modify('00:00'); - } - - public function getBillingDate(): DateTime - { - return new DateTime('now'); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_last_call_not_return_current_object.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_last_call_not_return_current_object.php.inc deleted file mode 100644 index 98c489a6126..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_last_call_not_return_current_object.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -someClass = new FluentInterfaceClass(); - - return $this->someClass->someFunction() - ->otherFunction() - ->paginate(); - } - - public function someFunction2() - { - $this->someClass = new FluentInterfaceClass(); - - return $this->someClass->someFunction() - ->otherFunction() - ->voidReturningMethod(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_func_call.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_func_call.php.inc deleted file mode 100644 index dae6a4c614d..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_func_call.php.inc +++ /dev/null @@ -1,22 +0,0 @@ -someFunction()->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_static_call.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_static_call.php.inc deleted file mode 100644 index 070b6de430a..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_static_call.php.inc +++ /dev/null @@ -1,22 +0,0 @@ -someFunction() - ->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_variable_call.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_variable_call.php.inc deleted file mode 100644 index 942ec249aec..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_method_call_var_as_variable_call.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -someFunction() - ->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_base_control_in_forms.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_base_control_in_forms.php.inc deleted file mode 100644 index d5e8088a468..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_nette_base_control_in_forms.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -addText('textInput'); - $someControl->setRequired(1)->addRule(1); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_possibly_last_not_return_this.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_possibly_last_not_return_this.php.inc deleted file mode 100644 index 29b3166f074..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_possibly_last_not_return_this.php.inc +++ /dev/null @@ -1,26 +0,0 @@ -someClass = new FluentInterfaceClass(); - - return $this->someClass->someFunction() - ->otherFunction() - ->possiblyNotReturnThis(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_query_builder.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_query_builder.php.inc deleted file mode 100644 index dc5ca649ec5..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_query_builder.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -addQuery()->select(); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_return_new.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_return_new.php.inc deleted file mode 100644 index ff90f475ac8..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_return_new.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -someFunction()->otherFunction(); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_used_as_parameter.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_used_as_parameter.php.inc deleted file mode 100644 index e9048f98764..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Fixture/skip_used_as_parameter.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -processMoreFluentInterface($someClass->someFunction()->otherFunction()); - } - - public function processMoreFluentInterface(FluentInterfaceClass $someClass) - { - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/ReturnFluentChainMethodCallToNormalMethodCallRectorTest.php b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/ReturnFluentChainMethodCallToNormalMethodCallRectorTest.php deleted file mode 100644 index 4c51a5504e7..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/ReturnFluentChainMethodCallToNormalMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Source/Cell.php b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Source/Cell.php deleted file mode 100644 index b8a42f1f19c..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Source/Cell.php +++ /dev/null @@ -1,13 +0,0 @@ -value = 100; - } - - public function possiblyNotReturnThis() - { - if (rand(0,1)) { - return $this; - } - - return []; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Source/FluentInterfaceClassInterface.php b/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Source/FluentInterfaceClassInterface.php deleted file mode 100644 index 5dfea2c9eab..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector/Source/FluentInterfaceClassInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(ReturnFluentChainMethodCallToNormalMethodCallRector::class); -}; diff --git a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/do_not_remove_return_from_parameter.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/do_not_remove_return_from_parameter.php.inc deleted file mode 100644 index 0aa8b1854bd..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/do_not_remove_return_from_parameter.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -someFunction()->otherFunction(); - } -} - -?> ------ -someFunction(); - return $fluentInterfaceClass->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/do_not_remove_return_from_property.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/do_not_remove_return_from_property.php.inc deleted file mode 100644 index bffc9fcb2cf..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/do_not_remove_return_from_property.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -property = $fluentInterfaceClass; - } - - public function someFunction() - { - return $this->property->someFunction()->otherFunction(); - } -} - -?> ------ -property = $fluentInterfaceClass; - } - - public function someFunction() - { - $this->property->someFunction(); - return $this->property->otherFunction(); - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/return_with_new.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/return_with_new.php.inc deleted file mode 100644 index 78ea80f2a73..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/return_with_new.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -someFunction()->otherFunction(); - } -} - -?> ------ -someFunction(); - $fluentInterfaceClass->otherFunction(); - return $fluentInterfaceClass; - } -} - -?> diff --git a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/skip_no_return_with_new.php.inc b/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/skip_no_return_with_new.php.inc deleted file mode 100644 index c815d865850..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Fixture/skip_no_return_with_new.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -someClass = (new FluentInterfaceClass())->someFunction()->otherFunction(); - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/ReturnNewFluentChainMethodCallToNonFluentRectorTest.php b/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/ReturnNewFluentChainMethodCallToNonFluentRectorTest.php deleted file mode 100644 index c6132510e2c..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/ReturnNewFluentChainMethodCallToNonFluentRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Source/DifferentReturnValues.php b/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Source/DifferentReturnValues.php deleted file mode 100644 index cca3a87299c..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Source/DifferentReturnValues.php +++ /dev/null @@ -1,18 +0,0 @@ -value = 100; - } -} diff --git a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Source/FluentInterfaceClassInterface.php b/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Source/FluentInterfaceClassInterface.php deleted file mode 100644 index 98552d04df7..00000000000 --- a/rules-tests/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector/Source/FluentInterfaceClassInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(ReturnNewFluentChainMethodCallToNonFluentRector::class); -}; diff --git a/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/AddMethodParentCallRectorTest.php b/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/AddMethodParentCallRectorTest.php deleted file mode 100644 index 407c6d44a9d..00000000000 --- a/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/AddMethodParentCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/Fixture/fixture.php.inc b/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/Fixture/fixture.php.inc deleted file mode 100644 index 35ffbad3829..00000000000 --- a/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/Fixture/skip_already_has.php.inc b/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/Fixture/skip_already_has.php.inc deleted file mode 100644 index 05c12d1b965..00000000000 --- a/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/Fixture/skip_already_has.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -defaultValue = 5; - } -} diff --git a/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/config/configured_rule.php b/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/config/configured_rule.php deleted file mode 100644 index d6c24217470..00000000000 --- a/rules-tests/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector/config/configured_rule.php +++ /dev/null @@ -1,17 +0,0 @@ -services(); - $services->set(AddMethodParentCallRector::class) - ->call('configure', [[ - AddMethodParentCallRector::METHODS_BY_PARENT_TYPES => [ - ParentClassWithNewConstructor::class => '__construct', - ], - ]]); -}; diff --git a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/ActionInjectionToConstructorInjectionRectorTest.php b/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/ActionInjectionToConstructorInjectionRectorTest.php deleted file mode 100644 index 6e23ffcbf98..00000000000 --- a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/ActionInjectionToConstructorInjectionRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Fixture/fixture.php.inc b/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Fixture/fixture.php.inc deleted file mode 100644 index 05662f3abed..00000000000 --- a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -fetchAll(); - } -} - -?> ------ -productRepository->fetchAll(); - } -} - -?> diff --git a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Fixture/fixture2.php.inc b/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Fixture/fixture2.php.inc deleted file mode 100644 index bbe072deb19..00000000000 --- a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -fetchAll(); - } - public function detail(SomeRequest $request, SomeProduct $product) - { - } -} - -?> ------ -productRepository->fetchAll(); - } - public function detail(SomeRequest $request, SomeProduct $product) - { - } -} - -?> diff --git a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Source/ProductRepository.php b/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Source/ProductRepository.php deleted file mode 100644 index 8181985fe3d..00000000000 --- a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/Source/ProductRepository.php +++ /dev/null @@ -1,10 +0,0 @@ -parameters(); - $parameters->set(Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER, __DIR__ . '/../xml/services.xml'); - - $services = $containerConfigurator->services(); - $services->set(ActionInjectionToConstructorInjectionRector::class); - $services->set(ReplaceVariableByPropertyFetchRector::class); -}; diff --git a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/xml/services.xml b/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/xml/services.xml deleted file mode 100644 index c204bff38bc..00000000000 --- a/rules-tests/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector/xml/services.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/DirConstToFileConstRectorTest.php b/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/DirConstToFileConstRectorTest.php deleted file mode 100644 index 9e1b425100b..00000000000 --- a/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/DirConstToFileConstRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/Fixture/some_class.php.inc deleted file mode 100644 index 4c1e1f8ea94..00000000000 --- a/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/config/configured_rule.php b/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/config/configured_rule.php deleted file mode 100644 index fee5fa48bd3..00000000000 --- a/rules-tests/DowngradePhp53/Rector/Dir/DirConstToFileConstRector/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(DirConstToFileConstRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/DowngradeParentTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/DowngradeParentTypeDeclarationRectorTest.php deleted file mode 100644 index 8c1e579276c..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/DowngradeParentTypeDeclarationRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/already_has_return_doc.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/already_has_return_doc.php.inc deleted file mode 100644 index 4d7d23200c2..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/already_has_return_doc.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/fixture.php.inc deleted file mode 100644 index 1074320c9ef..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/in_interface.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/in_interface.php.inc deleted file mode 100644 index d43b91d87ac..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/in_interface.php.inc +++ /dev/null @@ -1,53 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/in_trait.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/in_trait.php.inc deleted file mode 100644 index 247d2990177..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/in_trait.php.inc +++ /dev/null @@ -1,50 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/skip_no_parent.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/skip_no_parent.php.inc deleted file mode 100644 index fbd1754f0dc..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/Fixture/skip_no_parent.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/config/configured_rule.php b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/config/configured_rule.php deleted file mode 100644 index e08ae8c9ef2..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeParentTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/DowngradeSelfTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/DowngradeSelfTypeDeclarationRectorTest.php deleted file mode 100644 index 230fca98556..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/DowngradeSelfTypeDeclarationRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/docblock_exists.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/docblock_exists.php.inc deleted file mode 100644 index 9b6824ae488..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/docblock_exists.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/fixture.php.inc deleted file mode 100644 index 6483639a252..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/interface.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/interface.php.inc deleted file mode 100644 index 7eb552f7a96..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/interface.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/nullable_type.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/nullable_type.php.inc deleted file mode 100644 index 6ff15a74d2d..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/nullable_type.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/trait.php.inc b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/trait.php.inc deleted file mode 100644 index 689f09b3819..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/Fixture/trait.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/config/configured_rule.php b/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/config/configured_rule.php deleted file mode 100644 index 998280dd2d3..00000000000 --- a/rules-tests/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeSelfTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/DowngradeNullCoalesceRectorTest.php b/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/DowngradeNullCoalesceRectorTest.php deleted file mode 100644 index cb6a1908700..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/DowngradeNullCoalesceRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_70.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/fixture.php.inc deleted file mode 100644 index d642cd99c0c..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/property_fetch.php.inc b/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/property_fetch.php.inc deleted file mode 100644 index 6e2f203ba36..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/property_fetch.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -property ?? 'value'; - } -} - -?> ------ -property) ? $this->property : 'value'; - } -} - -?> diff --git a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/some_method_call.php.inc b/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/some_method_call.php.inc deleted file mode 100644 index 88fd0bcadad..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/some_method_call.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -getRealPath() ?? 'some'; - } -} - -?> ------ -getRealPath() !== null ? $fileInfo->getRealPath() : 'some'; - } -} - -?> diff --git a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/static_property_fetch.php.inc b/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/static_property_fetch.php.inc deleted file mode 100644 index 4b23e93f6e9..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/Fixture/static_property_fetch.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/config/php_70.php b/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/config/php_70.php deleted file mode 100644 index 562ffde3212..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector/config/php_70.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::SCALAR_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(DowngradeNullCoalesceRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/DowngradeStrictTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/DowngradeStrictTypeDeclarationRectorTest.php deleted file mode 100644 index 154668f21ef..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/DowngradeStrictTypeDeclarationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_70.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/Fixture/fixture.php.inc deleted file mode 100644 index 4d6056bd39c..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/Fixture/skip_different_declare.php.inc b/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/Fixture/skip_different_declare.php.inc deleted file mode 100644 index db2b6c57a6f..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/Fixture/skip_different_declare.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/config/php_70.php b/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/config/php_70.php deleted file mode 100644 index 1adc8c70d2d..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector/config/php_70.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::SCALAR_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(DowngradeStrictTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/DowngradeDefineArrayConstantRectorTest.php b/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/DowngradeDefineArrayConstantRectorTest.php deleted file mode 100644 index b2a5e2febc3..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/DowngradeDefineArrayConstantRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_70.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/fixture.php.inc deleted file mode 100644 index e4d5102179e..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_inside_function.php.inc b/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_inside_function.php.inc deleted file mode 100644 index 43332fff7ef..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_inside_function.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_inside_method.php.inc b/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_inside_method.php.inc deleted file mode 100644 index 3010ac32749..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_inside_method.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_array.php.inc b/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_array.php.inc deleted file mode 100644 index f953a75dc20..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_array.php.inc +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_define.php.inc b/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_define.php.inc deleted file mode 100644 index c2672b4966a..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_define.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_in_expression.php.inc b/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_in_expression.php.inc deleted file mode 100644 index c931ee08958..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/Fixture/skip_not_in_expression.php.inc +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/config/php_70.php b/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/config/php_70.php deleted file mode 100644 index 79c4ed4380e..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector/config/php_70.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::SCALAR_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(DowngradeDefineArrayConstantRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/DowngradeSessionStartArrayOptionsRectorTest.php b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/DowngradeSessionStartArrayOptionsRectorTest.php deleted file mode 100644 index 1daf848f64b..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/DowngradeSessionStartArrayOptionsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_70.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/boolean_value.php.inc b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/boolean_value.php.inc deleted file mode 100644 index ade74208b90..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/boolean_value.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - true, - ]); - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/fixture.php.inc deleted file mode 100644 index bca29700c61..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - 'private', - 'save_path' => '/tmp', - ]); - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/inside_if.php.inc b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/inside_if.php.inc deleted file mode 100644 index 6b081d18034..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/inside_if.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - 'private', - 'save_path' => '/tmp', - ])) { - } - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_no_array_options.php.inc b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_no_array_options.php.inc deleted file mode 100644 index d9eefdd5d6e..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_no_array_options.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_array_options.php.inc b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_array_options.php.inc deleted file mode 100644 index 7bbf69d1a2a..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_array_options.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_session_start.php.inc b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_session_start.php.inc deleted file mode 100644 index 6421f33f97e..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_session_start.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_string_key.php.inc b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_string_key.php.inc deleted file mode 100644 index e8866f72418..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_not_string_key.php.inc +++ /dev/null @@ -1,16 +0,0 @@ - 'private', - ]); - } -} - -?> - diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_object_value.php.inc b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_object_value.php.inc deleted file mode 100644 index 580f7204b6b..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/Fixture/skip_object_value.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - new \stdClass(), - ]); - } -} - -?> diff --git a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/config/php_70.php b/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/config/php_70.php deleted file mode 100644 index 28133e8ca77..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector/config/php_70.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::SCALAR_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(DowngradeSessionStartArrayOptionsRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/DowngradeScalarTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/DowngradeScalarTypeDeclarationRectorTest.php deleted file mode 100644 index b254c284a9c..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/DowngradeScalarTypeDeclarationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_70.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/body_empty_string.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/body_empty_string.php.inc deleted file mode 100644 index 41b35acfa67..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/body_empty_string.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/closure.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/closure.php.inc deleted file mode 100644 index f106e523675..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/closure.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - \ No newline at end of file diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/fixture.php.inc deleted file mode 100644 index 4af64ebf587..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/multiple_object_stringable_param.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/multiple_object_stringable_param.php.inc deleted file mode 100644 index 813fac150af..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/multiple_object_stringable_param.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -definitions[$id] . $name; - } -} - -?> ------ -definitions[$id] . $name; - } -} - -?> diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/object_stringable_param.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/object_stringable_param.php.inc deleted file mode 100644 index 49c0fec692c..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/object_stringable_param.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -definitions[$id]; - } -} - -?> ------ -definitions[$id]; - } -} - -?> diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/return_array.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/return_array.php.inc deleted file mode 100644 index 229daac67ca..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/return_array.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/return_string.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/return_string.php.inc deleted file mode 100644 index b66a26f2b44..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/return_string.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/retype_integer.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/retype_integer.php.inc deleted file mode 100644 index 7ffc1ca633d..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/retype_integer.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_array.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_array.php.inc deleted file mode 100644 index c89b731aeb8..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_array.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_callable.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_callable.php.inc deleted file mode 100644 index b52cc5e6612..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_callable.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_class_construct.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_class_construct.php.inc deleted file mode 100644 index a9bf46b22de..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_class_construct.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -someObjectService = $someObjectService; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_doc_type_only.php.inc b/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_doc_type_only.php.inc deleted file mode 100644 index 6b821e85549..00000000000 --- a/rules-tests/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector/Fixture/skip_doc_type_only.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::SCALAR_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(DowngradeScalarTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/Fixture/some_class.php.inc deleted file mode 100644 index 0c36b08e062..00000000000 --- a/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/SplitGroupedUseImportsRectorTest.php b/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/SplitGroupedUseImportsRectorTest.php deleted file mode 100644 index 2d0705e4840..00000000000 --- a/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/SplitGroupedUseImportsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/config/configured_rule.php b/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/config/configured_rule.php deleted file mode 100644 index bcae6d26649..00000000000 --- a/rules-tests/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(SplitGroupedUseImportsRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector/DowngradeAnonymousClassRectorTest.php b/rules-tests/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector/DowngradeAnonymousClassRectorTest.php deleted file mode 100644 index 62644e9c6cb..00000000000 --- a/rules-tests/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector/DowngradeAnonymousClassRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_70.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector/Fixture/class_exist.php.inc b/rules-tests/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector/Fixture/class_exist.php.inc deleted file mode 100644 index 1a5fa311b33..00000000000 --- a/rules-tests/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector/Fixture/class_exist.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - ------ - ------ - ------ - ------ - ------ - ------ - ------ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::SCALAR_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(DowngradeAnonymousClassRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/DowngradeSpaceshipRectorTest.php b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/DowngradeSpaceshipRectorTest.php deleted file mode 100644 index 66ad5c7d879..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/DowngradeSpaceshipRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_70.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/compare_property_fetch.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/compare_property_fetch.php.inc deleted file mode 100644 index 1ae4f6399fa..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/compare_property_fetch.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -a <=> $this->b; - } -} - -?> ------ -a, $this->b); - } -} - -?> diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/echo.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/echo.php.inc deleted file mode 100644 index 29ff56a32cb..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/echo.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - $b; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/existing_variable_assign.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/existing_variable_assign.php.inc deleted file mode 100644 index bc742fdee06..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/existing_variable_assign.php.inc +++ /dev/null @@ -1,59 +0,0 @@ - $b) { - } - } - - public function run2($a, $b) - { - $battleShipcompare = 'test'; - $battleShipcompare2 = 'test2'; - if ($a <=> $b) { - } - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/fixture.php.inc deleted file mode 100644 index 03fe7ed7f95..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - $b; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/func_call.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/func_call.php.inc deleted file mode 100644 index 8c90f01b082..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/func_call.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - getRand(); - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/in_if.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/in_if.php.inc deleted file mode 100644 index d845c46ebee..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/in_if.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - $b) { - } - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/some_static_property_fetch.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/some_static_property_fetch.php.inc deleted file mode 100644 index 683e83b43c2..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/some_static_property_fetch.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - self::$b; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/static_property_fetch2.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/static_property_fetch2.php.inc deleted file mode 100644 index 7c4b76f4979..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/static_property_fetch2.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - StaticPropertyFetch2::$b; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/config/php_70.php b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/config/php_70.php deleted file mode 100644 index 63ce1f297f3..00000000000 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/config/php_70.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::SCALAR_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(DowngradeSpaceshipRector::class); -}; diff --git a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/DowngradeGeneratedScalarTypesRectorTest.php b/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/DowngradeGeneratedScalarTypesRectorTest.php deleted file mode 100644 index 3f13ff7cf16..00000000000 --- a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/DowngradeGeneratedScalarTypesRectorTest.php +++ /dev/null @@ -1,37 +0,0 @@ -isWindows()) { - $this->markTestSkipped('minor differences on windows, see https://github.com/rectorphp/rector/issues/6571'); - } - - $this->doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/orignal_php_dumper.php.inc b/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/orignal_php_dumper.php.inc deleted file mode 100644 index ede2cf182b6..00000000000 --- a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/orignal_php_dumper.php.inc +++ /dev/null @@ -1,112 +0,0 @@ -buildParameters[$name])) { - return $this->buildParameters[$name]; - } - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } - if (isset($this->loadedDynamicParameters[$name])) { - return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - return $this->parameters[$name]; - } - public function hasParameter(string $name): bool - { - if (isset($this->buildParameters[$name])) { - return true; - } - return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters); - } - public function setParameter(string $name, $value): void - { - throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); - } - public function getParameterBag(): ParameterBagInterface - { - if (null === $this->parameterBag) { - $parameters = $this->parameters; - foreach ($this->loadedDynamicParameters as $name => $loaded) { - $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - foreach ($this->buildParameters as $name => $value) { - $parameters[$name] = $value; - } - $this->parameterBag = new FrozenParameterBag($parameters); - } - return $this->parameterBag; - } -EOF; - -?> ------ -buildParameters[$name])) { - return $this->buildParameters[$name]; - } - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } - if (isset($this->loadedDynamicParameters[$name])) { - return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - return $this->parameters[$name]; -} -/** - * @param string $name - * @return bool - */ -public function hasParameter($name) -{ - $name = (string) $name; - if (isset($this->buildParameters[$name])) { - return true; - } - return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters); -} -/** - * @param string $name - * @return void - */ -public function setParameter($name, $value) -{ - $name = (string) $name; - throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); -} -public function getParameterBag() -{ - if (null === $this->parameterBag) { - $parameters = $this->parameters; - foreach ($this->loadedDynamicParameters as $name => $loaded) { - $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - foreach ($this->buildParameters as $name => $value) { - $parameters[$name] = $value; - } - $this->parameterBag = new FrozenParameterBag($parameters); - } - return $this->parameterBag; -} - -EOF; - -?> diff --git a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/skip_invalid_eof.php.inc b/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/skip_invalid_eof.php.inc deleted file mode 100644 index b1dddd86c4c..00000000000 --- a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/skip_invalid_eof.php.inc +++ /dev/null @@ -1,7 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/some_class.php.inc deleted file mode 100644 index 28806b44a56..00000000000 --- a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/config/configured_rule.php b/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/config/configured_rule.php deleted file mode 100644 index 839a5eb34af..00000000000 --- a/rules-tests/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector/config/configured_rule.php +++ /dev/null @@ -1,17 +0,0 @@ -services(); - $services->set(DowngradeGeneratedScalarTypesRector::class); - - // dependendent rules - $services->set(DowngradeScalarTypeDeclarationRector::class); - $services->set(DowngradeVoidTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/fixture.php.inc deleted file mode 100644 index eef4380aafa..00000000000 --- a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/in_foreach.php.inc b/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/in_foreach.php.inc deleted file mode 100644 index 040f28a1dba..00000000000 --- a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/in_foreach.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/include_empty.php.inc b/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/include_empty.php.inc deleted file mode 100644 index cff239ac48d..00000000000 --- a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/Fixture/include_empty.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/SymmetricArrayDestructuringToListRectorTest.php b/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/SymmetricArrayDestructuringToListRectorTest.php deleted file mode 100644 index e110e63fd93..00000000000 --- a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/SymmetricArrayDestructuringToListRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/config/configured_rule.php deleted file mode 100644 index 80a985fd4d8..00000000000 --- a/rules-tests/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SymmetricArrayDestructuringToListRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/ClassConst/DowngradeClassConstantVisibilityRectorTest.php b/rules-tests/DowngradePhp71/Rector/ClassConst/DowngradeClassConstantVisibilityRectorTest.php deleted file mode 100644 index 0c08bb886f0..00000000000 --- a/rules-tests/DowngradePhp71/Rector/ClassConst/DowngradeClassConstantVisibilityRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/ClassConst/Fixture/fixture.php.inc b/rules-tests/DowngradePhp71/Rector/ClassConst/Fixture/fixture.php.inc deleted file mode 100644 index f9efc151a06..00000000000 --- a/rules-tests/DowngradePhp71/Rector/ClassConst/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/ClassConst/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/ClassConst/config/configured_rule.php deleted file mode 100644 index 52c9709b3ba..00000000000 --- a/rules-tests/DowngradePhp71/Rector/ClassConst/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeClassConstantVisibilityRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/DowngradeIsIterableRectorTest.php b/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/DowngradeIsIterableRectorTest.php deleted file mode 100644 index fdb610a4039..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/DowngradeIsIterableRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/Fixture/fixture.php.inc deleted file mode 100644 index cb1d29a9df7..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/Fixture/skip_not_is_iterable.php.inc b/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/Fixture/skip_not_is_iterable.php.inc deleted file mode 100644 index 30bfe137440..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/Fixture/skip_not_is_iterable.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/config/configured_rule.php deleted file mode 100644 index d57363695ef..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeIsIterableRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/DowngradeIterablePseudoTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/DowngradeIterablePseudoTypeDeclarationRectorTest.php deleted file mode 100644 index a52857ba96b..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/DowngradeIterablePseudoTypeDeclarationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/closure_iterable.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/closure_iterable.php.inc deleted file mode 100644 index 5ee8bfe7d9f..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/closure_iterable.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/param_iterable.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/param_iterable.php.inc deleted file mode 100644 index e938f2f5153..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/param_iterable.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/return_iterable.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/return_iterable.php.inc deleted file mode 100644 index 9930d30bfaf..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/Fixture/return_iterable.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/config/configured_rule.php deleted file mode 100644 index 074fe63ec55..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeIterablePseudoTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/DowngradeNullableTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/DowngradeNullableTypeDeclarationRectorTest.php deleted file mode 100644 index cb506bd85e9..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/DowngradeNullableTypeDeclarationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/nullable_return_on_closure.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/nullable_return_on_closure.php.inc deleted file mode 100644 index 15a807ee941..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/nullable_return_on_closure.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (\PhpParser\Node $node) use($propertyName, &$assignedParamName) : ?int { - return null; - }); - } -} - -?> ------ -simpleCallableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (\PhpParser\Node $node) use($propertyName, &$assignedParamName) { - return null; - }); - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/param_type.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/param_type.php.inc deleted file mode 100644 index 32b8314c990..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/param_type.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/return_type.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/return_type.php.inc deleted file mode 100644 index 543e6539784..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/Fixture/return_type.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/config/configured_rule.php deleted file mode 100644 index 1cc51959316..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeNullableTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/DowngradeVoidTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/DowngradeVoidTypeDeclarationRectorTest.php deleted file mode 100644 index 4350770c28d..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/DowngradeVoidTypeDeclarationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/closure_void.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/closure_void.php.inc deleted file mode 100644 index f04f3578caf..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/closure_void.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/docblock_exists.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/docblock_exists.php.inc deleted file mode 100644 index e38e8f58731..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/docblock_exists.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc deleted file mode 100644 index 2be59d84ffe..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/fixture.php.inc deleted file mode 100644 index 3ea6c8a0e56..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/config/configured_rule.php deleted file mode 100644 index 0349e370232..00000000000 --- a/rules-tests/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeVoidTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/DowngradeKeysInListRectorTest.php b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/DowngradeKeysInListRectorTest.php deleted file mode 100644 index 2ad86d208cc..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/DowngradeKeysInListRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value.php.inc deleted file mode 100644 index dbeb36dafde..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as list("id" => $id1, "name" => $name1)) { - } - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as $singleData) { - $id1 = $singleData["id"]; - $name1 = $singleData["name"]; - } - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value2.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value2.php.inc deleted file mode 100644 index 6eef9b3fd81..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value2.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as list("id" => $id1, "name" => $name1)) { - echo 'statement'; - } - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as $singleData) { - $id1 = $singleData["id"]; - $name1 = $singleData["name"]; - echo 'statement'; - } - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value_keyed_array.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value_keyed_array.php.inc deleted file mode 100644 index ecc05e83661..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value_keyed_array.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as ["id" => $id1, "name" => $name1]) { - } - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as $singleData) { - $id1 = $singleData["id"]; - $name1 = $singleData["name"]; - } - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value_used.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value_used.php.inc deleted file mode 100644 index 08eb5b95dc7..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/as_foreach_value_used.php.inc +++ /dev/null @@ -1,69 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as list("id" => $id1, "name" => $name1)) { - echo $singleData; - } - } - - public function run2($singleData): void - { - $data = [ - ["id" => 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - $singleData2 = $this->run(); - foreach ($data as list("id" => $id1, "name" => $name1)) { - echo $singleData; - } - echo $singleData2; - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as $singleData2) { - $id1 = $singleData2["id"]; - $name1 = $singleData2["name"]; - echo $singleData; - } - } - - public function run2($singleData): void - { - $data = [ - ["id" => 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - $singleData2 = $this->run(); - foreach ($data as $singleData3) { - $id1 = $singleData3["id"]; - $name1 = $singleData3["name"]; - echo $singleData; - } - echo $singleData2; - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/fixture.php.inc deleted file mode 100644 index 5d82e841db3..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - list("id" => $id1, "name" => $name1) = $data[0]; - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - $id1 = $data[0]["id"]; - $name1 = $data[0]["name"]; - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/mirror_comments.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/mirror_comments.php.inc deleted file mode 100644 index de12f0a1428..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/mirror_comments.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - // a comment - list("id" => $id1, "name" => $name1) = $data[0]; - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - // a comment - $id1 = $data[0]["id"]; - $name1 = $data[0]["name"]; - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list.php.inc deleted file mode 100644 index baa2c652792..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - - diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list2.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list2.php.inc deleted file mode 100644 index 6ffd14ac666..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list2.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - - diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list3.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list3.php.inc deleted file mode 100644 index 90543a7aff5..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/skip_not_keys_in_list3.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - - diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/use_keyed_array.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/use_keyed_array.php.inc deleted file mode 100644 index d61227780cb..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/use_keyed_array.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - ["id" => $id1, "name" => $name1] = $data[0]; - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - $id1 = $data[0]["id"]; - $name1 = $data[0]["name"]; - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/variable_key.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/variable_key.php.inc deleted file mode 100644 index 353b48a0959..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/variable_key.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - list($id => $id1, $name => $name1) = $data[0]; - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - $id1 = $data[0][$id]; - $name1 = $data[0][$name]; - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/variable_key_in_foreach_value.php.inc b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/variable_key_in_foreach_value.php.inc deleted file mode 100644 index 6e95802ba22..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/Fixture/variable_key_in_foreach_value.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as list($id => $id1, $name => $name1)) { - echo 'statement'; - } - } -} - -?> ------ - 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - foreach ($data as $singleData) { - $id1 = $singleData[$id]; - $name1 = $singleData[$name]; - echo 'statement'; - } - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/config/configured_rule.php deleted file mode 100644 index fcfda2fb821..00000000000 --- a/rules-tests/DowngradePhp71/Rector/List_/DowngradeKeysInListRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeKeysInListRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/DowngradeNegativeStringOffsetToStrlenRectorTest.php b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/DowngradeNegativeStringOffsetToStrlenRectorTest.php deleted file mode 100644 index 78bc8b80dc4..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/DowngradeNegativeStringOffsetToStrlenRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/fixture.php.inc deleted file mode 100644 index 4dd079dbfaf..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/property_fetch.php.inc b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/property_fetch.php.inc deleted file mode 100644 index c633d2e3ac2..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/property_fetch.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -var = 'abc'; - echo $this->var[-2]; - } -} - -?> ------ -var = 'abc'; - echo $this->var[strlen($this->var) - 2]; - } -} - -?> diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_no_offset.php.inc b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_no_offset.php.inc deleted file mode 100644 index a80555d64a3..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_no_offset.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_not_strpos.php.inc b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_not_strpos.php.inc deleted file mode 100644 index a970b7076af..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_not_strpos.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_positive.php.inc b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_positive.php.inc deleted file mode 100644 index ee5ee7b6569..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/skip_positive.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/some_static_property_fetch.php.inc b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/some_static_property_fetch.php.inc deleted file mode 100644 index 262af56d971..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/some_static_property_fetch.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/variable.php.inc b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/variable.php.inc deleted file mode 100644 index 11b703f975b..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/variable.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/config/configured_rule.php deleted file mode 100644 index 962d1e277bb..00000000000 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeNegativeStringOffsetToStrlenRector::class); -}; diff --git a/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/DowngradePipeToMultiCatchExceptionRectorTest.php b/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/DowngradePipeToMultiCatchExceptionRectorTest.php deleted file mode 100644 index 422374eb38d..00000000000 --- a/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/DowngradePipeToMultiCatchExceptionRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/Fixture/fixture.php.inc deleted file mode 100644 index 1af9d87b411..00000000000 --- a/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,69 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/config/configured_rule.php b/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/config/configured_rule.php deleted file mode 100644 index 7b31f97f361..00000000000 --- a/rules-tests/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector/config/configured_rule.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::MULTI_EXCEPTION_CATCH - 1); - - $services = $containerConfigurator->services(); - $services->set(DowngradePipeToMultiCatchExceptionRector::class); -}; diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/DowngradeParameterTypeWideningRectorTest.php b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/DowngradeParameterTypeWideningRectorTest.php deleted file mode 100644 index db9b7e99d4a..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/DowngradeParameterTypeWideningRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/anonymous_class.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/anonymous_class.php.inc deleted file mode 100644 index 550b87fd035..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/anonymous_class.php.inc +++ /dev/null @@ -1,55 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/container_has_interface.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/container_has_interface.php.inc deleted file mode 100644 index 3e3fe57a3f0..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/container_has_interface.php.inc +++ /dev/null @@ -1,52 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/fixture.php.inc deleted file mode 100644 index 12c8647dba8..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,55 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/include_used_trait.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/include_used_trait.php.inc deleted file mode 100644 index 5d74ad29702..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/include_used_trait.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/indirect_implements_native_interface.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/indirect_implements_native_interface.php.inc deleted file mode 100644 index 387650c54de..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/indirect_implements_native_interface.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/interface_on_parent_class.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/interface_on_parent_class.php.inc deleted file mode 100644 index 033cd38b563..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/interface_on_parent_class.php.inc +++ /dev/null @@ -1,48 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/keep_interface_downgraded.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/keep_interface_downgraded.php.inc deleted file mode 100644 index f484207ed28..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/keep_interface_downgraded.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_anonymous_class_private_method.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_anonymous_class_private_method.php.inc deleted file mode 100644 index dd58d67af6f..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_anonymous_class_private_method.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_autowired_method.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_autowired_method.php.inc deleted file mode 100644 index bff4bc6e19b..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_autowired_method.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_native_interface.php.inc b/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_native_interface.php.inc deleted file mode 100644 index d460f691d9b..00000000000 --- a/rules-tests/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector/Fixture/skip_native_interface.php.inc +++ /dev/null @@ -1,10 +0,0 @@ -services(); - - $services->set(DowngradeParameterTypeWideningRector::class) - ->call('configure', [[ - DowngradeParameterTypeWideningRector::SAFE_TYPES => [RectorInterface::class], - DowngradeParameterTypeWideningRector::SAFE_TYPES_TO_METHODS => [ - ContainerInterface::class => ['setParameter', 'getParameter', 'hasParameter'], - ], - ]]); -}; diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/DowngradePregUnmatchedAsNullConstantRectorTest.php b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/DowngradePregUnmatchedAsNullConstantRectorTest.php deleted file mode 100644 index 3112535d376..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/DowngradePregUnmatchedAsNullConstantRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/class_contant.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/class_contant.php.inc deleted file mode 100644 index 46793e8d19d..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/class_contant.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if.php.inc deleted file mode 100644 index 0a1113b8769..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { - } - } catch (\Exception $e) { - - } - } -} - -?> ------ -string, $matches, $flags, $offset)) { - } - array_walk_recursive($matches, function (&$value) { - if ($value === '') { - $value = null; - } - }); - } catch (\Exception $e) { - - } - } -} - -?> diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if2.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if2.php.inc deleted file mode 100644 index 2c76537da7e..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if2.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { - echo 'statement'; - } - } catch (\Exception $e) { - - } - } -} - -?> ------ -string, $matches, $flags, $offset)) { - echo 'statement'; - } - array_walk_recursive($matches, function (&$value) { - if ($value === '') { - $value = null; - } - }); - } catch (\Exception $e) { - - } - } -} - -?> diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if3.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if3.php.inc deleted file mode 100644 index e48115cb219..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if3.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { - echo 'statement'; - } - } catch (\Exception $e) { - - } - } -} - -?> ------ -string, $matches, $flags, $offset)) { - array_walk_recursive($matches, function (&$value) { - if ($value === '') { - $value = null; - } - }); - echo 'statement'; - } - } catch (\Exception $e) { - - } - } -} - -?> diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if_direct_call.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if_direct_call.php.inc deleted file mode 100644 index a8f80baf4d9..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if_direct_call.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { - } - } catch (\Exception $e) { - - } - } -} - -?> ------ -string, $matches, $flags, $offset)) { - array_walk_recursive($matches, function (&$value) { - if ($value === '') { - $value = null; - } - }); - } - } catch (\Exception $e) { - - } - } -} - -?> diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if_negation.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if_negation.php.inc deleted file mode 100644 index 26ec3708af9..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/complex_in_if_negation.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { - } - } catch (\Exception $e) { - - } - } -} - -?> ------ -string, $matches, $flags, $offset)) { - } - array_walk_recursive($matches, function (&$value) { - if ($value === '') { - $value = null; - } - }); - } catch (\Exception $e) { - - } - } -} - -?> diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/fixture.php.inc deleted file mode 100644 index ad1a537a994..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/fqn_constant.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/fqn_constant.php.inc deleted file mode 100644 index a9845455a65..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/fqn_constant.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/preg_match_all.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/preg_match_all.php.inc deleted file mode 100644 index e201f40612e..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/preg_match_all.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag.php.inc deleted file mode 100644 index 181f34741d5..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag2.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag2.php.inc deleted file mode 100644 index e350726c42f..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag2.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag3.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag3.php.inc deleted file mode 100644 index 3e3e9c8e0aa..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_different_flag3.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_no_flag.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_no_flag.php.inc deleted file mode 100644 index ba47b2e5b85..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_no_flag.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_not_regex_functions.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_not_regex_functions.php.inc deleted file mode 100644 index 1f608c2e4ae..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/skip_not_regex_functions.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags.php.inc deleted file mode 100644 index 569e27c3c3b..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_2.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_2.php.inc deleted file mode 100644 index 70f8d4febd0..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_2.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_3.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_3.php.inc deleted file mode 100644 index 971364f99f9..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_3.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_4.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_4.php.inc deleted file mode 100644 index 8286807440b..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/Fixture/with_flags_4.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/config/configured_rule.php b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/config/configured_rule.php deleted file mode 100644 index 9bd46f842af..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradePregUnmatchedAsNullConstantRector::class); -}; diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/DowngradeStreamIsattyRectorTest.php b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/DowngradeStreamIsattyRectorTest.php deleted file mode 100644 index 601207474c3..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/DowngradeStreamIsattyRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/return_is.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/return_is.php.inc deleted file mode 100644 index 8306e91a510..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/return_is.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/return_or.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/return_or.php.inc deleted file mode 100644 index b62d9ffbfa9..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/return_or.php.inc +++ /dev/null @@ -1,64 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/skip_in_wrapper.php.inc b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/skip_in_wrapper.php.inc deleted file mode 100644 index df8c64402ef..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/Fixture/skip_in_wrapper.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/config/configured_rule.php b/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/config/configured_rule.php deleted file mode 100644 index ae006d2a6db..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(DowngradeStreamIsattyRector::class); -}; diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/DowngradeObjectTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/DowngradeObjectTypeDeclarationRectorTest.php deleted file mode 100644 index 6d0ca3d0b6d..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/DowngradeObjectTypeDeclarationRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/including_trait_prefer_interface.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/including_trait_prefer_interface.php.inc deleted file mode 100644 index 3927a865772..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/including_trait_prefer_interface.php.inc +++ /dev/null @@ -1,22 +0,0 @@ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/keep_better_type.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/keep_better_type.php.inc deleted file mode 100644 index 259e8af5ed0..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/keep_better_type.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_docblock_exists.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_docblock_exists.php.inc deleted file mode 100644 index 15651c22162..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_docblock_exists.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_docblock_tag_exists.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_docblock_tag_exists.php.inc deleted file mode 100644 index 59d89077820..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_docblock_tag_exists.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_fixture.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_fixture.php.inc deleted file mode 100644 index 5a5071f8056..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_fixture.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_multiple_matching_params.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_multiple_matching_params.php.inc deleted file mode 100644 index a3ef7cc6d3e..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_multiple_matching_params.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_multiple_params.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_multiple_params.php.inc deleted file mode 100644 index 76239805591..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_multiple_params.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_nullable_type.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_nullable_type.php.inc deleted file mode 100644 index df690c3eda2..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/param_nullable_type.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_docblock_exists.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_docblock_exists.php.inc deleted file mode 100644 index 3758ee10021..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_docblock_exists.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_docblock_tag_exists.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_docblock_tag_exists.php.inc deleted file mode 100644 index d570942a637..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_docblock_tag_exists.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_fixture.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_fixture.php.inc deleted file mode 100644 index 4325a5dbd26..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_fixture.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_nullable_type.php.inc b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_nullable_type.php.inc deleted file mode 100644 index c839a898a0d..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Fixture/return_nullable_type.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Source/AnotherClass.php b/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Source/AnotherClass.php deleted file mode 100644 index 092b8e5eec1..00000000000 --- a/rules-tests/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector/Source/AnotherClass.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(DowngradeObjectTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/DowngradeArrayKeyFirstLastRectorTest.php b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/DowngradeArrayKeyFirstLastRectorTest.php deleted file mode 100644 index 64d849c14d6..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/DowngradeArrayKeyFirstLastRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/Fixture/array_key_last.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/Fixture/array_key_last.php.inc deleted file mode 100644 index 998780eca32..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/Fixture/array_key_last.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/Fixture/some_class.php.inc deleted file mode 100644 index 4123e2037ed..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/config/configured_rule.php b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/config/configured_rule.php deleted file mode 100644 index e9ff85616f5..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeArrayKeyFirstLastRector::class); -}; diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/DowngradeIsCountableRectorTest.php b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/DowngradeIsCountableRectorTest.php deleted file mode 100644 index f36a89eb9ec..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/DowngradeIsCountableRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/Fixture/some_class.php.inc deleted file mode 100644 index e58cf93f52c..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/config/configured_rule.php b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/config/configured_rule.php deleted file mode 100644 index 5d685b0d65b..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(DowngradeIsCountableRector::class); -}; diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/DowngradeTrailingCommasInFunctionCallsRectorTest.php b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/DowngradeTrailingCommasInFunctionCallsRectorTest.php deleted file mode 100644 index 3d0c2620753..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/DowngradeTrailingCommasInFunctionCallsRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -= 7.3 - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void - { - $this->doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/fixture.php.inc deleted file mode 100644 index 801aaf491a5..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -setData('posts','units',); - - self::run('posts','units',); - - self::run('posts', - 'units',); - } -} - -?> ------ -setData('posts', 'units'); - - self::run('posts', 'units'); - - self::run('posts', 'units'); - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/in_stirng_variable.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/in_stirng_variable.php.inc deleted file mode 100644 index 7dc016454db..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/in_stirng_variable.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -setOnClick("[Zip ID: {$modelid}] {$e->getMessage($modelId,)}",); - } -} - -?> ------ -setOnClick("[Zip ID: {$modelid}] {$e->getMessage($modelId)}"); - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/skip_none.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/skip_none.php.inc deleted file mode 100644 index 8e1cca74a17..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/Fixture/skip_none.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -print( - $key, - $value - ); - } -} diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/config/configured_rule.php b/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/config/configured_rule.php deleted file mode 100644 index 6fd54b9cc09..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeTrailingCommasInFunctionCallsRector::class); -}; diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_scalars.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_scalars.php.inc deleted file mode 100644 index e8816ffd62a..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_scalars.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - 3600]); - setcookie('name', 'value', ['expires' => 1, 'path' => 'path']); - setcookie('name', 'value', ['expires' => 1, 'path' => 'path', 'domain' => 'domain']); - setcookie('name', 'value', ['expires' => 1, 'path' => 'path', 'domain' => 'domain', 'secure' => true]); - setcookie('name', 'value', ['expires' => 1, 'path' => 'path', 'domain' => 'domain', 'secure' => true, 'httponly' => true]); - setrawcookie('name', 'value', ['expires' => 3600]); - setrawcookie('name', 'value', ['expires' => 3600, 'ignored' => 5000]); - } -} -?> ------ - diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_skipped_values.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_skipped_values.php.inc deleted file mode 100644 index 1d3a7ba29ea..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_skipped_values.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - 'path']); - setcookie('name', 'value', ['domain' => 'domain']); - setcookie('name', 'value', ['secure' => true]); - setcookie('name', 'value', ['httponly' => true]); - } -} -?> ------ - diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_variables.php.inc b/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_variables.php.inc deleted file mode 100644 index 4bafa6d0b74..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/Fixture/options_variables.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - $expire, 'path' => $path]); - setcookie('name', 'value', ['expires' => $expire, 'path' => $path, 'domain' => $domain]); - setcookie('name', 'value', ['expires' => $expire, 'path' => $path, 'domain' => $domain, 'secure' => $secure]); - setcookie('name', 'value', ['expires' => $expire, 'path' => $path, 'domain' => $domain, 'secure' => $secure, 'httponly' => $httponly]); - } -} -?> ------ - diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/SetCookieOptionsArrayToArgumentsRectorTest.php b/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/SetCookieOptionsArrayToArgumentsRectorTest.php deleted file mode 100644 index acff1babfa6..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/SetCookieOptionsArrayToArgumentsRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/config/configured_rule.php b/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/config/configured_rule.php deleted file mode 100644 index e4acc1c764e..00000000000 --- a/rules-tests/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(SetCookieOptionsArrayToArgumentsRector::class); -}; diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/DowngradeListReferenceAssignmentRectorTest.php b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/DowngradeListReferenceAssignmentRectorTest.php deleted file mode 100644 index 6aa5df46d63..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/DowngradeListReferenceAssignmentRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_all_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_all_by_ref.php.inc deleted file mode 100644 index ffddb743766..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_all_by_ref.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_end_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_end_by_ref.php.inc deleted file mode 100644 index 496f3a0006a..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_end_by_ref.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_end_by_value.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_end_by_value.php.inc deleted file mode 100644 index a1d6d388084..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_end_by_value.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_all_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_all_by_ref.php.inc deleted file mode 100644 index 4e628f1b02b..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_all_by_ref.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - 1, 'b' => 2]; - ['a' => &$a, 'b' => &$b] = $array; - - $array = [3 => 1, 5 => 2]; - [3 => &$a, 5 => &$b] = $array; - } -} - -?> ------ - 1, 'b' => 2]; - $a =& $array['a']; - $b =& $array['b']; - - $array = [3 => 1, 5 => 2]; - $a =& $array[3]; - $b =& $array[5]; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_end_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_end_by_ref.php.inc deleted file mode 100644 index d17eb5993e1..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_end_by_ref.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - 1, 'b' => 2]; - ['a' => $a, 'b' => &$b] = $array; - - $array = [3 => 1, 5 => 2]; - [3 => $a, 5 => &$b] = $array; - } -} - -?> ------ - 1, 'b' => 2]; - ['a' => $a] = $array; - $b =& $array['b']; - - $array = [3 => 1, 5 => 2]; - [3 => $a] = $array; - $b =& $array[5]; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_end_by_value.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_end_by_value.php.inc deleted file mode 100644 index c03200fabd1..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/array_destructuring_with_array_keys_end_by_value.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - 1, 'b' => 2, 'c' => 3]; - ['a' => $a, 'b' => &$b, 'c' => $c] = $array; - - $array = [3 => 1, 5 => 2, 8 => 3]; - [3 => $a, 5 => &$b, 8 => $c] = $array; - } -} - -?> ------ - 1, 'b' => 2, 'c' => 3]; - ['a' => $a, 'b' => $b, 'c' => $c] = $array; - $b =& $array['b']; - - $array = [3 => 1, 5 => 2, 8 => 3]; - [3 => $a, 5 => $b, 8 => $c] = $array; - $b =& $array[5]; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_all_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_all_by_ref.php.inc deleted file mode 100644 index b9ae8fc07ad..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_all_by_ref.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_end_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_end_by_ref.php.inc deleted file mode 100644 index 918a7aa6c6b..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_end_by_ref.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_end_by_value.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_end_by_value.php.inc deleted file mode 100644 index c5c379dce26..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/fixture_end_by_value.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_array_destructuring_with_array_keys.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_array_destructuring_with_array_keys.php.inc deleted file mode 100644 index 6e63b848ebb..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_array_destructuring_with_array_keys.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - 1, 'b' => 2, 'firstList' => ['&c' => 3, 'd' => 4, 'secondList' => ['e' => 5, 'f' => 6, 'thirdList' => ['g' => 7, '&h' => 8, 'i' => 9]]]]; - ['a' => $a, 'b' => $b, 'firstList' => ['&c' => &$c, 'd' => $d, 'secondList' => ['e' => $e, 'f' => $f, 'thirdList' => ['g' => $g, '&h' => &$h, 'i' => $i]]]] = $array; - } -} - -?> ------ - 1, 'b' => 2, 'firstList' => ['&c' => 3, 'd' => 4, 'secondList' => ['e' => 5, 'f' => 6, 'thirdList' => ['g' => 7, '&h' => 8, 'i' => 9]]]]; - ['a' => $a, 'b' => $b, 'firstList' => ['&c' => $c, 'd' => $d, 'secondList' => ['e' => $e, 'f' => $f, 'thirdList' => ['g' => $g, '&h' => $h, 'i' => $i]]]] = $array; - $c =& $array['firstList']['&c']; - $h =& $array['firstList']['secondList']['thirdList']['&h']; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list.php.inc deleted file mode 100644 index 56663367069..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_all_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_all_by_ref.php.inc deleted file mode 100644 index 46ee9657c5b..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_all_by_ref.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - 1, '&b' => 2, 'firstList' => ['&c' => 3, '&d' => 4, 'secondList' => ['&e' => 5, '&f' => 6, 'thirdList' => ['&g' => 7, '&h' => 8, '&i' => 9]]]]; - list('&a' => &$a, '&b' => &$b, 'firstList' => list('&c' => &$c, '&d' => &$d, 'secondList' => list('&e' => &$e, '&f' => &$f, 'thirdList' => list('&g' => &$g, '&h' => &$h, '&i' => &$i)))) = $array; - } -} - -?> ------ - 1, '&b' => 2, 'firstList' => ['&c' => 3, '&d' => 4, 'secondList' => ['&e' => 5, '&f' => 6, 'thirdList' => ['&g' => 7, '&h' => 8, '&i' => 9]]]]; - $a =& $array['&a']; - $b =& $array['&b']; - $c =& $array['firstList']['&c']; - $d =& $array['firstList']['&d']; - $e =& $array['firstList']['secondList']['&e']; - $f =& $array['firstList']['secondList']['&f']; - $g =& $array['firstList']['secondList']['thirdList']['&g']; - $h =& $array['firstList']['secondList']['thirdList']['&h']; - $i =& $array['firstList']['secondList']['thirdList']['&i']; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_end_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_end_by_ref.php.inc deleted file mode 100644 index 4227f5a5d8f..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_end_by_ref.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - 1, 'b' => 2, 'firstList' => ['&c' => 3, 'd' => 4, 'secondList' => ['e' => 5, 'f' => 6, 'thirdList' => ['g' => 7, '&h' => 8, 'i' => 9]]], '&j' => 10]; - list('a' => $a, 'b' => $b, 'firstList' => list('&c' => &$c, 'd' => $d, 'secondList' => list('e' => $e, 'f' => $f, 'thirdList' => list('g' => $g, '&h' => &$h, 'i' => $i))), '&j' => &$j) = $array; - } -} - -?> ------ - 1, 'b' => 2, 'firstList' => ['&c' => 3, 'd' => 4, 'secondList' => ['e' => 5, 'f' => 6, 'thirdList' => ['g' => 7, '&h' => 8, 'i' => 9]]], '&j' => 10]; - list('a' => $a, 'b' => $b, 'firstList' => list('&c' => $c, 'd' => $d, 'secondList' => list('e' => $e, 'f' => $f, 'thirdList' => list('g' => $g, '&h' => $h, 'i' => $i)))) = $array; - $c =& $array['firstList']['&c']; - $h =& $array['firstList']['secondList']['thirdList']['&h']; - $j =& $array['&j']; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_end_by_value.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_end_by_value.php.inc deleted file mode 100644 index c76998bd595..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/multilevel_nested_list_with_array_keys_end_by_value.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - 1, 'b' => 2, 'firstList' => ['&c' => 3, 'd' => 4, 'secondList' => ['e' => 5, 'f' => 6, 'thirdList' => ['g' => 7, '&h' => 8, 'i' => 9]]], 'j' => 10]; - list('a' => $a, 'b' => $b, 'firstList' => list('&c' => &$c, 'd' => $d, 'secondList' => list('e' => $e, 'f' => $f, 'thirdList' => list('g' => $g, '&h' => &$h, 'i' => $i))), 'j' => $j) = $array; - } -} - -?> ------ - 1, 'b' => 2, 'firstList' => ['&c' => 3, 'd' => 4, 'secondList' => ['e' => 5, 'f' => 6, 'thirdList' => ['g' => 7, '&h' => 8, 'i' => 9]]], 'j' => 10]; - list('a' => $a, 'b' => $b, 'firstList' => list('&c' => $c, 'd' => $d, 'secondList' => list('e' => $e, 'f' => $f, 'thirdList' => list('g' => $g, '&h' => $h, 'i' => $i))), 'j' => $j) = $array; - $c =& $array['firstList']['&c']; - $h =& $array['firstList']['secondList']['thirdList']['&h']; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_array_destructuring.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_array_destructuring.php.inc deleted file mode 100644 index c62bf5b7707..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_array_destructuring.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list.php.inc deleted file mode 100644 index e7653a99cb0..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list_all_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list_all_by_ref.php.inc deleted file mode 100644 index 7b17217c249..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list_all_by_ref.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list_end_by_all_ref_list.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list_end_by_all_ref_list.php.inc deleted file mode 100644 index 871c8f51660..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/nested_list_end_by_all_ref_list.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/values.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/values.php.inc deleted file mode 100644 index 58147d2ac0f..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/values.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_all_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_all_by_ref.php.inc deleted file mode 100644 index f3a8e7827f7..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_all_by_ref.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - 1, 'b' => 2]; - list('a' => &$a, 'b' => &$b) = $array; - - $array = [0 => 1, 3 => 2]; - list(0 => &$a, 3 => &$b) = $array; - } -} - -?> ------ - 1, 'b' => 2]; - $a =& $array['a']; - $b =& $array['b']; - - $array = [0 => 1, 3 => 2]; - $a =& $array[0]; - $b =& $array[3]; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_end_by_ref.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_end_by_ref.php.inc deleted file mode 100644 index 6a58565cac1..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_end_by_ref.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - 1, 'b' => 2]; - list('a' => $a, 'b' => &$b) = $array; - - $array = [0 => 1, 3 => 2]; - list(0 => $a, 3 => &$b) = $array; - } -} - -?> ------ - 1, 'b' => 2]; - list('a' => $a) = $array; - $b =& $array['b']; - - $array = [0 => 1, 3 => 2]; - list(0 => $a) = $array; - $b =& $array[3]; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_end_by_value.php.inc b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_end_by_value.php.inc deleted file mode 100644 index 122cb8f931c..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/Fixture/with_array_keys_end_by_value.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - 1, 'b' => 2, 'c' => 3]; - list('a' => $a, 'b' => &$b, 'c' => $c) = $array; - - $array = [0 => 1, 3 => 2, 7 => 3]; - list(0 => $a, 3 => &$b, 7 => $c) = $array; - } -} - -?> ------ - 1, 'b' => 2, 'c' => 3]; - list('a' => $a, 'b' => $b, 'c' => $c) = $array; - $b =& $array['b']; - - $array = [0 => 1, 3 => 2, 7 => 3]; - list(0 => $a, 3 => $b, 7 => $c) = $array; - $b =& $array[3]; - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/config/configured_rule.php b/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/config/configured_rule.php deleted file mode 100644 index 656a4ad72a1..00000000000 --- a/rules-tests/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeListReferenceAssignmentRector::class); -}; diff --git a/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/DowngradeFlexibleHeredocSyntaxRectorTest.php b/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/DowngradeFlexibleHeredocSyntaxRectorTest.php deleted file mode 100644 index 25c086ef9d8..00000000000 --- a/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/DowngradeFlexibleHeredocSyntaxRectorTest.php +++ /dev/null @@ -1,40 +0,0 @@ -= 7.3 - */ -final class DowngradeFlexibleHeredocSyntaxRectorTest extends AbstractRectorTestCase -{ - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void - { - if ($this->isWindows()) { - $this->markTestSkipped('minor differences on windows, see https://github.com/rectorphp/rector/issues/6571'); - } - - $this->doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/Fixture/fixture.php.inc deleted file mode 100644 index 1ae4609fd06..00000000000 --- a/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,55 +0,0 @@ -setOnClick(<<setOnClick(<<getHtmlId()}').value = ''; - document.getElementById('{$this->getHtmlId()}').onchange(); - JAVASCRIPT); - } -} - -?> ------ -setOnClick(<<setOnClick(<<getHtmlId()}').value = ''; -document.getElementById('{$this->getHtmlId()}').onchange(); -JAVASCRIPT -); - } -} - -?> diff --git a/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/Fixture/skip_correct_position.php.inc b/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/Fixture/skip_correct_position.php.inc deleted file mode 100644 index 8f1aeeab2d2..00000000000 --- a/rules-tests/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector/Fixture/skip_correct_position.php.inc +++ /dev/null @@ -1,26 +0,0 @@ -services(); - $services->set(DowngradeFlexibleHeredocSyntaxRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/DowngradeArraySpreadRectorTest.php b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/DowngradeArraySpreadRectorTest.php deleted file mode 100644 index 2ed3e4e1683..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/DowngradeArraySpreadRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/array_fn_name.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/array_fn_name.php.inc deleted file mode 100644 index 83f587504d0..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/array_fn_name.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/combined_items.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/combined_items.php.inc deleted file mode 100644 index bc51ce76f45..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/combined_items.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/different_positions.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/different_positions.php.inc deleted file mode 100644 index 46b9f5d1a81..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/different_positions.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/fixture.php.inc deleted file mode 100644 index 8c85055fd1a..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/known_array_type.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/known_array_type.php.inc deleted file mode 100644 index 8c9fa5a34da..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/known_array_type.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/known_iterator_type.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/known_iterator_type.php.inc deleted file mode 100644 index cfba3f86fc2..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/known_iterator_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/multiple_unpacks.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/multiple_unpacks.php.inc deleted file mode 100644 index 61b11134f90..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/multiple_unpacks.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/single_unpack.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/single_unpack.php.inc deleted file mode 100644 index 2d2207e544c..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/single_unpack.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/skip_no_unpacks.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/skip_no_unpacks.php.inc deleted file mode 100644 index 6357ebc6a6d..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/skip_no_unpacks.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/unknown_type.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/unknown_type.php.inc deleted file mode 100644 index 178a677d4eb..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/unknown_type.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_item.php.inc deleted file mode 100644 index 3797fadd489..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_item.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_iterator_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_iterator_item.php.inc deleted file mode 100644 index 4c9a80e6660..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_iterator_item.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_type_function_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_type_function_item.php.inc deleted file mode 100644 index 4d4d42597b0..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_array_type_function_item.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -getArray(), 'watermelon']; - } -} - -?> ------ -getArray(); - $fruits = array_merge(['banana', 'orange'], $item2Unpacked, ['watermelon']); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_function_item_and_existing_var.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_function_item_and_existing_var.php.inc deleted file mode 100644 index fc0388a7d94..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_function_item_and_existing_var.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -getArray(), 'watermelon']; - } -} - -?> ------ -getArray(); - $fruits = array_merge(['banana', 'orange'], $item2Unpacked2, ['watermelon']); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterable_pseudotype_array.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterable_pseudotype_array.php.inc deleted file mode 100644 index 0c65b0feead..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterable_pseudotype_array.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -getData([]); - $result = [...$result]; - } - - private function getData(iterable $iterable): iterable - { - return [...$iterable]; - } -} -?> ------ -getData([]); - $result = array_merge(is_array($result) ? $result : iterator_to_array($result)); - } - - private function getData(iterable $iterable): iterable - { - return array_merge(is_array($iterable) ? $iterable : iterator_to_array($iterable)); - } -} -?> diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterable_pseudotype_function_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterable_pseudotype_function_item.php.inc deleted file mode 100644 index 9d1c09e8d27..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterable_pseudotype_function_item.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -getIterable(), 'watermelon']; - } -} - -?> ------ -getIterable(); - $fruits = array_merge(['banana', 'orange'], is_array($item2Unpacked) ? $item2Unpacked : iterator_to_array($item2Unpacked), ['watermelon']); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterator_type_function_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterator_type_function_item.php.inc deleted file mode 100644 index 52950a60f1e..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_iterator_type_function_item.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -getIterator(), 'watermelon']; - } -} - -?> ------ -getIterator(); - $fruits = array_merge(['banana', 'orange'], iterator_to_array($item2Unpacked), ['watermelon']); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_multiple_array_iterator_items.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_multiple_array_iterator_items.php.inc deleted file mode 100644 index 842152968b6..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_multiple_array_iterator_items.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_multiple_function_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_multiple_function_item.php.inc deleted file mode 100644 index 1c6786d09cb..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_multiple_function_item.php.inc +++ /dev/null @@ -1,49 +0,0 @@ -getFirstArray(), 'watermelon', ...$this->getSecondArray()]; - } -} - -?> ------ -getFirstArray(); - $item4Unpacked = $this->getSecondArray(); - $fruits = array_merge(['banana', 'orange'], $item2Unpacked, ['watermelon'], $item4Unpacked); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_null_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_null_item.php.inc deleted file mode 100644 index 9424f186bf9..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_null_item.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_unknown_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_unknown_item.php.inc deleted file mode 100644 index e439277701b..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_unknown_item.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_unknown_type_function_item.php.inc b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_unknown_type_function_item.php.inc deleted file mode 100644 index c1d544860fb..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/Fixture/with_unknown_type_function_item.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -getArrayOrTraversable(), 'watermelon']; - } -} - -?> ------ -getArrayOrTraversable(); - $fruits = array_merge(['banana', 'orange'], is_array($item2Unpacked) ? $item2Unpacked : iterator_to_array($item2Unpacked), ['watermelon']); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/config/configured_rule.php b/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/config/configured_rule.php deleted file mode 100644 index 8cbe3314640..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeArraySpreadRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/ArrowFunctionToAnonymousFunctionRectorTest.php b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/ArrowFunctionToAnonymousFunctionRectorTest.php deleted file mode 100644 index 030ab733477..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/ArrowFunctionToAnonymousFunctionRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/fixture.php.inc deleted file mode 100644 index 5a0024f4c78..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - $delimiter . strtolower($matches[1]); - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_params.php.inc b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_params.php.inc deleted file mode 100644 index 9ca07cdec8d..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_params.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - 'Hello' . $delimiter . 'world'; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_params_or_use.php.inc b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_params_or_use.php.inc deleted file mode 100644 index 05a7eebddd3..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_params_or_use.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - 'Hello world'; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_use.php.inc b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_use.php.inc deleted file mode 100644 index 5fd8c5b4e4a..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/no_use.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - $number * 2, $numbers); - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/params_with_type.php.inc b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/params_with_type.php.inc deleted file mode 100644 index 384dfc9c2ac..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/params_with_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - $delimiter . strtolower($matches[1]); - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/with_nullable_return_type.php.inc b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/with_nullable_return_type.php.inc deleted file mode 100644 index 9ba1bcc464a..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/with_nullable_return_type.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - 'Hello world'; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/with_return_type.php.inc b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/with_return_type.php.inc deleted file mode 100644 index 3c45e3e3139..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/Fixture/with_return_type.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - 'Hello world'; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/config/configured_rule.php b/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/config/configured_rule.php deleted file mode 100644 index 4828bd96523..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ArrowFunctionToAnonymousFunctionRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/DowngradeContravariantArgumentTypeRectorTest.php b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/DowngradeContravariantArgumentTypeRectorTest.php deleted file mode 100644 index 7101ae4ac5f..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/DowngradeContravariantArgumentTypeRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/fixture.php.inc deleted file mode 100644 index 7fd02297a6b..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/gap_level.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/gap_level.php.inc deleted file mode 100644 index 0ac09fe70b8..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/gap_level.php.inc +++ /dev/null @@ -1,52 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/gap_level_interface.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/gap_level_interface.php.inc deleted file mode 100644 index ddec9b14d03..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/gap_level_interface.php.inc +++ /dev/null @@ -1,48 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_construct.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_construct.php.inc deleted file mode 100644 index 43ee966e0e2..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_construct.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -setName = $setName; - - parent::__construct($message); - } -} diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_nothing_happens.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_nothing_happens.php.inc deleted file mode 100644 index 98fd08ee853..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_nothing_happens.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_overloading_construct.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_overloading_construct.php.inc deleted file mode 100644 index 60ca751184f..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_overloading_construct.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_phpdoc_method.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_phpdoc_method.php.inc deleted file mode 100644 index 924a5cf8c3f..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/skip_phpdoc_method.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_nullable_object_type.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_nullable_object_type.php.inc deleted file mode 100644 index c6260ee253f..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_nullable_object_type.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_nullable_type.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_nullable_type.php.inc deleted file mode 100644 index 40873e4d980..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_nullable_type.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_object_type.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_object_type.php.inc deleted file mode 100644 index 7b83271d2bd..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/using_object_type.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/with_interface.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/with_interface.php.inc deleted file mode 100644 index b655450df81..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Fixture/with_interface.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Source/InterfaceWithPhpdocMethod.php b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Source/InterfaceWithPhpdocMethod.php deleted file mode 100644 index 3e9998a2302..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector/Source/InterfaceWithPhpdocMethod.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(DowngradeContravariantArgumentTypeRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/DowngradeCovariantReturnTypeRectorTest.php b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/DowngradeCovariantReturnTypeRectorTest.php deleted file mode 100644 index 5538b64f5c6..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/DowngradeCovariantReturnTypeRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/fixture.php.inc deleted file mode 100644 index 1a101c0ebb1..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/gap_level_ancestor.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/gap_level_ancestor.php.inc deleted file mode 100644 index f4da7aa3f6f..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/gap_level_ancestor.php.inc +++ /dev/null @@ -1,53 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/iterable.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/iterable.php.inc deleted file mode 100644 index 635cdfe9967..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/iterable.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/nullable_type.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/nullable_type.php.inc deleted file mode 100644 index 968dd830f33..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/nullable_type.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/self.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/self.php.inc deleted file mode 100644 index 038de9eaa53..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/self.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/skip_before_traverse.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/skip_before_traverse.php.inc deleted file mode 100644 index 7effde94338..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/skip_before_traverse.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/two_level_ancestor.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/two_level_ancestor.php.inc deleted file mode 100644 index f2e940e45bb..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/two_level_ancestor.php.inc +++ /dev/null @@ -1,57 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/two_level_interface.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/two_level_interface.php.inc deleted file mode 100644 index 942045dddcf..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/two_level_interface.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/with_interface.php.inc b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/with_interface.php.inc deleted file mode 100644 index 5f7290a14f0..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Fixture/with_interface.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Source/TwoLevelChildType.php b/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Source/TwoLevelChildType.php deleted file mode 100644 index 60e2d49e2ce..00000000000 --- a/rules-tests/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector/Source/TwoLevelChildType.php +++ /dev/null @@ -1,9 +0,0 @@ -services(); - $services->set(DowngradeCovariantReturnTypeRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/DowngradeNullCoalescingOperatorRectorTest.php b/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/DowngradeNullCoalescingOperatorRectorTest.php deleted file mode 100644 index ea17a4d5f90..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/DowngradeNullCoalescingOperatorRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/Fixture/fixture.php.inc deleted file mode 100644 index f414796affb..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/config/configured_rule.php b/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/config/configured_rule.php deleted file mode 100644 index 7a0a3b5b474..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeNullCoalescingOperatorRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/DowngradeArrayMergeCallWithoutArgumentsRectorTest.php b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/DowngradeArrayMergeCallWithoutArgumentsRectorTest.php deleted file mode 100644 index 7612dbfdf5e..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/DowngradeArrayMergeCallWithoutArgumentsRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/array_merge.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/array_merge.php.inc deleted file mode 100644 index 3e9b464b88b..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/array_merge.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/array_merge_recursive.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/array_merge_recursive.php.inc deleted file mode 100644 index fdb9a3f8597..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/array_merge_recursive.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_array.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_array.php.inc deleted file mode 100644 index 6995e598362..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_array.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_multiple_arguments.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_multiple_arguments.php.inc deleted file mode 100644 index 7e7bd993bb6..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_multiple_arguments.php.inc +++ /dev/null @@ -1,22 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_variable.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_variable.php.inc deleted file mode 100644 index e3159cab783..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/Fixture/skip_variable.php.inc +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/config/configured_rule.php b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/config/configured_rule.php deleted file mode 100644 index 0938e129657..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeArrayMergeCallWithoutArgumentsRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/DowngradeStripTagsCallWithArrayRectorTest.php b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/DowngradeStripTagsCallWithArrayRectorTest.php deleted file mode 100644 index b2af88d7747..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/DowngradeStripTagsCallWithArrayRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/assign_to_existing_var.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/assign_to_existing_var.php.inc deleted file mode 100644 index 3378648619b..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/assign_to_existing_var.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -getTags()); - } -} - -?> ------ -<', $stringAllowableTags2) . '>' : $stringAllowableTags2); - $stringAllowableTags2 = $this->getTags(); - strip_tags($string, $stringAllowableTags2 !== null && is_array($stringAllowableTags2) ? '<' . implode('><', $stringAllowableTags2) . '>' : $stringAllowableTags2); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/assign_to_var.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/assign_to_var.php.inc deleted file mode 100644 index b09549f535f..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/assign_to_var.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -getTags()); - - strip_tags($string, $someVar ?? getTags()); - } -} - -?> ------ -getTags(); - strip_tags($string, $stringAllowableTags !== null && is_array($stringAllowableTags) ? '<' . implode('><', $stringAllowableTags) . '>' : $stringAllowableTags); - $stringAllowableTags = $someVar ?? getTags(); - - strip_tags($string, $stringAllowableTags !== null && is_array($stringAllowableTags) ? '<' . implode('><', $stringAllowableTags) . '>' : $stringAllowableTags); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/consts.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/consts.php.inc deleted file mode 100644 index 35390e2232f..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/consts.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ -<', SOME_DEFINE) . '>' : SOME_DEFINE); - strip_tags($string, self::SOME_CONST !== null && is_array(self::SOME_CONST) ? '<' . implode('><', self::SOME_CONST) . '>' : self::SOME_CONST); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/fixture.php.inc deleted file mode 100644 index ced11182c7e..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ -<', ['a', 'p']) . '>'); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/properties.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/properties.php.inc deleted file mode 100644 index c8a174f8f4e..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/properties.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -tags); - } -} - -?> ------ -tags !== null && is_array($this->tags) ? '<' . implode('><', $this->tags) . '>' : $this->tags); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_empty.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_empty.php.inc deleted file mode 100644 index 8a0c9c34dce..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_empty.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_null.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_null.php.inc deleted file mode 100644 index 92dac167489..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_null.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_strings.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_strings.php.inc deleted file mode 100644 index 2caf0b246cf..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/skip_strings.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -

'); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/variables.php.inc b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/variables.php.inc deleted file mode 100644 index 317b6721157..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/Fixture/variables.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ -<', $tags) . '>' : $tags); - } -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/config/configured_rule.php b/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/config/configured_rule.php deleted file mode 100644 index 87beaabc34a..00000000000 --- a/rules-tests/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeStripTagsCallWithArrayRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/DowngradeFreadFwriteFalsyToNegationRectorTest.php b/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/DowngradeFreadFwriteFalsyToNegationRectorTest.php deleted file mode 100644 index 2a41bd3e270..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/DowngradeFreadFwriteFalsyToNegationRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/fixture.php.inc deleted file mode 100644 index eb13159735c..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/skip_not_compare_with_false.php.inc b/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/skip_not_compare_with_false.php.inc deleted file mode 100644 index 0b361eead11..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/skip_not_compare_with_false.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/skip_not_fread_fwrite.php.inc b/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/skip_not_fread_fwrite.php.inc deleted file mode 100644 index 8ab398a1453..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/Fixture/skip_not_fread_fwrite.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/config/configured_rule.php b/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/config/configured_rule.php deleted file mode 100644 index 9f613b1a9ef..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeFreadFwriteFalsyToNegationRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/DowngradeNumericLiteralSeparatorRectorTest.php b/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/DowngradeNumericLiteralSeparatorRectorTest.php deleted file mode 100644 index 65ace1c5072..00000000000 --- a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/DowngradeNumericLiteralSeparatorRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/fixture.php.inc deleted file mode 100644 index 93c4790bd59..00000000000 --- a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/skip_float_max.php.inc b/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/skip_float_max.php.inc deleted file mode 100644 index 721d8700f07..00000000000 --- a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/skip_float_max.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/skip_no_change.php.inc b/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/skip_no_change.php.inc deleted file mode 100644 index 03b220f0288..00000000000 --- a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/Fixture/skip_no_change.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - diff --git a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/config/configured_rule.php b/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/config/configured_rule.php deleted file mode 100644 index def41ac90de..00000000000 --- a/rules-tests/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeNumericLiteralSeparatorRector::class); -}; diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/DowngradeTypedPropertyRectorTest.php b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/DowngradeTypedPropertyRectorTest.php deleted file mode 100644 index a17c6dbf759..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/DowngradeTypedPropertyRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/classname_nullable_type.php.inc b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/classname_nullable_type.php.inc deleted file mode 100644 index 8c7e5cb3d75..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/classname_nullable_type.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/classname_type.php.inc b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/classname_type.php.inc deleted file mode 100644 index 73c158d4749..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/classname_type.php.inc +++ /dev/null @@ -1,22 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/docblock_exists.php.inc b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/docblock_exists.php.inc deleted file mode 100644 index 31ba9e84148..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/docblock_exists.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/docblock_with_var_type.php.inc b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/docblock_with_var_type.php.inc deleted file mode 100644 index 57a74450134..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/docblock_with_var_type.php.inc +++ /dev/null @@ -1,87 +0,0 @@ - - */ - private array $associativeArrayProperty; - - /** - * This property is misleading - * @var int - */ - private float $misleadinglyAnnotatedProperty; - - /** - * This property is the worst - * @var string - */ - private bool $wronglyAnnotatedProperty; - - /** - * This property allows less than the annotation suggests - * @var int|null - */ - private int $widerAnnotatedProperty; - - /** - * This property allows more than the annotation suggests - * @var int - */ - private ?int $stricterAnnotatedProperty; -} - -?> ------ - - */ - private $associativeArrayProperty; - - /** - * This property is misleading - * @var int - */ - private $misleadinglyAnnotatedProperty; - - /** - * This property is the worst - * @var string - */ - private $wronglyAnnotatedProperty; - - /** - * This property allows less than the annotation suggests - * @var int|null - */ - private $widerAnnotatedProperty; - - /** - * This property allows more than the annotation suggests - * @var int - */ - private $stricterAnnotatedProperty; -} - -?> diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/fixture.php.inc deleted file mode 100644 index 680079be9d0..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/nullable_type.php.inc b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/nullable_type.php.inc deleted file mode 100644 index 57adc79e2c5..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/nullable_type.php.inc +++ /dev/null @@ -1,22 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/nullable_type_with_default.php.inc b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/nullable_type_with_default.php.inc deleted file mode 100644 index 741e4d8b682..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Fixture/nullable_type_with_default.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Source/AnotherClass.php b/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Source/AnotherClass.php deleted file mode 100644 index 24ce994c2c9..00000000000 --- a/rules-tests/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector/Source/AnotherClass.php +++ /dev/null @@ -1,8 +0,0 @@ -services(); - $services->set(DowngradeTypedPropertyRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/DowngradeNonCapturingCatchesRectorTest.php b/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/DowngradeNonCapturingCatchesRectorTest.php deleted file mode 100644 index d3a3cd72e3d..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/DowngradeNonCapturingCatchesRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/fixture.php.inc deleted file mode 100644 index 1267ffd4bb3..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/multi_catch.php.inc b/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/multi_catch.php.inc deleted file mode 100644 index 47be40cb553..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/multi_catch.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/skip_already.php.inc b/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/skip_already.php.inc deleted file mode 100644 index 16855a2dd2e..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector/Fixture/skip_already.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -services(); - $services->set(DowngradeNonCapturingCatchesRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/DowngradeClassOnObjectToGetClassRectorTest.php b/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/DowngradeClassOnObjectToGetClassRectorTest.php deleted file mode 100644 index 4386fd36998..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/DowngradeClassOnObjectToGetClassRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/fixture.php.inc deleted file mode 100644 index a82466d6ef0..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/func_call.php.inc b/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/func_call.php.inc deleted file mode 100644 index 215fc7cf125..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/func_call.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/property_fetch.php.inc b/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/property_fetch.php.inc deleted file mode 100644 index bb74dcada62..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/property_fetch.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - - */ - public function getRectorClass(): string - { - return $this->rector::class; - } -} - -?> ------ - - */ - public function getRectorClass(): string - { - return get_class($this->rector); - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/property_fetch2.php.inc b/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/property_fetch2.php.inc deleted file mode 100644 index a3c651ebbd0..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/property_fetch2.php.inc +++ /dev/null @@ -1,51 +0,0 @@ - - */ - public function getRectorClass(): string - { - $expr = new \stdClass; - $expr->var = $this->rector; - return $expr->var::class; - } -} - -?> ------ - - */ - public function getRectorClass(): string - { - $expr = new \stdClass; - $expr->var = $this->rector; - return get_class($expr->var); - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/static_property_fetch.php.inc b/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/static_property_fetch.php.inc deleted file mode 100644 index 998ba1249f5..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/Fixture/static_property_fetch.php.inc +++ /dev/null @@ -1,51 +0,0 @@ - - */ - public function getRectorClass(): string - { - return self::$rector::class; - } -} - -?> ------ - - */ - public function getRectorClass(): string - { - return get_class(self::$rector); - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/config/configured_rule.php deleted file mode 100644 index 96345893e50..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeClassOnObjectToGetClassRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/DowngradeAbstractPrivateMethodInTraitRectorTest.php b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/DowngradeAbstractPrivateMethodInTraitRectorTest.php deleted file mode 100644 index e8c6f592173..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/DowngradeAbstractPrivateMethodInTraitRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_abstract_non_private.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_abstract_non_private.php.inc deleted file mode 100644 index d7a3f0d4e73..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_abstract_non_private.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_non_abstract_private.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_non_abstract_private.php.inc deleted file mode 100644 index 01050564f18..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_non_abstract_private.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_non_trait.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_non_trait.php.inc deleted file mode 100644 index d4bb8b97c63..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/skip_non_trait.php.inc +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/with_abstract_private.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/with_abstract_private.php.inc deleted file mode 100644 index d0c839a20a3..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/Fixture/with_abstract_private.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/config/configured_rule.php deleted file mode 100644 index e7c7888c597..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeAbstractPrivateMethodInTraitRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/DowngradeStaticTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/DowngradeStaticTypeDeclarationRectorTest.php deleted file mode 100644 index 451672f68ff..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/DowngradeStaticTypeDeclarationRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/also_trait.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/also_trait.php.inc deleted file mode 100644 index 78f22712f4a..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/also_trait.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/docblock_exists.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/docblock_exists.php.inc deleted file mode 100644 index eb6e0e31c4e..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/docblock_exists.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc deleted file mode 100644 index e3fe1f2694a..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/fixture.php.inc deleted file mode 100644 index 27d8e623686..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/skip_self.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/skip_self.php.inc deleted file mode 100644 index 97fcc047b9a..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector/Fixture/skip_self.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(DowngradeStaticTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/DowngradeTrailingCommasInParamUseRectorTest.php b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/DowngradeTrailingCommasInParamUseRectorTest.php deleted file mode 100644 index 9075c606116..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/DowngradeTrailingCommasInParamUseRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/fixture.php.inc deleted file mode 100644 index 719a7767847..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure.php.inc deleted file mode 100644 index 6480d3cfda4..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure_has_param_and_use.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure_has_param_and_use.php.inc deleted file mode 100644 index 87bdf7f56bb..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure_has_param_and_use.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure_use.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure_use.php.inc deleted file mode 100644 index 00a88582c04..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_closure_use.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_constructor_call.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_constructor_call.php.inc deleted file mode 100644 index 5086c42aa67..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_constructor_call.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_func_call.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_func_call.php.inc deleted file mode 100644 index 1401aee9fe8..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_func_call.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_function.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_function.php.inc deleted file mode 100644 index 3a0e0a4b073..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_function.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_method_call.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_method_call.php.inc deleted file mode 100644 index c6a1c02d1e0..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_method_call.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -getString('foo',); - // ... - } - public function getString(...$params): string - { - return implode(', ', $params); - } -} - -?> ------ -getString('foo'); - // ... - } - public function getString(...$params): string - { - return implode(', ', $params); - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_static_call.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_static_call.php.inc deleted file mode 100644 index 8dd9b202ce6..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/in_static_call.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/skip_no_trailing_comma_in_last.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/skip_no_trailing_comma_in_last.php.inc deleted file mode 100644 index 80087e7933d..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/skip_no_trailing_comma_in_last.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/skip_none.php.inc b/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/skip_none.php.inc deleted file mode 100644 index 60a2b29642a..00000000000 --- a/rules-tests/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector/Fixture/skip_none.php.inc +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(DowngradeTrailingCommasInParamUseRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/DowngradeAttributeToAnnotationRectorTest.php b/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/DowngradeAttributeToAnnotationRectorTest.php deleted file mode 100644 index abe95ccb2ea..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/DowngradeAttributeToAnnotationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/some_class.php.inc deleted file mode 100644 index bc3e1be8687..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/symfony_required.php.inc b/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/symfony_required.php.inc deleted file mode 100644 index 610d5e87f19..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/symfony_required.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/use_attribute.php.inc b/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/use_attribute.php.inc deleted file mode 100644 index 70c7c604617..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/Fixture/use_attribute.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/config/configured_rule.php deleted file mode 100644 index 8da4c9aeff9..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector/config/configured_rule.php +++ /dev/null @@ -1,24 +0,0 @@ -services(); - - $services->set(DowngradeAttributeToAnnotationRector::class) - ->call('configure', [[ - DowngradeAttributeToAnnotationRector::ATTRIBUTE_TO_ANNOTATION => ValueObjectInliner::inline([ - new DowngradeAttributeToAnnotation( - 'Symfony\Component\Routing\Annotation\Route', - 'Symfony\Component\Routing\Annotation\Route' - ), - new DowngradeAttributeToAnnotation('Symfony\Contracts\Service\Attribute\Required', 'required'), - new DowngradeAttributeToAnnotation('Attribute', 'Attribute'), - ]), - ]]); -}; diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/DowngradePropertyPromotionRectorTest.php b/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/DowngradePropertyPromotionRectorTest.php deleted file mode 100644 index f6ec86535f8..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/DowngradePropertyPromotionRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/add_array_defaults.php.inc b/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/add_array_defaults.php.inc deleted file mode 100644 index 9428a0c0d66..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/add_array_defaults.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ -values = $values; - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/fixture.php.inc deleted file mode 100644 index 4b250ac0809..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ -value = $value; - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/use_param_type.php.inc b/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/use_param_type.php.inc deleted file mode 100644 index 75b6c3c1ced..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/use_param_type.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - $values - */ - public function __construct( - private array $values - ) { - } -} - -?> ------ - - */ - private array $values; - /** - * @param array $values - */ - public function __construct(array $values) - { - $this->values = $values; - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/with_attributes.php.inc b/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/with_attributes.php.inc deleted file mode 100644 index bc4ddd914f2..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/Fixture/with_attributes.php.inc +++ /dev/null @@ -1,62 +0,0 @@ - ------ -property = $property; - // normal comment on property promotion should be moved to assign - $this->property2 = $property2; - // a comment - // with attribute too - $this->property3 = $property3; - $this->property4 = $property4; - // a comment on one liner attribute - $this->property5 = $property5; - } -} -?> diff --git a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/config/configured_rule.php deleted file mode 100644 index b6f865b0446..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradePropertyPromotionRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/DowngradeMatchToSwitchRectorTest.php b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/DowngradeMatchToSwitchRectorTest.php deleted file mode 100644 index b7554542ef3..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/DowngradeMatchToSwitchRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/Fixture/fixture.php.inc deleted file mode 100644 index 9207b3b57b3..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - null, - 400 => 'not found', - default => 'unknown status code', - }; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/Fixture/return.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/Fixture/return.php.inc deleted file mode 100644 index 95aacf2317e..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/Fixture/return.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - null, - 400 => 'not found', - default => 'unknown status code', - }; - } -} - -?> ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/config/configured_rule.php deleted file mode 100644 index 5c76614020c..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeMatchToSwitchRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/DowngradeThrowExprRectorTest.php b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/DowngradeThrowExprRectorTest.php deleted file mode 100644 index 8c1a74b2419..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/DowngradeThrowExprRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/assign_direct_throw.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/assign_direct_throw.php.inc deleted file mode 100644 index 02e1db967dc..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/assign_direct_throw.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/coalesce_without_assign.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/coalesce_without_assign.php.inc deleted file mode 100644 index 5f0c6e2ce42..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/coalesce_without_assign.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/fixture.php.inc deleted file mode 100644 index 00e73d03965..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/skip_direct_throw.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/skip_direct_throw.php.inc deleted file mode 100644 index 24a040e7290..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/skip_direct_throw.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/skip_no_throw_in_assign.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/skip_no_throw_in_assign.php.inc deleted file mode 100644 index 11fc87f95c0..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/skip_no_throw_in_assign.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/truthy_ternary.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/truthy_ternary.php.inc deleted file mode 100644 index c8b88273369..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/truthy_ternary.php.inc +++ /dev/null @@ -1,51 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/truthy_ternary_without_assign.php.inc b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/truthy_ternary_without_assign.php.inc deleted file mode 100644 index 854b6ca4749..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/Fixture/truthy_ternary_without_assign.php.inc +++ /dev/null @@ -1,49 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/config/configured_rule.php deleted file mode 100644 index a1435f34f49..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeThrowExprRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/DowngradeStrContainsRectorTest.php b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/DowngradeStrContainsRectorTest.php deleted file mode 100644 index f89fd9060ea..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/DowngradeStrContainsRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains.php.inc deleted file mode 100644 index 249e420250f..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains_with_mixed.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains_with_mixed.php.inc deleted file mode 100644 index 17e3a39362f..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains_with_mixed.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains_with_variables.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains_with_variables.php.inc deleted file mode 100644 index 08fc5fdab90..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/not_str_contains_with_variables.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php.inc deleted file mode 100644 index b7e8b2c53f2..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php_with_mixed.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php_with_mixed.php.inc deleted file mode 100644 index f4515a75ce0..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php_with_mixed.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php_with_variables.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php_with_variables.php.inc deleted file mode 100644 index 779aed4496d..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/Fixture/str_contains.php_with_variables.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/config/configured_rule.php deleted file mode 100644 index 0b0208a8ea6..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeStrContainsRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/DowngradeStrEndsWithRectorTest.php b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/DowngradeStrEndsWithRectorTest.php deleted file mode 100644 index 3028d55150b..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/DowngradeStrEndsWithRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/Fixture/not_identical_downgrade_str_ends.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/Fixture/not_identical_downgrade_str_ends.php.inc deleted file mode 100644 index a793892dd67..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/Fixture/not_identical_downgrade_str_ends.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/Fixture/some_class.php.inc deleted file mode 100644 index 380a2cd8c40..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/config/configured_rule.php deleted file mode 100644 index f9eac39333f..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeStrEndsWithRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/DowngradeStrStartsWithRectorTest.php b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/DowngradeStrStartsWithRectorTest.php deleted file mode 100644 index ffe62812d97..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/DowngradeStrStartsWithRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/Fixture/not_compare.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/Fixture/not_compare.php.inc deleted file mode 100644 index f936405f1a1..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/Fixture/not_compare.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/Fixture/some_class.php.inc deleted file mode 100644 index 1ea3a68cad5..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/config/configured_rule.php deleted file mode 100644 index fb6b188a167..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeStrStartsWithRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/DowngradeMixedTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/DowngradeMixedTypeDeclarationRectorTest.php deleted file mode 100644 index e71247fb869..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/DowngradeMixedTypeDeclarationRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/docblock_exists.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/docblock_exists.php.inc deleted file mode 100644 index c82b5310fde..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/docblock_exists.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc deleted file mode 100644 index f9503829068..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/docblock_tag_exists.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/fixture.php.inc deleted file mode 100644 index df83bf1159b..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/multiple_matching_params.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/multiple_matching_params.php.inc deleted file mode 100644 index 9f7fab0858e..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/multiple_matching_params.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/multiple_params.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/multiple_params.php.inc deleted file mode 100644 index 6af2cd769fc..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/multiple_params.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/on_closure.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/on_closure.php.inc deleted file mode 100644 index b005e7cd2ae..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/on_closure.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_docblock_exists.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_docblock_exists.php.inc deleted file mode 100644 index ae878e13f4c..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_docblock_exists.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_docblock_tag_exists.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_docblock_tag_exists.php.inc deleted file mode 100644 index 83bf455750f..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_docblock_tag_exists.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_fixture.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_fixture.php.inc deleted file mode 100644 index 81f75b04a3d..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/return_fixture.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/skip_parent_return_self_child_return_parent.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/skip_parent_return_self_child_return_parent.php.inc deleted file mode 100644 index fac8d6286b6..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector/Fixture/skip_parent_return_self_child_return_parent.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -services(); - $services->set(DowngradeMixedTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeStaticTypeDeclarationRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeStaticTypeDeclarationRector/config/configured_rule.php deleted file mode 100644 index fb6681d737a..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeStaticTypeDeclarationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeStaticTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/DowngradeUnionTypeDeclarationRectorTest.php b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/DowngradeUnionTypeDeclarationRectorTest.php deleted file mode 100644 index a6a78f650da..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/DowngradeUnionTypeDeclarationRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/closure.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/closure.php.inc deleted file mode 100644 index e22440dd735..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/closure.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - \ No newline at end of file diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/function_union_too.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/function_union_too.php.inc deleted file mode 100644 index c39442fe749..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/function_union_too.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/param_union.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/param_union.php.inc deleted file mode 100644 index f138667265f..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/param_union.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/return_union.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/return_union.php.inc deleted file mode 100644 index c91e7da4b91..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/return_union.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/config/configured_rule.php deleted file mode 100644 index 947525cf0ce..00000000000 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeUnionTypeDeclarationRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/DowngradeNamedArgumentRectorTest.php b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/DowngradeNamedArgumentRectorTest.php deleted file mode 100644 index 007f6aa1486..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/DowngradeNamedArgumentRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/fixture.php.inc deleted file mode 100644 index 972c02df283..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -execute(a: [[$name ?? 0 => $attributes]]); - } -} - -?> ------ -execute([[$name ?? 0 => $attributes]]); - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/fixture2.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/fixture2.php.inc deleted file mode 100644 index 8fe1305ef76..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -execute(b: [[$name ?? 0 => $attributes]]); - } -} - -?> ------ -execute(null, [[$name ?? 0 => $attributes]]); - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/flip_order.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/flip_order.php.inc deleted file mode 100644 index 98e947cfa15..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/flip_order.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -execute(b: [[$name ?? 0 => $attributes]], a: []); - } -} - -?> ------ -execute([], [[$name ?? 0 => $attributes]]); - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_func_call.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_func_call.php.inc deleted file mode 100644 index e40ed704bd9..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_func_call.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new.php.inc deleted file mode 100644 index f9074eb85c3..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new2.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new2.php.inc deleted file mode 100644 index a5ed7e8692d..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new2.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new3.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new3.php.inc deleted file mode 100644 index c2e27633407..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/on_new3.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/rector_configuration.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/rector_configuration.php.inc deleted file mode 100644 index 580255fd4ab..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/rector_configuration.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/skip_no_arg.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/skip_no_arg.php.inc deleted file mode 100644 index 3205f162385..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/skip_no_arg.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/skip_no_named_arg.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/skip_no_named_arg.php.inc deleted file mode 100644 index c5b2f31851d..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/skip_no_named_arg.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/static_call.php.inc b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/static_call.php.inc deleted file mode 100644 index 6c375713038..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Fixture/static_call.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - $attributes]]); - } -} - -?> ------ - $attributes]]); - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Source/Foo.php b/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Source/Foo.php deleted file mode 100644 index e65643faa9b..00000000000 --- a/rules-tests/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector/Source/Foo.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(DowngradeNamedArgumentRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/DowngradeNullsafeToTernaryOperatorRectorTest.php b/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/DowngradeNullsafeToTernaryOperatorRectorTest.php deleted file mode 100644 index e92e979e76c..00000000000 --- a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/DowngradeNullsafeToTernaryOperatorRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -= 8.0 - */ - public function test(SmartFileInfo $fileInfo): void - { - $this->doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/fixture.php.inc deleted file mode 100644 index a0d68ef6a7a..00000000000 --- a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,59 +0,0 @@ -getStartDate($args1)?->asDateTimeString($arg2); - - // without argument, one is method call, next property fetch - $dateAsString = $booking->getStartDate()?->asDateTimeString; - - // without argument, one is property call, next method fetch - $dateAsString = $booking->getStartDate?->asDateTimeString(); - - // all are property fetch - $dateAsString = $booking->getStartDate?->asDateTimeString; - - $getStartDate = true; - $bookingGetStartDate = true; - - // previously named vars aren't reused - $dateAsString = $booking->getStartDate?->asDateTimeString; - } -} - -?> ------ -getStartDate($args1)) ? $getStartDate->asDateTimeString($arg2) : null; - - // without argument, one is method call, next property fetch - $dateAsString = ($getStartDate = $booking->getStartDate()) ? $getStartDate->asDateTimeString : null; - - // without argument, one is property call, next method fetch - $dateAsString = ($bookingGetStartDate = $booking->getStartDate) ? $bookingGetStartDate->asDateTimeString() : null; - - // all are property fetch - $dateAsString = ($bookingGetStartDate = $booking->getStartDate) ? $bookingGetStartDate->asDateTimeString : null; - - $getStartDate = true; - $bookingGetStartDate = true; - - // previously named vars aren't reused - $dateAsString = ($bookingGetStartDate2 = $booking->getStartDate) ? $bookingGetStartDate2->asDateTimeString : null; - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/get_nullable.php.inc b/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/get_nullable.php.inc deleted file mode 100644 index 39ac2a3938f..00000000000 --- a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/get_nullable.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -extractArrayItemByKey($value)?->value; - } - - protected function extractArrayItemByKey($value): ?ArrayItem - { - return null; - } -} - -?> ------ -extractArrayItemByKey($value)) ? $extractArrayItemByKey->value : null; - } - - protected function extractArrayItemByKey($value): ?ArrayItem - { - return null; - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/get_nullable_in_trait.php.inc b/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/get_nullable_in_trait.php.inc deleted file mode 100644 index 00c7494b870..00000000000 --- a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/get_nullable_in_trait.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -extractArrayItemByKey($value)?->value; - } - - protected function extractArrayItemByKey($value): ?ArrayItem - { - return null; - } -} - -?> ------ -extractArrayItemByKey($value)) ? $extractArrayItemByKey->value : null; - } - - protected function extractArrayItemByKey($value): ?ArrayItem - { - return null; - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/multiple_call.php.inc b/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/multiple_call.php.inc deleted file mode 100644 index 59f8c773fa1..00000000000 --- a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/Fixture/multiple_call.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -multiple($args1)?->call($args2)?->otherCall($args3); - $result = $object->multiple($args1)?->call($args2)?->otherCall($args3)?->anotherCall($args4); - } -} - -?> ------ -multiple($args1)) ? $multiple->call($args2) : null) ? $call->otherCall($args3) : null; - $result = ($otherCall = ($call = ($multiple = $object->multiple($args1)) ? $multiple->call($args2) : null) ? $call->otherCall($args3) : null) ? $otherCall->anotherCall($args4) : null; - } -} - -?> diff --git a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/config/configured_rule.php deleted file mode 100644 index fbf2e859529..00000000000 --- a/rules-tests/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeNullsafeToTernaryOperatorRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/DowngradeUnionTypeTypedPropertyRectorTest.php b/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/DowngradeUnionTypeTypedPropertyRectorTest.php deleted file mode 100644 index 2f8e4544190..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/DowngradeUnionTypeTypedPropertyRectorTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/Fixture/fixture.php.inc b/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/Fixture/fixture.php.inc deleted file mode 100644 index 70712942a0e..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/config/configured_rule.php deleted file mode 100644 index eebeb219976..00000000000 --- a/rules-tests/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DowngradeUnionTypeTypedPropertyRector::class); -}; diff --git a/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/DowngradePhpTokenRectorTest.php b/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/DowngradePhpTokenRectorTest.php deleted file mode 100644 index d6690d324e0..00000000000 --- a/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/DowngradePhpTokenRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/Fixture/some_class.php.inc deleted file mode 100644 index e8dcfd6a24b..00000000000 --- a/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -getTokenName(); - $text = $token->text; -} - -?> ------ - diff --git a/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/config/configured_rule.php b/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/config/configured_rule.php deleted file mode 100644 index a27eae3a475..00000000000 --- a/rules-tests/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(DowngradePhpTokenRector::class); -}; diff --git a/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/DowngradeFinalizePublicClassConstantRectorTest.php b/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/DowngradeFinalizePublicClassConstantRectorTest.php deleted file mode 100644 index 8ad7bfb8a7f..00000000000 --- a/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/DowngradeFinalizePublicClassConstantRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/Fixture/some_class.php.inc b/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/Fixture/some_class.php.inc deleted file mode 100644 index 189850571a6..00000000000 --- a/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - ------ - diff --git a/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/config/configured_rule.php b/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/config/configured_rule.php deleted file mode 100644 index 50341bb3c68..00000000000 --- a/rules-tests/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(DowngradeFinalizePublicClassConstantRector::class); -}; diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/ChangeNestedForeachIfsToEarlyContinueRectorTest.php b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/ChangeNestedForeachIfsToEarlyContinueRectorTest.php index 14adef190b1..aebd3138909 100644 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/ChangeNestedForeachIfsToEarlyContinueRectorTest.php +++ b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/ChangeNestedForeachIfsToEarlyContinueRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ChangeNestedForeachIfsToEarlyContinueRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/comment_inside_if_statement.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/comment_inside_if_statement.php.inc index ec3cc097ae9..b0dacbd139d 100644 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/comment_inside_if_statement.php.inc +++ b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/comment_inside_if_statement.php.inc @@ -35,7 +35,6 @@ class CommentInsideIfStatement if ($value !== 5) { continue; } - // why am I doing this? if ($value !== 10) { continue; } diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/multi_exprs_with_OR_both_true2.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/multi_exprs_with_OR_both_true2.php.inc new file mode 100644 index 00000000000..55068bebd51 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/multi_exprs_with_OR_both_true2.php.inc @@ -0,0 +1,60 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/skip_nested_with_or_root_if.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/skip_nested_with_or_root_if.php.inc new file mode 100644 index 00000000000..2832dd0ced8 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/Fixture/skip_nested_with_or_root_if.php.inc @@ -0,0 +1,19 @@ + $value) { + if ($key < 7000 || $key == 10000) { + if (array_key_exists($key, $arrayTwo)) { + $executed = true; + } + } + } + return $executed; + } +} diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/config/configured_rule.php index 6f729931e43..8ee16e43f54 100644 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/config/configured_rule.php +++ b/rules-tests/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ChangeNestedForeachIfsToEarlyContinueRector::class); -}; +return RectorConfig::configure() + ->withRules([ChangeNestedForeachIfsToEarlyContinueRector::class]); diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/fixture.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/fixture.php.inc deleted file mode 100644 index 70d995e6451..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_assign_not_in_expression.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_assign_not_in_expression.php.inc deleted file mode 100644 index 38a9c315770..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_assign_not_in_expression.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -pathOK = true; - foreach ($pathConstants as $allowedPath) { - if ($dirPath == $allowedPath) { - $this->pathOK = true; - break; - } - } - - return $this->pathOK; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_before_break_not_assign.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_before_break_not_assign.php.inc deleted file mode 100644 index 75136038c89..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_before_break_not_assign.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_before_break_not_expression.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_before_break_not_expression.php.inc deleted file mode 100644 index 569d4304a87..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_before_break_not_expression.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_in_if.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_in_if.php.inc deleted file mode 100644 index 41d768b66e0..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_in_if.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_multi_break.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_multi_break.php.inc deleted file mode 100644 index 41525e6951b..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_multi_break.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_break.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_break.php.inc deleted file mode 100644 index d8c6aac0cd2..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_break.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_previous.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_previous.php.inc deleted file mode 100644 index c460baa46e4..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_previous.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_return_after_foreach.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_return_after_foreach.php.inc deleted file mode 100644 index 4af33a59f19..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_no_return_after_foreach.php.inc +++ /dev/null @@ -1,18 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_prev_assign_not_linear_expression.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_prev_assign_not_linear_expression.php.inc deleted file mode 100644 index 9cf612951cb..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_prev_assign_not_linear_expression.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_var_used_multiple.php.inc b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_var_used_multiple.php.inc deleted file mode 100644 index 47962769280..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/Fixture/skip_var_used_multiple.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - - diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/ReturnAfterToEarlyOnBreakRectorTest.php b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/ReturnAfterToEarlyOnBreakRectorTest.php deleted file mode 100644 index cbdd724cadf..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/ReturnAfterToEarlyOnBreakRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/config/configured_rule.php deleted file mode 100644 index cbf017bc01a..00000000000 --- a/rules-tests/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ReturnAfterToEarlyOnBreakRector::class); -}; diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/ChangeAndIfToEarlyReturnRectorTest.php b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/ChangeAndIfToEarlyReturnRectorTest.php deleted file mode 100644 index f7b67f1564f..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/ChangeAndIfToEarlyReturnRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/always_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/always_return.php.inc deleted file mode 100644 index 9a14fa713bc..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/always_return.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - $sql, 'empty' => 'empty']; - } - - return true; - } -} - -?> ------ - $sql, 'empty' => 'empty']; - return true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/boolean_not.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/boolean_not.php.inc deleted file mode 100644 index 4715fd0d1d8..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/boolean_not.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -hasWheels && !$car->hasFuel) { - $this->canDrive = true; - } - - return; - } -} - -?> ------ -hasWheels) { - return; - } - if ($car->hasFuel) { - return; - } - $this->canDrive = true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/closure.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/closure.php.inc deleted file mode 100644 index 1e493cef044..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/closure.php.inc +++ /dev/null @@ -1,53 +0,0 @@ -traverseNodesWithCallable($nodes, function (Node $node) { - if ($node instanceof Name && $node instanceof Identifier) { - $this->processNameOrIdentifier($node); - } - - return; - }); - - return $nodes; - } -} - -?> ------ -traverseNodesWithCallable($nodes, function (Node $node) { - if (!$node instanceof Name) { - return; - } - if (!$node instanceof Identifier) { - return; - } - $this->processNameOrIdentifier($node); - }); - - return $nodes; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/continue_in_foreach.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/continue_in_foreach.php.inc deleted file mode 100644 index a61f8b7f5fa..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/continue_in_foreach.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -processNameOrIdentifier($node); - } - } - } -} - -?> ------ -processNameOrIdentifier($node); - } - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/dont_remove_comment_of_removed_node.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/dont_remove_comment_of_removed_node.php.inc deleted file mode 100644 index 486bf9df31c..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/dont_remove_comment_of_removed_node.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -hasWheels && $car->hasFuel) { - $this->canDrive = true; - } - - return; - } -} - -?> ------ -hasWheels) { - return; - } - if (!$car->hasFuel) { - return; - } - $this->canDrive = true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/equals.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/equals.php.inc deleted file mode 100644 index 7fd07b9892c..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/equals.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -hasWheels === true && $car->hasFuel === true) { - $this->canDrive = true; - } - - return; - } -} - -?> ------ -hasWheels !== true) { - return; - } - if ($car->hasFuel !== true) { - return; - } - $this->canDrive = true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/fixture.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/fixture.php.inc deleted file mode 100644 index f90a1da13e5..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -hasWheels && $car->hasFuel) { - $this->canDrive = true; - } - - return; - } -} - -?> ------ -hasWheels) { - return; - } - if (!$car->hasFuel) { - return; - } - $this->canDrive = true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/has_return_expr_in_if_stmt.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/has_return_expr_in_if_stmt.php.inc deleted file mode 100644 index 87309c194d3..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/has_return_expr_in_if_stmt.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -hasWheels && $car->hasFuel) { - return true; - } - return false; - } -} - -?> ------ -hasWheels) { - return false; - } - if (!$car->hasFuel) { - return false; - } - return true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_closure.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_closure.php.inc deleted file mode 100644 index 0a23add2b94..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_closure.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ - diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_foreach.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_foreach.php.inc deleted file mode 100644 index 04ed9530bd1..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_foreach.php.inc +++ /dev/null @@ -1,52 +0,0 @@ -processNameOrIdentifier($node); - } - - return; - } - - return; - } -} - -?> ------ -processNameOrIdentifier($node); - return; - } - - return; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_foreach_no_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_foreach_no_return.php.inc deleted file mode 100644 index 98dbecb1b20..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/if_in_foreach_no_return.php.inc +++ /dev/null @@ -1,49 +0,0 @@ -processNameOrIdentifier($node); - } - } - - return; - } -} - -?> ------ -processNameOrIdentifier($node); - } - - return; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/more_than_two_conditions.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/more_than_two_conditions.php.inc deleted file mode 100644 index e8580be33c8..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/more_than_two_conditions.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -hasWheels && $car->hasFuel && $car->hasEngine) { - $this->canDrive = true; - } - } -} - -?> ------ -hasWheels) { - return; - } - if (!$car->hasFuel) { - return; - } - if (!$car->hasEngine) { - return; - } - $this->canDrive = true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/move_comment_in_if_stmt.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/move_comment_in_if_stmt.php.inc deleted file mode 100644 index 9b08b237139..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/move_comment_in_if_stmt.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -hasWheels && $car->hasFuel) { - // a comment - return true; - } - - return false; - } -} - -?> ------ -hasWheels) { - return false; - } - if (!$car->hasFuel) { - return false; - } - // a comment - return true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/move_comment_in_if_stmt2.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/move_comment_in_if_stmt2.php.inc deleted file mode 100644 index 3c77675f21e..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/move_comment_in_if_stmt2.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -hasWheels && $car->hasFuel) { - // a comment - return true; - } - - // another comment - return false; - } -} - -?> ------ -hasWheels) { - // another comment - return false; - } - if (!$car->hasFuel) { - // another comment - return false; - } - // a comment - return true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/non_first_level_if.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/non_first_level_if.php.inc deleted file mode 100644 index bff8ac5b334..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/non_first_level_if.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -wheelsCount() > 2) { - if ($car->hasWheels && $car->hasFuel) { - $this->canDrive = true; - } - } - } -} - -?> ------ -wheelsCount() > 2) { - if (!$car->hasWheels) { - return; - } - if (!$car->hasFuel) { - return; - } - $this->canDrive = true; - } - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/only_one_if_before_class_method_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/only_one_if_before_class_method_return.php.inc deleted file mode 100644 index 9d07b2adb33..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/only_one_if_before_class_method_return.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -hasWheels && $car->hasFuel) { - $this->canDrive = true; - } - - if ($car->hasLights && $car->hasEngine) { - $this->canDrive = false; - } - - return; - } -} - -?> ------ -hasWheels && $car->hasFuel) { - $this->canDrive = true; - } - if (!$car->hasLights) { - return; - } - if (!$car->hasEngine) { - return; - } - $this->canDrive = false; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/return_parent_next.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/return_parent_next.php.inc deleted file mode 100644 index c01c1fa1bb7..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/return_parent_next.php.inc +++ /dev/null @@ -1,65 +0,0 @@ -hasArg('widget') && $this->getArg('widget')) { - - } else { - if ($value && $this->hasArg('output') && 'id' != $this->getArg('output')) { - $value = rex_getUrl($value); - } - } - return $value; - } -} - -abstract class rex_var { - /** - * Returns the output. - * - * @return false|string - */ - abstract protected function getOutput(); -} -?> ------ -hasArg('widget') && $this->getArg('widget')) { - - } else { - if (!$value) { - return $value; - } - if (!$this->hasArg('output')) { - return $value; - } - if ('id' == $this->getArg('output')) { - return $value; - } - $value = rex_getUrl($value); - return $value; - } - return $value; - } -} - -abstract class rex_var { - /** - * Returns the output. - * - * @return false|string - */ - abstract protected function getOutput(); -} -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_assign_in_loop_with_indirect_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_assign_in_loop_with_indirect_return.php.inc deleted file mode 100644 index 945138078a2..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_assign_in_loop_with_indirect_return.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -specs as $key => $pdf) { - if ($this->shouldHaveArtwork($key) && !$this->hasArtwork($key)) { - $status = Release::ARTWORK_STATUS_INCOMPLETE; - } - } - - if ($this->artwork_status != $status) { - $this->artwork_status = $status; - $this->artwork_status_message = null; - } - - return $this; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_condition_with_or.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_condition_with_or.php.inc deleted file mode 100644 index 48680f8cadc..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_condition_with_or.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -hasWheels || $car->hasFuel) { - $this->canDrive = false; - } - - return; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_has_next_stmt_after_if.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_has_next_stmt_after_if.php.inc deleted file mode 100644 index cebed16121e..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_has_next_stmt_after_if.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_if_else_and.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_if_else_and.php.inc deleted file mode 100644 index 835dc3b18bd..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_if_else_and.php.inc +++ /dev/null @@ -1,28 +0,0 @@ -isClosureObjectType($argumentStaticType)) { - return true; - } - - return $argumentStaticType instanceof CallableType && $this->isClosureObjectType($parameterStaticType); - } - - private function isClosureObjectType(Type $type): bool - { - if (! $type instanceof ObjectType) { - return false; - } - - return $type->getClassName() === 'Closure'; - } -} diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_if_stmt_used_in_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_if_stmt_used_in_return.php.inc deleted file mode 100644 index 8ce6817b4f1..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_if_stmt_used_in_return.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_nested_if_in_loop.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_nested_if_in_loop.php.inc deleted file mode 100644 index d3945affd20..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_nested_if_in_loop.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_not_before_first_level_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_not_before_first_level_return.php.inc deleted file mode 100644 index 2294b57e982..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_not_before_first_level_return.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -hasWheels && $car->hasFuel) { - $this->canDrive = true; - } - - if (!$car->hasLights || !$car->hasEngine) { - $this->canDrive = false; - } - - return; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_parent_if_has_final_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_parent_if_has_final_return.php.inc deleted file mode 100644 index bc78c2f26a0..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_parent_if_has_final_return.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_parent_if_void_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_parent_if_void_return.php.inc deleted file mode 100644 index 686025641f2..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_parent_if_void_return.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -isFerrari) { - if ($car->hasWheels && $car->hasFuel) { - $this->canDrive = true; - } - - return; - } - - $this->canDrive = false; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_with_else_else_if.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_with_else_else_if.php.inc deleted file mode 100644 index 440ce3ba840..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_with_else_else_if.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -hasWheels && $car->hasFuel) { - $this->canDrive = true; - } else { - return; - } - - return; - } - - public function hasWheels(Car $car): void - { - if ($car->hasWheels && $car->hasFuel) { - $this->canDrive = true; - } elseif ($car->hasFuel) { - return; - } - - return; - } -} diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_with_void_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_with_void_return.php.inc deleted file mode 100644 index 2cacc580762..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/Fixture/skip_with_void_return.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -isFerrari) { - if ($car->hasWheels && $car->hasFuel) { - return; - } - } - - $this->canDrive = true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/config/configured_rule.php deleted file mode 100644 index 5be9f97016a..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ChangeAndIfToEarlyReturnRector::class); -}; diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/ChangeIfElseValueAssignToEarlyReturnRectorTest.php b/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/ChangeIfElseValueAssignToEarlyReturnRectorTest.php index 8fb3e3abbe8..c1b4472e071 100644 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/ChangeIfElseValueAssignToEarlyReturnRectorTest.php +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/ChangeIfElseValueAssignToEarlyReturnRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ChangeIfElseValueAssignToEarlyReturnRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/Fixture/else_with_multiple_statements.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/Fixture/else_with_multiple_statements.php.inc new file mode 100644 index 00000000000..72410e7bdec --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/Fixture/else_with_multiple_statements.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/config/configured_rule.php index 90495cbe0b9..1efc78365f3 100644 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/config/configured_rule.php +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ChangeIfElseValueAssignToEarlyReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([ChangeIfElseValueAssignToEarlyReturnRector::class]); diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/ChangeNestedIfsToEarlyReturnRectorTest.php b/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/ChangeNestedIfsToEarlyReturnRectorTest.php index 4a1133919c7..17e5f5d57ae 100644 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/ChangeNestedIfsToEarlyReturnRectorTest.php +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/ChangeNestedIfsToEarlyReturnRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\EarlyReturn\Rector\If_\ChangeNestedIfsToEarlyReturnRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ChangeNestedIfsToEarlyReturnRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/Fixture/boolean_and.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/Fixture/boolean_and.php.inc deleted file mode 100644 index c6755312c0c..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/Fixture/boolean_and.php.inc +++ /dev/null @@ -1,49 +0,0 @@ -getAttribute(AttributeKey::PREVIOUS_STATEMENT); - if ($prePreviousExpression instanceof Expression && $prePreviousExpression->expr instanceof AssignOp) { - if ($this->nodeComparator->areNodesEqual($prePreviousExpression->expr->var, $previousNode->var)) { - return true; - } - } - - return false; - } -} - -?> ------ -getAttribute(AttributeKey::PREVIOUS_STATEMENT); - if (!$prePreviousExpression instanceof Expression) { - return false; - } - if (!$prePreviousExpression->expr instanceof AssignOp) { - return false; - } - if ($this->nodeComparator->areNodesEqual($prePreviousExpression->expr->var, $previousNode->var)) { - return true; - } - - return false; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/Fixture/do_not_remove_previous_if_stmts.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/Fixture/do_not_remove_previous_if_stmts.php.inc new file mode 100644 index 00000000000..57f3e91ba21 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/Fixture/do_not_remove_previous_if_stmts.php.inc @@ -0,0 +1,44 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/config/configured_rule.php index 3896dcc952b..5430533faa0 100644 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/config/configured_rule.php +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\EarlyReturn\Rector\If_\ChangeNestedIfsToEarlyReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ChangeNestedIfsToEarlyReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([ChangeNestedIfsToEarlyReturnRector::class]); diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/ChangeOrIfContinueToMultiContinueRectorTest.php b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/ChangeOrIfContinueToMultiContinueRectorTest.php index c1fae4c38ac..ec052f2aad2 100644 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/ChangeOrIfContinueToMultiContinueRectorTest.php +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/ChangeOrIfContinueToMultiContinueRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ChangeOrIfContinueToMultiContinueRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/Fixture/identical.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/Fixture/identical.php.inc deleted file mode 100644 index fe66eca91d4..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/Fixture/identical.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -getWheel() === 4 || $car->getFuel() === 'full') { - continue; - } - $car->setWheel($newCar->wheel); - $car->setFuel($newCar->fuel); - } - } -} - -?> ------ -getWheel() === 4) { - continue; - } - if ($car->getFuel() === 'full') { - continue; - } - $car->setWheel($newCar->wheel); - $car->setFuel($newCar->fuel); - } - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/Fixture/split_or_identical.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/Fixture/split_or_identical.php.inc new file mode 100644 index 00000000000..30f4e9d0a47 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/Fixture/split_or_identical.php.inc @@ -0,0 +1,42 @@ +getWheel() === 4 || $car->getFuel() === 'full') { + continue; + } + $car->setWheel($newCar->wheel); + $car->setFuel($newCar->fuel); + } + } +} + +?> +----- +getWheel() === 4) { + continue; + } + if ($car->getFuel() === 'full') { + continue; + } + $car->setWheel($newCar->wheel); + $car->setFuel($newCar->fuel); + } + } +} + +?> diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/config/configured_rule.php index a21453a9377..dc88fbf53fc 100644 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/config/configured_rule.php +++ b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ChangeOrIfContinueToMultiContinueRector::class); -}; +return RectorConfig::configure() + ->withRules([ChangeOrIfContinueToMultiContinueRector::class]); diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/ChangeOrIfReturnToEarlyReturnRectorTest.php b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/ChangeOrIfReturnToEarlyReturnRectorTest.php deleted file mode 100644 index 56e423f5e14..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/ChangeOrIfReturnToEarlyReturnRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/fixture.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/fixture.php.inc deleted file mode 100644 index 28e94f5014f..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - ------ - diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/skip_and.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/skip_and.php.inc deleted file mode 100644 index f0e5f3a2a66..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/skip_and.php.inc +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/skip_instanceof.php.inc b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/skip_instanceof.php.inc deleted file mode 100644 index 9fe0a39c1d6..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/Fixture/skip_instanceof.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/config/configured_rule.php deleted file mode 100644 index b672e1f01ea..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ChangeOrIfReturnToEarlyReturnRector::class); -}; diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/assign_in_if_else_before.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/assign_in_if_else_before.php.inc new file mode 100644 index 00000000000..42fb9655102 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/assign_in_if_else_before.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/elseif.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/elseif.php.inc deleted file mode 100644 index c6a15298a74..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/elseif.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ - diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/exit.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/exit.php.inc deleted file mode 100644 index 5f06372e965..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/exit.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/if_elseif_else.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/if_elseif_else.php.inc new file mode 100644 index 00000000000..0f2a926d295 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/if_elseif_else.php.inc @@ -0,0 +1,42 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/if_return.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/if_return.php.inc new file mode 100644 index 00000000000..80b8941cf13 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/if_return.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/keep_the_else_if_elseif_has_no_stmt.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/keep_the_else_if_elseif_has_no_stmt.php.inc index b778fb2a7c2..77d60665033 100644 --- a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/keep_the_else_if_elseif_has_no_stmt.php.inc +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/keep_the_else_if_elseif_has_no_stmt.php.inc @@ -1,6 +1,6 @@ +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/multiple_elseifs_with_non_terminating_elseif.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/multiple_elseifs_with_non_terminating_elseif.php.inc new file mode 100644 index 00000000000..29f3858f5b5 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/multiple_elseifs_with_non_terminating_elseif.php.inc @@ -0,0 +1,51 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_else_and_non_terminating_elseif.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_else_and_non_terminating_elseif.php.inc new file mode 100644 index 00000000000..1f8aa07aaa6 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_else_and_non_terminating_elseif.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_non_terminating_else.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_non_terminating_else.php.inc new file mode 100644 index 00000000000..5f556ddf12c --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_non_terminating_else.php.inc @@ -0,0 +1,42 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_terminating_elseif_and_else.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_terminating_elseif_and_else.php.inc new file mode 100644 index 00000000000..be7b8010571 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/nested_if_with_terminating_elseif_and_else.php.inc @@ -0,0 +1,46 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/process_empty_return_last.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/process_empty_return_last.php.inc index ca6d518b0b9..d3989cd29e5 100644 --- a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/process_empty_return_last.php.inc +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/process_empty_return_last.php.inc @@ -47,9 +47,7 @@ class ProcessEmptyReturnLast $value = 55; return 10; } - else { - return; - } + return; } public function secondRun($value) diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/split_after_throw.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/split_after_throw.php.inc new file mode 100644 index 00000000000..474a636fb57 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/split_after_throw.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/throw.php.inc b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/throw.php.inc deleted file mode 100644 index a2d1a102023..00000000000 --- a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/Fixture/throw.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/RemoveAlwaysElseRectorTest.php b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/RemoveAlwaysElseRectorTest.php index f68a2be245e..ec89160ed96 100644 --- a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/RemoveAlwaysElseRectorTest.php +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/RemoveAlwaysElseRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\EarlyReturn\Rector\If_\RemoveAlwaysElseRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveAlwaysElseRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/config/configured_rule.php index 0c5a0a67049..8b67e8422bf 100644 --- a/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/config/configured_rule.php +++ b/rules-tests/EarlyReturn/Rector/If_/RemoveAlwaysElseRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveAlwaysElseRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveAlwaysElseRector::class]); diff --git a/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value.php.inc b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value.php.inc new file mode 100644 index 00000000000..342dac2ce05 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value.php.inc @@ -0,0 +1,29 @@ + diff --git a/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value_before_if.php.inc b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value_before_if.php.inc new file mode 100644 index 00000000000..37fc1ff0c92 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value_before_if.php.inc @@ -0,0 +1,23 @@ + 3000) { + $travels = ($shorten) ? 'any' : 'nationwide'; + } + + return $travels; + } +} + +?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value_in_if.php.inc b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value_in_if.php.inc new file mode 100644 index 00000000000..c3d7bdef1bd --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_append_variable_value_in_if.php.inc @@ -0,0 +1,20 @@ + diff --git a/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_reassign_in_foreach.php.inc b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_reassign_in_foreach.php.inc new file mode 100644 index 00000000000..5ea249bd83a --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/Fixture/skip_reassign_in_foreach.php.inc @@ -0,0 +1,16 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/config/configured_rule.php index 6f08cbf89af..4647dd47f3d 100644 --- a/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/config/configured_rule.php +++ b/rules-tests/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(PreparedValueToEarlyReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([PreparedValueToEarlyReturnRector::class]); diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/comment.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/comment.php.inc deleted file mode 100644 index 7f2bd67291d..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/comment.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -something() && $this->somethingElse(); - } - - // another next comment - return 1; - } -} - -?> ------ -something()) { - return false; - } - return (bool) $this->somethingElse(); - } - - // another next comment - return 1; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/dont_change_already_casted.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/dont_change_already_casted.php.inc deleted file mode 100644 index e3fef64aa54..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/dont_change_already_casted.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -something() && (bool) $this->somethingelse(); - } -} - -?> ------ -something()) { - return false; - } - return (bool) $this->somethingelse(); - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/dont_change_already_return_typed.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/dont_change_already_return_typed.php.inc deleted file mode 100644 index 498c5bf25bf..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/dont_change_already_return_typed.php.inc +++ /dev/null @@ -1,50 +0,0 @@ -something() && $this->somethingElse(); - } - - private function something(): bool - { - return true; - } - - private function somethingElse(): bool - { - return true; - } -} - -?> ------ -something()) { - return false; - } - return $this->somethingElse(); - } - - private function something(): bool - { - return true; - } - - private function somethingElse(): bool - { - return true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/fixture.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/fixture.php.inc deleted file mode 100644 index 9872f9da134..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -something() && $this->somethingElse(); - } -} - -?> ------ -something()) { - return false; - } - return (bool) $this->somethingElse(); - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/identical.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/identical.php.inc deleted file mode 100644 index a9ff86a4c62..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/identical.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -something() === 1 && !$this->somethingElse(); - } -} - -?> ------ -something() !== 1) { - return false; - } - return !$this->somethingElse(); - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/last_return_bool.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/last_return_bool.php.inc deleted file mode 100644 index dd0c9c3e7ee..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/last_return_bool.php.inc +++ /dev/null @@ -1,53 +0,0 @@ -something() && $this->getSomethingElse(); - } - - public function accept2() - { - return $this->something() && $this->somethingelse() === 'something else'; - } - - private function getSomethingElse(): bool - { - return true; - } -} - -?> ------ -something()) { - return false; - } - return $this->getSomethingElse(); - } - - public function accept2() - { - if (!$this->something()) { - return false; - } - return $this->somethingelse() === 'something else'; - } - - private function getSomethingElse(): bool - { - return true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/multiple_binary_and.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/multiple_binary_and.php.inc deleted file mode 100644 index d50a49c8bf6..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/multiple_binary_and.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -something() && $this->somethingelse() && $this->anotherelse() && $this->last(); - } -} - -?> ------ -something()) { - return false; - } - if (!$this->somethingelse()) { - return false; - } - if (!$this->anotherelse()) { - return false; - } - return (bool) $this->last(); - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/multiple_binary_and_start_with_method_call.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/multiple_binary_and_start_with_method_call.php.inc deleted file mode 100644 index a5aecabef22..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/multiple_binary_and_start_with_method_call.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -execute() && $a && $b; - } - - private function execute() {} -} - -?> ------ -execute()) { - return false; - } - if (!$a) { - return false; - } - return (bool) $b; - } - - private function execute() {} -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/not_object_call_in_last.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/not_object_call_in_last.php.inc deleted file mode 100644 index faddd806d46..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/not_object_call_in_last.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -something() && true; - } -} - -?> ------ -something()) { - return false; - } - return true; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/skip_not_object_call.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/skip_not_object_call.php.inc deleted file mode 100644 index cef6a678b8f..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/skip_not_object_call.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -something || $this->somethingelse; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/skip_or_in_next.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/skip_or_in_next.php.inc deleted file mode 100644 index 9203dcf9d29..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/skip_or_in_next.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -something() && $this->somethingelse() || $this->anotherelse() && $this->last(); - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/some_not_identical.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/some_not_identical.php.inc deleted file mode 100644 index 54b16bd9fc8..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/some_not_identical.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -something() !== 1 && !$this->somethingelse(); - } -} - -?> ------ -something() === 1) { - return false; - } - return !$this->somethingelse(); - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/truthy_negation.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/truthy_negation.php.inc deleted file mode 100644 index 705d41edd6b..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/Fixture/truthy_negation.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -something() && !$this->somethingelse(); - } -} - -?> ------ -something()) { - return false; - } - return !$this->somethingelse(); - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/ReturnBinaryAndToEarlyReturnRectorTest.php b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/ReturnBinaryAndToEarlyReturnRectorTest.php deleted file mode 100644 index 484c7421a0e..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/ReturnBinaryAndToEarlyReturnRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/config/configured_rule.php deleted file mode 100644 index efa13bdcc12..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ReturnBinaryAndToEarlyReturnRector::class); -}; diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/comment.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/comment.php.inc deleted file mode 100644 index 46be10ba8cf..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/comment.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -something() || $this->somethingElse(); - } - - // another next comment - return 1; - } -} - -?> ------ -something()) { - return true; - } - return (bool) $this->somethingElse(); - } - - // another next comment - return 1; - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/identical.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/identical.php.inc deleted file mode 100644 index d599325497e..00000000000 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/identical.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -something() === 1 || !$this->somethingElse(); - } -} - -?> ------ -something() === 1) { - return true; - } - return !$this->somethingElse(); - } -} - -?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/identical_compare.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/identical_compare.php.inc new file mode 100644 index 00000000000..b802529800b --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/identical_compare.php.inc @@ -0,0 +1,30 @@ +something() === 1 || !$this->somethingElse(); + } +} + +?> +----- +something() === 1) { + return true; + } + return !$this->somethingElse(); + } +} + +?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/multiple_binary_or_start_with_method_call.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/multiple_binary_or_start_with_method_call.php.inc index c5742262f8c..312432d6612 100644 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/multiple_binary_or_start_with_method_call.php.inc +++ b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/multiple_binary_or_start_with_method_call.php.inc @@ -34,4 +34,4 @@ class MultipleBinaryOrStartWithMethodCall private function execute() {} } -?> \ No newline at end of file +?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/with_comment.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/with_comment.php.inc new file mode 100644 index 00000000000..527f8032a06 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/with_comment.php.inc @@ -0,0 +1,44 @@ +something() || $this->somethingElse(); + } + + // another next comment + return 1; + } +} + +?> +----- +something()) { + return true; + } + return (bool) $this->somethingElse(); + } + + // another next comment + return 1; + } +} + +?> diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/ReturnBinaryOrToEarlyReturnRectorTest.php b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/ReturnBinaryOrToEarlyReturnRectorTest.php index 78f569bfcb0..a0255c0f729 100644 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/ReturnBinaryOrToEarlyReturnRectorTest.php +++ b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/ReturnBinaryOrToEarlyReturnRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReturnBinaryOrToEarlyReturnRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/config/configured_rule.php index e522e37fe0d..535296e20e0 100644 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/config/configured_rule.php +++ b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReturnBinaryOrToEarlyReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([ReturnBinaryOrToEarlyReturnRector::class]); diff --git a/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/mirror_comment.php.inc b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/mirror_comment.php.inc new file mode 100644 index 00000000000..fbe4a9f3d0a --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/mirror_comment.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/skip_global_var.php.inc b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/skip_global_var.php.inc new file mode 100644 index 00000000000..6d06b51a06b --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/skip_global_var.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/ReturnEarlyIfVariableRectorTest.php b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/ReturnEarlyIfVariableRectorTest.php new file mode 100644 index 00000000000..62c775046b7 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/ReturnEarlyIfVariableRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/config/configured_rule.php new file mode 100644 index 00000000000..7356bc030fe --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReturnEarlyIfVariableRector::class]); diff --git a/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Fixture/correct_case.php.inc b/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Fixture/correct_case.php.inc new file mode 100644 index 00000000000..f6397f447c5 --- /dev/null +++ b/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Fixture/correct_case.php.inc @@ -0,0 +1,15 @@ +getPrice() : null; + } +} + +?> diff --git a/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Fixture/skip_no_cond.php.inc b/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Fixture/skip_no_cond.php.inc new file mode 100644 index 00000000000..5ec72080e23 --- /dev/null +++ b/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Fixture/skip_no_cond.php.inc @@ -0,0 +1,13 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Source/TypeForCheck.php b/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Source/TypeForCheck.php new file mode 100644 index 00000000000..dd259219252 --- /dev/null +++ b/rules-tests/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector/Source/TypeForCheck.php @@ -0,0 +1,7 @@ +withRules([FlipNegatedTernaryInstanceofRector::class]); diff --git a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/DispatchStringToObjectRectorTest.php b/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/DispatchStringToObjectRectorTest.php deleted file mode 100644 index 50f29a24f22..00000000000 --- a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/DispatchStringToObjectRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/event-object.php.inc b/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/event-object.php.inc deleted file mode 100644 index 7cae8e234a5..00000000000 --- a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/event-object.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -dispatcher->dispatch(new \League\Event\Event('my-event')); - } -} - -?> diff --git a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/fixture.php.inc b/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/fixture.php.inc deleted file mode 100644 index affba56944b..00000000000 --- a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -dispatcher->dispatch('my-event'); - } -} - -?> ------ -dispatcher->dispatch(new class('my-event') implements \League\Event\HasEventName - { - private $name; - public function __construct(string $name) - { - $this->name = $name; - } - public function eventName(): string - { - return $this->name; - } - }); - } -} - -?> diff --git a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/method-call.php.inc b/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/method-call.php.inc deleted file mode 100644 index 78b17581775..00000000000 --- a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/method-call.php.inc +++ /dev/null @@ -1,54 +0,0 @@ -dispatcher->dispatch($this->createEventName()); - } - - private function createEventName(): string - { - return 'my-event'; - } -} - -?> ------ -dispatcher->dispatch(new class($this->createEventName()) implements \League\Event\HasEventName - { - private $name; - public function __construct(string $name) - { - $this->name = $name; - } - public function eventName(): string - { - return $this->name; - } - }); - } - - private function createEventName(): string - { - return 'my-event'; - } -} - -?> diff --git a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/variable.php.inc b/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/variable.php.inc deleted file mode 100644 index c4ce3dddf03..00000000000 --- a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/Fixture/variable.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -dispatcher->dispatch($name); - } -} - -?> ------ -dispatcher->dispatch(new class($name) implements \League\Event\HasEventName - { - private $name; - public function __construct(string $name) - { - $this->name = $name; - } - public function eventName(): string - { - return $this->name; - } - }); - } -} - -?> diff --git a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/config/configured_rule.php b/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/config/configured_rule.php deleted file mode 100644 index e7f8df00e83..00000000000 --- a/rules-tests/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(DispatchStringToObjectRector::class); -}; diff --git a/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/Fixture/MockCreation.php.inc b/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/Fixture/MockCreation.php.inc deleted file mode 100644 index 6d3c0cf8d24..00000000000 --- a/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/Fixture/MockCreation.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -injectDependency($mock); - } -} - -?> ------ -prophesize('MyClass'); - - $service = new Service(); - $service->injectDependency($mock->reveal()); - } -} - -?> diff --git a/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/Fixture/MockCreationParameter.php.inc b/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/Fixture/MockCreationParameter.php.inc deleted file mode 100644 index 1c64968867f..00000000000 --- a/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/Fixture/MockCreationParameter.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -injectDependency(\Mockery::mock('MyClass')); - } -} - -?> ------ -injectDependency($this->prophesize('MyClass')->reveal()); - } -} - -?> diff --git a/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/MockeryToProphecyRectorTest.php b/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/MockeryToProphecyRectorTest.php deleted file mode 100644 index f31fe3899ed..00000000000 --- a/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/MockeryToProphecyRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($file); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/config/configured_rule.php b/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/config/configured_rule.php deleted file mode 100644 index 8b4e3385c54..00000000000 --- a/rules-tests/MockeryToProphecy/Rector/ClassMethod/MockeryToProphecyRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(MockeryCreateMockToProphizeRector::class); -}; diff --git a/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/Fixture/MockeryClose.php.inc b/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/Fixture/MockeryClose.php.inc deleted file mode 100644 index 3fad95c6d92..00000000000 --- a/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/Fixture/MockeryClose.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/MockeryToProphecyRectorTest.php b/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/MockeryToProphecyRectorTest.php deleted file mode 100644 index 1dace651cf2..00000000000 --- a/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/MockeryToProphecyRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($file); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/config/configured_rule.php b/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/config/configured_rule.php deleted file mode 100644 index 92fbbea3047..00000000000 --- a/rules-tests/MockeryToProphecy/Rector/StaticCall/MockeryToProphecyRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(MockeryCloseRemoveRector::class); -}; diff --git a/rules-tests/MysqlToMysqli/Fixture/SetFixture.php.inc b/rules-tests/MysqlToMysqli/Fixture/SetFixture.php.inc deleted file mode 100644 index 66e5c999dfb..00000000000 --- a/rules-tests/MysqlToMysqli/Fixture/SetFixture.php.inc +++ /dev/null @@ -1,213 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Fixture/different_name_connection_variable.php.inc b/rules-tests/MysqlToMysqli/Fixture/different_name_connection_variable.php.inc deleted file mode 100644 index 0e3c141ad74..00000000000 --- a/rules-tests/MysqlToMysqli/Fixture/different_name_connection_variable.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Fixture/full_tests.php.inc b/rules-tests/MysqlToMysqli/Fixture/full_tests.php.inc deleted file mode 100644 index f5d37277507..00000000000 --- a/rules-tests/MysqlToMysqli/Fixture/full_tests.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture.php.inc b/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture.php.inc deleted file mode 100644 index 414af1c4684..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture2.php.inc b/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture2.php.inc deleted file mode 100644 index 70751ce39cb..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ -length; - $name = mysqli_fetch_field_direct($result, 5)->name; - $table = mysqli_fetch_field_direct($result, 5)->table; -} - -?> diff --git a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture3.php.inc b/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture3.php.inc deleted file mode 100644 index 4880b80e025..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,18 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture4.php.inc b/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture4.php.inc deleted file mode 100644 index 3b7b2a72d32..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/Fixture/fixture4.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/MysqlAssignToMysqliRectorTest.php b/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/MysqlAssignToMysqliRectorTest.php deleted file mode 100644 index 4008a20b8b5..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/MysqlAssignToMysqliRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/config/configured_rule.php b/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/config/configured_rule.php deleted file mode 100644 index 9deaabf2497..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(MysqlAssignToMysqliRector::class); -}; diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/Fixture/fixture.php.inc b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/Fixture/fixture.php.inc deleted file mode 100644 index a259a052e5c..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/MysqlFuncCallToMysqliRectorTest.php b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/MysqlFuncCallToMysqliRectorTest.php deleted file mode 100644 index b9ab30142e1..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/MysqlFuncCallToMysqliRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/config/configured_rule.php b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/config/configured_rule.php deleted file mode 100644 index 03c73394e5c..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(MysqlFuncCallToMysqliRector::class); -}; diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/Fixture/fixture.php.inc b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/Fixture/fixture.php.inc deleted file mode 100644 index 17f416c8ad2..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/MysqlPConnectToMysqliConnectRectorTest.php b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/MysqlPConnectToMysqliConnectRectorTest.php deleted file mode 100644 index 3789258f72a..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/MysqlPConnectToMysqliConnectRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/config/configured_rule.php b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/config/configured_rule.php deleted file mode 100644 index d0553fa4ab4..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(MysqlPConnectToMysqliConnectRector::class); -}; diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/connection_provided.php.inc b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/connection_provided.php.inc deleted file mode 100644 index 194e10784b4..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/connection_provided.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/fixture.php.inc b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/fixture.php.inc deleted file mode 100644 index 28340525578..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/property_connection.php.inc b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/property_connection.php.inc deleted file mode 100644 index cb7196681d7..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/property_connection.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -conn = mysqli_connect('host', 'user', 'pass'); - - mysql_error(); - $sql = 'SELECT'; - - return mysql_query($sql); - } -} - -?> ------ -conn = mysqli_connect('host', 'user', 'pass'); - - mysqli_error($this->conn); - $sql = 'SELECT'; - - return mysqli_query($this->conn, $sql); - } -} - -?> diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/skip_missing_connection.php.inc b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/skip_missing_connection.php.inc deleted file mode 100644 index e6b45960987..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/Fixture/skip_missing_connection.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/config/configured_rule.php b/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/config/configured_rule.php deleted file mode 100644 index 8840e3f5dd5..00000000000 --- a/rules-tests/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(MysqlQueryMysqlErrorWithLinkRector::class); -}; diff --git a/rules-tests/MysqlToMysqli/SetTest.php b/rules-tests/MysqlToMysqli/SetTest.php deleted file mode 100644 index 7fe7ab6829b..00000000000 --- a/rules-tests/MysqlToMysqli/SetTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/../../config/set/mysql-to-mysqli.php'; - } -} diff --git a/rules-tests/Naming/Naming/PropertyNamingTest.php b/rules-tests/Naming/Naming/PropertyNamingTest.php index 10cb2506402..0d0d1b92ec6 100644 --- a/rules-tests/Naming/Naming/PropertyNamingTest.php +++ b/rules-tests/Naming/Naming/PropertyNamingTest.php @@ -5,39 +5,40 @@ namespace Rector\Tests\Naming\Naming; use Iterator; +use PHPStan\Type\ObjectType; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Naming\Naming\PropertyNaming; use Rector\Naming\ValueObject\ExpectedName; -use Rector\Testing\PHPUnit\AbstractTestCase; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class PropertyNamingTest extends AbstractTestCase +final class PropertyNamingTest extends AbstractLazyTestCase { private PropertyNaming $propertyNaming; protected function setUp(): void { - $this->boot(); - $this->propertyNaming = $this->getService(PropertyNaming::class); + parent::setUp(); + + $this->propertyNaming = $this->make(PropertyNaming::class); } - /** - * @dataProvider getExpectedNameFromMethodNameDataProvider - */ + #[DataProvider('getExpectedNameFromMethodNameDataProvider')] public function testGetExpectedNameFromMethodName(string $methodName, ?string $expectedPropertyName): void { - /** @var ExpectedName $actualPropertyName */ - $actualPropertyName = $this->propertyNaming->getExpectedNameFromMethodName($methodName); + $expectedName = $this->propertyNaming->getExpectedNameFromMethodName($methodName); if ($expectedPropertyName === null) { - $this->assertNull($actualPropertyName); + $this->assertNotInstanceOf(ExpectedName::class, $expectedName); } else { - $this->assertSame($expectedPropertyName, $actualPropertyName->getSingularized()); + $this->assertInstanceOf(ExpectedName::class, $expectedName); + $this->assertSame($expectedPropertyName, $expectedName->getSingularized()); } } /** * @return Iterator */ - public function getExpectedNameFromMethodNameDataProvider(): Iterator + public static function getExpectedNameFromMethodNameDataProvider(): Iterator { yield ['getMethods', 'method']; yield ['getUsedTraits', 'usedTrait']; @@ -48,4 +49,26 @@ public function getExpectedNameFromMethodNameDataProvider(): Iterator yield ['getSpaceshipsInfo', 'spaceshipInfo']; yield ['resolveDependencies', null]; } + + #[DataProvider('provideDataPropertyName')] + public function testPropertyName(string $objectName, string $expectedVariableName): void + { + $variableName = $this->propertyNaming->fqnToVariableName(new ObjectType($objectName)); + $this->assertSame($expectedVariableName, $variableName); + } + + /** + * @return Iterator> + */ + public static function provideDataPropertyName(): Iterator + { + yield ['SomeVariable', 'someVariable']; + yield ['IControl', 'control']; + yield ['AbstractValueClass', 'valueClass']; + yield ['App\AbstractValueClass', 'valueClass']; + yield ['Twig_Extension', 'twigExtension']; + yield ['NodeVisitorAbstract', 'nodeVisitor']; + yield ['AbstractNodeVisitor', 'nodeVisitor']; + yield ['Twig_ExtensionInterface', 'twigExtension']; + } } diff --git a/rules-tests/Naming/Naming/UseImportsResolver/Fixture/group_use.php.inc b/rules-tests/Naming/Naming/UseImportsResolver/Fixture/group_use.php.inc new file mode 100644 index 00000000000..8ebd63f478c --- /dev/null +++ b/rules-tests/Naming/Naming/UseImportsResolver/Fixture/group_use.php.inc @@ -0,0 +1,13 @@ +useImportsResolver = $this->make(UseImportsResolver::class); + $this->testingParser = $this->make(TestingParser::class); + $this->betterNodeFinder = $this->make(BetterNodeFinder::class); + } + + #[DataProvider('provideData')] + public function testUsesFromProperty(string $filePath): void + { + $nodes = $this->testingParser->parseFileToDecoratedNodes($filePath); + + $firstProperty = $this->betterNodeFinder->findFirstInstanceOf($nodes, Property::class); + $this->assertInstanceOf(Property::class, $firstProperty); + + $resolvedUses = $this->useImportsResolver->resolve(); + + $stringUses = []; + + foreach ($resolvedUses as $resolvedUse) { + foreach ($resolvedUse->uses as $useUse) { + $stringUses[] = $resolvedUse instanceof Use_ + ? $useUse->name->toString() + : $resolvedUse->prefix->toString() . '\\' . $useUse->name->toString(); + } + } + + $this->assertContains(FirstClass::class, $stringUses); + $this->assertContains(SecondClass::class, $stringUses); + } + + public static function provideData(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/Fixture'); + } +} diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/clousure.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/clousure.php.inc deleted file mode 100644 index 13ca28db88a..00000000000 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/clousure.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -getTimestamp(); - return $a; - }, [1, 2, 3]); - } -} - -?> ------ -getTimestamp(); - return $timestamp; - }, [1, 2, 3]); - } -} - -?> diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/double_method_call.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/double_method_call.php.inc new file mode 100644 index 00000000000..4fd17542eb8 --- /dev/null +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/double_method_call.php.inc @@ -0,0 +1,49 @@ +createClassConstFetch('SomeClass', 'MAGIC_GET'); + echo $magicGet; + + $magicSet = $this->createClassConstFetch('SomeClass', 'MAGIC_SET'); + echo $magicSet; + } + + protected function createClassConstFetch(string $class, string $constant): ClassConstFetch + { + return new ClassConstFetch($class, $constant); + } +} + +?> +----- +createClassConstFetch('SomeClass', 'MAGIC_GET'); + echo $classConstFetch; + + $magicSet = $this->createClassConstFetch('SomeClass', 'MAGIC_SET'); + echo $magicSet; + } + + protected function createClassConstFetch(string $class, string $constant): ClassConstFetch + { + return new ClassConstFetch($class, $constant); + } +} + +?> diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/multiple_expression.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/multiple_expression.php.inc new file mode 100644 index 00000000000..e41b11de26f --- /dev/null +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/multiple_expression.php.inc @@ -0,0 +1,61 @@ +getFastRunner(); + $a->run(); + + $b = $this->getFullName(); + $b->getName(); + } + + public function getFastRunner(): FastRunner + { + return new FastRunner(); + } + + public function getFullName(): FullName + { + return new FullName(); + } +} + +?> +----- +getFastRunner(); + $fastRunner->run(); + + $fullName = $this->getFullName(); + $fullName->getName(); + } + + public function getFastRunner(): FastRunner + { + return new FastRunner(); + } + + public function getFullName(): FullName + { + return new FullName(); + } +} + +?> diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_arrow_function.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_arrow_function.php.inc new file mode 100644 index 00000000000..dc449ec73ba --- /dev/null +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_arrow_function.php.inc @@ -0,0 +1,13 @@ + $x + $stamp = $sameNameMethod->getName(); + } +} diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_assign_date_time.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_assign_date_time.php.inc new file mode 100644 index 00000000000..0001a4b36ae --- /dev/null +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_assign_date_time.php.inc @@ -0,0 +1,14 @@ +getAttribute(AttributeKey::CLASS_NAME); - } -} diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_dot_name.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_dot_name.php.inc new file mode 100644 index 00000000000..ed3b9ca2299 --- /dev/null +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_dot_name.php.inc @@ -0,0 +1,27 @@ +getService('some.type'); + } + + /** + * @template TService as object + * + * @param class-string $type + * @return TService + */ + public function getService(string $type): object + { + /** @var Container $container */ + $container = self::$container; + + return $container->get($type); + } +} diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_double_method_call.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_double_method_call.php.inc deleted file mode 100644 index 81d30c0237a..00000000000 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_double_method_call.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -createClassConstFetch('SomeClass', 'MAGIC_GET'); - $magicSet = $this->createClassConstFetch('SomeClass', 'MAGIC_SET'); - } - - protected function createClassConstFetch(string $class, string $constant): ClassConstFetch - { - return new ClassConstFetch($class, $constant); - } -} -?> diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_overly_generic.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_overly_generic.php.inc index b7de19a51d3..71114c3515e 100644 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_overly_generic.php.inc +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_overly_generic.php.inc @@ -3,10 +3,10 @@ namespace Rector\Tests\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector\Fixture; use PhpParser\Node\Expr; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; -class SkipOverlyGeneric +final class SkipOverlyGeneric { public function run() { @@ -19,6 +19,6 @@ class SkipOverlyGeneric return new String_('hey'); } - return new LNumber(1000); + return new Int_(1000); } } diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_parent_node.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_parent_node.php.inc deleted file mode 100644 index 205b58901a6..00000000000 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_parent_node.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE); - } -} diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_variable_used_in_arrow_function_args.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_variable_used_in_arrow_function_args.php.inc new file mode 100644 index 00000000000..0e249817b8d --- /dev/null +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Fixture/skip_variable_used_in_arrow_function_args.php.inc @@ -0,0 +1,19 @@ +getFastRunner(fn (FastRunner $a) => $a->run()); + $a->exit(); + } + + public function getFastRunner(callable $c): FastRunner + { + return new FastRunner(); + } +} diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/FixturePhp74/arrow_function.php.inc b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/FixturePhp74/arrow_function.php.inc deleted file mode 100644 index 1eadc496aa6..00000000000 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/FixturePhp74/arrow_function.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - $x + $stamp = $sameNameMethod->getName(); - } -} - -?> ------ - $x + $fullName = $sameNameMethod->getName(); - } -} - -?> diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Php74Test.php b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Php74Test.php deleted file mode 100644 index b3f5454fc2e..00000000000 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Php74Test.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/RenameVariableToMatchMethodCallReturnTypeRectorTest.php b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/RenameVariableToMatchMethodCallReturnTypeRectorTest.php index 2c613dd4d79..7bd4ef75766 100644 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/RenameVariableToMatchMethodCallReturnTypeRectorTest.php +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/RenameVariableToMatchMethodCallReturnTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameVariableToMatchMethodCallReturnTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Source/FastRunner.php b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Source/FastRunner.php index 89b57a209a5..710411add66 100644 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Source/FastRunner.php +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Source/FastRunner.php @@ -6,5 +6,8 @@ final class FastRunner { - + public function run() + { + return $this; + } } diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Source/FullName.php b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Source/FullName.php index 85112b57666..d4cf4213bf7 100644 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Source/FullName.php +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/Source/FullName.php @@ -6,5 +6,8 @@ final class FullName { - + public function getName(): string + { + return 'full name'; + } } diff --git a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/config/configured_rule.php b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/config/configured_rule.php index f8566dba41f..0b296ebb325 100644 --- a/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/config/configured_rule.php +++ b/rules-tests/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameVariableToMatchMethodCallReturnTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([RenameVariableToMatchMethodCallReturnTypeRector::class]); diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/array_item.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/array_item.php.inc index 875d967ae24..7ceb7e4f848 100644 --- a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/array_item.php.inc +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/array_item.php.inc @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Rector\Tests\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector\Fixture; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\List_; use PhpParser\Node\Expr\Variable; @@ -42,7 +42,7 @@ declare(strict_types=1); namespace Rector\Tests\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector\Fixture; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\List_; use PhpParser\Node\Expr\Variable; diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/callback_use.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/callback_use.php.inc deleted file mode 100644 index cddf34f97d5..00000000000 --- a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/callback_use.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -stmts, function (Node $stmt) use ($node) { - return $stmt + $node; - }); - - function someFunction($node) - { - } - } -} - -?> ------ -stmts, function (Node $stmt) use ($classMethod) { - return $stmt + $classMethod; - }); - - function someFunction($node) - { - } - } -} - -?> diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/closure_only.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/closure_only.php.inc new file mode 100644 index 00000000000..9a144128543 --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/closure_only.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/dynamic_property.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/dynamic_property.php.inc new file mode 100644 index 00000000000..b5c232eac90 --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/dynamic_property.php.inc @@ -0,0 +1,35 @@ +$nonSense; + } catch (\Throwable $e) {} + } +} + +?> +----- +$eliteManager; + } catch (\Throwable $e) {} + } +} + +?> diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/function_callback_use.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/function_callback_use.php.inc new file mode 100644 index 00000000000..0a4ffaa3701 --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/function_callback_use.php.inc @@ -0,0 +1,33 @@ +stmts, function (Node $stmt) use ($node) { + return $stmt + $node; + }); + + function someFunction($node) + { + } +} + +?> +----- +stmts, function (Node $node) use ($classMethod) { + return $node + $classMethod; + }); + + function someFunction($node) + { + } +} + +?> diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/keep_date.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/keep_date.php.inc new file mode 100644 index 00000000000..61b978c8032 --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/keep_date.php.inc @@ -0,0 +1,12 @@ +eventManager = $eventManager; + } +} + diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/skip_variadic.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/skip_variadic.php.inc new file mode 100644 index 00000000000..94710e730dc --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/skip_variadic.php.inc @@ -0,0 +1,12 @@ + +----- + diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/update_arrow_function_param.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/update_arrow_function_param.php.inc new file mode 100644 index 00000000000..2e1f233640c --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/update_arrow_function_param.php.inc @@ -0,0 +1,33 @@ + true; + $function(new SingleSomeClass()); + } +} + +?> +----- + true; + $function(new SingleSomeClass()); + } +} + +?> diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/update_doc_name.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/update_doc_name.php.inc index 8b67469a676..77b84da9d25 100644 --- a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/update_doc_name.php.inc +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/update_doc_name.php.inc @@ -5,13 +5,14 @@ namespace Rector\Tests\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector\Fi use PhpParser\Node; use PhpParser\Node\Identifier; use PhpParser\Node\Name; +use PHPStan\Node\ClassMethod; interface UpdateDocName { /** - * @param Identifier|Name $value + * @param ClassMethod|Node\Stmt\Property $value */ - public function method(Node $value); + public function method(Node\Stmt $value); } ?> @@ -23,13 +24,14 @@ namespace Rector\Tests\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector\Fi use PhpParser\Node; use PhpParser\Node\Identifier; use PhpParser\Node\Name; +use PHPStan\Node\ClassMethod; interface UpdateDocName { /** - * @param Identifier|Name $node + * @param ClassMethod|Node\Stmt\Property $stmt */ - public function method(Node $node); + public function method(Node\Stmt $stmt); } ?> diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/used_in_arrow_function_param.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/used_in_arrow_function_param.php.inc new file mode 100644 index 00000000000..c4ec1616196 --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/used_in_arrow_function_param.php.inc @@ -0,0 +1,49 @@ + $node, + [] + ); + + var_dump($node); + } +} + +?> +----- + $variable, + [] + ); + + var_dump($foreach); + } +} + +?> diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/used_in_closure_param.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/used_in_closure_param.php.inc new file mode 100644 index 00000000000..3421b2af63d --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Fixture/used_in_closure_param.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/FixturePhp80/skip_union_type.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/FixturePhp80/skip_union_type.php.inc deleted file mode 100644 index 424a0acb0db..00000000000 --- a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/FixturePhp80/skip_union_type.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -eventManager = $eventManager; - } -} - diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Php80Test.php b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Php80Test.php deleted file mode 100644 index 46547ecc0ee..00000000000 --- a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Php80Test.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/RenameParamToMatchTypeRectorTest.php b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/RenameParamToMatchTypeRectorTest.php index b9656b283c6..6bb6697494e 100644 --- a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/RenameParamToMatchTypeRectorTest.php +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/RenameParamToMatchTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameParamToMatchTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Source/ValueObject/Repository.php b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Source/ValueObject/Repository.php new file mode 100644 index 00000000000..36658644636 --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector/Source/ValueObject/Repository.php @@ -0,0 +1,7 @@ +services(); - $services->set(RenameParamToMatchTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([RenameParamToMatchTypeRector::class]); diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/allow_date_suffix.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/allow_date_suffix.php.inc new file mode 100644 index 00000000000..14191cb8983 --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/allow_date_suffix.php.inc @@ -0,0 +1,11 @@ +getAssign(); - - $jsonDataAssign = new Assign(1, 2); - $this->addNodeBeforeNode($jsonDataAssign, $assign); - } - - private function getAssign() - { - return new Assign(); - } -} diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_method_call_rename_below.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_method_call_rename_below.php.inc new file mode 100644 index 00000000000..fa9bae7de8e --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_method_call_rename_below.php.inc @@ -0,0 +1,21 @@ +getAssign(); + + $jsonDataAssign = new Assign(1, 2); + $this->someMethod($jsonDataAssign, $assign); + } + + private function getAssign() + { + return new Assign(); + } +} diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_override_in_other_branch.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_override_in_other_branch.php.inc index 91d488f6f68..ae2a68cab3d 100644 --- a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_override_in_other_branch.php.inc +++ b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_override_in_other_branch.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRec use Nette\Utils\Strings; use PhpParser\Node\Expr\Cast\String_; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; class SkipOverrideInOtherBranch { @@ -13,7 +13,7 @@ class SkipOverrideInOtherBranch if (Strings::contains($serviceType, '_') && ! Strings::contains($serviceType, '\\')) { $getArgumentValue = new String_($serviceType); } else { - $getArgumentValue = new LNumber(100); + $getArgumentValue = new Int_(100); } return $getArgumentValue; diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_param_rename_bellow.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_param_rename_bellow.php.inc deleted file mode 100644 index 4844cd54945..00000000000 --- a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_param_rename_bellow.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -addNodeBeforeNode($jsonDataAssign, $assign); - } -} diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_param_rename_below.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_param_rename_below.php.inc new file mode 100644 index 00000000000..48fd1e63346 --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_param_rename_below.php.inc @@ -0,0 +1,14 @@ +someMethod($jsonDataAssign, $assign); + } +} diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_self.php.inc b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_self.php.inc new file mode 100644 index 00000000000..e84e5acaccf --- /dev/null +++ b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture/skip_self.php.inc @@ -0,0 +1,11 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/config/configured_rule.php b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/config/configured_rule.php index 3751108b907..fb359b02de4 100644 --- a/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/config/configured_rule.php +++ b/rules-tests/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameVariableToMatchNewTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([RenameVariableToMatchNewTypeRector::class]); diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/fixture.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/fixture.php.inc deleted file mode 100644 index 1477eae6991..00000000000 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -eventManager = $eventManager; - } -} - -?> ------ -eliteManager = $eventManager; - } -} - -?> diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/keep_date_time.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/keep_date_time.php.inc new file mode 100644 index 00000000000..82e8adcf85b --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/keep_date_time.php.inc @@ -0,0 +1,14 @@ +timestamp = $timestamp; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/keep_date_time_interface.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/keep_date_time_interface.php.inc new file mode 100644 index 00000000000..9db9203b513 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/keep_date_time_interface.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/nullable_expr_type.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/nullable_expr_type.php.inc similarity index 95% rename from rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/nullable_expr_type.php.inc rename to rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/nullable_expr_type.php.inc index 49147086d39..8852382cce8 100644 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/nullable_expr_type.php.inc +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/nullable_expr_type.php.inc @@ -1,6 +1,6 @@ wrapper = $wrapper; + } +} + +?> +----- +gitWrapper = $wrapper; + } +} + +?> diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/promoted_property_rename_fetch.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/promoted_property_rename_fetch.php.inc similarity index 93% rename from rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/promoted_property_rename_fetch.php.inc rename to rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/promoted_property_rename_fetch.php.inc index dc4ef148496..4d6b146051c 100644 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/promoted_property_rename_fetch.php.inc +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/promoted_property_rename_fetch.php.inc @@ -1,6 +1,6 @@ +----- + diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/property_type_with_only_comment.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/property_type_with_only_comment.php.inc new file mode 100644 index 00000000000..af690e1a066 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/property_type_with_only_comment.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/rename_param_doc.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/rename_param_doc.php.inc similarity index 83% rename from rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/rename_param_doc.php.inc rename to rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/rename_param_doc.php.inc index 6d240e23872..d6e51e8ce93 100644 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/rename_param_doc.php.inc +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/rename_param_doc.php.inc @@ -1,8 +1,8 @@ eventManager = $eventManager; + } +} + +?> +----- +eliteManager = $eventManager; + } +} + +?> diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_another_doctrine_collection.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_another_doctrine_collection.php.inc new file mode 100644 index 00000000000..294cc5ae251 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_another_doctrine_collection.php.inc @@ -0,0 +1,26 @@ + + */ + #[OneToMany(mappedBy: 'user', targetEntity: SomeAnswerEntity::class)] + private Collection $someAnswers; + + public function __construct( + ) { + $this->someAnswers = new ArrayCollection(); + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_capital_numberz.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_capital_numberz.php.inc deleted file mode 100644 index c2c89078b0c..00000000000 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_capital_numberz.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_change_duplicate_type.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_change_duplicate_type.php.inc new file mode 100644 index 00000000000..b35334efc84 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_change_duplicate_type.php.inc @@ -0,0 +1,24 @@ +eventManager1 = $eventManager1; + $this->eventManager2 = $eventManager2; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_change_promoted_duplicate_type.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_change_promoted_duplicate_type.php.inc new file mode 100644 index 00000000000..98e5d4a507b --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_change_promoted_duplicate_type.php.inc @@ -0,0 +1,14 @@ +updatedAt; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_date_time_again.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_date_time_again.php.inc new file mode 100644 index 00000000000..695c33f07af --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_date_time_again.php.inc @@ -0,0 +1,13 @@ +today = $today; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_datetime_immutable.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_datetime_immutable.php.inc new file mode 100644 index 00000000000..b2ce9286082 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_datetime_immutable.php.inc @@ -0,0 +1,10 @@ + + */ + public Collection $checkboxes; + + public function __construct() + { + $this->checkboxes = new ArrayCollection(); + } + + public function getCheckboxes(): Collection + { + return $this->checkboxes; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_doctrine_collection_private_property.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_doctrine_collection_private_property.php.inc new file mode 100644 index 00000000000..cd33853a8b7 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_doctrine_collection_private_property.php.inc @@ -0,0 +1,24 @@ + + */ + private Collection $checkboxes; + + public function __construct() + { + $this->checkboxes = new ArrayCollection(); + } + + public function getCheckboxes(): Collection + { + return $this->checkboxes; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_laravel_collection_of_type.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_laravel_collection_of_type.php.inc new file mode 100644 index 00000000000..2fd41cb8f4f --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_laravel_collection_of_type.php.inc @@ -0,0 +1,20 @@ +items = $items; + } + + public function getItems(): Collection + { + return $this->items; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_mock_property.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_mock_property.php.inc new file mode 100644 index 00000000000..80952b5a94e --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_mock_property.php.inc @@ -0,0 +1,17 @@ +eliteManagerMock = $this->createMock(EliteManager::class); + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_numeric_string.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_numeric_string.php.inc new file mode 100644 index 00000000000..81cfd765938 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_numeric_string.php.inc @@ -0,0 +1,11 @@ +wrapper = $wrapper; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_protected_property_promotion.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_protected_property_promotion.php.inc new file mode 100644 index 00000000000..686fbc31782 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_protected_property_promotion.php.inc @@ -0,0 +1,12 @@ +eventManager = $eventManager; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_used_by_trait_as_property_promotion.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_used_by_trait_as_property_promotion.php.inc new file mode 100644 index 00000000000..47ec5937d76 --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_used_by_trait_as_property_promotion.php.inc @@ -0,0 +1,15 @@ +someStmt = $stmt; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_with_shortname_subnamespace.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_with_shortname_subnamespace.php.inc new file mode 100644 index 00000000000..5b612fc0b9e --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/skip_with_shortname_subnamespace.php.inc @@ -0,0 +1,18 @@ +someStmt = $stmt; + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/union_types_to_expr.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/union_types_to_expr.php.inc similarity index 95% rename from rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/union_types_to_expr.php.inc rename to rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/union_types_to_expr.php.inc index 2d584b81941..32ac83f1ceb 100644 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/union_types_to_expr.php.inc +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/union_types_to_expr.php.inc @@ -1,6 +1,6 @@ +----- + diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/use_nullable_aliased_on_promoted_property.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/use_nullable_aliased_on_promoted_property.php.inc new file mode 100644 index 00000000000..f8c397eddce --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Fixture/use_nullable_aliased_on_promoted_property.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp74/skip_change_duplicate_type.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp74/skip_change_duplicate_type.php.inc deleted file mode 100644 index f293f14c4ee..00000000000 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp74/skip_change_duplicate_type.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -eventManager1 = $eventManager1; - $this->eventManager2 = $eventManager2; - } -} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp74/skip_doctrine_collection.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp74/skip_doctrine_collection.php.inc deleted file mode 100644 index 7d3913ccacb..00000000000 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp74/skip_doctrine_collection.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - - */ - public Collection $checkboxes; - - public function __construct() - { - $this->checkboxes = new ArrayCollection(); - } - - public function getCheckboxes(): Collection - { - return $this->checkboxes; - } -} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/skip_change_duplicate_type.php.inc b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/skip_change_duplicate_type.php.inc deleted file mode 100644 index 1e3af2e6285..00000000000 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/FixturePhp80/skip_change_duplicate_type.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Php80Test.php b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Php80Test.php deleted file mode 100644 index 394d5560dd7..00000000000 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Php80Test.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/property_promotion.php'; - } -} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/RenamePropertyToMatchTypeRectorTest.php b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/RenamePropertyToMatchTypeRectorTest.php index 9528d7b5576..23c5c3638d4 100644 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/RenamePropertyToMatchTypeRectorTest.php +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/RenamePropertyToMatchTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenamePropertyToMatchTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Source/Entity/SomeAnswerEntity.php b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Source/Entity/SomeAnswerEntity.php new file mode 100644 index 00000000000..1f34e68cf5a --- /dev/null +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/Source/Entity/SomeAnswerEntity.php @@ -0,0 +1,10 @@ +eventManager->trigger($name); + } +} diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/config/configured_rule.php b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/config/configured_rule.php index 8dca55e6f2b..d39c9d8981d 100644 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/config/configured_rule.php +++ b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/config/configured_rule.php @@ -2,10 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenamePropertyToMatchTypeRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(RenamePropertyToMatchTypeRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_81); }; diff --git a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/config/property_promotion.php b/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/config/property_promotion.php deleted file mode 100644 index 9539504c128..00000000000 --- a/rules-tests/Naming/Rector/Class_/RenamePropertyToMatchTypeRector/config/property_promotion.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::PROPERTY_PROMOTION); - - $services = $containerConfigurator->services(); - $services->set(RenamePropertyToMatchTypeRector::class); -}; diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/keep_underscore.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/keep_underscore.php.inc index 8b04cf6cfc7..0c0a32558f6 100644 --- a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/keep_underscore.php.inc +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/keep_underscore.php.inc @@ -12,22 +12,3 @@ class KeepUnderscore } } } - -?> ------ - diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/single_prefix.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/single_prefix.php.inc deleted file mode 100644 index e56c77f772e..00000000000 --- a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/single_prefix.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_cms_middle.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_cms_middle.php.inc new file mode 100644 index 00000000000..ca4459af104 --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_cms_middle.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_old_value_used_next.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_old_value_used_next.php.inc new file mode 100644 index 00000000000..7195e8bd70e --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_old_value_used_next.php.inc @@ -0,0 +1,17 @@ + diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_var_defined_early_from_parameter.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_var_defined_early_from_parameter.php.inc new file mode 100644 index 00000000000..4023b74a864 --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_var_defined_early_from_parameter.php.inc @@ -0,0 +1,20 @@ + diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_with_number.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_with_number.php.inc new file mode 100644 index 00000000000..db1b864fa7d --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_with_number.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/some_static_property_fetch2.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/some_static_property_fetch2.php.inc new file mode 100644 index 00000000000..d467b1745ac --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/some_static_property_fetch2.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/RenameForeachValueVariableToMatchExprVariableRectorTest.php b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/RenameForeachValueVariableToMatchExprVariableRectorTest.php index 54f3fe5bb5b..2b21a488dfd 100644 --- a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/RenameForeachValueVariableToMatchExprVariableRectorTest.php +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/RenameForeachValueVariableToMatchExprVariableRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameForeachValueVariableToMatchExprVariableRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/config/configured_rule.php b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/config/configured_rule.php index 31217dc8cf3..aaba8580001 100644 --- a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/config/configured_rule.php +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameForeachValueVariableToMatchExprVariableRector::class); -}; +return RectorConfig::configure() + ->withRules([RenameForeachValueVariableToMatchExprVariableRector::class]); diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/fixture.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/fixture.php.inc deleted file mode 100644 index 3c887cf12c4..00000000000 --- a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,53 +0,0 @@ -getMethods() as $property) { - $array[] = $property; - } - } - - /** - * @return Method[] - */ - public function getMethods(): array - { - - } -} - -?> ------ -getMethods() as $method) { - $array[] = $method; - } - } - - /** - * @return Method[] - */ - public function getMethods(): array - { - - } -} - -?> diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/foreach_prefer_method_name_over_return_type_for_iterables.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/foreach_prefer_method_name_over_return_type_for_iterables.php.inc new file mode 100644 index 00000000000..6ebd8a8e903 --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/foreach_prefer_method_name_over_return_type_for_iterables.php.inc @@ -0,0 +1,59 @@ +books = new DummyCollection(); + } + + public function run(): void + { + + foreach ($this->getBooks() as $item) { + var_dump($item); + } + } + + public function getBooks(): DummyCollection + { + return $this->books; + } +} + +?> +----- +books = new DummyCollection(); + } + + public function run(): void + { + + foreach ($this->getBooks() as $book) { + var_dump($book); + } + } + + public function getBooks(): DummyCollection + { + return $this->books; + } +} + +?> diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/get_other_method.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/get_other_method.php.inc new file mode 100644 index 00000000000..9e52a0d166d --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/get_other_method.php.inc @@ -0,0 +1,53 @@ +getMethods() as $property) { + $array[] = $property; + } + } + + /** + * @return Method[] + */ + public function getMethods(): array + { + + } +} + +?> +----- +getMethods() as $method) { + $array[] = $method; + } + } + + /** + * @return Method[] + */ + public function getMethods(): array + { + + } +} + +?> diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/multiple_array_types_with_advanced_type_hints.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/multiple_array_types_with_advanced_type_hints.php.inc new file mode 100644 index 00000000000..aa20384d5eb --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/multiple_array_types_with_advanced_type_hints.php.inc @@ -0,0 +1,85 @@ +getVariants() as $property) { + $array[] = $property; + } + } + + public function associativeArray() + { + $associativeArray = []; + foreach ($this->getNamesAndVariants() as $name => $value) { + $associativeArray[$name] = $value; + } + } + + /** + * @return Method[] + */ + public function getVariants(): array + { + + } + + /** + * @return array + */ + public function getNamesAndVariants(): array + { + + } +} + +?> +----- +getVariants() as $method) { + $array[] = $method; + } + } + + public function associativeArray() + { + $associativeArray = []; + foreach ($this->getNamesAndVariants() as $name => $method) { + $associativeArray[$name] = $method; + } + } + + /** + * @return Method[] + */ + public function getVariants(): array + { + + } + + /** + * @return array + */ + public function getNamesAndVariants(): array + { + + } +} + +?> diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/skip_get_iterator.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/skip_get_iterator.php.inc new file mode 100644 index 00000000000..c6592d8d1f0 --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Fixture/skip_get_iterator.php.inc @@ -0,0 +1,15 @@ +getIterator() as $someItem) { + echo $someItem; + } + } +} diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/RenameForeachValueVariableToMatchMethodCallReturnTypeRectorTest.php b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/RenameForeachValueVariableToMatchMethodCallReturnTypeRectorTest.php index e81c72801cd..831e4218942 100644 --- a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/RenameForeachValueVariableToMatchMethodCallReturnTypeRectorTest.php +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/RenameForeachValueVariableToMatchMethodCallReturnTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchMethodCallReturnTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameForeachValueVariableToMatchMethodCallReturnTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Source/DummyCollection.php b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Source/DummyCollection.php new file mode 100644 index 00000000000..09728ceafd8 --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector/Source/DummyCollection.php @@ -0,0 +1,36 @@ +services(); - $services->set(RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class]); diff --git a/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/Fixture/skip_some_class.php.inc b/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/Fixture/skip_some_class.php.inc deleted file mode 100644 index 4cb78788bb8..00000000000 --- a/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/Fixture/skip_some_class.php.inc +++ /dev/null @@ -1,25 +0,0 @@ -eventManager = $eventManager; - } - - public function getEliteManager(): EliteManager - { - return $this->eventManager; - } -} diff --git a/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/PropertyRenameFactoryTest.php b/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/PropertyRenameFactoryTest.php deleted file mode 100644 index 882cb84b162..00000000000 --- a/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/PropertyRenameFactoryTest.php +++ /dev/null @@ -1,81 +0,0 @@ -boot(); - - $this->propertyRenameFactory = $this->getService(PropertyRenameFactory::class); - $this->matchPropertyTypeExpectedNameResolver = $this->getService( - MatchPropertyTypeExpectedNameResolver::class - ); - - $this->fileInfoParser = $this->getService(FileInfoParser::class); - $this->betterNodeFinder = $this->getService(BetterNodeFinder::class); - } - - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfoWithProperty, string $expectedName, string $currentName): void - { - $property = $this->getPropertyFromFileInfo($fileInfoWithProperty); - - $expectedPropertyName = $this->matchPropertyTypeExpectedNameResolver->resolve($property); - if ($expectedPropertyName === null) { - return; - } - - $actualPropertyRename = $this->propertyRenameFactory->createFromExpectedName($property, $expectedPropertyName); - $this->assertNotNull($actualPropertyRename); - - /** @var PropertyRename $actualPropertyRename */ - $this->assertSame($property, $actualPropertyRename->getProperty()); - $this->assertSame($expectedName, $actualPropertyRename->getExpectedName()); - $this->assertSame($currentName, $actualPropertyRename->getCurrentName()); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - yield [new SmartFileInfo(__DIR__ . '/Fixture/skip_some_class.php.inc'), 'eliteManager', 'eventManager']; - } - - private function getPropertyFromFileInfo(SmartFileInfo $fileInfo): Property - { - $nodes = $this->fileInfoParser->parseFileInfoToNodesAndDecorate($fileInfo); - - $property = $this->betterNodeFinder->findFirstInstanceOf($nodes, Property::class); - if (! $property instanceof Property) { - throw new ShouldNotHappenException(); - } - - return $property; - } -} diff --git a/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/Source/EliteManager.php b/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/Source/EliteManager.php deleted file mode 100644 index 8ce61a90e9f..00000000000 --- a/rules-tests/Naming/ValueObjectFactory/PropertyRenameFactory/Source/EliteManager.php +++ /dev/null @@ -1,9 +0,0 @@ - +----- + diff --git a/rules-tests/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector/Fixture/skip_already_filled.php.inc b/rules-tests/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector/Fixture/skip_already_filled.php.inc new file mode 100644 index 00000000000..eb8157d87b1 --- /dev/null +++ b/rules-tests/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector/Fixture/skip_already_filled.php.inc @@ -0,0 +1,14 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector/config/configured_rule.php b/rules-tests/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector/config/configured_rule.php new file mode 100644 index 00000000000..81f622bfc1c --- /dev/null +++ b/rules-tests/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([UtilsJsonStaticCallNamedArgRector::class]); diff --git a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/fixture.php.inc b/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/fixture.php.inc deleted file mode 100644 index e4af13eb0bc..00000000000 --- a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -call1(); - $this->call2(); - } - - private function call2() - { - } - - private function call1() - { - } -} - -?> ------ -call1(); - $this->call2(); - } - - private function call1() - { - } - private function call2() - { - } -} - -?> diff --git a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/multi_call.php.inc b/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/multi_call.php.inc deleted file mode 100644 index a825883790a..00000000000 --- a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/multi_call.php.inc +++ /dev/null @@ -1,53 +0,0 @@ -call3(); - } - - private function call2() - { - } - - private function call1() - { - } - - private function call3() - { - $this->call1(); - $this->call2(); - } -} - -?> ------ -call3(); - } - - private function call3() - { - $this->call1(); - $this->call2(); - } - private function call1() - { - } - private function call2() - { - } -} - -?> diff --git a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/multiple_runs.php.inc b/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/multiple_runs.php.inc deleted file mode 100644 index a40dc19559f..00000000000 --- a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/multiple_runs.php.inc +++ /dev/null @@ -1,155 +0,0 @@ -shouldSkip($node)) { - return null; - } - - $node->params = $this->getSortedParams($node); - - return $node; - } - - public function configure(array $configuration): void - { - - } - - private function getSortedParams(ClassMethod $classMethod): array - { - $params = $classMethod->getParams(); - usort($params, function (Param $firstParam, Param $secondParam) { - $firstParamType = $this->getParamType($firstParam); - $secondParamType = $this->getParamType($secondParam); - - return $this->getShortName($firstParamType) <=> $this->getShortName($secondParamType); - }); - - return $params; - } - - private function hasPrimitiveDataTypeParam(ClassMethod $classMethod): bool - { - - } - - private function shouldSkip(ClassMethod $classMethod): bool - { - if ($this->hasPrimitiveDataTypeParam($classMethod)) { - return true; - } - - return $this->hasParamWithNoType($classMethod); - } - - private function getParamType(Param $param) - { - - } - - private function hasParamWithNoType(ClassMethod $classMethod): bool - { - - } - - private function isFileInfoMatch(SmartFileInfo $smartFileInfo): bool - { - foreach ($this->skipPatterns as $pattern) { - if (fnmatch($pattern, $smartFileInfo->getRelativeFilePath(), FNM_NOESCAPE)) { - return true; - } - } - - return false; - } -} - -?> ------ -shouldSkip($node)) { - return null; - } - - $node->params = $this->getSortedParams($node); - - return $node; - } - - public function configure(array $configuration): void - { - - } - private function shouldSkip(ClassMethod $classMethod): bool - { - if ($this->hasPrimitiveDataTypeParam($classMethod)) { - return true; - } - - return $this->hasParamWithNoType($classMethod); - } - - private function getSortedParams(ClassMethod $classMethod): array - { - $params = $classMethod->getParams(); - usort($params, function (Param $firstParam, Param $secondParam) { - $firstParamType = $this->getParamType($firstParam); - $secondParamType = $this->getParamType($secondParam); - - return $this->getShortName($firstParamType) <=> $this->getShortName($secondParamType); - }); - - return $params; - } - - private function hasPrimitiveDataTypeParam(ClassMethod $classMethod): bool - { - - } - - private function hasParamWithNoType(ClassMethod $classMethod): bool - { - - } - private function getParamType(Param $param) - { - - } - - private function isFileInfoMatch(SmartFileInfo $smartFileInfo): bool - { - foreach ($this->skipPatterns as $pattern) { - if (fnmatch($pattern, $smartFileInfo->getRelativeFilePath(), FNM_NOESCAPE)) { - return true; - } - } - - return false; - } -} - -?> diff --git a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/skip_different_amount_of_methods.php.inc b/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/skip_different_amount_of_methods.php.inc deleted file mode 100644 index 34c784ffca2..00000000000 --- a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/Fixture/skip_different_amount_of_methods.php.inc +++ /dev/null @@ -1,64 +0,0 @@ -call1(); - $this->call2(); - $this->publicCall1(); - } - - public function publicCall1() - { - } - - private function call2() - { - } - - private function call1() - { - } - - private function call3() - { - - } -} - -?> ------ -call1(); - $this->call2(); - $this->publicCall1(); - } - - public function publicCall1() - { - } - - private function call1() - { - } - private function call2() - { - } - - private function call3() - { - - } -} - -?> diff --git a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/OrderPrivateMethodsByUseRectorTest.php b/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/OrderPrivateMethodsByUseRectorTest.php deleted file mode 100644 index 4d88f6cf61f..00000000000 --- a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/OrderPrivateMethodsByUseRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/config/configured_rule.php b/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/config/configured_rule.php deleted file mode 100644 index 7a0800c7ed0..00000000000 --- a/rules-tests/Order/Rector/Class_/OrderPrivateMethodsByUseRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(OrderPrivateMethodsByUseRector::class); -}; diff --git a/rules-tests/Order/StmtOrderTest.php b/rules-tests/Order/StmtOrderTest.php deleted file mode 100644 index c8767c4041d..00000000000 --- a/rules-tests/Order/StmtOrderTest.php +++ /dev/null @@ -1,113 +0,0 @@ - 0, - 1 => 2, - 2 => 1, - ]; - - private StmtOrder $stmtOrder; - - private NodeNameResolver $nodeNameResolver; - - protected function setUp(): void - { - $this->boot(); - - $this->stmtOrder = $this->getService(StmtOrder::class); - $this->nodeNameResolver = $this->getService(NodeNameResolver::class); - } - - /** - * @return Iterator>> - */ - public function dataProvider(): Iterator - { - yield [ - ['first', 'second', 'third'], - ['third', 'first', 'second'], - [ - 0 => 1, - 1 => 2, - 2 => 0, - ], - ]; - yield [ - ['first', 'second', 'third'], - ['third', 'second', 'first'], - [ - 0 => 2, - 1 => 1, - 2 => 0, - ], - ]; - yield [ - ['first', 'second', 'third'], - ['first', 'second', 'third'], - [ - 0 => 0, - 1 => 1, - 2 => 2, - ], - ]; - } - - /** - * @dataProvider dataProvider - * @param string[] $desiredStmtOrder - * @param string[] $currentStmtOrder - * @param int[] $expected - */ - public function testCreateOldToNewKeys(array $desiredStmtOrder, array $currentStmtOrder, array $expected): void - { - $actual = $this->stmtOrder->createOldToNewKeys($desiredStmtOrder, $currentStmtOrder); - $this->assertSame($expected, $actual); - } - - public function testReorderClassStmtsByOldToNewKeys(): void - { - $class = $this->getTestClassNode(); - - $this->stmtOrder->reorderClassStmtsByOldToNewKeys($class, self::OLD_TO_NEW_KEYS); - - $expectedClass = $this->getExpectedClassNode(); - $this->assertTrue($this->nodeNameResolver->areNamesEqual($expectedClass->stmts[0], $class->stmts[0])); - $this->assertTrue($this->nodeNameResolver->areNamesEqual($expectedClass->stmts[1], $class->stmts[1])); - $this->assertTrue($this->nodeNameResolver->areNamesEqual($expectedClass->stmts[2], $class->stmts[2])); - } - - private function getTestClassNode(): Class_ - { - $class = new Class_('ClassUnderTest'); - $class->stmts[] = new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty('name')]); - $class->stmts[] = new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty('service')]); - $class->stmts[] = new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty('price')]); - return $class; - } - - private function getExpectedClassNode(): Class_ - { - $expectedClass = new Class_('ExpectedClass'); - $expectedClass->stmts[] = new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty('name')]); - $expectedClass->stmts[] = new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty('price')]); - $expectedClass->stmts[] = new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty('service')]); - return $expectedClass; - } -} diff --git a/rules-tests/PSR4/Composer/Fixture-dashed/Config.php b/rules-tests/PSR4/Composer/Fixture-dashed/Config.php deleted file mode 100644 index eaafffd69ff..00000000000 --- a/rules-tests/PSR4/Composer/Fixture-dashed/Config.php +++ /dev/null @@ -1,7 +0,0 @@ -boot(); - } - - public function test(): void - { - $smartFileInfo = new SmartFileInfo(__DIR__ . '/Fixture-dashed/Config.php'); - $file = new File($smartFileInfo, $smartFileInfo->getContents()); - - $psr4AutoloadPathsProvider = $this->getService(PSR4AutoloadPathsProvider::class); - $psr4NamespaceMatcher = new PSR4NamespaceMatcher($psr4AutoloadPathsProvider); - - $this->assertNull($psr4NamespaceMatcher->getExpectedNamespace($file, new FileWithoutNamespace([]))); - } -} diff --git a/rules-tests/PSR4/FileRelocationResolverTest.php b/rules-tests/PSR4/FileRelocationResolverTest.php deleted file mode 100644 index 0c385e13b3f..00000000000 --- a/rules-tests/PSR4/FileRelocationResolverTest.php +++ /dev/null @@ -1,52 +0,0 @@ -boot(); - - $this->fileRelocationResolver = $this->getService(FileRelocationResolver::class); - } - - /** - * @dataProvider provideData() - */ - public function test(string $file, string $oldClass, string $newClass, string $expectedNewFileLocation): void - { - $smartFileInfo = new SmartFileInfo($file); - - $newFileLocation = $this->fileRelocationResolver->resolveNewFileLocationFromOldClassToNewClass( - $smartFileInfo, - $oldClass, - $newClass - ); - - $this->assertSame($expectedNewFileLocation, $newFileLocation); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - yield [ - __DIR__ . '/Source/SomeFile.php', - SomeFile::class, - 'Rector\Tests\PSR10\Source\SomeFile', - 'rules-tests/PSR10/Source/SomeFile.php', - ]; - } -} diff --git a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/case_insensitive.php.inc b/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/case_insensitive.php.inc deleted file mode 100644 index 06223ddc8cc..00000000000 --- a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/case_insensitive.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/namespace_less_class.php.inc b/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/namespace_less_class.php.inc deleted file mode 100644 index 8786a756c9e..00000000000 --- a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/namespace_less_class.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - ------ - ------ - diff --git a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/skip_class_consts.php.inc b/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/skip_class_consts.php.inc deleted file mode 100644 index e37a7498ae5..00000000000 --- a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/skip_class_consts.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/skip_inline_html.php.inc b/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/skip_inline_html.php.inc deleted file mode 100644 index 50ebb59f064..00000000000 --- a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/skip_inline_html.php.inc +++ /dev/null @@ -1 +0,0 @@ -

{{ content }}

diff --git a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/wrong_namespace.php.inc b/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/wrong_namespace.php.inc deleted file mode 100644 index a1caad7146b..00000000000 --- a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Fixture/wrong_namespace.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - ------ - diff --git a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/NormalizeNamespaceByPSR4ComposerAutoloadRectorTest.php b/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/NormalizeNamespaceByPSR4ComposerAutoloadRectorTest.php deleted file mode 100644 index 8ceeff0778f..00000000000 --- a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/NormalizeNamespaceByPSR4ComposerAutoloadRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($smartFileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/normalize_namespace_without_namespace_config.php'; - } -} diff --git a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Source/DummyPSR4AutoloadWithoutNamespaceMatcher.php b/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Source/DummyPSR4AutoloadWithoutNamespaceMatcher.php deleted file mode 100644 index ad7d4af2e26..00000000000 --- a/rules-tests/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector/Source/DummyPSR4AutoloadWithoutNamespaceMatcher.php +++ /dev/null @@ -1,17 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, false); - - $services = $containerConfigurator->services(); - $services->set(NormalizeNamespaceByPSR4ComposerAutoloadRector::class); - $services->set(DummyPSR4AutoloadWithoutNamespaceMatcher::class); - - $services->alias(PSR4AutoloadNamespaceMatcherInterface::class, DummyPSR4AutoloadWithoutNamespaceMatcher::class); -}; diff --git a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Expected/ClassMatchesFilenameException.php b/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Expected/ClassMatchesFilenameException.php deleted file mode 100644 index a3c1ce2eb48..00000000000 --- a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Expected/ClassMatchesFilenameException.php +++ /dev/null @@ -1,7 +0,0 @@ - 'Internal error', PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted', PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted', PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data', PREG_BAD_UTF8_OFFSET_ERROR => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', 6 => 'Failed due to limited JIT stack space']; -} diff --git a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Expected/SkipWithoutNamespace.php b/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Expected/SkipWithoutNamespace.php deleted file mode 100644 index fe379f7887b..00000000000 --- a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Expected/SkipWithoutNamespace.php +++ /dev/null @@ -1,5 +0,0 @@ -doTestFileInfo($originalFileInfo); - - $this->assertCount($this->removedAndAddedFilesCollector->getAddedFileCount(), $expectedFilePathsWithContents); - - $this->assertFilesWereAdded($expectedFilePathsWithContents); - - $inputFileInfoAndExpectedFileInfo = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpectedFileInfos( - $originalFileInfo - ); - - $this->assertSame( - $expectedOriginalFileWasRemoved, - $this->removedAndAddedFilesCollector->isFileRemoved($inputFileInfoAndExpectedFileInfo->getInputFileInfo()) - ); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $smartFileSystem = new SmartFileSystem(); - - $filePathsWithContents = [ - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/SkipWithoutNamespace.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/SkipWithoutNamespace.php') - ), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/JustTwoExceptionWithoutNamespace.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/JustTwoExceptionWithoutNamespace.php') - ), - ]; - - yield [ - new SmartFileInfo(__DIR__ . '/FixtureFileWithoutNamespace/some_without_namespace.php.inc'), - $filePathsWithContents, - ]; - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/ClassMatchesFilename.php.inc b/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/ClassMatchesFilename.php.inc deleted file mode 100644 index 51ecacda211..00000000000 --- a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/ClassMatchesFilename.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/class_trait_and_interface.php.inc b/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/class_trait_and_interface.php.inc deleted file mode 100644 index 6c277437066..00000000000 --- a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/class_trait_and_interface.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/nette_exceptions.php.inc b/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/nette_exceptions.php.inc deleted file mode 100644 index 954954177bf..00000000000 --- a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Fixture/nette_exceptions.php.inc +++ /dev/null @@ -1,48 +0,0 @@ - 'Internal error', - PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted', - PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted', - PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data', - PREG_BAD_UTF8_OFFSET_ERROR => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', - 6 => 'Failed due to limited JIT stack space', // PREG_JIT_STACKLIMIT_ERROR - ]; -} - -?> ------ - diff --git a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/FixtureFileWithoutNamespace/some_without_namespace.php.inc b/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/FixtureFileWithoutNamespace/some_without_namespace.php.inc deleted file mode 100644 index 493d6802ca3..00000000000 --- a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/FixtureFileWithoutNamespace/some_without_namespace.php.inc +++ /dev/null @@ -1,9 +0,0 @@ -doTestFileInfo($originalFileInfo); - - $this->assertCount($this->removedAndAddedFilesCollector->getAddedFileCount(), $expectedFilePathsWithContents); - - $this->assertFilesWereAdded($expectedFilePathsWithContents); - - $inputFileInfoAndExpectedFileInfo = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpectedFileInfos( - $originalFileInfo - ); - $this->assertSame( - $expectedOriginalFileWasRemoved, - $this->removedAndAddedFilesCollector->isFileRemoved($inputFileInfoAndExpectedFileInfo->getInputFileInfo()) - ); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - $smartFileSystem = new SmartFileSystem(); - - // source: https://github.com/nette/utils/blob/798f8c1626a8e0e23116d90e588532725cce7d0e/src/Utils/exceptions.php - $filePathsWithContents = [ - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/RegexpException.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/RegexpException.php') - ), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/UnknownImageFileException.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/UnknownImageFileException.php') - ), - ]; - yield [new SmartFileInfo(__DIR__ . '/Fixture/nette_exceptions.php.inc'), $filePathsWithContents]; - - $filePathsWithContents = [ - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/MyTrait.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/MyTrait.php') - ), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/ClassTraitAndInterface.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/ClassTraitAndInterface.php') - ), - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/MyInterface.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/MyInterface.php') - ), - ]; - - yield [new SmartFileInfo(__DIR__ . '/Fixture/class_trait_and_interface.php.inc'), $filePathsWithContents]; - - $filePathsWithContents = [ - new AddedFileWithContent( - $this->getFixtureTempDirectory() . '/ClassMatchesFilenameException.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/ClassMatchesFilenameException.php') - ), - ]; - - yield [new SmartFileInfo(__DIR__ . '/Fixture/ClassMatchesFilename.php.inc'), $filePathsWithContents, false]; - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/config/configured_rule.php b/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/config/configured_rule.php deleted file mode 100644 index 82a4a6134d1..00000000000 --- a/rules-tests/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(MultipleClassFileToPsr4ClassesRector::class); -}; diff --git a/rules-tests/PSR4/Source/SomeFile.php b/rules-tests/PSR4/Source/SomeFile.php deleted file mode 100644 index cecabccd9aa..00000000000 --- a/rules-tests/PSR4/Source/SomeFile.php +++ /dev/null @@ -1,10 +0,0 @@ -doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php52/Rector/Property/VarToPublicPropertyRector/config/configured_rule.php b/rules-tests/Php52/Rector/Property/VarToPublicPropertyRector/config/configured_rule.php index e6007f9a605..fc14c04610d 100644 --- a/rules-tests/Php52/Rector/Property/VarToPublicPropertyRector/config/configured_rule.php +++ b/rules-tests/Php52/Rector/Property/VarToPublicPropertyRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php52\Rector\Property\VarToPublicPropertyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(VarToPublicPropertyRector::class); -}; +return RectorConfig::configure() + ->withRules([VarToPublicPropertyRector::class]); diff --git a/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/ContinueToBreakInSwitchRectorTest.php b/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/ContinueToBreakInSwitchRectorTest.php index 0fc2bf5b901..badda7875b8 100644 --- a/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/ContinueToBreakInSwitchRectorTest.php +++ b/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/ContinueToBreakInSwitchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php52\Rector\Switch_\ContinueToBreakInSwitchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ContinueToBreakInSwitchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/Fixture/in_else_stmt.php.inc b/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/Fixture/in_else_stmt.php.inc new file mode 100644 index 00000000000..f90fa008d1a --- /dev/null +++ b/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/Fixture/in_else_stmt.php.inc @@ -0,0 +1,73 @@ + +----- + diff --git a/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/Fixture/in_if_stmt.php.inc b/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/Fixture/in_if_stmt.php.inc new file mode 100644 index 00000000000..efa1bdec8a4 --- /dev/null +++ b/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/Fixture/in_if_stmt.php.inc @@ -0,0 +1,71 @@ + +----- + diff --git a/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/config/configured_rule.php b/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/config/configured_rule.php index 6cd941a8938..a8c352db35b 100644 --- a/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/config/configured_rule.php +++ b/rules-tests/Php52/Rector/Switch_/ContinueToBreakInSwitchRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php52\Rector\Switch_\ContinueToBreakInSwitchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ContinueToBreakInSwitchRector::class); -}; +return RectorConfig::configure() + ->withRules([ContinueToBreakInSwitchRector::class]); diff --git a/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/ClearReturnNewByReferenceRectorTest.php b/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/ClearReturnNewByReferenceRectorTest.php deleted file mode 100644 index e1765633dd7..00000000000 --- a/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/ClearReturnNewByReferenceRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/Fixture/fixture.php.inc b/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/Fixture/fixture.php.inc deleted file mode 100644 index 23df06a5550..00000000000 --- a/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ - diff --git a/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/config/configured_rule.php b/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/config/configured_rule.php deleted file mode 100644 index a502b2ea0b9..00000000000 --- a/rules-tests/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ClearReturnNewByReferenceRector::class); -}; diff --git a/rules-tests/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector/DirNameFileConstantToDirConstantRectorTest.php b/rules-tests/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector/DirNameFileConstantToDirConstantRectorTest.php index b4865f51029..c292b53a5e2 100644 --- a/rules-tests/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector/DirNameFileConstantToDirConstantRectorTest.php +++ b/rules-tests/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector/DirNameFileConstantToDirConstantRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php53\Rector\FuncCall\DirNameFileConstantToDirConstantRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class DirNameFileConstantToDirConstantRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector/config/configured_rule.php b/rules-tests/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector/config/configured_rule.php index 08ad35f6e14..8e19ab3c145 100644 --- a/rules-tests/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector/config/configured_rule.php +++ b/rules-tests/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php53\Rector\FuncCall\DirNameFileConstantToDirConstantRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(DirNameFileConstantToDirConstantRector::class); -}; +return RectorConfig::configure() + ->withRules([DirNameFileConstantToDirConstantRector::class]); diff --git a/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/Fixture/parentheses_in_else.php.inc b/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/Fixture/parentheses_in_else.php.inc new file mode 100644 index 00000000000..e141905abef --- /dev/null +++ b/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/Fixture/parentheses_in_else.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/TernaryToElvisRectorTest.php b/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/TernaryToElvisRectorTest.php index 1d9b30a9692..7161f540d90 100644 --- a/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/TernaryToElvisRectorTest.php +++ b/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/TernaryToElvisRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php53\Rector\Ternary\TernaryToElvisRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class TernaryToElvisRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/config/configured_rule.php b/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/config/configured_rule.php index 233f2daab0c..0540340b70d 100644 --- a/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/config/configured_rule.php +++ b/rules-tests/Php53/Rector/Ternary/TernaryToElvisRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php53\Rector\Ternary\TernaryToElvisRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(TernaryToElvisRector::class); -}; +return RectorConfig::configure() + ->withRules([TernaryToElvisRector::class]); diff --git a/rules-tests/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector/ReplaceHttpServerVarsByServerRectorTest.php b/rules-tests/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector/ReplaceHttpServerVarsByServerRectorTest.php index 5272518187a..8cdc83f5ab3 100644 --- a/rules-tests/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector/ReplaceHttpServerVarsByServerRectorTest.php +++ b/rules-tests/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector/ReplaceHttpServerVarsByServerRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\Php53\Rector\Variable\ReplaceHttpServerVarsByServerRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use SplFileInfo; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReplaceHttpServerVarsByServerRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector/config/configured_rule.php b/rules-tests/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector/config/configured_rule.php index a9a1182c029..62a41c9c69c 100644 --- a/rules-tests/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector/config/configured_rule.php +++ b/rules-tests/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php53\Rector\Variable\ReplaceHttpServerVarsByServerRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReplaceHttpServerVarsByServerRector::class); -}; +return RectorConfig::configure() + ->withRules([ReplaceHttpServerVarsByServerRector::class]); diff --git a/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/fixture.php.inc b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..fe46a616509 --- /dev/null +++ b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/fixture.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/multi_nested.php.inc b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/multi_nested.php.inc new file mode 100644 index 00000000000..21b33e4494c --- /dev/null +++ b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/multi_nested.php.inc @@ -0,0 +1,39 @@ + true, + ), + ), + ); + } +} + +?> +----- + true, + ], + ], + ]; + } +} + +?> diff --git a/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/skip_already_short_array.php.inc b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/skip_already_short_array.php.inc new file mode 100644 index 00000000000..a5ddc722c51 --- /dev/null +++ b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/skip_already_short_array.php.inc @@ -0,0 +1,10 @@ + diff --git a/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/spaced_array.php.inc b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/spaced_array.php.inc new file mode 100644 index 00000000000..83f9abe5d5d --- /dev/null +++ b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/Fixture/spaced_array.php.inc @@ -0,0 +1,39 @@ + true, + ), + ), + ); + } +} + +?> +----- + true, + ], + ], + ]; + } +} + +?> diff --git a/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/LongArrayToShortArrayRectorTest.php b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/LongArrayToShortArrayRectorTest.php new file mode 100644 index 00000000000..bdd1d10102a --- /dev/null +++ b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/LongArrayToShortArrayRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/config/configured_rule.php b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/config/configured_rule.php new file mode 100644 index 00000000000..fa1b6ce9b2d --- /dev/null +++ b/rules-tests/Php54/Rector/Array_/LongArrayToShortArrayRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([LongArrayToShortArrayRector::class]); diff --git a/rules-tests/Php54/Rector/Break_/RemoveZeroBreakContinueRector/RemoveZeroBreakContinueRectorTest.php b/rules-tests/Php54/Rector/Break_/RemoveZeroBreakContinueRector/RemoveZeroBreakContinueRectorTest.php index cb649d776fe..4c55076970a 100644 --- a/rules-tests/Php54/Rector/Break_/RemoveZeroBreakContinueRector/RemoveZeroBreakContinueRectorTest.php +++ b/rules-tests/Php54/Rector/Break_/RemoveZeroBreakContinueRector/RemoveZeroBreakContinueRectorTest.php @@ -5,26 +5,21 @@ namespace Rector\Tests\Php54\Rector\Break_\RemoveZeroBreakContinueRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveZeroBreakContinueRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { // to prevent loading PHP 5.4+ invalid code - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php54/Rector/Break_/RemoveZeroBreakContinueRector/config/configured_rule.php b/rules-tests/Php54/Rector/Break_/RemoveZeroBreakContinueRector/config/configured_rule.php index dd87b167a88..bcd218d37e9 100644 --- a/rules-tests/Php54/Rector/Break_/RemoveZeroBreakContinueRector/config/configured_rule.php +++ b/rules-tests/Php54/Rector/Break_/RemoveZeroBreakContinueRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php54\Rector\Break_\RemoveZeroBreakContinueRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveZeroBreakContinueRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveZeroBreakContinueRector::class]); diff --git a/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/Fixture/some_method_call.php.inc b/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/Fixture/some_method_call.php.inc new file mode 100644 index 00000000000..f315b434cb2 --- /dev/null +++ b/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/Fixture/some_method_call.php.inc @@ -0,0 +1,27 @@ +bar(&$one); + } +} + +?> +----- +bar($one); + } +} + +?> diff --git a/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/Fixture/some_static_call.php.inc b/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/Fixture/some_static_call.php.inc new file mode 100644 index 00000000000..60908d07a4a --- /dev/null +++ b/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/Fixture/some_static_call.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/RemoveReferenceFromCallRectorTest.php b/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/RemoveReferenceFromCallRectorTest.php index 04a8a4f3b58..90c4dad7681 100644 --- a/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/RemoveReferenceFromCallRectorTest.php +++ b/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/RemoveReferenceFromCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php54\Rector\FuncCall\RemoveReferenceFromCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveReferenceFromCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/config/configured_rule.php b/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/config/configured_rule.php index 862b8426891..f6ab78d8046 100644 --- a/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/config/configured_rule.php +++ b/rules-tests/Php54/Rector/FuncCall/RemoveReferenceFromCallRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php54\Rector\FuncCall\RemoveReferenceFromCallRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveReferenceFromCallRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveReferenceFromCallRector::class]); diff --git a/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/Fixture/fixture.php.inc b/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..f75d9c971fc --- /dev/null +++ b/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/Fixture/skip_already_self.php.inc b/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/Fixture/skip_already_self.php.inc new file mode 100644 index 00000000000..8982b2492bd --- /dev/null +++ b/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/Fixture/skip_already_self.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/config/configured_rule.php b/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/config/configured_rule.php new file mode 100644 index 00000000000..cbc790a462f --- /dev/null +++ b/rules-tests/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StaticToSelfOnFinalClassRector::class]); diff --git a/rules-tests/Php55/Rector/Class_/ClassConstantToSelfClassRector/ClassConstantToSelfClassRectorTest.php b/rules-tests/Php55/Rector/Class_/ClassConstantToSelfClassRector/ClassConstantToSelfClassRectorTest.php index bfc5f46bde3..effcfd550c5 100644 --- a/rules-tests/Php55/Rector/Class_/ClassConstantToSelfClassRector/ClassConstantToSelfClassRectorTest.php +++ b/rules-tests/Php55/Rector/Class_/ClassConstantToSelfClassRector/ClassConstantToSelfClassRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php55\Rector\Class_\ClassConstantToSelfClassRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ClassConstantToSelfClassRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php55/Rector/Class_/ClassConstantToSelfClassRector/config/configured_rule.php b/rules-tests/Php55/Rector/Class_/ClassConstantToSelfClassRector/config/configured_rule.php index bc159446be8..79a8814b80a 100644 --- a/rules-tests/Php55/Rector/Class_/ClassConstantToSelfClassRector/config/configured_rule.php +++ b/rules-tests/Php55/Rector/Class_/ClassConstantToSelfClassRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php55\Rector\Class_\ClassConstantToSelfClassRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ClassConstantToSelfClassRector::class); -}; +return RectorConfig::configure() + ->withRules([ClassConstantToSelfClassRector::class]); diff --git a/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/anonymous_class.php.inc b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/anonymous_class.php.inc new file mode 100644 index 00000000000..dbd52fc6f31 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/anonymous_class.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/fixture.php.inc b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..d728e1771a8 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/skip_after_die.php.inc b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/skip_after_die.php.inc new file mode 100644 index 00000000000..9ef8eeb763f --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/Fixture/skip_after_die.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/config/configured_rule.php b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/config/configured_rule.php new file mode 100644 index 00000000000..7b7e57e8589 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([GetCalledClassToSelfClassRector::class]); diff --git a/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/fixture.php.inc b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..75e01b05b04 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/skip_anonymous_class.php.inc new file mode 100644 index 00000000000..378b929de67 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/skip_anonymous_class.php.inc @@ -0,0 +1,10 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/GetCalledClassToStaticClassRectorTest.php b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/GetCalledClassToStaticClassRectorTest.php new file mode 100644 index 00000000000..44d99b25c18 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/GetCalledClassToStaticClassRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/config/configured_rule.php b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/config/configured_rule.php new file mode 100644 index 00000000000..9a4af3742fb --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([GetCalledClassToStaticClassRector::class]); diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/call_function.php.inc b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/call_function.php.inc index a889eeacf10..67b1e3f30a3 100644 --- a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/call_function.php.inc +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/call_function.php.inc @@ -25,7 +25,7 @@ class CallFunction public function run() { $result = preg_replace_callback( - '/\{([<>])([a-zA-Z0-9_]*)(\?{0,1})([a-zA-Z0-9_]*)\}(.*)\{\1\/\2\}/isU', + "/\\{([<>])([a-zA-Z0-9_]*)(\\?{0,1})([a-zA-Z0-9_]*)\\}(.*)\\{\\1\\/\\2\\}/isU", function ($matches) { return CallFunction($matches[1], $matches[2], $matches[3], $matches[4], $matches[5]); }, diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/call_function_variable_hex_back_reference.php.inc b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/call_function_variable_hex_back_reference.php.inc new file mode 100644 index 00000000000..ae1e255f260 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/call_function_variable_hex_back_reference.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/concat_variable.php.inc b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/concat_variable.php.inc new file mode 100644 index 00000000000..f9ee8394238 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/concat_variable.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/curly_parentheses_delimiter.php.inc b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/curly_parentheses_delimiter.php.inc new file mode 100644 index 00000000000..2db46fdd9af --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/curly_parentheses_delimiter.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/direct_dollar_number.php.inc b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/direct_dollar_number.php.inc new file mode 100644 index 00000000000..a69d86496c1 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/direct_dollar_number.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/no_quote_in_back_reference.php.inc b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/no_quote_in_back_reference.php.inc new file mode 100644 index 00000000000..e829f1a798a --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/no_quote_in_back_reference.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/skip_e_inside_regex_no_e_modifier.php.inc b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/skip_e_inside_regex_no_e_modifier.php.inc new file mode 100644 index 00000000000..4b954a96896 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/skip_e_inside_regex_no_e_modifier.php.inc @@ -0,0 +1,16 @@ +_quote_replace($this->left_delimiter) . 'php' + . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'" + . $this->_quote_replace($this->right_delimiter) + . "'" + , $source_content); + } +} diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/with_slashes.php.inc b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/with_slashes.php.inc new file mode 100644 index 00000000000..a0103d22a70 --- /dev/null +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/Fixture/with_slashes.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/PregReplaceEModifierRectorTest.php b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/PregReplaceEModifierRectorTest.php index 6b25f458429..0cba3f81e29 100644 --- a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/PregReplaceEModifierRectorTest.php +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/PregReplaceEModifierRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class PregReplaceEModifierRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/config/configured_rule.php b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/config/configured_rule.php index 20bf1a5a870..922a0a6e6c1 100644 --- a/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/config/configured_rule.php +++ b/rules-tests/Php55/Rector/FuncCall/PregReplaceEModifierRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php55\Rector\FuncCall\PregReplaceEModifierRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(PregReplaceEModifierRector::class); -}; +return RectorConfig::configure() + ->withRules([PregReplaceEModifierRector::class]); diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/cover_enum.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/cover_enum.php.inc new file mode 100644 index 00000000000..d4c10c6b076 --- /dev/null +++ b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/cover_enum.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/fixture.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/fixture.php.inc index 5a819dd25ec..00d1e8dee5e 100644 --- a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/fixture.php.inc +++ b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/fixture.php.inc @@ -8,11 +8,6 @@ class Fixture { return 'Rector\Tests\Php55\Rector\String_\StringClassNameToClassConstantRector\Source\AnotherClass'; } - - public function preSlash() - { - return '\Rector\Tests\Php55\Rector\String_\StringClassNameToClassConstantRector\Source\AnotherClass'; - } } ?> @@ -27,11 +22,6 @@ class Fixture { return \Rector\Tests\Php55\Rector\String_\StringClassNameToClassConstantRector\Source\AnotherClass::class; } - - public function preSlash() - { - return \Rector\Tests\Php55\Rector\String_\StringClassNameToClassConstantRector\Source\AnotherClass::class; - } } ?> diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/include_class_const.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/include_class_const.php.inc new file mode 100644 index 00000000000..2656ff1960d --- /dev/null +++ b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/include_class_const.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/keep_short_class_names.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/keep_short_class_names.php.inc new file mode 100644 index 00000000000..e57d2bc1e35 --- /dev/null +++ b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/keep_short_class_names.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/skip_class_name_case_insensitive.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/skip_class_name_case_insensitive.php.inc new file mode 100644 index 00000000000..ed3a3c023aa --- /dev/null +++ b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/skip_class_name_case_insensitive.php.inc @@ -0,0 +1,11 @@ + ['dd1' => 1, 'dd2' => 2]]; + + for ($i=1; $i <= 2; $i++) { + ${'field'.$i} = $employee->data['dd'.$i]; + } + } + + public function foo() + { + $dd1 = 1; + + for ($i=1; $i <= 2; $i++) { + ${'field'.$i} = ${'dd'.$i}; + } + } +} diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/skip_in_array_constant.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/skip_in_array_constant.php.inc deleted file mode 100644 index b1cd9f302ce..00000000000 --- a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Fixture/skip_in_array_constant.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - +----- + diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/import_of_just_replaced_classes.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/import_of_just_replaced_classes.php.inc deleted file mode 100644 index ceeccf05f40..00000000000 --- a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/import_of_just_replaced_classes.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/skip_conflicting_annotation_import.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/skip_conflicting_annotation_import.php.inc deleted file mode 100644 index f18d13e1f0e..00000000000 --- a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/skip_conflicting_annotation_import.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -view; - } -} diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/skip_conflicting_param_docblock_and_create_php.php.inc b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/skip_conflicting_param_docblock_and_create_php.php.inc deleted file mode 100644 index bd0efef7bd5..00000000000 --- a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/FixtureImport/skip_conflicting_param_docblock_and_create_php.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureImport'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/import_config.php'; - } -} diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Source/Nested/AnotherClass.php b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Source/Nested/AnotherClass.php new file mode 100644 index 00000000000..a70b21dcf08 --- /dev/null +++ b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/Source/Nested/AnotherClass.php @@ -0,0 +1,10 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/config/configured_rule.php b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/config/configured_rule.php index d14cf892862..9005b51b350 100644 --- a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/config/configured_rule.php +++ b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/config/configured_rule.php @@ -2,10 +2,10 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StringClassNameToClassConstantRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(StringClassNameToClassConstantRector::class, ['Nette\*', 'Error', 'Exception']); }; diff --git a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/config/import_config.php b/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/config/import_config.php deleted file mode 100644 index 13607493f0d..00000000000 --- a/rules-tests/Php55/Rector/String_/StringClassNameToClassConstantRector/config/import_config.php +++ /dev/null @@ -1,15 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(StringClassNameToClassConstantRector::class); -}; diff --git a/rules-tests/Php56/Rector/FuncCall/PowToExpRector/Fixture/fixture.php.inc b/rules-tests/Php56/Rector/FuncCall/PowToExpRector/Fixture/fixture.php.inc index 9adbc31dafa..4d75e608320 100644 --- a/rules-tests/Php56/Rector/FuncCall/PowToExpRector/Fixture/fixture.php.inc +++ b/rules-tests/Php56/Rector/FuncCall/PowToExpRector/Fixture/fixture.php.inc @@ -8,8 +8,6 @@ function powToExp() $result = pow(1.2, 2.3); - echo pow(-2, 3); - pow($a--, ++$b); \a\pow(5, 6); @@ -38,9 +36,7 @@ function powToExp() $result = 1.2 ** 2.3; - echo (-2) ** 3; - - ($a--) ** (++$b); + $a-- ** ++$b; \a\pow(5, 6); 7 ** 8; diff --git a/rules-tests/Php56/Rector/FuncCall/PowToExpRector/Fixture/with_minus.php.inc b/rules-tests/Php56/Rector/FuncCall/PowToExpRector/Fixture/with_minus.php.inc new file mode 100644 index 00000000000..bfb32cf1297 --- /dev/null +++ b/rules-tests/Php56/Rector/FuncCall/PowToExpRector/Fixture/with_minus.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Php56/Rector/FuncCall/PowToExpRector/PowToExpRectorTest.php b/rules-tests/Php56/Rector/FuncCall/PowToExpRector/PowToExpRectorTest.php index 0b79ae65f3d..08753781c69 100644 --- a/rules-tests/Php56/Rector/FuncCall/PowToExpRector/PowToExpRectorTest.php +++ b/rules-tests/Php56/Rector/FuncCall/PowToExpRector/PowToExpRectorTest.php @@ -5,8 +5,8 @@ namespace Rector\Tests\Php56\Rector\FuncCall\PowToExpRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * Some tests copied from: @@ -14,20 +14,15 @@ */ final class PowToExpRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php56/Rector/FuncCall/PowToExpRector/config/configured_rule.php b/rules-tests/Php56/Rector/FuncCall/PowToExpRector/config/configured_rule.php index e69781183cc..f40e2ec35b0 100644 --- a/rules-tests/Php56/Rector/FuncCall/PowToExpRector/config/configured_rule.php +++ b/rules-tests/Php56/Rector/FuncCall/PowToExpRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php56\Rector\FuncCall\PowToExpRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(PowToExpRector::class); -}; +return RectorConfig::configure() + ->withRules([PowToExpRector::class]); diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/AddDefaultValueForUndefinedVariableRectorTest.php b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/AddDefaultValueForUndefinedVariableRectorTest.php deleted file mode 100644 index 00200572f99..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/AddDefaultValueForUndefinedVariableRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/fixture.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/fixture.php.inc deleted file mode 100644 index ec77bb712cd..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/in_foreach.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/in_foreach.php.inc deleted file mode 100644 index 321f69cc7ce..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/in_foreach.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/keep_vimeo_unset.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/keep_vimeo_unset.php.inc deleted file mode 100644 index 8f5bc574cf2..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/keep_vimeo_unset.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - $value1, - 'value_2' => $value2, - ]; - }, $lines); - } -} diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_foreach_assign.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_foreach_assign.php.inc deleted file mode 100644 index a53ab15d1a9..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_foreach_assign.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -$collectionArray; - } -} diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_take_static_into_account.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_take_static_into_account.php.inc deleted file mode 100644 index 85d218642a2..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_take_static_into_account.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -val; -}; - -$closure = \Closure::bind($fn, new SkipThisAssign(), '\Rector\Tests\Php56\Rector\FunctionLike\AddDefaultValueForUndefinedVariableRector\Fixture\SkipThisAssign'); diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_unset.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_unset.php.inc deleted file mode 100644 index 9a9e6ad7abb..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/skip_unset.php.inc +++ /dev/null @@ -1,18 +0,0 @@ - -

Let's insert stuff here.

- diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/undefined_array.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/undefined_array.php.inc deleted file mode 100644 index c41e6316ecd..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/undefined_array.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_else.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_else.php.inc deleted file mode 100644 index 7daec61e942..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_else.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_one.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_one.php.inc deleted file mode 100644 index 4f3845edb7e..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_one.php.inc +++ /dev/null @@ -1,56 +0,0 @@ - ------ - diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_two.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_two.php.inc deleted file mode 100644 index 9949f39fd9a..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Fixture/vimeo_two.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/FixturePhp74/arrow_function.php.inc b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/FixturePhp74/arrow_function.php.inc deleted file mode 100644 index 3395a365ad3..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/FixturePhp74/arrow_function.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - $a + $b + $c; - - return $func(2); -} - -?> ------ - $a + $b + $c; - - return $func(2); -} - -?> diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Php74Test.php b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Php74Test.php deleted file mode 100644 index 2888fc69fcf..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/Php74Test.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/config/configured_rule.php b/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/config/configured_rule.php deleted file mode 100644 index 6ef17666368..00000000000 --- a/rules-tests/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(AddDefaultValueForUndefinedVariableRector::class); -}; diff --git a/rules-tests/Php70/EregToPcreTransformerTest.php b/rules-tests/Php70/EregToPcreTransformerTest.php index 599f055dfa6..dfe89fbda98 100644 --- a/rules-tests/Php70/EregToPcreTransformerTest.php +++ b/rules-tests/Php70/EregToPcreTransformerTest.php @@ -5,6 +5,7 @@ namespace Rector\Tests\Php70; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Rector\Php70\EregToPcreTransformer; @@ -17,10 +18,8 @@ protected function setUp(): void $this->eregToPcreTransformer = new EregToPcreTransformer(); } - /** - * @dataProvider provideDataDropping() - * @dataProvider provideDataCaseSensitive() - */ + #[DataProvider('provideDataDropping')] + #[DataProvider('provideDataCaseSensitive')] public function testCaseSensitive(string $ereg, string $expectedPcre): void { $pcre = $this->eregToPcreTransformer->transform($ereg, false); @@ -30,14 +29,12 @@ public function testCaseSensitive(string $ereg, string $expectedPcre): void /** * @return Iterator */ - public function provideDataCaseSensitive(): Iterator + public static function provideDataCaseSensitive(): Iterator { yield ['hi', '#hi#m']; } - /** - * @dataProvider provideDataCaseInsensitive() - */ + #[DataProvider('provideDataCaseInsensitive')] public function testCaseInsensitive(string $ereg, string $expectedPcre): void { $pcre = $this->eregToPcreTransformer->transform($ereg, true); @@ -47,7 +44,7 @@ public function testCaseInsensitive(string $ereg, string $expectedPcre): void /** * @return Iterator */ - public function provideDataCaseInsensitive(): Iterator + public static function provideDataCaseInsensitive(): Iterator { yield ['hi', '#hi#mi']; } @@ -55,7 +52,7 @@ public function provideDataCaseInsensitive(): Iterator /** * @return Iterator */ - public function provideDataDropping(): Iterator + public static function provideDataDropping(): Iterator { yield ['mearie\.org', '#mearie\.org#m']; yield ['mearie[.,]org', '#mearie[\.,]org#m']; diff --git a/rules-tests/Php70/Rector/Assign/ListSplitStringRector/ListSplitStringRectorTest.php b/rules-tests/Php70/Rector/Assign/ListSplitStringRector/ListSplitStringRectorTest.php index f4d3af08757..da325aea88f 100644 --- a/rules-tests/Php70/Rector/Assign/ListSplitStringRector/ListSplitStringRectorTest.php +++ b/rules-tests/Php70/Rector/Assign/ListSplitStringRector/ListSplitStringRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\Assign\ListSplitStringRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ListSplitStringRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/Assign/ListSplitStringRector/config/configured_rule.php b/rules-tests/Php70/Rector/Assign/ListSplitStringRector/config/configured_rule.php index 6311ad6ce52..5260a1d4e73 100644 --- a/rules-tests/Php70/Rector/Assign/ListSplitStringRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/Assign/ListSplitStringRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\Assign\ListSplitStringRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ListSplitStringRector::class); -}; +return RectorConfig::configure() + ->withRules([ListSplitStringRector::class]); diff --git a/rules-tests/Php70/Rector/Assign/ListSwapArrayOrderRector/ListSwapArrayOrderRectorTest.php b/rules-tests/Php70/Rector/Assign/ListSwapArrayOrderRector/ListSwapArrayOrderRectorTest.php index 85e4bbf277a..a3cdbdc8ccd 100644 --- a/rules-tests/Php70/Rector/Assign/ListSwapArrayOrderRector/ListSwapArrayOrderRectorTest.php +++ b/rules-tests/Php70/Rector/Assign/ListSwapArrayOrderRector/ListSwapArrayOrderRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\Assign\ListSwapArrayOrderRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ListSwapArrayOrderRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/Assign/ListSwapArrayOrderRector/config/configured_rule.php b/rules-tests/Php70/Rector/Assign/ListSwapArrayOrderRector/config/configured_rule.php index 161bb9162e8..276a2b2869e 100644 --- a/rules-tests/Php70/Rector/Assign/ListSwapArrayOrderRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/Assign/ListSwapArrayOrderRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\Assign\ListSwapArrayOrderRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ListSwapArrayOrderRector::class); -}; +return RectorConfig::configure() + ->withRules([ListSwapArrayOrderRector::class]); diff --git a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/BreakNotInLoopOrSwitchToReturnRectorTest.php b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/BreakNotInLoopOrSwitchToReturnRectorTest.php index 912de8a8081..e00c6bb1d61 100644 --- a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/BreakNotInLoopOrSwitchToReturnRectorTest.php +++ b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/BreakNotInLoopOrSwitchToReturnRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class BreakNotInLoopOrSwitchToReturnRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/fixture.php.inc b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/fixture.php.inc index 23dbcacfdd2..beabce5d763 100644 --- a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/fixture.php.inc +++ b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/fixture.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector\Fixture; -function break_missplaced_function() +function break_misplaced_function() { $zhrs = abs($gmt)/3600; $hrs = floor($zhrs); @@ -13,7 +13,7 @@ function break_missplaced_function() break; } -function break_missplace_again() +function break_misplace_again() { $errors = []; if (isset($errors)) { @@ -32,7 +32,7 @@ function break_missplace_again() namespace Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector\Fixture; -function break_missplaced_function() +function break_misplaced_function() { $zhrs = abs($gmt)/3600; $hrs = floor($zhrs); @@ -42,7 +42,7 @@ function break_missplaced_function() return sprintf('%s%02d%02d',($gmt<0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60); } -function break_missplace_again() +function break_misplace_again() { $errors = []; if (isset($errors)) { diff --git a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/foreach_not.php.inc b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/foreach_not.php.inc index 09eceaee15c..74c31989424 100644 --- a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/foreach_not.php.inc +++ b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/foreach_not.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector\Fixture; -function break_missplaced_function_foreach() +function break_misplaced_function_foreach() { $ip_block = []; @@ -25,7 +25,7 @@ function break_missplaced_function_foreach() namespace Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector\Fixture; -function break_missplaced_function_foreach() +function break_misplaced_function_foreach() { $ip_block = []; diff --git a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/return.php.inc b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/return.php.inc index 2f87e38c04c..148325049ad 100644 --- a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/return.php.inc +++ b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/Fixture/return.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector\Fixture; -function break_missplaced_return() +function break_misplaced_return() { if (true == $objWebsite ) { $arrobjProperties = (array) $objWebsite->fetchProperties( ); @@ -21,7 +21,7 @@ function break_missplaced_return() namespace Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector\Fixture; -function break_missplaced_return() +function break_misplaced_return() { if (true == $objWebsite ) { $arrobjProperties = (array) $objWebsite->fetchProperties( ); diff --git a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/config/configured_rule.php b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/config/configured_rule.php index 462c6cad6d9..aa5bd88d931 100644 --- a/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(BreakNotInLoopOrSwitchToReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([BreakNotInLoopOrSwitchToReturnRector::class]); diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/delegating.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/delegating.php.inc index 9a3f40a6943..ad0c0f56278 100644 --- a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/delegating.php.inc +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/delegating.php.inc @@ -2,6 +2,8 @@ declare(strict_types=1); +// PHP4 code is not allowed to contain namespaces + final class DelegatingPhp4ConstructorClass { /** @@ -33,6 +35,8 @@ final class DelegatingPhp4ConstructorClass declare(strict_types=1); +// PHP4 code is not allowed to contain namespaces + final class DelegatingPhp4ConstructorClass { /** diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/delegating_2.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/delegating_2.php.inc index 724813e4467..a29176a8ab2 100644 --- a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/delegating_2.php.inc +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/delegating_2.php.inc @@ -2,6 +2,8 @@ declare(strict_types=1); +// PHP4 code is not allowed to contain namespaces + final class DelegatingPhp4ConstructorClassAgain { /** @@ -28,6 +30,8 @@ final class DelegatingPhp4ConstructorClassAgain declare(strict_types=1); +// PHP4 code is not allowed to contain namespaces + final class DelegatingPhp4ConstructorClassAgain { /** diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/fixture.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/fixture.php.inc index 8c47ffc485f..f93ce1330f6 100644 --- a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/fixture.php.inc +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/fixture.php.inc @@ -1,5 +1,7 @@ ----- diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/just_for.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/just_for.php.inc index 6550c4bb344..c44e59f4055 100644 --- a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/just_for.php.inc +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/just_for.php.inc @@ -2,6 +2,8 @@ declare(strict_types=1); +// PHP4 code is not allowed to contain namespaces + final class JustFor { public function JustFor() @@ -17,6 +19,8 @@ final class JustFor declare(strict_types=1); +// PHP4 code is not allowed to contain namespaces + final class JustFor { public function __construct() diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/method_call_method_exists.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/method_call_method_exists.php.inc new file mode 100644 index 00000000000..6367edb761d --- /dev/null +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/method_call_method_exists.php.inc @@ -0,0 +1,43 @@ +SomeParentA2(); + } + + public function SomeParentA2() + { + } +} + +?> +----- +SomeParentA2(); + } + + public function SomeParentA2() + { + } +} + +?> diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/method_call_parent.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/method_call_parent.php.inc new file mode 100644 index 00000000000..d891e4384dc --- /dev/null +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/method_call_parent.php.inc @@ -0,0 +1,35 @@ +SomeParentA(); + } +} + +?> +----- + diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/parent_constructor_call.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/parent_constructor_call.php.inc new file mode 100644 index 00000000000..50c04114d35 --- /dev/null +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/parent_constructor_call.php.inc @@ -0,0 +1,23 @@ +ParentClass(); + } +} + +?> +----- + diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/pool.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/pool.php.inc new file mode 100644 index 00000000000..1f2401ad72e --- /dev/null +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/pool.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/skip_only_other_method.php.inc b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/skip_only_other_method.php.inc new file mode 100644 index 00000000000..e25c2afd871 --- /dev/null +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/skip_only_other_method.php.inc @@ -0,0 +1,10 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Source/ParentClass.php b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Source/ParentClass.php new file mode 100644 index 00000000000..ec6dc35cfb7 --- /dev/null +++ b/rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Source/ParentClass.php @@ -0,0 +1,8 @@ +services(); - $services->set(Php4ConstructorRector::class); -}; +return RectorConfig::configure() + ->withRules([Php4ConstructorRector::class]); diff --git a/rules-tests/Php70/Rector/FuncCall/CallUserMethodRector/CallUserMethodRectorTest.php b/rules-tests/Php70/Rector/FuncCall/CallUserMethodRector/CallUserMethodRectorTest.php index ce8550432c4..b2bcac6a19a 100644 --- a/rules-tests/Php70/Rector/FuncCall/CallUserMethodRector/CallUserMethodRectorTest.php +++ b/rules-tests/Php70/Rector/FuncCall/CallUserMethodRector/CallUserMethodRectorTest.php @@ -5,28 +5,23 @@ namespace Rector\Tests\Php70\Rector\FuncCall\CallUserMethodRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * @see https://www.mail-archive.com/php-dev@lists.php.net/msg11576.html */ final class CallUserMethodRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/FuncCall/CallUserMethodRector/config/configured_rule.php b/rules-tests/Php70/Rector/FuncCall/CallUserMethodRector/config/configured_rule.php index 8c0c34eacfc..9ca2ec18ad5 100644 --- a/rules-tests/Php70/Rector/FuncCall/CallUserMethodRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/FuncCall/CallUserMethodRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\FuncCall\CallUserMethodRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CallUserMethodRector::class); -}; +return RectorConfig::configure() + ->withRules([CallUserMethodRector::class]); diff --git a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/EregToPregMatchRectorTest.php b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/EregToPregMatchRectorTest.php index 4724cd9133f..6e2472d7cae 100644 --- a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/EregToPregMatchRectorTest.php +++ b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/EregToPregMatchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\FuncCall\EregToPregMatchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class EregToPregMatchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/empty_branch.php.inc b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/empty_branch.php.inc new file mode 100644 index 00000000000..8437983f82a --- /dev/null +++ b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/empty_branch.php.inc @@ -0,0 +1,27 @@ +'; + } +} + +?> +----- +'; + } +} + +?> diff --git a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/fixture3.php.inc b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/fixture3.php.inc index 24dd5b04a7e..29891883b7e 100644 --- a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/fixture3.php.inc +++ b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/fixture3.php.inc @@ -9,6 +9,8 @@ function eregToPregMatch3() split('hi', 'hi, she said', 0); spliti('hi', 'hi, she said', 1); + + split("\r\n", "line 1\r\nline 2\r\n"); } ?> @@ -24,6 +26,8 @@ function eregToPregMatch3() preg_split('#hi#m', 'hi, she said', 1); preg_split('#hi#mi', 'hi, she said', 1); + + preg_split("#\r\n#m", "line 1\r\nline 2\r\n"); } ?> diff --git a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/fixture5.php.inc b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/fixture5.php.inc new file mode 100644 index 00000000000..d48d0e1781a --- /dev/null +++ b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/fixture5.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/with_delimiter_char_in_middle.php.inc b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/with_delimiter_char_in_middle.php.inc new file mode 100644 index 00000000000..0286ac30ffb --- /dev/null +++ b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/with_delimiter_char_in_middle.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/with_delimiter_char_in_middle2.php.inc b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/with_delimiter_char_in_middle2.php.inc new file mode 100644 index 00000000000..39227185858 --- /dev/null +++ b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/Fixture/with_delimiter_char_in_middle2.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/config/configured_rule.php b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/config/configured_rule.php index 2de5daed2b8..d23122219d4 100644 --- a/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/FuncCall/EregToPregMatchRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\FuncCall\EregToPregMatchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(EregToPregMatchRector::class); -}; +return RectorConfig::configure() + ->withRules([EregToPregMatchRector::class]); diff --git a/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/Fixture/fixture.php.inc b/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/Fixture/fixture.php.inc index cd65e502deb..3799a0aa042 100644 --- a/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/Fixture/fixture.php.inc +++ b/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/Fixture/fixture.php.inc @@ -7,16 +7,6 @@ function multiDirname() dirname(dirname($path)); new dirname(dirname(dirname($path))); - - -// untouched - dirname(dirname($path, $level)); - - dirname("foo/" . dirname($path)); - - dirname(dirname($path) . $foo); - - foo\dirname(dirname($path)); } ?> @@ -30,16 +20,6 @@ function multiDirname() dirname($path, 2); new dirname(dirname($path, 2)); - - -// untouched - dirname(dirname($path, $level)); - - dirname("foo/" . dirname($path)); - - dirname(dirname($path) . $foo); - - foo\dirname(dirname($path)); } ?> diff --git a/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/Fixture/skip.php.inc b/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/Fixture/skip.php.inc new file mode 100644 index 00000000000..57d0da0c4d3 --- /dev/null +++ b/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/Fixture/skip.php.inc @@ -0,0 +1,22 @@ + diff --git a/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/MultiDirnameRectorTest.php b/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/MultiDirnameRectorTest.php index 14f7169f51b..b6a9484aa04 100644 --- a/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/MultiDirnameRectorTest.php +++ b/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/MultiDirnameRectorTest.php @@ -5,8 +5,8 @@ namespace Rector\Tests\Php70\Rector\FuncCall\MultiDirnameRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * Some tests copied from: @@ -14,20 +14,15 @@ */ final class MultiDirnameRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/config/configured_rule.php b/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/config/configured_rule.php index 2fca1204e18..ca5185de414 100644 --- a/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/FuncCall/MultiDirnameRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\FuncCall\MultiDirnameRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(MultiDirnameRector::class); -}; +return RectorConfig::configure() + ->withRules([MultiDirnameRector::class]); diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/anonymous_class.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/anonymous_class.php.inc deleted file mode 100644 index 7d80351f690..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/anonymous_class.php.inc +++ /dev/null @@ -1,28 +0,0 @@ -bar(baz()); -} - -?> ------ -bar($baz); -} - -?> diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/anonymous_function.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/anonymous_function.php.inc deleted file mode 100644 index 6f2f66f9f22..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/anonymous_function.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/array_with_func_calls.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/array_with_func_calls.php.inc deleted file mode 100644 index be20e7da84f..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/array_with_func_calls.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/assignment.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/assignment.php.inc deleted file mode 100644 index 475c74fe0b2..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/assignment.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -bar); - reset($var = \stdClass::$bar); -} - -?> ------ -bar; - reset($var); - $var = \stdClass::$bar; - reset($var); -} - -?> diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/binary_op.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/binary_op.php.inc deleted file mode 100644 index 5545e5abc3c..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/binary_op.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/func_calls.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/func_calls.php.inc deleted file mode 100644 index e6518a3d02f..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/func_calls.php.inc +++ /dev/null @@ -1,46 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/invokable.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/invokable.php.inc deleted file mode 100644 index 39ed7a1c292..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/invokable.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_array_dim_fetch.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_array_dim_fetch.php.inc deleted file mode 100644 index 92f4882cb71..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_array_dim_fetch.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -models as $model) { - $methods = (new \ReflectionClass($model))->getMethods(); - - $model = new $model(); - - foreach ($methods as $method) { - $model->{$method->getName()}(); - } - } - } -} diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_property_fetch.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_property_fetch.php.inc deleted file mode 100644 index 90f4c443e7c..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_property_fetch.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -dummy); -} diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_static_property_fetch.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_static_property_fetch.php.inc deleted file mode 100644 index bee37e2f977..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/keep_static_property_fetch.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -bar()); - reset((new \stdClass())->bar()->baz()); -} - -?> ------ -bar(); - reset($bar); - $baz = (new \stdClass())->bar()->baz(); - reset($baz); -} - -?> diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/method_calls.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/method_calls.php.inc deleted file mode 100644 index 56af4dbf73f..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/method_calls.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -baz(baz()); - $aClass->child()->bar(bar()); - } -} - -?> ------ -baz($baz); - $bar2 = bar(); - $aClass->child()->bar($bar2); - } -} - -?> diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/new.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/new.php.inc deleted file mode 100644 index e3922f7f65b..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/new.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/reset_in_condition.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/reset_in_condition.php.inc deleted file mode 100644 index 3a73f89d302..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/reset_in_condition.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/reset_over_static_call.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/reset_over_static_call.php.inc deleted file mode 100644 index f2dec7d734f..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/reset_over_static_call.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/skip_optional_parameter.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/skip_optional_parameter.php.inc deleted file mode 100644 index 53d806b8143..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/skip_optional_parameter.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/stringy_calls.php.inc b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/stringy_calls.php.inc deleted file mode 100644 index 1019daeace7..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Fixture/stringy_calls.php.inc +++ /dev/null @@ -1,46 +0,0 @@ - ------ - diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/NonVariableToVariableOnFunctionCallRectorTest.php b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/NonVariableToVariableOnFunctionCallRectorTest.php deleted file mode 100644 index 7783084b407..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/NonVariableToVariableOnFunctionCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Source/ArrayCallable.php b/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Source/ArrayCallable.php deleted file mode 100644 index bb480d455f3..00000000000 --- a/rules-tests/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector/Source/ArrayCallable.php +++ /dev/null @@ -1,16 +0,0 @@ -services(); - $services->set(NonVariableToVariableOnFunctionCallRector::class); -}; diff --git a/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/Fixture/fixture.php.inc b/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/Fixture/fixture.php.inc index 7757f1987c8..614ccacef92 100644 --- a/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/Fixture/fixture.php.inc +++ b/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/Fixture/fixture.php.inc @@ -55,7 +55,7 @@ function randomFunction() random_int($a, \Other\Scope\mt_rand($a)); - $a = random_int(1, 2) + random_int(3, 4); + $a = random_int(1, 2) + mt_rand(3, 4); } ?> diff --git a/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/Fixture/swap_min_higher.php.inc b/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/Fixture/swap_min_higher.php.inc new file mode 100644 index 00000000000..f0d6c1ccd27 --- /dev/null +++ b/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/Fixture/swap_min_higher.php.inc @@ -0,0 +1,21 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/RandomFunctionRectorTest.php b/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/RandomFunctionRectorTest.php index 2acf84db0ed..4f8f26d1184 100644 --- a/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/RandomFunctionRectorTest.php +++ b/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/RandomFunctionRectorTest.php @@ -5,28 +5,23 @@ namespace Rector\Tests\Php70\Rector\FuncCall\RandomFunctionRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * Some tests copied from https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.12/tests/Fixer/Alias/RandomApiMigrationFixerTest.php */ final class RandomFunctionRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/config/configured_rule.php b/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/config/configured_rule.php index db16f72ff88..4072353a3a3 100644 --- a/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/FuncCall/RandomFunctionRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\FuncCall\RandomFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RandomFunctionRector::class); -}; +return RectorConfig::configure() + ->withRules([RandomFunctionRector::class]); diff --git a/rules-tests/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector/RenameMktimeWithoutArgsToTimeRectorTest.php b/rules-tests/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector/RenameMktimeWithoutArgsToTimeRectorTest.php index 2c3a669b17b..509f05a130c 100644 --- a/rules-tests/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector/RenameMktimeWithoutArgsToTimeRectorTest.php +++ b/rules-tests/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector/RenameMktimeWithoutArgsToTimeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\FuncCall\RenameMktimeWithoutArgsToTimeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameMktimeWithoutArgsToTimeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector/config/configured_rule.php b/rules-tests/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector/config/configured_rule.php index 4862ea20505..9c4ffef0145 100644 --- a/rules-tests/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\FuncCall\RenameMktimeWithoutArgsToTimeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameMktimeWithoutArgsToTimeRector::class); -}; +return RectorConfig::configure() + ->withRules([RenameMktimeWithoutArgsToTimeRector::class]); diff --git a/rules-tests/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector/ExceptionHandlerTypehintRectorTest.php b/rules-tests/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector/ExceptionHandlerTypehintRectorTest.php index 6cc2d0271b2..66c299d8d46 100644 --- a/rules-tests/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector/ExceptionHandlerTypehintRectorTest.php +++ b/rules-tests/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector/ExceptionHandlerTypehintRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ExceptionHandlerTypehintRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector/config/configured_rule.php b/rules-tests/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector/config/configured_rule.php index 094e1f14215..d96cd77e17a 100644 --- a/rules-tests/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ExceptionHandlerTypehintRector::class); -}; +return RectorConfig::configure() + ->withRules([ExceptionHandlerTypehintRector::class]); diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/ascending.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/ascending.php.inc new file mode 100644 index 00000000000..c55ce0af9a4 --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/ascending.php.inc @@ -0,0 +1,71 @@ + $b[0]) ? 1 : -1; + } + }); + + usort($languages, function ($a, $b) { + if ($a[0] === $b[0]) { + return 0; + } + return ($a[0] > $b[0]) ? 1 : -1; + }); + } +} + +?> +----- + $b[0]; + }); + + usort($languages, function ($a, $b) { + return $a[0] <=> $b[0]; + }); + + usort($languages, function ($a, $b) { + return $a[0] <=> $b[0]; + }); + + usort($languages, function ($a, $b) { + return $a[0] <=> $b[0]; + }); + } +} + +?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/ascending_flip.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/ascending_flip.php.inc new file mode 100644 index 00000000000..127e0bfa3b2 --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/ascending_flip.php.inc @@ -0,0 +1,37 @@ + +----- + $ascendingSecond[0]; + }); + + } +} + +?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/complex.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/complex.php.inc index 2ad18b33aec..15c548f8ccc 100644 --- a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/complex.php.inc +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/complex.php.inc @@ -21,6 +21,14 @@ class Complex return ($a[0] > $b[0]) ? -1 : 1; } }); + + usort($languages, function($a, $b) { + if (count($a) === count($b)) { + return 0; + } + + return count($a) > count($b) ? -1 : 1; + }); } } @@ -39,7 +47,11 @@ class Complex }); usort($languages, function ($a, $b) { - return $a[0] <=> $b[0]; + return $b[0] <=> $a[0]; + }); + + usort($languages, function ($a, $b) { + return count($b) <=> count($a); }); } } diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/descending.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/descending.php.inc new file mode 100644 index 00000000000..4978b06631f --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/descending.php.inc @@ -0,0 +1,71 @@ + $b[0]) ? -1 : 1; + } + }); + + usort($languages, function ($a, $b) { + if ($a[0] === $b[0]) { + return 0; + } + return ($a[0] > $b[0]) ? -1 : 1; + }); + } +} + +?> +----- + $a[0]; + }); + + usort($languages, function ($a, $b) { + return $b[0] <=> $a[0]; + }); + + usort($languages, function ($a, $b) { + return $b[0] <=> $a[0]; + }); + + usort($languages, function ($a, $b) { + return $b[0] <=> $a[0]; + }); + } +} + +?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/descending_flip.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/descending_flip.php.inc new file mode 100644 index 00000000000..fa807fd38d8 --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/descending_flip.php.inc @@ -0,0 +1,35 @@ + +----- + $firstLanguage[0]; + }); + } +} + +?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/do_not_take_side_effect.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/do_not_take_side_effect.php.inc new file mode 100644 index 00000000000..9bd5ff94782 --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/do_not_take_side_effect.php.inc @@ -0,0 +1,49 @@ + $b['bar'] ) ? -1 : 1; + } + } + +} + +?> +----- + $a['bar']; + } + +} + +?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/fixture.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/fixture.php.inc index 987a096b8ee..bfa28c99fc8 100644 --- a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/fixture.php.inc +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/fixture.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\Php70\Rector\If_\IfToSpaceshipRector\Fixture; class Fixture { - public function run() + public function run(array $languages) { usort($languages, function ($a, $b) { if ($a[0] == $b[0]) { @@ -14,28 +14,6 @@ class Fixture return ($a[0] < $b[0]) ? 1 : -1; }); } - - public function runAgain() - { - usort($languages, function ($a, $b) { - if ($b[0] === $a[0]) { - return 0; - } - - return ($a[0] < $b[0]) ? 1 : -1; - }); - } - - public function runOneMore() - { - usort($languages, function ($a, $b) { - if ($b[0] === $a[0]) { - return 0; - } - - return ($a[0] > $b[0]) ? -1 : 1; - }); - } } ?> @@ -46,26 +24,12 @@ namespace Rector\Tests\Php70\Rector\If_\IfToSpaceshipRector\Fixture; class Fixture { - public function run() - { - usort($languages, function ($a, $b) { - return $b[0] <=> $a[0]; - }); - } - - public function runAgain() + public function run(array $languages) { usort($languages, function ($a, $b) { return $b[0] <=> $a[0]; }); } - - public function runOneMore() - { - usort($languages, function ($a, $b) { - return $a[0] <=> $b[0]; - }); - } } ?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse.php.inc new file mode 100644 index 00000000000..32d2bd0a22f --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse.php.inc @@ -0,0 +1,49 @@ + $b[0]) ? 1 : -1; + } + }); + + usort($languages, function ($a, $b) { + if ($a[0] === $b[0]) { + return 0; + } else { + return ($a[0] < $b[0]) ? -1 : 1; + } + }); + } +} + +?> +----- + $b[0]; + }); + + usort($languages, function ($a, $b) { + return $a[0] <=> $b[0]; + }); + } +} + +?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse2.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse2.php.inc new file mode 100644 index 00000000000..ded73fd6d70 --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse2.php.inc @@ -0,0 +1,37 @@ + +----- + $b[0]; + }); + } +} + +?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse3.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse3.php.inc new file mode 100644 index 00000000000..936a6e12157 --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/reverse3.php.inc @@ -0,0 +1,49 @@ + $b[0]) ? -1 : 1; + }); + + usort($languages, function ($a, $b) { + if ($a[0] === $b[0]) { + return 0; + } else { + return ($a[0] > $b[0]) ? -1 : 1; + } + }); + } +} + +?> +----- + $a[0]; + }); + + usort($languages, function ($a, $b) { + return $b[0] <=> $a[0]; + }); + } +} + +?> diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/skip.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/skip.php.inc index 0575066f820..688721cd4b9 100644 --- a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/skip.php.inc +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/skip.php.inc @@ -30,14 +30,6 @@ class Skip return ($a[0] <= $b[0]) ? 1 : -1; }); - usort($languages, function ($a, $b) { - if ($a[0] === $b[0]) { - return 0; - } - - return ($a[0] < $b[0]) ? -1 : 1; - }); - usort($languages, function ($a, $b) { if ($a[0] === $b[0]) { return 0; diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/skip_crash_with_enum.php.inc b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/skip_crash_with_enum.php.inc new file mode 100644 index 00000000000..ad03167db83 --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Fixture/skip_crash_with_enum.php.inc @@ -0,0 +1,21 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Source/Status.php b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Source/Status.php new file mode 100644 index 00000000000..928f5ac370e --- /dev/null +++ b/rules-tests/Php70/Rector/If_/IfToSpaceshipRector/Source/Status.php @@ -0,0 +1,10 @@ +services(); - $services->set(IfToSpaceshipRector::class); -}; +return RectorConfig::configure() + ->withRules([IfToSpaceshipRector::class]); diff --git a/rules-tests/Php70/Rector/List_/EmptyListRector/EmptyListRectorTest.php b/rules-tests/Php70/Rector/List_/EmptyListRector/EmptyListRectorTest.php index f43c7342bb1..b9c326c02ce 100644 --- a/rules-tests/Php70/Rector/List_/EmptyListRector/EmptyListRectorTest.php +++ b/rules-tests/Php70/Rector/List_/EmptyListRector/EmptyListRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\List_\EmptyListRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class EmptyListRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/List_/EmptyListRector/config/configured_rule.php b/rules-tests/Php70/Rector/List_/EmptyListRector/config/configured_rule.php index 336f43d57db..3bc90cf8056 100644 --- a/rules-tests/Php70/Rector/List_/EmptyListRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/List_/EmptyListRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\List_\EmptyListRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(EmptyListRector::class); -}; +return RectorConfig::configure() + ->withRules([EmptyListRector::class]); diff --git a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..98e30089307 --- /dev/null +++ b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,24 @@ +topLevelDomain(...)), + ]; + } + + public static function topLevelDomain(?string $host = null): string + { + if (null === $host) { + $host = $_SERVER['HTTP_HOST']; + } + + $urlPieces = explode('.', (string) $host); + + return end($urlPieces); + } +} diff --git a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_inside_encapsed.php.inc b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_inside_encapsed.php.inc new file mode 100644 index 00000000000..5fb764731cd --- /dev/null +++ b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_inside_encapsed.php.inc @@ -0,0 +1,13 @@ +getLogDate()}"; + } + + private static function getLogDate(): string { + return 'foo'; + } +} diff --git a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_mark_as_skipped.php.inc b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_mark_as_skipped.php.inc new file mode 100644 index 00000000000..125db0d61cf --- /dev/null +++ b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_mark_as_skipped.php.inc @@ -0,0 +1,13 @@ +markTestSkipped('whatever'); + } +} diff --git a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_named_curly_variable.php.inc b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_named_curly_variable.php.inc new file mode 100644 index 00000000000..48904abcb77 --- /dev/null +++ b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/skip_named_curly_variable.php.inc @@ -0,0 +1,23 @@ + 'test', 'whateverName' => 'macro code example']; + + foreach (array_keys($macros) as $macro) { + if (isset($attributes[$macro])) { + is_array($attributes[$macro]) ? $this->{$macro}($attributes[$macro]) : $this->{$macro}([]); + continue; + } + } + } +} diff --git a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/static_method_in_annotation.php.inc b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/static_method_in_annotation.php.inc deleted file mode 100644 index 51bdd924e47..00000000000 --- a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/Fixture/static_method_in_annotation.php.inc +++ /dev/null @@ -1,49 +0,0 @@ -staticReturnStatic(); - $this->staticReturnMixed(); - $this->withoutReturnType(); - $this->withoutParameters(); - $this->returnStatic(); - } -} - -?> ------ -returnStatic(); - } -} - -?> diff --git a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/ThisCallOnStaticMethodToStaticCallRectorTest.php b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/ThisCallOnStaticMethodToStaticCallRectorTest.php index ccb7773c6bf..90767a45cb5 100644 --- a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/ThisCallOnStaticMethodToStaticCallRectorTest.php +++ b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/ThisCallOnStaticMethodToStaticCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\MethodCall\ThisCallOnStaticMethodToStaticCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ThisCallOnStaticMethodToStaticCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/config/configured_rule.php b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/config/configured_rule.php index 68c20ba5f6a..c0fe3a63484 100644 --- a/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\MethodCall\ThisCallOnStaticMethodToStaticCallRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ThisCallOnStaticMethodToStaticCallRector::class); -}; +return RectorConfig::configure() + ->withRules([ThisCallOnStaticMethodToStaticCallRector::class]); diff --git a/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/allow_custom_magic_method.php.inc b/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/allow_custom_magic_method.php.inc new file mode 100644 index 00000000000..aa244ee7689 --- /dev/null +++ b/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/allow_custom_magic_method.php.inc @@ -0,0 +1,41 @@ + +----- +__customMagicMethod(); + } +} + +?> diff --git a/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/keep_parent_parent_method.php.inc b/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/keep_parent_parent_method.php.inc new file mode 100644 index 00000000000..a9445399139 --- /dev/null +++ b/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/keep_parent_parent_method.php.inc @@ -0,0 +1,26 @@ + +----- +doWork(); + } + + public function doWork() + { + return 'work done'; + } +} + +?> diff --git a/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/skip_abstract_class.php.inc b/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/skip_abstract_class.php.inc new file mode 100644 index 00000000000..6816d2b0b5a --- /dev/null +++ b/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/Fixture/skip_abstract_class.php.inc @@ -0,0 +1,23 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/config/configured_rule.php b/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/config/configured_rule.php index 15ad15711dd..d58fdbff18b 100644 --- a/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StaticCallOnNonStaticToInstanceCallRector::class); -}; +return RectorConfig::configure() + ->withRules([StaticCallOnNonStaticToInstanceCallRector::class]); diff --git a/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/Fixture/some_class.php.inc b/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..adc1b2c84df --- /dev/null +++ b/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/Fixture/some_class.php.inc @@ -0,0 +1,35 @@ +items[$key])) { + return $this->items[$key]; + } + + return 'fallback value'; + } +} + +?> +----- +items[$key] ?? 'fallback value'; + } +} + +?> diff --git a/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/Fixture/with_ternary_return.php.inc b/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/Fixture/with_ternary_return.php.inc new file mode 100644 index 00000000000..cd2b6ddb0cc --- /dev/null +++ b/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/Fixture/with_ternary_return.php.inc @@ -0,0 +1,37 @@ +values[$param])) { + return $this->values[$param]; + } + + return $this->nullable ? null : false; + } +} + +?> +----- +values[$param] ?? ($this->nullable ? null : false); + } +} + +?> diff --git a/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/IfIssetToCoalescingRectorTest.php b/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/IfIssetToCoalescingRectorTest.php new file mode 100644 index 00000000000..af655029023 --- /dev/null +++ b/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/IfIssetToCoalescingRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/config/configured_rule.php b/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/config/configured_rule.php new file mode 100644 index 00000000000..9c88c99f7ae --- /dev/null +++ b/rules-tests/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([IfIssetToCoalescingRector::class]); diff --git a/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/Fixture/hidden_in_middle.php.inc b/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/Fixture/hidden_in_middle.php.inc index a255f6f577c..638172d7f5a 100644 --- a/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/Fixture/hidden_in_middle.php.inc +++ b/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/Fixture/hidden_in_middle.php.inc @@ -8,7 +8,7 @@ function multipleDefaults2() case 'A3': $format = array(841.89,1190.55); break; - case 'A4': default: $format = array(595.28,841.89); + default: $format = array(595.28,841.89); break; case 'A5': $format = array(419.53,595.28); @@ -33,9 +33,6 @@ function multipleDefaults2() case 'A3': $format = array(841.89,1190.55); break; - case 'A4': - $format = array(595.28,841.89); - break; case 'A5': $format = array(419.53,595.28); break; diff --git a/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/ReduceMultipleDefaultSwitchRectorTest.php b/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/ReduceMultipleDefaultSwitchRectorTest.php index d465a5b3c23..5b0d0302543 100644 --- a/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/ReduceMultipleDefaultSwitchRectorTest.php +++ b/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/ReduceMultipleDefaultSwitchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\Switch_\ReduceMultipleDefaultSwitchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReduceMultipleDefaultSwitchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/config/configured_rule.php b/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/config/configured_rule.php index 6d75a3b0180..4d88e965c21 100644 --- a/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\Switch_\ReduceMultipleDefaultSwitchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReduceMultipleDefaultSwitchRector::class); -}; +return RectorConfig::configure() + ->withRules([ReduceMultipleDefaultSwitchRector::class]); diff --git a/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_right_parentheses.php.inc b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_right_parentheses.php.inc new file mode 100644 index 00000000000..909fd0915f6 --- /dev/null +++ b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_right_parentheses.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_right_parentheses_binaryop.php.inc b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_right_parentheses_binaryop.php.inc new file mode 100644 index 00000000000..c8623cb74e5 --- /dev/null +++ b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_right_parentheses_binaryop.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/TernaryToNullCoalescingRectorTest.php b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/TernaryToNullCoalescingRectorTest.php index e20e68ff57c..0233b7cccf4 100644 --- a/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/TernaryToNullCoalescingRectorTest.php +++ b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/TernaryToNullCoalescingRectorTest.php @@ -5,8 +5,8 @@ namespace Rector\Tests\Php70\Rector\Ternary\TernaryToNullCoalescingRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * Some tests copied from: @@ -15,20 +15,15 @@ */ final class TernaryToNullCoalescingRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/config/configured_rule.php b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/config/configured_rule.php index eb7f46ddbde..8654f1a87e0 100644 --- a/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\Ternary\TernaryToNullCoalescingRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(TernaryToNullCoalescingRector::class); -}; +return RectorConfig::configure() + ->withRules([TernaryToNullCoalescingRector::class]); diff --git a/rules-tests/Php70/Rector/Ternary/TernaryToSpaceshipRector/TernaryToSpaceshipRectorTest.php b/rules-tests/Php70/Rector/Ternary/TernaryToSpaceshipRector/TernaryToSpaceshipRectorTest.php index 09f0d32f0c5..0ef94bc8b00 100644 --- a/rules-tests/Php70/Rector/Ternary/TernaryToSpaceshipRector/TernaryToSpaceshipRectorTest.php +++ b/rules-tests/Php70/Rector/Ternary/TernaryToSpaceshipRector/TernaryToSpaceshipRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\Ternary\TernaryToSpaceshipRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class TernaryToSpaceshipRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/Ternary/TernaryToSpaceshipRector/config/configured_rule.php b/rules-tests/Php70/Rector/Ternary/TernaryToSpaceshipRector/config/configured_rule.php index 3e1e48dcb1d..22e40b8a30a 100644 --- a/rules-tests/Php70/Rector/Ternary/TernaryToSpaceshipRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/Ternary/TernaryToSpaceshipRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\Ternary\TernaryToSpaceshipRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(TernaryToSpaceshipRector::class); -}; +return RectorConfig::configure() + ->withRules([TernaryToSpaceshipRector::class]); diff --git a/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/Fixture/array_dim_fetch_with_object.php.inc b/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/Fixture/array_dim_fetch_with_object.php.inc new file mode 100644 index 00000000000..46225d43c00 --- /dev/null +++ b/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/Fixture/array_dim_fetch_with_object.php.inc @@ -0,0 +1,41 @@ + new stdClass() + ]; + + echo $$foo['bar']; + } +} + +?> +----- + new stdClass() + ]; + + echo ${$foo}['bar']; + } +} + +?> diff --git a/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/Fixture/skip_wrapped_array_dim_fetch_variable_variables.php.inc b/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/Fixture/skip_wrapped_array_dim_fetch_variable_variables.php.inc new file mode 100644 index 00000000000..ff8f26e4442 --- /dev/null +++ b/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/Fixture/skip_wrapped_array_dim_fetch_variable_variables.php.inc @@ -0,0 +1,14 @@ + 'baz'); + + echo ${$foo['bar']}; + } +} diff --git a/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/WrapVariableVariableNameInCurlyBracesRectorTest.php b/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/WrapVariableVariableNameInCurlyBracesRectorTest.php index 17028eae53d..d12116273a0 100644 --- a/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/WrapVariableVariableNameInCurlyBracesRectorTest.php +++ b/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/WrapVariableVariableNameInCurlyBracesRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php70\Rector\Variable\WrapVariableVariableNameInCurlyBracesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class WrapVariableVariableNameInCurlyBracesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/config/configured_rule.php b/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/config/configured_rule.php index 8a8f9c79762..29bb8bda67f 100644 --- a/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/config/configured_rule.php +++ b/rules-tests/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php70\Rector\Variable\WrapVariableVariableNameInCurlyBracesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(WrapVariableVariableNameInCurlyBracesRector::class); -}; +return RectorConfig::configure() + ->withRules([WrapVariableVariableNameInCurlyBracesRector::class]); diff --git a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/AssignArrayToStringRectorTest.php b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/AssignArrayToStringRectorTest.php index ee7ba66b639..5a6cf7e5ef1 100644 --- a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/AssignArrayToStringRectorTest.php +++ b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/AssignArrayToStringRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php71\Rector\Assign\AssignArrayToStringRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AssignArrayToStringRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/fixture5.php.inc b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/fixture5.php.inc deleted file mode 100644 index d1879dad475..00000000000 --- a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/fixture5.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -property = ''; - $this->property[] = 1; - } -} - -?> ------ -property = []; - $this->property[] = 1; - } -} - -?> diff --git a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/in_closure.php.inc b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/in_closure.php.inc new file mode 100644 index 00000000000..00584caacb4 --- /dev/null +++ b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/in_closure.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_already_array.php.inc b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_already_array.php.inc new file mode 100644 index 00000000000..bd3baa60113 --- /dev/null +++ b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_already_array.php.inc @@ -0,0 +1,16 @@ + diff --git a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_cast_undefined_var3.php.inc b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_cast_undefined_var3.php.inc deleted file mode 100644 index 2c76e80c691..00000000000 --- a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_cast_undefined_var3.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - diff --git a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_direct_string_assign.php.inc b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_direct_string_assign.php.inc new file mode 100644 index 00000000000..dc2881f578c --- /dev/null +++ b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_direct_string_assign.php.inc @@ -0,0 +1,15 @@ +doFoo($condition); + + $condition = []; + $condition['typ'] = 'select'; + if (random_int(0,1)) { + $condition[] = 'id NOT IN (456)'; + } + + $this->doFoo($condition); + } + + private function doFoo($condition) + {} +} diff --git a/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_reassigned_as_string.php.inc b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_reassigned_as_string.php.inc new file mode 100644 index 00000000000..9e4e614608c --- /dev/null +++ b/rules-tests/Php71/Rector/Assign/AssignArrayToStringRector/Fixture/skip_reassigned_as_string.php.inc @@ -0,0 +1,21 @@ +services(); - $services->set(AssignArrayToStringRector::class); -}; +return RectorConfig::configure() + ->withRules([AssignArrayToStringRector::class]); diff --git a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/BinaryOpBetweenNumberAndStringRectorTest.php b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/BinaryOpBetweenNumberAndStringRectorTest.php index ff60f83de18..cc21d25bc52 100644 --- a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/BinaryOpBetweenNumberAndStringRectorTest.php +++ b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/BinaryOpBetweenNumberAndStringRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php71\Rector\BinaryOp\BinaryOpBetweenNumberAndStringRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class BinaryOpBetweenNumberAndStringRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/fixture.php.inc b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/fixture.php.inc index bd65b781c32..7d8bb995c9b 100644 --- a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/fixture.php.inc +++ b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/fixture.php.inc @@ -26,7 +26,7 @@ class Fixture public function run() { $value = 5 + 0; - $value = 5.0 + 0; + $value = 5.0 + 0.0; $value = 5 + 0; $value = 5 * 0; diff --git a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/from_typed_param.php.inc b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/from_typed_param.php.inc new file mode 100644 index 00000000000..c508f4835de --- /dev/null +++ b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/from_typed_param.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/not_identical_float.php.inc b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/not_identical_float.php.inc new file mode 100644 index 00000000000..7ff974248c3 --- /dev/null +++ b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/not_identical_float.php.inc @@ -0,0 +1,43 @@ +foo(); + if (! is_null($data)) { + var_dump($data !== ''); + } + } +} + +?> +----- +foo(); + if (! is_null($data)) { + var_dump($data !== 0.0); + } + } +} + +?> diff --git a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_concatenation_dot.php.inc b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_concatenation_dot.php.inc index d6b192fa656..a7951b4b051 100644 --- a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_concatenation_dot.php.inc +++ b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_concatenation_dot.php.inc @@ -6,7 +6,7 @@ final class SkipConcatenationDot { public function run() { - $string = 'this is a string to cancatenate with the number'; + $string = 'this is a string to concatenate with the number'; $number = 10; $value = $string . ' ' . $number; } diff --git a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_phpdoc.php.inc b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_phpdoc.php.inc new file mode 100644 index 00000000000..6b8f043cb6f --- /dev/null +++ b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_phpdoc.php.inc @@ -0,0 +1,17 @@ +adresseid_lieferung) { + } + } +} diff --git a/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_type_by_param_doc.php.inc b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_type_by_param_doc.php.inc new file mode 100644 index 00000000000..c2bab56f3f2 --- /dev/null +++ b/rules-tests/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector/Fixture/skip_type_by_param_doc.php.inc @@ -0,0 +1,14 @@ +services(); - $services->set(BinaryOpBetweenNumberAndStringRector::class); -}; +return RectorConfig::configure() + ->withRules([BinaryOpBetweenNumberAndStringRector::class]); diff --git a/rules-tests/Php71/Rector/BooleanOr/IsIterableRector/IsIterableRectorTest.php b/rules-tests/Php71/Rector/BooleanOr/IsIterableRector/IsIterableRectorTest.php index 2ab1e90cf16..e61d1711648 100644 --- a/rules-tests/Php71/Rector/BooleanOr/IsIterableRector/IsIterableRectorTest.php +++ b/rules-tests/Php71/Rector/BooleanOr/IsIterableRector/IsIterableRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php71\Rector\BooleanOr\IsIterableRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class IsIterableRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php71/Rector/BooleanOr/IsIterableRector/config/configured_rule.php b/rules-tests/Php71/Rector/BooleanOr/IsIterableRector/config/configured_rule.php index 4e7481a098c..d58b1d40a85 100644 --- a/rules-tests/Php71/Rector/BooleanOr/IsIterableRector/config/configured_rule.php +++ b/rules-tests/Php71/Rector/BooleanOr/IsIterableRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php71\Rector\BooleanOr\IsIterableRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(IsIterableRector::class); -}; +return RectorConfig::configure() + ->withRules([IsIterableRector::class]); diff --git a/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/Fixture/SomeClass.php.inc b/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/Fixture/SomeClass.php.inc deleted file mode 100644 index 71d1936b2c4..00000000000 --- a/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/Fixture/SomeClass.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/PublicConstantVisibilityRectorTest.php b/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/PublicConstantVisibilityRectorTest.php deleted file mode 100644 index 836691bf6cc..00000000000 --- a/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/PublicConstantVisibilityRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/config/configured_rule.php b/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/config/configured_rule.php deleted file mode 100644 index 5c0e9b1b77b..00000000000 --- a/rules-tests/Php71/Rector/ClassConst/PublicConstantVisibilityRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(PublicConstantVisibilityRector::class); -}; diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/CountOnNullRectorTest.php b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/CountOnNullRectorTest.php deleted file mode 100644 index 697a9eb0763..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/CountOnNullRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_71.php'; - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/CountOnNullRectorWithPHP73Test.php b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/CountOnNullRectorWithPHP73Test.php deleted file mode 100644 index f0da12ef965..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/CountOnNullRectorWithPHP73Test.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureForPhp73'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/is_countable.php'; - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/false_true_class.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/false_true_class.php.inc deleted file mode 100644 index 2d6ab0d3229..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/false_true_class.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/local_property.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/local_property.php.inc deleted file mode 100644 index a7711ee0ef2..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/local_property.php.inc +++ /dev/null @@ -1,61 +0,0 @@ -titles = null; - $titleCount = count($this->titles); - - $alsoTitlesCount = count($this->alsoTitles); - - $notTitlesCount = count($this->notTitles); - } -} - -?> ------ -titles = null; - $titleCount = $this->titles === null ? 0 : count($this->titles); - - $alsoTitlesCount = count($this->alsoTitles); - - $notTitlesCount = is_array($this->notTitles) || $this->notTitles instanceof \Countable ? count($this->notTitles) : 0; - } -} - -?> diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/nullable_array.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/nullable_array.php.inc deleted file mode 100644 index 8bf49236cec..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/nullable_array.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/on_null.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/on_null.php.inc deleted file mode 100644 index 7f6fe525773..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/on_null.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -= 2) { - echo 'yes'; - } -} - -?> ------ -= 2) { - echo 'yes'; - } -} - -?> diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/property_with_doc.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/property_with_doc.php.inc deleted file mode 100644 index deab521a044..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/property_with_doc.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -fail); - } -} - -?> ------ -fail); - } -} - -?> diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_array_merge.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_array_merge.php.inc deleted file mode 100644 index 7762cbf3617..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_array_merge.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - 0 ? $results : null; - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_countable_annotated_params.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_countable_annotated_params.php.inc deleted file mode 100644 index 265b057f640..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_countable_annotated_params.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -titles); - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_countable_remote_property.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_countable_remote_property.php.inc deleted file mode 100644 index 1e1aac7b188..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_countable_remote_property.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -remote = new ClassWithCountableProperty(); - } - - public function getTitle() - { - count($this->remote->countable); - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_double_same_variable.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_double_same_variable.php.inc deleted file mode 100644 index ba2ceac7f2d..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_double_same_variable.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - 1) { - throw new \Exception("y"); - } - }; - } -} - - -class Dibi -{ - public static function getColumns(): array - { - return []; - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_external_property.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_external_property.php.inc deleted file mode 100644 index bca7329493d..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_external_property.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -vars[0]) || count($issetNode->vars) > 1) { - } - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_init_array.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_init_array.php.inc deleted file mode 100644 index 10f1d9f645f..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_init_array.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -items)) { - return; - } - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_preg_match_array.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_preg_match_array.php.inc deleted file mode 100644 index 801cad543eb..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_preg_match_array.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - 0) { - return 'found'; - } - - preg_match_all('#\d\.\d(\.\d)?(-?\S*)?#i', $version, $matches); - - if (count($matches) > 0) { - return 'found'; - } - - return 'none'; - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_property_within_trait_method.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_property_within_trait_method.php.inc deleted file mode 100644 index 228757cde6d..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_property_within_trait_method.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -array); - } -} diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_simple_xml_element.php.inc b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_simple_xml_element.php.inc deleted file mode 100644 index cc6ffaba05c..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Fixture/skip_simple_xml_element.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Source/ClassWithCountableProperty.php b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Source/ClassWithCountableProperty.php deleted file mode 100644 index 9b048780fd3..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/Source/ClassWithCountableProperty.php +++ /dev/null @@ -1,13 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::IS_COUNTABLE); - - $services = $containerConfigurator->services(); - $services->set(CountOnNullRector::class); -}; diff --git a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/config/php_71.php b/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/config/php_71.php deleted file mode 100644 index e21f1ea6011..00000000000 --- a/rules-tests/Php71/Rector/FuncCall/CountOnNullRector/config/php_71.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::COUNT_ON_NULL); - - $services = $containerConfigurator->services(); - $services->set(CountOnNullRector::class); -}; diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/fixture.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/fixture.php.inc index f19752e104d..b4715b6f9b0 100644 --- a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/fixture.php.inc +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/fixture.php.inc @@ -13,7 +13,7 @@ function removeExtraParams() function functionWithVariadics(...$variadic) { - $argumemnts = $variadic; + $arguments = $variadic; } ?> @@ -33,7 +33,7 @@ function removeExtraParams() function functionWithVariadics(...$variadic) { - $argumemnts = $variadic; + $arguments = $variadic; } ?> diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/on_cannot_get_scope_of_function_call.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/on_cannot_get_scope_of_function_call.php.inc index 1e97845c19d..1252698e255 100644 --- a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/on_cannot_get_scope_of_function_call.php.inc +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/on_cannot_get_scope_of_function_call.php.inc @@ -36,4 +36,4 @@ final class OnCannotGetScopeOfFunctionCall } } -?> \ No newline at end of file +?> diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/remove_another_class_method_call_extra_argument.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/remove_another_class_method_call_extra_argument.php.inc index b8bb49b226c..10f157a7649 100644 --- a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/remove_another_class_method_call_extra_argument.php.inc +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/remove_another_class_method_call_extra_argument.php.inc @@ -11,8 +11,8 @@ final class RemoveAnotherClassMethodCallExtraArgument function performBetter($value) { - $argumemnts = better_func_get_args(); - var_dump($argumemnts); + $arguments = better_func_get_args(); + var_dump($arguments); } } @@ -34,8 +34,8 @@ final class RemoveAnotherClassMethodCallExtraArgument function performBetter($value) { - $argumemnts = better_func_get_args(); - var_dump($argumemnts); + $arguments = better_func_get_args(); + var_dump($arguments); } } diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_call_abstract_method.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_call_abstract_method.php.inc new file mode 100644 index 00000000000..ed8ea77c69a --- /dev/null +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_call_abstract_method.php.inc @@ -0,0 +1,12 @@ +doVariadic(...$this->getArgs($param)); + } +} \ No newline at end of file diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_defer_function.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_defer_function.php.inc new file mode 100644 index 00000000000..885174dbb8b --- /dev/null +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_defer_function.php.inc @@ -0,0 +1,11 @@ +getTitle(...), [ + 'is_safe' => ['html'], + ]), + ]; + } + + public function getTitle(): string + { + return 'title'; + } +} diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_first_class_callable_direct_expression.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_first_class_callable_direct_expression.php.inc new file mode 100644 index 00000000000..e78d1617288 --- /dev/null +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_first_class_callable_direct_expression.php.inc @@ -0,0 +1,16 @@ +getTitle(...); + } + + public function getTitle(): string + { + return 'title'; + } +} diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_func_get_all.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_func_get_all.php.inc index 9c1cdf58cb6..08722f155a5 100644 --- a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_func_get_all.php.inc +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_func_get_all.php.inc @@ -13,7 +13,7 @@ final class SkipFuncGetAll function perform() { - $argumemnts = func_get_args(); - var_dump($argumemnts); + $arguments = func_get_args(); + var_dump($arguments); } } diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_phpstan_phar.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_phpstan_phar.php.inc new file mode 100644 index 00000000000..e8e15c4c88e --- /dev/null +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_phpstan_phar.php.inc @@ -0,0 +1,16 @@ +foo(1, 2); + + Stan::bar(1, 2); + } +} diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_replaced_function.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_replaced_function.php.inc new file mode 100644 index 00000000000..908f9ffc424 --- /dev/null +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/skip_replaced_function.php.inc @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/use_replaced_function.php.inc b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/use_replaced_function.php.inc new file mode 100644 index 00000000000..d212d51739c --- /dev/null +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Fixture/use_replaced_function.php.inc @@ -0,0 +1,43 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/RemoveExtraParametersRectorTest.php b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/RemoveExtraParametersRectorTest.php index e1b2dd0fa43..3182294add9 100644 --- a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/RemoveExtraParametersRectorTest.php +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/RemoveExtraParametersRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php71\Rector\FuncCall\RemoveExtraParametersRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveExtraParametersRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Source/SomeAbstractClass.php b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Source/SomeAbstractClass.php new file mode 100644 index 00000000000..f1f10cf46db --- /dev/null +++ b/rules-tests/Php71/Rector/FuncCall/RemoveExtraParametersRector/Source/SomeAbstractClass.php @@ -0,0 +1,10 @@ +services(); - $services->set(RemoveExtraParametersRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveExtraParametersRector::class]); diff --git a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/fixture.php.inc b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/fixture.php.inc index 7509c4881a4..7cfe48c0289 100644 --- a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/fixture.php.inc +++ b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/fixture.php.inc @@ -7,9 +7,6 @@ class Fixture public function run() { list($id1, $name1) = $data; - - foreach ($data as list($id, $name)) { - } } } @@ -24,9 +21,6 @@ class Fixture public function run() { [$id1, $name1] = $data; - - foreach ($data as [$id, $name]) { - } } } diff --git a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/part_of_foreach.php.inc b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/part_of_foreach.php.inc new file mode 100644 index 00000000000..20e56442fcb --- /dev/null +++ b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/part_of_foreach.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/skip_already_array_destruct.php.inc b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/skip_already_array_destruct.php.inc new file mode 100644 index 00000000000..1b5b0bddad6 --- /dev/null +++ b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/skip_already_array_destruct.php.inc @@ -0,0 +1,14 @@ + $line, + 'file' => $file, + ] = $trace; + } +} diff --git a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/skip_partial_destruct.php.inc b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/skip_partial_destruct.php.inc new file mode 100644 index 00000000000..2a0790987ab --- /dev/null +++ b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/Fixture/skip_partial_destruct.php.inc @@ -0,0 +1,10 @@ +id); + } +} diff --git a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/ListToArrayDestructRectorTest.php b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/ListToArrayDestructRectorTest.php index 541b9375a7c..54b28d3cc28 100644 --- a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/ListToArrayDestructRectorTest.php +++ b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/ListToArrayDestructRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php71\Rector\List_\ListToArrayDestructRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ListToArrayDestructRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/config/configured_rule.php b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/config/configured_rule.php index 9865362de09..c15355d0c70 100644 --- a/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/config/configured_rule.php +++ b/rules-tests/Php71/Rector/List_/ListToArrayDestructRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php71\Rector\List_\ListToArrayDestructRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ListToArrayDestructRector::class); -}; +return RectorConfig::configure() + ->withRules([ListToArrayDestructRector::class]); diff --git a/rules-tests/Php71/Rector/Name/ReservedObjectRector/Fixture/ReservedObject.php.inc b/rules-tests/Php71/Rector/Name/ReservedObjectRector/Fixture/ReservedObject.php.inc deleted file mode 100644 index d8534d271cb..00000000000 --- a/rules-tests/Php71/Rector/Name/ReservedObjectRector/Fixture/ReservedObject.php.inc +++ /dev/null @@ -1,61 +0,0 @@ - ------ - diff --git a/rules-tests/Php71/Rector/Name/ReservedObjectRector/Fixture/skip_type_declaration_object.php.inc b/rules-tests/Php71/Rector/Name/ReservedObjectRector/Fixture/skip_type_declaration_object.php.inc deleted file mode 100644 index f66f6b75143..00000000000 --- a/rules-tests/Php71/Rector/Name/ReservedObjectRector/Fixture/skip_type_declaration_object.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php71/Rector/Name/ReservedObjectRector/config/configured_rule.php b/rules-tests/Php71/Rector/Name/ReservedObjectRector/config/configured_rule.php deleted file mode 100644 index fa759732074..00000000000 --- a/rules-tests/Php71/Rector/Name/ReservedObjectRector/config/configured_rule.php +++ /dev/null @@ -1,17 +0,0 @@ -services(); - $services->set(ReservedObjectRector::class) - ->call('configure', [[ - ReservedObjectRector::RESERVED_KEYWORDS_TO_REPLACEMENTS => [ - 'ReservedObject' => 'SmartObject', - 'Object' => 'AnotherSmartObject', - ], - ]]); -}; diff --git a/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/fixture.php.inc b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/fixture.php.inc index 03c706c84b1..631487b41fe 100644 --- a/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/fixture.php.inc +++ b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/fixture.php.inc @@ -4,26 +4,6 @@ namespace Rector\Tests\Php71\Rector\TryCatch\MultiExceptionCatchRector\Fixture; function multiExceptionCatch() { - try { - // Some code... - } catch (ExceptionType1 $e) { - // Code to handle the exception - } catch (ExceptionType2 $e) { - // Code to handle the exception - } catch (Exception $e) { - // ... - } - - - try { - // Some code... - } catch (ExceptionType1 $e) { - // Code to handle the exception - } catch (ExceptionType2 $e) { - $differentContent = 'hey'; - } - - try { // Some code... } catch (ExceptionType1 $e) { @@ -41,24 +21,6 @@ namespace Rector\Tests\Php71\Rector\TryCatch\MultiExceptionCatchRector\Fixture; function multiExceptionCatch() { - try { - // Some code... - } catch (ExceptionType1|ExceptionType2 $e) { - // Code to handle the exception - } catch (Exception $e) { - // ... - } - - - try { - // Some code... - } catch (ExceptionType1 $e) { - // Code to handle the exception - } catch (ExceptionType2 $e) { - $differentContent = 'hey'; - } - - try { // Some code... } catch (ExceptionType1|ExceptionType2 $e) { diff --git a/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/merge_commented_same.php.inc b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/merge_commented_same.php.inc new file mode 100644 index 00000000000..ec6ca61d720 --- /dev/null +++ b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/merge_commented_same.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/multiple_items.php.inc b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/multiple_items.php.inc new file mode 100644 index 00000000000..18cf3fb5f14 --- /dev/null +++ b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/multiple_items.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/skip_different_contents.php.inc b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/skip_different_contents.php.inc new file mode 100644 index 00000000000..b5f763268ae --- /dev/null +++ b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/Fixture/skip_different_contents.php.inc @@ -0,0 +1,17 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/config/configured_rule.php b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/config/configured_rule.php index fc37fb401c1..f2534623be5 100644 --- a/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/config/configured_rule.php +++ b/rules-tests/Php71/Rector/TryCatch/MultiExceptionCatchRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php71\Rector\TryCatch\MultiExceptionCatchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(MultiExceptionCatchRector::class); -}; +return RectorConfig::configure() + ->withRules([MultiExceptionCatchRector::class]); diff --git a/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/fixture2.php.inc b/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/fixture2.php.inc index ab3f087883f..1bf04bb98ec 100644 --- a/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/fixture2.php.inc +++ b/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/fixture2.php.inc @@ -22,9 +22,7 @@ function each2() $key = key($opt->option); $val = current($opt->option); next($opt->option); - $tid = key($option->option); - $curr = current($tree); next($tree); } diff --git a/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/list_each_next.php.inc b/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/list_each_next.php.inc index b83af04cef2..721e2b0bf1a 100644 --- a/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/list_each_next.php.inc +++ b/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/list_each_next.php.inc @@ -27,11 +27,9 @@ final class ListEachNext public function run() { $parentArray = ['a' => 1, 'b' => 2]; - $key = key($parentArray); $value = current($parentArray); next($parentArray); - $key2 = key($parentArray); $value2 = current($parentArray); next($parentArray); diff --git a/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/skip_while_loop.php.inc b/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/skip_while_loop.php.inc new file mode 100644 index 00000000000..0232824967c --- /dev/null +++ b/rules-tests/Php72/Rector/Assign/ListEachRector/Fixture/skip_while_loop.php.inc @@ -0,0 +1,15 @@ +option)) { + return false; + } + + return true; + } +} diff --git a/rules-tests/Php72/Rector/Assign/ListEachRector/ListEachRectorTest.php b/rules-tests/Php72/Rector/Assign/ListEachRector/ListEachRectorTest.php index 27c5d5e24b1..fe43009c420 100644 --- a/rules-tests/Php72/Rector/Assign/ListEachRector/ListEachRectorTest.php +++ b/rules-tests/Php72/Rector/Assign/ListEachRector/ListEachRectorTest.php @@ -5,8 +5,8 @@ namespace Rector\Tests\Php72\Rector\Assign\ListEachRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * Test battery inspired by: @@ -15,21 +15,15 @@ */ final class ListEachRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - * @requires PHP < 8.0 - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/Assign/ListEachRector/config/configured_rule.php b/rules-tests/Php72/Rector/Assign/ListEachRector/config/configured_rule.php index c393b132f14..18ffa9ebb47 100644 --- a/rules-tests/Php72/Rector/Assign/ListEachRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/Assign/ListEachRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\Assign\ListEachRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ListEachRector::class); -}; +return RectorConfig::configure() + ->withRules([ListEachRector::class]); diff --git a/rules-tests/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector/ReplaceEachAssignmentWithKeyCurrentRectorTest.php b/rules-tests/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector/ReplaceEachAssignmentWithKeyCurrentRectorTest.php index a7689deff82..05ee0af9381 100644 --- a/rules-tests/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector/ReplaceEachAssignmentWithKeyCurrentRectorTest.php +++ b/rules-tests/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector/ReplaceEachAssignmentWithKeyCurrentRectorTest.php @@ -5,27 +5,20 @@ namespace Rector\Tests\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use SplFileInfo; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReplaceEachAssignmentWithKeyCurrentRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - * @requires PHP < 8.0 - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector/config/configured_rule.php b/rules-tests/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector/config/configured_rule.php index 1545fa07e2a..0cbc46d03f9 100644 --- a/rules-tests/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReplaceEachAssignmentWithKeyCurrentRector::class); -}; +return RectorConfig::configure() + ->withRules([ReplaceEachAssignmentWithKeyCurrentRector::class]); diff --git a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/CreateFunctionToAnonymousFunctionRectorTest.php b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/CreateFunctionToAnonymousFunctionRectorTest.php index eb4d3d365dc..670340b55a1 100644 --- a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/CreateFunctionToAnonymousFunctionRectorTest.php +++ b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/CreateFunctionToAnonymousFunctionRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CreateFunctionToAnonymousFunctionRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/quoted_variable_arg_concat.php.inc b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/quoted_variable_arg_concat.php.inc new file mode 100644 index 00000000000..4b8d455777f --- /dev/null +++ b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/quoted_variable_arg_concat.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/quoted_variable_arg_concat2.php.inc b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/quoted_variable_arg_concat2.php.inc new file mode 100644 index 00000000000..c47a507307f --- /dev/null +++ b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/quoted_variable_arg_concat2.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/use_increment.php.inc b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/use_increment.php.inc new file mode 100644 index 00000000000..e9254626876 --- /dev/null +++ b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/use_increment.php.inc @@ -0,0 +1,37 @@ +callQuoteIdentifier($v, '.++$this->_qiCallbacksCount.');'); + } + +} + +?> +----- +callQuoteIdentifier($v, ++$this->_qiCallbacksCount); + }; + } + +} + +?> diff --git a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/variable_as_operator.php.inc b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/variable_as_operator.php.inc new file mode 100644 index 00000000000..db6be6be418 --- /dev/null +++ b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/variable_as_operator.php.inc @@ -0,0 +1,31 @@ +'; + $func = create_function('$a, $b', "return \$a $f \$b;"); + } +} + +?> +----- +'; + $func = function ($a, $b) { + return $a > $b; + }; + } +} + +?> diff --git a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/wordpress.php.inc b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/wordpress.php.inc index 70082de2111..5a59d74f456 100644 --- a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/wordpress.php.inc +++ b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/Fixture/wordpress.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector\Fixture; -class Wordpress +class WordPress { public function run() { @@ -23,7 +23,7 @@ class Wordpress namespace Rector\Tests\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector\Fixture; -class Wordpress +class WordPress { public function run() { diff --git a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/config/configured_rule.php b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/config/configured_rule.php index 08608a6fac2..7d1750f6cdd 100644 --- a/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(CreateFunctionToAnonymousFunctionRector::class); -}; +return RectorConfig::configure() + ->withRules([CreateFunctionToAnonymousFunctionRector::class]); diff --git a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/Fixture/skip_tricky_cases.php.inc b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/Fixture/skip_tricky_cases.php.inc new file mode 100644 index 00000000000..6ebfefd9f3d --- /dev/null +++ b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/Fixture/skip_tricky_cases.php.inc @@ -0,0 +1,20 @@ + ------ - diff --git a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/FixturePostImport/instanceof_some_class.php.inc b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/FixturePostImport/instanceof_some_class.php.inc deleted file mode 100644 index da657a3f03e..00000000000 --- a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/FixturePostImport/instanceof_some_class.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/FixturePostImport/post_import_class.php.inc b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/FixturePostImport/post_import_class.php.inc deleted file mode 100644 index 06e017fb38d..00000000000 --- a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/FixturePostImport/post_import_class.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -cache); - } -} - -?> ------ -cache !== null ? get_class($this->cache) : self::class; - } -} - -?> diff --git a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/GetClassOnNullRectorTest.php b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/GetClassOnNullRectorTest.php index 967bb48de4f..bd5a4749fd6 100644 --- a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/GetClassOnNullRectorTest.php +++ b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/GetClassOnNullRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php72\Rector\FuncCall\GetClassOnNullRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class GetClassOnNullRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/PostImportTest.php b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/PostImportTest.php deleted file mode 100644 index eb7513e4af9..00000000000 --- a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/PostImportTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePostImport'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/auto_import.php'; - } -} diff --git a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/Source/SomeSuperLongClass.php b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/Source/SomeSuperLongClass.php deleted file mode 100644 index 527a8dcaa53..00000000000 --- a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/Source/SomeSuperLongClass.php +++ /dev/null @@ -1,10 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(GetClassOnNullRector::class); -}; diff --git a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/config/auto_import_doc_block.php b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/config/auto_import_doc_block.php deleted file mode 100644 index 3197820ea18..00000000000 --- a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/config/auto_import_doc_block.php +++ /dev/null @@ -1,15 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(GetClassOnNullRector::class); -}; diff --git a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/config/configured_rule.php b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/config/configured_rule.php index b6503b9eca7..1a6341b3bf7 100644 --- a/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/FuncCall/GetClassOnNullRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\FuncCall\GetClassOnNullRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(GetClassOnNullRector::class); -}; +return RectorConfig::configure() + ->withRules([GetClassOnNullRector::class]); diff --git a/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/Fixture/fixture.php.inc b/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/Fixture/fixture.php.inc deleted file mode 100644 index a0661bc16b7..00000000000 --- a/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/Fixture/skip_normal_class.php.inc b/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/Fixture/skip_normal_class.php.inc deleted file mode 100644 index 19c1bab8cd2..00000000000 --- a/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/Fixture/skip_normal_class.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/config/configured_rule.php b/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/config/configured_rule.php deleted file mode 100644 index d73cc129531..00000000000 --- a/rules-tests/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(IsObjectOnIncompleteClassRector::class); -}; diff --git a/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/Fixture/multiple_parse_str_usage.php.inc b/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/Fixture/multiple_parse_str_usage.php.inc new file mode 100644 index 00000000000..529eff703c4 --- /dev/null +++ b/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/Fixture/multiple_parse_str_usage.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/ParseStrWithResultArgumentRectorTest.php b/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/ParseStrWithResultArgumentRectorTest.php index 88f7433ef12..26d0f00d63c 100644 --- a/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/ParseStrWithResultArgumentRectorTest.php +++ b/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/ParseStrWithResultArgumentRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP < 8.0 - */ final class ParseStrWithResultArgumentRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/config/configured_rule.php b/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/config/configured_rule.php index 2f070167a56..5124d285d7f 100644 --- a/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ParseStrWithResultArgumentRector::class); -}; +return RectorConfig::configure() + ->withRules([ParseStrWithResultArgumentRector::class]); diff --git a/rules-tests/Php72/Rector/FuncCall/StringifyDefineRector/StringifyDefineRectorTest.php b/rules-tests/Php72/Rector/FuncCall/StringifyDefineRector/StringifyDefineRectorTest.php index 7a4fa16af4b..cf5631aea37 100644 --- a/rules-tests/Php72/Rector/FuncCall/StringifyDefineRector/StringifyDefineRectorTest.php +++ b/rules-tests/Php72/Rector/FuncCall/StringifyDefineRector/StringifyDefineRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\Php72\Rector\FuncCall\StringifyDefineRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP < 8.0 - */ final class StringifyDefineRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/FuncCall/StringifyDefineRector/config/configured_rule.php b/rules-tests/Php72/Rector/FuncCall/StringifyDefineRector/config/configured_rule.php index 93dc708ffef..151fc23e04b 100644 --- a/rules-tests/Php72/Rector/FuncCall/StringifyDefineRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/FuncCall/StringifyDefineRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\FuncCall\StringifyDefineRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StringifyDefineRector::class); -}; +return RectorConfig::configure() + ->withRules([StringifyDefineRector::class]); diff --git a/rules-tests/Php72/Rector/FuncCall/StringsAssertNakedRector/StringsAssertNakedRectorTest.php b/rules-tests/Php72/Rector/FuncCall/StringsAssertNakedRector/StringsAssertNakedRectorTest.php index e51f8e1619e..d596951bd2c 100644 --- a/rules-tests/Php72/Rector/FuncCall/StringsAssertNakedRector/StringsAssertNakedRectorTest.php +++ b/rules-tests/Php72/Rector/FuncCall/StringsAssertNakedRector/StringsAssertNakedRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\Php72\Rector\FuncCall\StringsAssertNakedRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP < 8.0 - */ final class StringsAssertNakedRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/FuncCall/StringsAssertNakedRector/config/configured_rule.php b/rules-tests/Php72/Rector/FuncCall/StringsAssertNakedRector/config/configured_rule.php index 39f8034c9e0..caaf5a1bcb3 100644 --- a/rules-tests/Php72/Rector/FuncCall/StringsAssertNakedRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/FuncCall/StringsAssertNakedRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\FuncCall\StringsAssertNakedRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StringsAssertNakedRector::class); -}; +return RectorConfig::configure() + ->withRules([StringsAssertNakedRector::class]); diff --git a/rules-tests/Php72/Rector/Unset_/UnsetCastRector/UnsetCastRectorTest.php b/rules-tests/Php72/Rector/Unset_/UnsetCastRector/UnsetCastRectorTest.php index 1eb02d86776..cb3b629008e 100644 --- a/rules-tests/Php72/Rector/Unset_/UnsetCastRector/UnsetCastRectorTest.php +++ b/rules-tests/Php72/Rector/Unset_/UnsetCastRector/UnsetCastRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\Php72\Rector\Unset_\UnsetCastRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP <= 8.0 - */ final class UnsetCastRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/Unset_/UnsetCastRector/config/configured_rule.php b/rules-tests/Php72/Rector/Unset_/UnsetCastRector/config/configured_rule.php index 21dfdb7343f..6a3437eadff 100644 --- a/rules-tests/Php72/Rector/Unset_/UnsetCastRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/Unset_/UnsetCastRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\Unset_\UnsetCastRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(UnsetCastRector::class); -}; +return RectorConfig::configure() + ->withRules([UnsetCastRector::class]); diff --git a/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/Fixture/include_suppress.php.inc b/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/Fixture/include_suppress.php.inc new file mode 100644 index 00000000000..8965afe0810 --- /dev/null +++ b/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/Fixture/include_suppress.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/Fixture/trailing_comma_last.php.inc b/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/Fixture/trailing_comma_last.php.inc new file mode 100644 index 00000000000..69955633cc6 --- /dev/null +++ b/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/Fixture/trailing_comma_last.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/WhileEachToForeachRectorTest.php b/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/WhileEachToForeachRectorTest.php index 3e6fcad813a..ebf10ced08c 100644 --- a/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/WhileEachToForeachRectorTest.php +++ b/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/WhileEachToForeachRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\Php72\Rector\While_\WhileEachToForeachRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP < 8.0 - */ final class WhileEachToForeachRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/config/configured_rule.php b/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/config/configured_rule.php index 04763ec046f..a6530e9a6d9 100644 --- a/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/config/configured_rule.php +++ b/rules-tests/Php72/Rector/While_/WhileEachToForeachRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php72\Rector\While_\WhileEachToForeachRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(WhileEachToForeachRector::class); -}; +return RectorConfig::configure() + ->withRules([WhileEachToForeachRector::class]); diff --git a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/Fixture/on_method_call.php.inc b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/Fixture/on_method_call.php.inc new file mode 100644 index 00000000000..3413c545ad0 --- /dev/null +++ b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/Fixture/on_method_call.php.inc @@ -0,0 +1,27 @@ +execute()) || $foo->execute() instanceof \Countable; + } +} + +?> +----- +execute()); + } +} + +?> diff --git a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/Fixture/skip_different_method_call.php.inc b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/Fixture/skip_different_method_call.php.inc new file mode 100644 index 00000000000..56c8226f336 --- /dev/null +++ b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/Fixture/skip_different_method_call.php.inc @@ -0,0 +1,11 @@ +execute()) || $foo->run() instanceof \Countable; + } +} diff --git a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/FixtureWithPolyfill/polyfill_function.php.inc b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/FixtureWithPolyfill/polyfill_function.php.inc deleted file mode 100644 index 70fad49f739..00000000000 --- a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/FixtureWithPolyfill/polyfill_function.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - ------ - diff --git a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/IsCountableRectorTest.php b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/IsCountableRectorTest.php index 8d3d076c093..4b7bbdcc471 100644 --- a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/IsCountableRectorTest.php +++ b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/IsCountableRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\Php73\Rector\BinaryOr\IsCountableRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class IsCountableRectorTest extends AbstractRectorTestCase { - /** - * @requires PHP 7.3 - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/PolyfillRectorTest.php b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/PolyfillRectorTest.php deleted file mode 100644 index a8abf572f17..00000000000 --- a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/PolyfillRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureWithPolyfill'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/polyfill_config.php'; - } -} diff --git a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/config/configured_rule.php b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/config/configured_rule.php index 196a5f0de34..90cec566632 100644 --- a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/config/configured_rule.php +++ b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php73\Rector\BooleanOr\IsCountableRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(IsCountableRector::class); -}; +return RectorConfig::configure() + ->withRules([IsCountableRector::class]); diff --git a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/config/polyfill_config.php b/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/config/polyfill_config.php deleted file mode 100644 index 05ba13e3436..00000000000 --- a/rules-tests/Php73/Rector/BinaryOr/IsCountableRector/config/polyfill_config.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::IS_COUNTABLE - 1); - - $services = $containerConfigurator->services(); - $services->set(IsCountableRector::class); -}; diff --git a/rules-tests/Php73/Rector/ConstFetch/SensitiveConstantNameRector/Fixture/skip_found_namespaced_constant.php.inc b/rules-tests/Php73/Rector/ConstFetch/SensitiveConstantNameRector/Fixture/skip_found_namespaced_constant.php.inc new file mode 100644 index 00000000000..73443bf1883 --- /dev/null +++ b/rules-tests/Php73/Rector/ConstFetch/SensitiveConstantNameRector/Fixture/skip_found_namespaced_constant.php.inc @@ -0,0 +1,13 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/ConstFetch/SensitiveConstantNameRector/config/configured_rule.php b/rules-tests/Php73/Rector/ConstFetch/SensitiveConstantNameRector/config/configured_rule.php index 7354cdfdc66..064bf4eaf2c 100644 --- a/rules-tests/Php73/Rector/ConstFetch/SensitiveConstantNameRector/config/configured_rule.php +++ b/rules-tests/Php73/Rector/ConstFetch/SensitiveConstantNameRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php73\Rector\ConstFetch\SensitiveConstantNameRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SensitiveConstantNameRector::class); -}; +return RectorConfig::configure() + ->withRules([SensitiveConstantNameRector::class]); diff --git a/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/ArrayKeyFirstLastRectorTest.php b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/ArrayKeyFirstLastRectorTest.php index c7aff690f80..2e759182b3d 100644 --- a/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/ArrayKeyFirstLastRectorTest.php +++ b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/ArrayKeyFirstLastRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php73\Rector\FuncCall\ArrayKeyFirstLastRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ArrayKeyFirstLastRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/array_key_first.php.inc b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/array_key_first.php.inc index fb4784edd7e..875c1af0f22 100644 --- a/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/array_key_first.php.inc +++ b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/array_key_first.php.inc @@ -8,7 +8,7 @@ function process() { $firstKey = key($items); reset($items); - $firstKey = key($differntItems); + $firstKey = key($differentItems); } ?> @@ -22,7 +22,7 @@ function process() { $firstKey = array_key_first($items); reset($items); - $firstKey = key($differntItems); + $firstKey = key($differentItems); } ?> diff --git a/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/key_called_multiple_times.php.inc b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/key_called_multiple_times.php.inc new file mode 100644 index 00000000000..aa733fd9ead --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/key_called_multiple_times.php.inc @@ -0,0 +1,67 @@ + +----- + diff --git a/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/multiple_usage.php.inc b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/multiple_usage.php.inc new file mode 100644 index 00000000000..fa9b180039a --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/multiple_usage.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/skip_both_reset_and_key_assigned.php.inc b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/skip_both_reset_and_key_assigned.php.inc new file mode 100644 index 00000000000..c519060b79c --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/skip_both_reset_and_key_assigned.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/skip_prev_call_after.php.inc b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/skip_prev_call_after.php.inc new file mode 100644 index 00000000000..6386850dc5b --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/ArrayKeyFirstLastRector/Fixture/skip_prev_call_after.php.inc @@ -0,0 +1,27 @@ +services(); - $services->set(ArrayKeyFirstLastRector::class); -}; +return RectorConfig::configure() + ->withRules([ArrayKeyFirstLastRector::class]); diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/fixture.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/fixture.php.inc index e47df536fe3..4447810e978 100644 --- a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/fixture.php.inc +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/fixture.php.inc @@ -10,6 +10,8 @@ function jsonThrowOnError() json_decode($json, true, 215); json_decode($json, true, 122, JSON_THROW_ON_ERROR); + + json_decode($json, true, 122, JSON_UNESCAPED_UNICODE); } ?> @@ -26,6 +28,8 @@ function jsonThrowOnError() json_decode($json, true, 215, JSON_THROW_ON_ERROR); json_decode($json, true, 122, JSON_THROW_ON_ERROR); + + json_decode($json, true, 122, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); } ?> diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/json_last_error_next_after_return.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/json_last_error_next_after_return.php.inc index ae1fe8e7f06..7f8e807748c 100644 --- a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/json_last_error_next_after_return.php.inc +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/json_last_error_next_after_return.php.inc @@ -30,4 +30,4 @@ function JsonLastErrorNextAfterReturn($str) json_last_error_msg(); } -?> \ No newline at end of file +?> diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/json_last_error_next_after_return2.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/json_last_error_next_after_return2.php.inc index 380829b8441..8be2214843c 100644 --- a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/json_last_error_next_after_return2.php.inc +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/json_last_error_next_after_return2.php.inc @@ -28,4 +28,4 @@ function JsonLastErrorNextAfterReturn2($str) json_last_error_msg(); } -?> \ No newline at end of file +?> diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/process_content_from_param.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/process_content_from_param.php.inc new file mode 100644 index 00000000000..6dcfc79aac6 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/process_content_from_param.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/process_mixed_content.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/process_mixed_content.php.inc new file mode 100644 index 00000000000..3792dcd129c --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/process_mixed_content.php.inc @@ -0,0 +1,53 @@ + $message, + 'type' => 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html', + 'title' => 'Unprocessable Entity', + 'status' => $code, + 'detail' => $detail, + ]; + + $response = json_encode($jsonData); + assert(is_string($response)); +} + +function processMixedContent2($data){ + $json = "{$data}"; + + $response = json_decode($json); + assert(is_array($response)); +} + +?> +----- + $message, + 'type' => 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html', + 'title' => 'Unprocessable Entity', + 'status' => $code, + 'detail' => $detail, + ]; + + $response = json_encode($jsonData, JSON_THROW_ON_ERROR); + assert(is_string($response)); +} + +function processMixedContent2($data){ + $json = "{$data}"; + + $response = json_decode($json, null, 512, JSON_THROW_ON_ERROR); + assert(is_array($response)); +} + +?> diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_exact_value.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_exact_value.php.inc new file mode 100644 index 00000000000..a66833e5a7a --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_exact_value.php.inc @@ -0,0 +1,28 @@ + + [ + 'foo' => [ + 'regexNotMatch' => 'The input does not match against pattern \'/^[a-zA-Z0-9 .\-]+$/\'' + ] + ], + 'type' => 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html', + 'title' => 'Unprocessable Entity', + 'status' => 422, + 'detail' => 'Failed Validation' + ]; + + $response = json_encode($jsonData); + assert(is_string($response)); +} + +function skipExactValue2(){ + $json = '{}'; + + $response = json_decode($json); + assert(is_array($response)); +} diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_json_last_error_next3.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_json_last_error_next3.php.inc deleted file mode 100644 index a5ff8de488f..00000000000 --- a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_json_last_error_next3.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_named_argument_flags.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_named_argument_flags.php.inc new file mode 100644 index 00000000000..24b0df4e808 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/skip_named_argument_flags.php.inc @@ -0,0 +1,8 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/config/configured_rule.php b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/config/configured_rule.php index 17d4542208c..d5ae69666d9 100644 --- a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/config/configured_rule.php +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(JsonThrowOnErrorRector::class); -}; +return RectorConfig::configure() + ->withRules([JsonThrowOnErrorRector::class]); diff --git a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/escaped_quote.php.inc b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/escaped_quote.php.inc new file mode 100644 index 00000000000..96a4f225f78 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/escaped_quote.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_more_quantifiers.php.inc b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_more_quantifiers.php.inc new file mode 100644 index 00000000000..8274e9ad7f0 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_more_quantifiers.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_next_non_quantifier.php.inc b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_next_non_quantifier.php.inc new file mode 100644 index 00000000000..c65297e43d8 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_next_non_quantifier.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_word_fixture.php.inc b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_word_fixture.php.inc new file mode 100644 index 00000000000..c74f03bc419 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_double_escape_word_fixture.php.inc @@ -0,0 +1,12 @@ + diff --git a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_heredoc.php.inc b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_heredoc.php.inc new file mode 100644 index 00000000000..bf15ad06d13 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_heredoc.php.inc @@ -0,0 +1,10 @@ + ------ - diff --git a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/RegexDashEscapeRectorTest.php b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/RegexDashEscapeRectorTest.php index fc7f50dcdcd..32bdd67f0bc 100644 --- a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/RegexDashEscapeRectorTest.php +++ b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/RegexDashEscapeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php73\Rector\FuncCall\RegexDashEscapeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RegexDashEscapeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/config/configured_rule.php b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/config/configured_rule.php index eeb1817d9dc..9094442e246 100644 --- a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/config/configured_rule.php +++ b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php73\Rector\FuncCall\RegexDashEscapeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RegexDashEscapeRector::class); -}; +return RectorConfig::configure() + ->withRules([RegexDashEscapeRector::class]); diff --git a/rules-tests/Php73/Rector/FuncCall/SensitiveDefineRector/SensitiveDefineRectorTest.php b/rules-tests/Php73/Rector/FuncCall/SensitiveDefineRector/SensitiveDefineRectorTest.php index e4fa2ea80a6..935548e0bcf 100644 --- a/rules-tests/Php73/Rector/FuncCall/SensitiveDefineRector/SensitiveDefineRectorTest.php +++ b/rules-tests/Php73/Rector/FuncCall/SensitiveDefineRector/SensitiveDefineRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php73\Rector\FuncCall\SensitiveDefineRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SensitiveDefineRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/FuncCall/SensitiveDefineRector/config/configured_rule.php b/rules-tests/Php73/Rector/FuncCall/SensitiveDefineRector/config/configured_rule.php index b171e075823..4aceb4525b5 100644 --- a/rules-tests/Php73/Rector/FuncCall/SensitiveDefineRector/config/configured_rule.php +++ b/rules-tests/Php73/Rector/FuncCall/SensitiveDefineRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php73\Rector\FuncCall\SensitiveDefineRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SensitiveDefineRector::class); -}; +return RectorConfig::configure() + ->withRules([SensitiveDefineRector::class]); diff --git a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/options_specified.php.inc b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/options_specified.php.inc index 746c8cf00c9..7366cde9fa1 100644 --- a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/options_specified.php.inc +++ b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/options_specified.php.inc @@ -12,6 +12,7 @@ class OptionsSpecified $secure = false; $httponly = false; setcookie('name', 'value', 3600); + setcookie('name', 'value', $expire); setcookie('name', 'value', $expire, $path); setcookie('name', 'value', $expire, $path, $domain); setcookie('name', 'value', $expire, $path, $domain, $secure); @@ -40,6 +41,7 @@ class OptionsSpecified $secure = false; $httponly = false; setcookie('name', 'value', ['expires' => 3600]); + setcookie('name', 'value', ['expires' => $expire]); setcookie('name', 'value', ['expires' => $expire, 'path' => $path]); setcookie('name', 'value', ['expires' => $expire, 'path' => $path, 'domain' => $domain]); setcookie('name', 'value', ['expires' => $expire, 'path' => $path, 'domain' => $domain, 'secure' => $secure]); diff --git a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/skip_non_integer_third_arg.inc b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/skip_non_integer_third_arg.inc new file mode 100644 index 00000000000..78f2e13f496 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/skip_non_integer_third_arg.inc @@ -0,0 +1,18 @@ + 12344, + "httponly" => true + ]; + } + + setcookie('mycookie', 'foobar', getCookieParams()); + } +} diff --git a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/skip_set_cookie_without_modifier.php.inc b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/skip_set_cookie_without_modifier.php.inc index 1c030267297..0bdbb6ae8fc 100644 --- a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/skip_set_cookie_without_modifier.php.inc +++ b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/Fixture/skip_set_cookie_without_modifier.php.inc @@ -14,7 +14,6 @@ class SkipSetCookieWithoutModifier $args = [$name, $value, $expire]; setcookie($name); setcookie($name, $value); - setcookie($name, $value, $expire); setcookie($name, $value, ...$args); setcookie(...$args); setrawcookie('name'); diff --git a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/SetCookieRectorTest.php b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/SetCookieRectorTest.php index 65d2955264f..a171e61176a 100644 --- a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/SetCookieRectorTest.php +++ b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/SetCookieRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php73\Rector\FuncCall\SetcookieRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SetCookieRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/config/configured_rule.php b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/config/configured_rule.php index 07ea070f944..77334d907ac 100644 --- a/rules-tests/Php73/Rector/FuncCall/SetcookieRector/config/configured_rule.php +++ b/rules-tests/Php73/Rector/FuncCall/SetcookieRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php73\Rector\FuncCall\SetCookieRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SetCookieRector::class); -}; +return RectorConfig::configure() + ->withRules([SetCookieRector::class]); diff --git a/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/Fixture/on_cast.php.inc b/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/Fixture/on_cast.php.inc new file mode 100644 index 00000000000..106fea17f07 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/Fixture/on_cast.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/Fixture/skip_encapsed.php.inc b/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/Fixture/skip_encapsed.php.inc new file mode 100644 index 00000000000..058fcaa1052 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/Fixture/skip_encapsed.php.inc @@ -0,0 +1,9 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/config/configured_rule.php b/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/config/configured_rule.php index ed45b0ed428..6ee663806e0 100644 --- a/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/config/configured_rule.php +++ b/rules-tests/Php73/Rector/FuncCall/StringifyStrNeedlesRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StringifyStrNeedlesRector::class); -}; +return RectorConfig::configure() + ->withRules([StringifyStrNeedlesRector::class]); diff --git a/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/Fixture/fixture.php.inc b/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/Fixture/fixture.php.inc index 8b8c2ae204b..c35f434f559 100644 --- a/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/Fixture/fixture.php.inc +++ b/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/Fixture/fixture.php.inc @@ -29,19 +29,19 @@ namespace Rector\Tests\Php73\Rector\String_\SensitiveHereNowDocRector\Fixture; function sensitiveHereNowDoc() { $value = << diff --git a/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/SensitiveHereNowDocRectorTest.php b/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/SensitiveHereNowDocRectorTest.php index 874f1847a96..e4d904a6e8f 100644 --- a/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/SensitiveHereNowDocRectorTest.php +++ b/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/SensitiveHereNowDocRectorTest.php @@ -5,32 +5,23 @@ namespace Rector\Tests\Php73\Rector\String_\SensitiveHereNowDocRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * Minor differences on windows, see https://github.com/rectorphp/rector/issues/6571 */ final class SensitiveHereNowDocRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - if ($this->isWindows()) { - $this->markTestSkipped('minor differences on windows, see https://github.com/rectorphp/rector/issues/6571'); - } - - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/config/configured_rule.php b/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/config/configured_rule.php index 233f599e4be..81e3fce3f33 100644 --- a/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/config/configured_rule.php +++ b/rules-tests/Php73/Rector/String_/SensitiveHereNowDocRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php73\Rector\String_\SensitiveHereNowDocRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SensitiveHereNowDocRector::class); -}; +return RectorConfig::configure() + ->withRules([SensitiveHereNowDocRector::class]); diff --git a/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/CurlyToSquareBracketArrayStringRectorTest.php b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/CurlyToSquareBracketArrayStringRectorTest.php new file mode 100644 index 00000000000..c23b060afc5 --- /dev/null +++ b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/CurlyToSquareBracketArrayStringRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/OnTrait.php.inc b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/OnTrait.php.inc new file mode 100644 index 00000000000..7eaa824839e --- /dev/null +++ b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/OnTrait.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..48f903b8e30 --- /dev/null +++ b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/fixture.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/skip_already_square.php.inc b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/skip_already_square.php.inc new file mode 100644 index 00000000000..db1c16e0462 --- /dev/null +++ b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/Fixture/skip_already_square.php.inc @@ -0,0 +1,11 @@ + 'AVAL']; + + echo "abc ${row['akey']} xyz"; + } +} diff --git a/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/config/configured_rule.php b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/config/configured_rule.php new file mode 100644 index 00000000000..269e8daab4f --- /dev/null +++ b/rules-tests/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector/config/configured_rule.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersionFeature::DEPRECATE_CURLY_BRACKET_ARRAY_STRING); + + $rectorConfig->rule(CurlyToSquareBracketArrayStringRector::class); +}; diff --git a/rules-tests/Php74/Rector/Assign/NullCoalescingOperatorRector/NullCoalescingOperatorRectorTest.php b/rules-tests/Php74/Rector/Assign/NullCoalescingOperatorRector/NullCoalescingOperatorRectorTest.php index 7d9704a1096..8730084adfc 100644 --- a/rules-tests/Php74/Rector/Assign/NullCoalescingOperatorRector/NullCoalescingOperatorRectorTest.php +++ b/rules-tests/Php74/Rector/Assign/NullCoalescingOperatorRector/NullCoalescingOperatorRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php74\Rector\Assign\NullCoalescingOperatorRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class NullCoalescingOperatorRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php74/Rector/Assign/NullCoalescingOperatorRector/config/configured_rule.php b/rules-tests/Php74/Rector/Assign/NullCoalescingOperatorRector/config/configured_rule.php index 7d6e369fa03..987fc2a93d2 100644 --- a/rules-tests/Php74/Rector/Assign/NullCoalescingOperatorRector/config/configured_rule.php +++ b/rules-tests/Php74/Rector/Assign/NullCoalescingOperatorRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php74\Rector\Assign\NullCoalescingOperatorRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(NullCoalescingOperatorRector::class); -}; +return RectorConfig::configure() + ->withRules([NullCoalescingOperatorRector::class]); diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/ClosureToArrowFunctionRectorTest.php b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/ClosureToArrowFunctionRectorTest.php index a92db90003c..ff0461bb02e 100644 --- a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/ClosureToArrowFunctionRectorTest.php +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/ClosureToArrowFunctionRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php74\Rector\Closure\ClosureToArrowFunctionRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ClosureToArrowFunctionRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/as_arg.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/as_arg.php.inc new file mode 100644 index 00000000000..448d2e4b1f9 --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/as_arg.php.inc @@ -0,0 +1,34 @@ +execute(function() { + // some comment + /** @psalm-suppress UndefinedFunction */ + return ff(); + }); + } +} + +?> +----- +execute( + // some comment + /** @psalm-suppress UndefinedFunction */ + fn() => ff()); + } +} + +?> diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/keep_docblock_on_return.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/keep_docblock_on_return.php.inc new file mode 100644 index 00000000000..d3463cd084a --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/keep_docblock_on_return.php.inc @@ -0,0 +1,39 @@ + +----- + ff(); + + // @psalm-suppress UndefinedFunction + fn() => ff(); + } +} + +?> diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/keep_multi_comments_with_variable_assign_on_return.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/keep_multi_comments_with_variable_assign_on_return.php.inc new file mode 100644 index 00000000000..05d9eeba973 --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/keep_multi_comments_with_variable_assign_on_return.php.inc @@ -0,0 +1,34 @@ + +----- + ff()); + } +} + +?> diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/nested_closures.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/nested_closures.php.inc new file mode 100644 index 00000000000..e7c520e0097 --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/nested_closures.php.inc @@ -0,0 +1,103 @@ + +----- + fn($bar) => fn() => [$foo, $bar]; + } + + public function many() + { + return fn($foo) => fn($bar) => fn($baz) => fn() => [$foo, $bar, $baz]; + } + + public function toomany() + { + return fn($foo) => fn($bar) => fn($baz) => fn($bat) => fn() => [$foo, $bar, $baz, $bat]; + } + + public function mixed() + { + return fn($foo) => fn($bar) => fn() => [$foo, $bar]; + } + + public function sameParamName() + { + return fn($foo) => fn($foo) => fn($bar) => $foo === $bar; + } +} + +?> diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/reference_to_inner_closure_unused.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/reference_to_inner_closure_unused.php.inc new file mode 100644 index 00000000000..f5e766391d3 --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/reference_to_inner_closure_unused.php.inc @@ -0,0 +1,35 @@ + +----- + fn() => 1); + + return $y; + } +} + +?> diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/referenced_but_not_used.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/referenced_but_not_used.php.inc index 0dd4566b0b2..42c561f484f 100644 --- a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/referenced_but_not_used.php.inc +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/referenced_but_not_used.php.inc @@ -22,7 +22,7 @@ class ReferencedButNotUsed { public function run() { - $callback = fn($b) => ++$b; + $callback = (fn($b) => ++$b); } } diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/referenced_inner_class.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/referenced_inner_class.php.inc new file mode 100644 index 00000000000..e30d558c51d --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/referenced_inner_class.php.inc @@ -0,0 +1,35 @@ + +----- + new class { public function __construct($i) + { + } + }); + } +} + +?> diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_arrow_function_already_with_comment.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_arrow_function_already_with_comment.php.inc new file mode 100644 index 00000000000..dcde48bb19d --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_arrow_function_already_with_comment.php.inc @@ -0,0 +1,9 @@ + + /** + * comment + */ + // something + // @psalm-suppress UndefinedFunction + ff(); diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_closure_with_var_docblock_generic.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_closure_with_var_docblock_generic.php.inc new file mode 100644 index 00000000000..7fcb85b2eba --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_closure_with_var_docblock_generic.php.inc @@ -0,0 +1,16 @@ + $query */ + return $query->count(); + }; + } +} + +?> diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_inside_attribute.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_inside_attribute.php.inc new file mode 100644 index 00000000000..72e9b75296c --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_inside_attribute.php.inc @@ -0,0 +1,14 @@ +myCallback( + function () use ($test) { + return compact('test'); + } + ); + } + + private function myCallback($callback) + { + $callback(); + } +} diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_with_specific_var_doc_type.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_with_specific_var_doc_type.php.inc new file mode 100644 index 00000000000..5c658f37f6e --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/skip_with_specific_var_doc_type.php.inc @@ -0,0 +1,16 @@ +put(...func_get_args()); + }); + } +} diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/with_equal_var_doc_type.php.inc b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/with_equal_var_doc_type.php.inc new file mode 100644 index 00000000000..27e647dd63a --- /dev/null +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/Fixture/with_equal_var_doc_type.php.inc @@ -0,0 +1,16 @@ + diff --git a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/config/configured_rule.php b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/config/configured_rule.php index 63c9794b7cf..3761960d6ce 100644 --- a/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/config/configured_rule.php +++ b/rules-tests/Php74/Rector/Closure/ClosureToArrowFunctionRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ClosureToArrowFunctionRector::class); -}; +return RectorConfig::configure() + ->withRules([ClosureToArrowFunctionRector::class]); diff --git a/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/Fixture/fixture.php.inc deleted file mode 100644 index d11a32c423c..00000000000 --- a/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/RealToFloatTypeCastRectorTest.php b/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/RealToFloatTypeCastRectorTest.php deleted file mode 100644 index a1f2be8e7e8..00000000000 --- a/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/RealToFloatTypeCastRectorTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/config/configured_rule.php b/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/config/configured_rule.php deleted file mode 100644 index 5c18edf0c0d..00000000000 --- a/rules-tests/Php74/Rector/Double/RealToFloatTypeCastRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RealToFloatTypeCastRector::class); -}; diff --git a/rules-tests/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector/ArrayKeyExistsOnPropertyRectorTest.php b/rules-tests/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector/ArrayKeyExistsOnPropertyRectorTest.php index 733935b9ab6..e42222d0191 100644 --- a/rules-tests/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector/ArrayKeyExistsOnPropertyRectorTest.php +++ b/rules-tests/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector/ArrayKeyExistsOnPropertyRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP < 8.0 - */ final class ArrayKeyExistsOnPropertyRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector/config/configured_rule.php b/rules-tests/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector/config/configured_rule.php index 94263315ff9..aca73187819 100644 --- a/rules-tests/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector/config/configured_rule.php +++ b/rules-tests/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ArrayKeyExistsOnPropertyRector::class); -}; +return RectorConfig::configure() + ->withRules([ArrayKeyExistsOnPropertyRector::class]); diff --git a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/ArraySpreadInsteadOfArrayMergeRectorTest.php b/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/ArraySpreadInsteadOfArrayMergeRectorTest.php deleted file mode 100644 index 98ccbc8b07f..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/ArraySpreadInsteadOfArrayMergeRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/integer_keys.php.inc b/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/integer_keys.php.inc deleted file mode 100644 index a1e36df88fe..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/integer_keys.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - 'two']; - $iter2 = [1 => 'four']; - - return array_merge($iter1, $iter2); - } -} - -?> ------ - 'two']; - $iter2 = [1 => 'four']; - - return [...$iter1, ...$iter2]; - } -} - -?> diff --git a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_get_iterator.php.inc b/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_get_iterator.php.inc deleted file mode 100644 index 667b98fa61e..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_get_iterator.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -files() - ->in(__DIR__ . '/Source'); - - $files = iterator_to_array($finder->getIterator()); - } -} diff --git a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_iterator_to_array.php.inc b/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_iterator_to_array.php.inc deleted file mode 100644 index a9be673de3e..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_iterator_to_array.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_simple_array_merge.php.inc b/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_simple_array_merge.php.inc deleted file mode 100644 index 762a704641d..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_simple_array_merge.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - 'two']; - $iter2 = ['three' => 'four']; - - return array_merge($iter1, $iter2); - } - - public function go() - { - $iter1 = [1 => 'two']; - $iter2 = ['three' => 'four']; - - return array_merge($iter1, $iter2); - } -} diff --git a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_string_keys_from_functions.php.inc b/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_string_keys_from_functions.php.inc deleted file mode 100644 index 79b70d9e9fc..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/Fixture/skip_string_keys_from_functions.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -1]; -} -function y(): array -{ - return ['a'=>1]; -} - -class SkipStringKeysFromFunctions -{ - public function run() - { - return array_merge(y(), x()); - } -} diff --git a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule.php b/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule.php deleted file mode 100644 index 5e6fdd1cc67..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ArraySpreadInsteadOfArrayMergeRector::class); -}; diff --git a/rules-tests/Php74/Rector/FuncCall/FilterVarToAddSlashesRector/FilterVarToAddSlashesRectorTest.php b/rules-tests/Php74/Rector/FuncCall/FilterVarToAddSlashesRector/FilterVarToAddSlashesRectorTest.php index 79c8e2661ce..6f4e14635ad 100644 --- a/rules-tests/Php74/Rector/FuncCall/FilterVarToAddSlashesRector/FilterVarToAddSlashesRectorTest.php +++ b/rules-tests/Php74/Rector/FuncCall/FilterVarToAddSlashesRector/FilterVarToAddSlashesRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\Php74\Rector\FuncCall\FilterVarToAddSlashesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP < 8.0 - */ final class FilterVarToAddSlashesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php74/Rector/FuncCall/FilterVarToAddSlashesRector/config/configured_rule.php b/rules-tests/Php74/Rector/FuncCall/FilterVarToAddSlashesRector/config/configured_rule.php index 0f05c3cf9f4..880e5910405 100644 --- a/rules-tests/Php74/Rector/FuncCall/FilterVarToAddSlashesRector/config/configured_rule.php +++ b/rules-tests/Php74/Rector/FuncCall/FilterVarToAddSlashesRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php74\Rector\FuncCall\FilterVarToAddSlashesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(FilterVarToAddSlashesRector::class); -}; +return RectorConfig::configure() + ->withRules([FilterVarToAddSlashesRector::class]); diff --git a/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/fixture.php.inc deleted file mode 100644 index 5c4f8a4d4c4..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/GetCalledClassToStaticClassRectorTest.php b/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/GetCalledClassToStaticClassRectorTest.php deleted file mode 100644 index 7eed89440fe..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/GetCalledClassToStaticClassRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/config/configured_rule.php b/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/config/configured_rule.php deleted file mode 100644 index 4691213f57d..00000000000 --- a/rules-tests/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(GetCalledClassToStaticClassRector::class); -}; diff --git a/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..948783ba763 --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..e01531e7fc2 --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/skip_other_func_call.php.inc b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/skip_other_func_call.php.inc new file mode 100644 index 00000000000..c96e11a7b97 --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/Fixture/skip_other_func_call.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/HebrevcToNl2brHebrevRectorTest.php b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/HebrevcToNl2brHebrevRectorTest.php new file mode 100644 index 00000000000..a6a9729a02d --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/HebrevcToNl2brHebrevRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/config/configured_rule.php b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/config/configured_rule.php new file mode 100644 index 00000000000..4ce16ba8ed8 --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([HebrevcToNl2brHebrevRector::class]); diff --git a/rules-tests/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector/MbStrrposEncodingArgumentPositionRectorTest.php b/rules-tests/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector/MbStrrposEncodingArgumentPositionRectorTest.php index 36e0ae4aabd..73f092520dc 100644 --- a/rules-tests/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector/MbStrrposEncodingArgumentPositionRectorTest.php +++ b/rules-tests/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector/MbStrrposEncodingArgumentPositionRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class MbStrrposEncodingArgumentPositionRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector/config/configured_rule.php b/rules-tests/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector/config/configured_rule.php index c62eac3b40d..e76c88f1059 100644 --- a/rules-tests/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector/config/configured_rule.php +++ b/rules-tests/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(MbStrrposEncodingArgumentPositionRector::class); -}; +return RectorConfig::configure() + ->withRules([MbStrrposEncodingArgumentPositionRector::class]); diff --git a/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..688bc429ceb --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/Fixture/return_stmt.php.inc b/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/Fixture/return_stmt.php.inc new file mode 100644 index 00000000000..b6362fd1c9e --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/Fixture/return_stmt.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/MoneyFormatToNumberFormatRectorTest.php b/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/MoneyFormatToNumberFormatRectorTest.php new file mode 100644 index 00000000000..bd5147f6f08 --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/MoneyFormatToNumberFormatRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/config/configured_rule.php b/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/config/configured_rule.php new file mode 100644 index 00000000000..9d785b51a2e --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([MoneyFormatToNumberFormatRector::class]); diff --git a/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..15098d44fac --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..76aea3538e4 --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/skip_other_func_call.php.inc b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/skip_other_func_call.php.inc new file mode 100644 index 00000000000..3688041de4a --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/Fixture/skip_other_func_call.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/RestoreIncludePathToIniRestoreRectorTest.php b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/RestoreIncludePathToIniRestoreRectorTest.php new file mode 100644 index 00000000000..0baf2f8328d --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/RestoreIncludePathToIniRestoreRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/config/configured_rule.php b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/config/configured_rule.php new file mode 100644 index 00000000000..fd80d007c59 --- /dev/null +++ b/rules-tests/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RestoreIncludePathToIniRestoreRector::class]); diff --git a/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/Fixture/fixture.php.inc deleted file mode 100644 index 8d3c1716fb4..00000000000 --- a/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/ReservedFnFunctionRectorTest.php b/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/ReservedFnFunctionRectorTest.php deleted file mode 100644 index 6c3a6ff3c65..00000000000 --- a/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/ReservedFnFunctionRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/config/configured_rule.php b/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/config/configured_rule.php deleted file mode 100644 index 387f14a9849..00000000000 --- a/rules-tests/Php74/Rector/Function_/ReservedFnFunctionRector/config/configured_rule.php +++ /dev/null @@ -1,17 +0,0 @@ -services(); - $services->set(ReservedFnFunctionRector::class) - ->call('configure', [[ - ReservedFnFunctionRector::RESERVED_NAMES_TO_NEW_ONES => [ - // for testing purposes of "fn" even on PHP 7.3- - 'reservedFn' => 'f', - ], - ]]); -}; diff --git a/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/AddLiteralSeparatorToNumberRectorTest.php b/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/AddLiteralSeparatorToNumberRectorTest.php deleted file mode 100644 index 09ecd2b0b35..00000000000 --- a/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/AddLiteralSeparatorToNumberRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/Fixture/fixture.php.inc deleted file mode 100644 index fa1e147b74a..00000000000 --- a/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/Fixture/skip_already_formatted.php.inc b/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/Fixture/skip_already_formatted.php.inc deleted file mode 100644 index e879eda9943..00000000000 --- a/rules-tests/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector/Fixture/skip_already_formatted.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(AddLiteralSeparatorToNumberRector::class) - ->call('configure', [[ - AddLiteralSeparatorToNumberRector::LIMIT_VALUE => 1_000_000, - ]]); -}; diff --git a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/ChangeReflectionTypeToStringToGetNameRectorTest.php b/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/ChangeReflectionTypeToStringToGetNameRectorTest.php deleted file mode 100644 index de6491fe52e..00000000000 --- a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/ChangeReflectionTypeToStringToGetNameRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/fixture.php.inc deleted file mode 100644 index 1e05913fa60..00000000000 --- a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -getParameters()[0]; - - $paramType = (string) $parameterReflection->getType(); - - $stringValue = 'hey' . $reflectionFunction->getReturnType(); - } -} - -?> ------ -getParameters()[0]; - - $paramType = (string) ($parameterReflection->getType() ? $parameterReflection->getType()->getName() : null); - - $stringValue = 'hey' . ($reflectionFunction->getReturnType() ? $reflectionFunction->getReturnType()->getName() : null); - } -} - -?> diff --git a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/keep_returned_value.php.inc b/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/keep_returned_value.php.inc deleted file mode 100644 index bb595454fe8..00000000000 --- a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/keep_returned_value.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -getReturnType(); - } -} diff --git a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/known_has_type.php.inc b/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/known_has_type.php.inc deleted file mode 100644 index bf7397a6028..00000000000 --- a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/known_has_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -hasReturnType()) { - return (string) $reflectionMethod->getReturnType(); - } - } -} - -?> ------ -hasReturnType()) { - return $reflectionMethod->getReturnType()->getName(); - } - } -} - -?> diff --git a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/parameter_type.php.inc b/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/parameter_type.php.inc deleted file mode 100644 index 1ff95af3ee6..00000000000 --- a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/parameter_type.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ -getName(); - } -} - -?> diff --git a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/skip_cast_string_on_reflection_type.php.inc b/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/skip_cast_string_on_reflection_type.php.inc deleted file mode 100644 index ce4ef023319..00000000000 --- a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/skip_cast_string_on_reflection_type.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -getReturnType(); - - return $returnType instanceof ReflectionNamedType - ? $returnType->getName() - : (string) $returnType; - } -} - -?> diff --git a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/skip_non_to_string.php.inc b/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/skip_non_to_string.php.inc deleted file mode 100644 index fc555ff7f8a..00000000000 --- a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/Fixture/skip_non_to_string.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -getType(); - if ($parameterType !== null) { - /** @var ReflectionType $parameterType */ - $parameterType = $parameterType->getName(); - } - } -} diff --git a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/config/configured_rule.php b/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/config/configured_rule.php deleted file mode 100644 index 485d0587ce5..00000000000 --- a/rules-tests/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ChangeReflectionTypeToStringToGetNameRector::class); -}; diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_array_type_on_trait.php.inc b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_array_type_on_trait.php.inc new file mode 100644 index 00000000000..2c56b545a03 --- /dev/null +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_array_type_on_trait.php.inc @@ -0,0 +1,16 @@ +targets = $targets; + + return $this; + } +} \ No newline at end of file diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_assign_with_method_call_property_same_name.php.inc b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_assign_with_method_call_property_same_name.php.inc new file mode 100644 index 00000000000..42920ba9e72 --- /dev/null +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_assign_with_method_call_property_same_name.php.inc @@ -0,0 +1,19 @@ +bookmarkFeature = $relation->getRelationParameters()->bookmarkFeature; + } +} \ No newline at end of file diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_assigned_in_construct.php.inc b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_assigned_in_construct.php.inc index a319ee3d995..c5feb1994ce 100644 --- a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_assigned_in_construct.php.inc +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_assigned_in_construct.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector\Fixture; -class SkipAssignedInConstruct +final class SkipAssignedInConstruct { public ?string $name; diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_checked.php.inc b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_checked.php.inc new file mode 100644 index 00000000000..388959e976e --- /dev/null +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_checked.php.inc @@ -0,0 +1,19 @@ +name = $item; + } + + if (! isset($this->item)) { + throw new \InvalidArgumentException(); + } + } +} diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_property_hook.php.inc b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_property_hook.php.inc new file mode 100644 index 00000000000..6600d8b318f --- /dev/null +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_property_hook.php.inc @@ -0,0 +1,10 @@ + 'bar'; + } +} diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_readonly.php.inc b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_readonly.php.inc new file mode 100644 index 00000000000..c117a206348 --- /dev/null +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_readonly.php.inc @@ -0,0 +1,8 @@ + self::LEVEL_NONE, + self::LEVEL_SHORT_TERM => self::LEVEL_SHORT_TERM, + ]; + + /** + * @var self::LEVEL_*|null + * + * @readonly + */ + private ?string $level; + + private function __construct(string $level) + { + if (isset(self::LEVELS[$level])) { + $this->level = self::LEVELS[$level]; + } else { + $this->level = null; + } + } + +} diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_readonly_phpdoc_class.php.inc b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_readonly_phpdoc_class.php.inc new file mode 100644 index 00000000000..846e5ab5a46 --- /dev/null +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/Fixture/skip_readonly_phpdoc_class.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/RestoreDefaultNullToNullableTypePropertyRectorTest.php b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/RestoreDefaultNullToNullableTypePropertyRectorTest.php index 687286f5896..7f377a3c8c7 100644 --- a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/RestoreDefaultNullToNullableTypePropertyRectorTest.php +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/RestoreDefaultNullToNullableTypePropertyRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RestoreDefaultNullToNullableTypePropertyRectorTest extends AbstractRectorTestCase { - /** - * @requires PHP 7.4 - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/config/configured_rule.php b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/config/configured_rule.php index 77c1bfb48f6..61052ae103a 100644 --- a/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/config/configured_rule.php +++ b/rules-tests/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RestoreDefaultNullToNullableTypePropertyRector::class); -}; +return RectorConfig::configure() + ->withRules([RestoreDefaultNullToNullableTypePropertyRector::class]); diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/ClassLikeTypesOnlyTest.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/ClassLikeTypesOnlyTest.php deleted file mode 100644 index 96224706441..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/ClassLikeTypesOnlyTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureClassLikeTypeOnly'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/class_types_only.php'; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/DoctrineTypedPropertyRectorTest.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/DoctrineTypedPropertyRectorTest.php deleted file mode 100644 index e7c3556fd7f..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/DoctrineTypedPropertyRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureDoctrine'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/add_null.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/add_null.php.inc deleted file mode 100644 index 6552a141d17..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/add_null.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -name = $name; - } - - public function getName(): ?string - { - return $this->name; - } -} - -?> ------ -name = $name; - } - - public function getName(): ?string - { - return $this->name; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/assert_choice.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/assert_choice.php.inc deleted file mode 100644 index 6b0d241e207..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/assert_choice.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/autowired_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/autowired_property.php.inc deleted file mode 100644 index b8a32d9ef17..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/autowired_property.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -anotherClass = $anotherClass; - } -} - -?> ------ -anotherClass = $anotherClass; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/bool_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/bool_property.php.inc deleted file mode 100644 index 840495a433c..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/bool_property.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/child_class_has_another_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/child_class_has_another_property.php.inc deleted file mode 100644 index f8074a86499..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/child_class_has_another_property.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/class_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/class_property.php.inc deleted file mode 100644 index 74975aed1e7..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/class_property.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/complex_array.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/complex_array.php.inc deleted file mode 100644 index ab61d733a53..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/complex_array.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - - */ - private $foo; - - /** - * @var int[] - */ - private $foos; -} - -?> ------ - - */ - private array $foo; - - /** - * @var int[] - */ - private array $foos; -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values.php.inc deleted file mode 100644 index 2065aa14470..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values.php.inc +++ /dev/null @@ -1,54 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_bool.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_bool.php.inc deleted file mode 100644 index 97a0eb14af5..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_bool.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_for_nullable_array.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_for_nullable_array.php.inc deleted file mode 100644 index 82a030d8aa1..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_for_nullable_array.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_for_nullable_iterables.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_for_nullable_iterables.php.inc deleted file mode 100644 index 9921483b612..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_for_nullable_iterables.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_string.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_string.php.inc deleted file mode 100644 index 033f9a20f63..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_string.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -name; - } -} - -?> ------ -name; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_with_nullable.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_with_nullable.php.inc deleted file mode 100644 index 66708dd43e8..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/default_values_with_nullable.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_add_default_value_filled_by_construct.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_add_default_value_filled_by_construct.php.inc deleted file mode 100644 index 8420f2211a3..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_add_default_value_filled_by_construct.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -result = $result; - } - - public function getResult(): ?array - { - return $this->result; - } -} -?> ------ -result = $result; - } - - public function getResult(): ?array - { - return $this->result; - } -} -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_add_default_value_filled_by_construct2.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_add_default_value_filled_by_construct2.php.inc deleted file mode 100644 index 44affdbc629..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_add_default_value_filled_by_construct2.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_remove_class_docblock_array.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_remove_class_docblock_array.php.inc deleted file mode 100644 index 2ff1732fdf2..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/do_not_remove_class_docblock_array.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/filled_by_parent_class.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/filled_by_parent_class.php.inc deleted file mode 100644 index 8e84c94543c..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/filled_by_parent_class.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/filled_by_trait.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/filled_by_trait.php.inc deleted file mode 100644 index 11b6e7d8087..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/filled_by_trait.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/generic_object_type.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/generic_object_type.php.inc deleted file mode 100644 index f756ed3bcf3..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/generic_object_type.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -command = $command; - } -} -?> ------ -command = $command; - } -} -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/if_conditional_union_nullable.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/if_conditional_union_nullable.php.inc deleted file mode 100644 index 019945b9fef..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/if_conditional_union_nullable.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -nullOrString = $returnString->getName(); - } - } -} - -?> ------ -nullOrString = $returnString->getName(); - } - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/infer_set_get.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/infer_set_get.php.inc deleted file mode 100644 index cb3d0bee72c..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/infer_set_get.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -name = $name; - } - - public function compareName(string $name): bool - { - return $name === $this->name; - } -} - -?> ------ -name = $name; - } - - public function compareName(string $name): bool - { - return $name === $this->name; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/inject_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/inject_property.php.inc deleted file mode 100644 index 37f79f6c057..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/inject_property.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/match_types.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/match_types.php.inc deleted file mode 100644 index a92a97bc173..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/match_types.php.inc +++ /dev/null @@ -1,80 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/match_types_parent.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/match_types_parent.php.inc deleted file mode 100644 index cfc06372b52..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/match_types_parent.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/michael_test.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/michael_test.php.inc deleted file mode 100644 index 5d12b9033c0..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/michael_test.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -count = 123; - } - - public function getCount():int - { - return $this->count; - } -} - -?> ------ -count = 123; - } - - public function getCount():int - { - return $this->count; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/no_condition_union_nullable.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/no_condition_union_nullable.php.inc deleted file mode 100644 index 193932d816c..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/no_condition_union_nullable.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -nullOrString = $returnString->getNameOrNull(); - } -} - -?> ------ -nullOrString = $returnString->getNameOrNull(); - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/non_array_generic_types.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/non_array_generic_types.php.inc deleted file mode 100644 index 95e69b99d24..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/non_array_generic_types.php.inc +++ /dev/null @@ -1,53 +0,0 @@ - - */ - private $foo; - - /** - * @var Promise - */ - private $bar; -} - -?> ------ - - */ - private \Traversable $foo; - - /** - * @var Promise - */ - private \Rector\Tests\Php74\Rector\Property\TypedPropertyRector\Fixture\Promise $bar; -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/not_in_tests_typed_property_in_tests.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/not_in_tests_typed_property_in_tests.php.inc deleted file mode 100644 index 7235a981ceb..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/not_in_tests_typed_property_in_tests.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -value = 1000; - } -} - -?> ------ -value = 1000; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/nullable_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/nullable_property.php.inc deleted file mode 100644 index 9f7eb232236..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/nullable_property.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/nullable_property_scalar.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/nullable_property_scalar.php.inc deleted file mode 100644 index 08b53a50fa3..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/nullable_property_scalar.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -countOrNull; - } -} - -?> ------ -countOrNull; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/property.php.inc deleted file mode 100644 index 6d16c43b4e1..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/property.php.inc +++ /dev/null @@ -1,54 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/simple_array.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/simple_array.php.inc deleted file mode 100644 index 18935209a34..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/simple_array.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_callable_property_type.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_callable_property_type.php.inc deleted file mode 100644 index dee45f893dc..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_callable_property_type.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -property = null; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_parent_from_vendor.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_parent_from_vendor.php.inc deleted file mode 100644 index 5c1ebc93555..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_parent_from_vendor.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -object = $this->createObject(); - } - - private function createObject() - { - return new \stdClass(); - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_trait.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_trait.php.inc deleted file mode 100644 index 93ec1cfacb8..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_trait.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -body = $body; - return $this; - } - - public function getJsonBody(): ?array { - return $this->body; - } -} - -class UsingTraig -{ - use JsonBodyTrait; -} - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_unioned_type.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_unioned_type.php.inc deleted file mode 100644 index 2c684ac09e8..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_unioned_type.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -cantTouchThis = 100; - } - - /** - * @return bool|int - */ - public function getCantTouchThis() - { - return $this->cantTouchThis; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_void.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_void.php.inc deleted file mode 100644 index ba7b3126afc..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/skip_void.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -count = $count; - } -} - -?> ------ -count = $count; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/static_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/static_property.php.inc deleted file mode 100644 index b8f9a23dab8..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/static_property.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/typed_property_in_tests.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/typed_property_in_tests.php.inc deleted file mode 100644 index eff16510087..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/typed_property_in_tests.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -phpDocInfoPrinter = $this->getService(PhpDocInfoPrinter::class); - } -} - -?> ------ -phpDocInfoPrinter = $this->getService(PhpDocInfoPrinter::class); - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureClassLikeTypeOnly/class_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureClassLikeTypeOnly/class_property.php.inc deleted file mode 100644 index 5d9656260ba..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureClassLikeTypeOnly/class_property.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureClassLikeTypeOnly/skip_bool_property.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureClassLikeTypeOnly/skip_bool_property.php.inc deleted file mode 100644 index ca1fe3f2f7f..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureClassLikeTypeOnly/skip_bool_property.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_collection_with_default_array.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_collection_with_default_array.php.inc deleted file mode 100644 index ea48cd62128..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_collection_with_default_array.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_id.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_id.php.inc deleted file mode 100644 index ee5cbef0916..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_id.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_intersect_collection.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_intersect_collection.php.inc deleted file mode 100644 index 83ea546a41c..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_intersect_collection.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_many_to_one_class_keyword.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_many_to_one_class_keyword.php.inc deleted file mode 100644 index 256ed886f8c..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_many_to_one_class_keyword.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_many_to_one_class_keyword_imported.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_many_to_one_class_keyword_imported.php.inc deleted file mode 100644 index fddf2c885e0..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_many_to_one_class_keyword_imported.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_one_to_one_class_keyword.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_one_to_one_class_keyword.php.inc deleted file mode 100644 index 1567181b79f..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureDoctrine/doctrine_one_to_one_class_keyword.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/already_imported_short_name.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/already_imported_short_name.php.inc deleted file mode 100644 index 65370338fd3..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/already_imported_short_name.php.inc +++ /dev/null @@ -1,42 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/conflicting_short_name.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/conflicting_short_name.php.inc deleted file mode 100644 index 0ebd0581895..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/conflicting_short_name.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/conflicting_short_name_in_constuctor.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/conflicting_short_name_in_constuctor.php.inc deleted file mode 100644 index 8f218146105..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureImported/conflicting_short_name_in_constuctor.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/include_unioned_type.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/include_unioned_type.php.inc deleted file mode 100644 index 505b4336fbe..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/include_unioned_type.php.inc +++ /dev/null @@ -1,50 +0,0 @@ -cantTouchThis = 100; - } - - /** - * @return bool|int - */ - public function getCantTouchThis() - { - return $this->cantTouchThis; - } -} - -?> ------ -cantTouchThis = 100; - } - - /** - * @return bool|int - */ - public function getCantTouchThis() - { - return $this->cantTouchThis; - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/set_if_else.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/set_if_else.php.inc deleted file mode 100644 index e917355392e..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/set_if_else.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -stringOrInteger = 'hey'; - } else { - $this->stringOrInteger = 1000; - } - } -} - -?> ------ -stringOrInteger = 'hey'; - } else { - $this->stringOrInteger = 1000; - } - } -} - -?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/two_types.php.inc b/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/two_types.php.inc deleted file mode 100644 index af7e394248e..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/FixtureUnionTypes/two_types.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/ImportedTest.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/ImportedTest.php deleted file mode 100644 index 5b189e09b32..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/ImportedTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureImported'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/imported_type.php'; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/AbstractSomeParent.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/AbstractSomeParent.php deleted file mode 100644 index 44cf30167eb..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/AbstractSomeParent.php +++ /dev/null @@ -1,9 +0,0 @@ -property = $anotherClass; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/FillerTrait.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/FillerTrait.php deleted file mode 100644 index d280ad83aac..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/FillerTrait.php +++ /dev/null @@ -1,13 +0,0 @@ -property = $anotherClass; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/Nested/ConflictingName.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/Nested/ConflictingName.php deleted file mode 100644 index fc1b7019acd..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Source/Nested/ConflictingName.php +++ /dev/null @@ -1,10 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/UnionTypedPropertyRectorTest.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/UnionTypedPropertyRectorTest.php deleted file mode 100644 index a39cd99d8fd..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/UnionTypedPropertyRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureUnionTypes'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/union_type_rule.php'; - } -} diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/class_types_only.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/class_types_only.php deleted file mode 100644 index 928cab8b8b8..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/class_types_only.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(TypedPropertyRector::class) - ->call('configure', [[ - TypedPropertyRector::CLASS_LIKE_TYPE_ONLY => true, - ]]); -}; diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/configured_rule.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/configured_rule.php deleted file mode 100644 index affc71024bd..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/configured_rule.php +++ /dev/null @@ -1,20 +0,0 @@ -services(); - $services->set(TypedPropertyRector::class); - - // should be ignored if typed property is used - $services->set(RemoveNullPropertyInitializationRector::class); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); -}; diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/imported_type.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/imported_type.php deleted file mode 100644 index 928da90c566..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/imported_type.php +++ /dev/null @@ -1,18 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(TypedPropertyRector::class) - ->call('configure', [[ - TypedPropertyRector::CLASS_LIKE_TYPE_ONLY => true, - ]]); -}; diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/union_type_rule.php b/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/union_type_rule.php deleted file mode 100644 index 84092566fb6..00000000000 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/config/union_type_rule.php +++ /dev/null @@ -1,16 +0,0 @@ -services(); - $services->set(TypedPropertyRector::class); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES); -}; diff --git a/rules-tests/Php74/Rector/StaticCall/ExportToReflectionFunctionRector/ExportToReflectionFunctionRectorTest.php b/rules-tests/Php74/Rector/StaticCall/ExportToReflectionFunctionRector/ExportToReflectionFunctionRectorTest.php index 961984f72cb..54b9395a261 100644 --- a/rules-tests/Php74/Rector/StaticCall/ExportToReflectionFunctionRector/ExportToReflectionFunctionRectorTest.php +++ b/rules-tests/Php74/Rector/StaticCall/ExportToReflectionFunctionRector/ExportToReflectionFunctionRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php74\Rector\StaticCall\ExportToReflectionFunctionRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ExportToReflectionFunctionRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php74/Rector/StaticCall/ExportToReflectionFunctionRector/config/configured_rule.php b/rules-tests/Php74/Rector/StaticCall/ExportToReflectionFunctionRector/config/configured_rule.php index f32ce5ffcaa..5f85a376255 100644 --- a/rules-tests/Php74/Rector/StaticCall/ExportToReflectionFunctionRector/config/configured_rule.php +++ b/rules-tests/Php74/Rector/StaticCall/ExportToReflectionFunctionRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php74\Rector\StaticCall\ExportToReflectionFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ExportToReflectionFunctionRector::class); -}; +return RectorConfig::configure() + ->withRules([ExportToReflectionFunctionRector::class]); diff --git a/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/fixture.php.inc b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..7403ff86e90 --- /dev/null +++ b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/skip_elvis.php.inc b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/skip_elvis.php.inc new file mode 100644 index 00000000000..f589d6b9b17 --- /dev/null +++ b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/skip_elvis.php.inc @@ -0,0 +1,8 @@ +lint($message) instanceof ParsingException + ? 'application/problem+json' + : (strip_tags($message) === $message ? 'text/plain' : 'text/html'); +} diff --git a/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/skip_parenthesize_ternary_if.php.inc b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/skip_parenthesize_ternary_if.php.inc new file mode 100644 index 00000000000..60a193d9b7c --- /dev/null +++ b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/Fixture/skip_parenthesize_ternary_if.php.inc @@ -0,0 +1,11 @@ +lint($message) instanceof ParsingException + ? (strip_tags($message) === $message ? 'text/plain' : 'text/html') + : 'application/problem+json'; +} diff --git a/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/ParenthesizeNestedTernaryRectorTest.php b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/ParenthesizeNestedTernaryRectorTest.php new file mode 100644 index 00000000000..121367fd7ea --- /dev/null +++ b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/ParenthesizeNestedTernaryRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/config/configured_rule.php b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/config/configured_rule.php new file mode 100644 index 00000000000..a97524ed903 --- /dev/null +++ b/rules-tests/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ParenthesizeNestedTernaryRector::class]); diff --git a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_used_in_compact.php.inc b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_used_in_compact.php.inc new file mode 100644 index 00000000000..e438fe048ca --- /dev/null +++ b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_used_in_compact.php.inc @@ -0,0 +1,14 @@ +log(compact('usedLater')); + } + } +} diff --git a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_used_in_finally.php.inc b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_used_in_finally.php.inc new file mode 100644 index 00000000000..85130db8256 --- /dev/null +++ b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_used_in_finally.php.inc @@ -0,0 +1,21 @@ +process($usedLater); + } +} diff --git a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_when_variable_is_used_outside_catch.php.inc b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_when_variable_is_used_outside_catch.php.inc deleted file mode 100644 index a4ccb6fade8..00000000000 --- a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/Fixture/skip_when_variable_is_used_outside_catch.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - - diff --git a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/FixtureSkipped/skipped.php.inc b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/FixtureSkipped/skipped.php.inc new file mode 100644 index 00000000000..075f830fec8 --- /dev/null +++ b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/FixtureSkipped/skipped.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/RemoveUnusedVariableInCatchRectorSkippedTest.php b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/RemoveUnusedVariableInCatchRectorSkippedTest.php new file mode 100644 index 00000000000..b5a1e4ca51d --- /dev/null +++ b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/RemoveUnusedVariableInCatchRectorSkippedTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureSkipped'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php_version_74.php'; + } +} diff --git a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/RemoveUnusedVariableInCatchRectorTest.php b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/RemoveUnusedVariableInCatchRectorTest.php index 9c221ece3c3..b5c03f5b9db 100644 --- a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/RemoveUnusedVariableInCatchRectorTest.php +++ b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/RemoveUnusedVariableInCatchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveUnusedVariableInCatchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/config/configured_rule.php b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/config/configured_rule.php index 16013826f3f..974aba5a505 100644 --- a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/config/configured_rule.php @@ -2,10 +2,11 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveUnusedVariableInCatchRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersion::PHP_80); + $rectorConfig->rule(RemoveUnusedVariableInCatchRector::class); }; diff --git a/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/config/configured_rule_php_version_74.php b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/config/configured_rule_php_version_74.php new file mode 100644 index 00000000000..146947b3aca --- /dev/null +++ b/rules-tests/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector/config/configured_rule_php_version_74.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersion::PHP_74); + $rectorConfig->rule(RemoveUnusedVariableInCatchRector::class); +}; diff --git a/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/ClassOnThisVariableObjectRectorTest.php b/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/ClassOnThisVariableObjectRectorTest.php new file mode 100644 index 00000000000..14eae0331fd --- /dev/null +++ b/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/ClassOnThisVariableObjectRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/final_class.php.inc b/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/final_class.php.inc new file mode 100644 index 00000000000..79289bef541 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/final_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/non_final_class.php.inc b/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/non_final_class.php.inc new file mode 100644 index 00000000000..0b8d00deb7c --- /dev/null +++ b/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/non_final_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/skip_call_static_property.php.inc b/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/skip_call_static_property.php.inc new file mode 100644 index 00000000000..9f42a1a0707 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector/Fixture/skip_call_static_property.php.inc @@ -0,0 +1,12 @@ +withRules([ClassOnThisVariableObjectRector::class]); diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/AddParamBasedOnParentClassMethodRectorTest.php b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/AddParamBasedOnParentClassMethodRectorTest.php new file mode 100644 index 00000000000..fdd8948d3df --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/AddParamBasedOnParentClassMethodRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/child_more_params_no_default_value.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/child_more_params_no_default_value.php.inc new file mode 100644 index 00000000000..d5fb9092ecd --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/child_more_params_no_default_value.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/do_not_change_same_param_different_name.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/do_not_change_same_param_different_name.php.inc new file mode 100644 index 00000000000..169b0ec0e8e --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/do_not_change_same_param_different_name.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_mysqli.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_mysqli.php.inc new file mode 100644 index 00000000000..484297289bf --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_mysqli.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_empty_array_string.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_empty_array_string.php.inc new file mode 100644 index 00000000000..a793f6bd195 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_empty_array_string.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_int.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_int.php.inc new file mode 100644 index 00000000000..b9527d519e4 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_int.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_non_empty_array_string.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_non_empty_array_string.php.inc new file mode 100644 index 00000000000..e414832ceae --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/extends_parent_default_non_empty_array_string.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/fixture.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..a3b51cd32e5 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/fixture_has_parent_typed_param.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/fixture_has_parent_typed_param.php.inc new file mode 100644 index 00000000000..5f59bbb25bd --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/fixture_has_parent_typed_param.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/implements_interface_from_source.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/implements_interface_from_source.php.inc new file mode 100644 index 00000000000..feab2b25f1b --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/implements_interface_from_source.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/include_default_param_value.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/include_default_param_value.php.inc new file mode 100644 index 00000000000..f4fcfaf2d15 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/include_default_param_value.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/skip_child_more_params_has_default_value.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/skip_child_more_params_has_default_value.php.inc new file mode 100644 index 00000000000..27aa80d1a32 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/skip_child_more_params_has_default_value.php.inc @@ -0,0 +1,12 @@ +addAnyValue(0); + } +} diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/skip_used_in_coalesce.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/skip_used_in_coalesce.php.inc new file mode 100644 index 00000000000..3d6045d6206 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/skip_used_in_coalesce.php.inc @@ -0,0 +1,13 @@ +attributes = $attributes; return $next($request); + } +} diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/used_but_in_closure.php.inc b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/used_but_in_closure.php.inc new file mode 100644 index 00000000000..a0ef611fddf --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Fixture/used_but_in_closure.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Source/InterfaceWithParam.php b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Source/InterfaceWithParam.php new file mode 100644 index 00000000000..695fd2a011e --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/Source/InterfaceWithParam.php @@ -0,0 +1,8 @@ +values[] = $value; + } +} diff --git a/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/config/configured_rule.php b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/config/configured_rule.php new file mode 100644 index 00000000000..464b5d3b595 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddParamBasedOnParentClassMethodRector::class]); diff --git a/rules-tests/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector/FinalPrivateToPrivateVisibilityRectorTest.php b/rules-tests/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector/FinalPrivateToPrivateVisibilityRectorTest.php index b43e97f381a..68828e45988 100644 --- a/rules-tests/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector/FinalPrivateToPrivateVisibilityRectorTest.php +++ b/rules-tests/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector/FinalPrivateToPrivateVisibilityRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class FinalPrivateToPrivateVisibilityRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - * @requires PHP < 8.0 - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector/Fixture/final_private_constructor.php.inc b/rules-tests/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector/Fixture/final_private_constructor.php.inc new file mode 100644 index 00000000000..069bf70a364 --- /dev/null +++ b/rules-tests/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector/Fixture/final_private_constructor.php.inc @@ -0,0 +1,10 @@ +services(); - $services->set(FinalPrivateToPrivateVisibilityRector::class); -}; +return RectorConfig::configure() + ->withRules([FinalPrivateToPrivateVisibilityRector::class]); diff --git a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/mashup.php.inc b/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/mashup.php.inc deleted file mode 100644 index 7f24d605374..00000000000 --- a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/mashup.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/new_the_constructor.php.inc b/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/new_the_constructor.php.inc deleted file mode 100644 index 2af5e9c0942..00000000000 --- a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/new_the_constructor.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_correct_order.php.inc b/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_correct_order.php.inc deleted file mode 100644 index 2196502b8de..00000000000 --- a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_correct_order.php.inc +++ /dev/null @@ -1,12 +0,0 @@ -go(1, 5, 100); - } -} diff --git a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_no_params.php.inc b/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_no_params.php.inc deleted file mode 100644 index bc6ca3b84c9..00000000000 --- a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/skip_no_params.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/treat_variadic_parameter_as_optional.php.inc b/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/treat_variadic_parameter_as_optional.php.inc deleted file mode 100644 index cab49025610..00000000000 --- a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/treat_variadic_parameter_as_optional.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -products = $products; - return $cart; - } -} diff --git a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call.php.inc b/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call.php.inc deleted file mode 100644 index 87cb9fee24b..00000000000 --- a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Fixture/update_method_call.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -run(1, 5); - } -} - -?> ------ -run(5, 1); - } -} - -?> diff --git a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/OptionalParametersAfterRequiredRectorTest.php b/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/OptionalParametersAfterRequiredRectorTest.php deleted file mode 100644 index 91f0e86c870..00000000000 --- a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/OptionalParametersAfterRequiredRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Source/vendor/SomeOutsideClass.php b/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Source/vendor/SomeOutsideClass.php deleted file mode 100644 index 3da71e73133..00000000000 --- a/rules-tests/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector/Source/vendor/SomeOutsideClass.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(OptionalParametersAfterRequiredRector::class); -}; diff --git a/rules-tests/Php80/Rector/ClassMethod/SetStateToStaticRector/SetStateToStaticRectorTest.php b/rules-tests/Php80/Rector/ClassMethod/SetStateToStaticRector/SetStateToStaticRectorTest.php index 76f507376f6..3aaf8377616 100644 --- a/rules-tests/Php80/Rector/ClassMethod/SetStateToStaticRector/SetStateToStaticRectorTest.php +++ b/rules-tests/Php80/Rector/ClassMethod/SetStateToStaticRector/SetStateToStaticRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\Php80\Rector\ClassMethod\SetStateToStaticRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SetStateToStaticRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - * @requires PHP < 8.0 - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/ClassMethod/SetStateToStaticRector/config/configured_rule.php b/rules-tests/Php80/Rector/ClassMethod/SetStateToStaticRector/config/configured_rule.php index bb9085fda33..989d3410d7b 100644 --- a/rules-tests/Php80/Rector/ClassMethod/SetStateToStaticRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/ClassMethod/SetStateToStaticRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php80\Rector\ClassMethod\SetStateToStaticRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SetStateToStaticRector::class); -}; +return RectorConfig::configure() + ->withRules([SetStateToStaticRector::class]); diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/AnnotationToAttributeRectorTest.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/AnnotationToAttributeRectorTest.php index 096e87d4834..80fd5742608 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/AnnotationToAttributeRectorTest.php +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/AnnotationToAttributeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AnnotationToAttributeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/AutoImportedAnnotationToAttributeRectorTest.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/AutoImportedAnnotationToAttributeRectorTest.php deleted file mode 100644 index 3c959c6b14c..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/AutoImportedAnnotationToAttributeRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureAutoImported'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/auto_import.php'; - } -} diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text.php.inc new file mode 100644 index 00000000000..1f48c384935 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_comment.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_comment.php.inc new file mode 100644 index 00000000000..d659038c71c --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_comment.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_comment2.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_comment2.php.inc new file mode 100644 index 00000000000..4bb683c22df --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_comment2.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_multiline.comment.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_multiline.comment.php.inc new file mode 100644 index 00000000000..4b872bbb377 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_with_newline_without_another_text_with_other_multiline.comment.php.inc @@ -0,0 +1,42 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_without_newline.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_without_newline.php.inc new file mode 100644 index 00000000000..ab1bb48a8aa --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/backslash_without_newline.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/disjoint_newline.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/disjoint_newline.php.inc new file mode 100644 index 00000000000..577314a3dd0 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/disjoint_newline.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/disjoint_newline_multi.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/disjoint_newline_multi.php.inc new file mode 100644 index 00000000000..31ec23041fc --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/disjoint_newline_multi.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/missed_slashes.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/missed_slashes.php.inc new file mode 100644 index 00000000000..b2aa2ede5a6 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/missed_slashes.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_mixed_attribute_and_annotations.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_mixed_attribute_and_annotations.php.inc new file mode 100644 index 00000000000..e1f701a0279 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_mixed_attribute_and_annotations.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_next_line.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_next_line.php.inc new file mode 100644 index 00000000000..68959e0125e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_next_line.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_small_letter.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_small_letter.php.inc new file mode 100644 index 00000000000..c665bf6396b --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_small_letter.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_transform_annotations.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_transform_annotations.php.inc new file mode 100644 index 00000000000..7a46394fc9d --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_transform_annotations.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_value_edge_cases.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_value_edge_cases.php.inc new file mode 100644 index 00000000000..44a811e4935 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Behat/with_value_edge_cases.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/doctrine_entity.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/doctrine_entity.php.inc new file mode 100644 index 00000000000..e0c5a7bc65b --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/doctrine_entity.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/handle_with_api_above.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/handle_with_api_above.php.inc new file mode 100644 index 00000000000..470231095f1 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/handle_with_api_above.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/nullable_bool_value.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/nullable_bool_value.php.inc new file mode 100644 index 00000000000..5e63f634183 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/nullable_bool_value.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/preserve_int_key_defined.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/preserve_int_key_defined.php.inc new file mode 100644 index 00000000000..d24cccf88ec --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/preserve_int_key_defined.php.inc @@ -0,0 +1,29 @@ + +----- + 'CostDetailEntity'])] +class PreserveIntKeyDefined +{ +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_cast_integer_parameter.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_cast_integer_parameter.php.inc new file mode 100644 index 00000000000..99df8ff08d4 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_cast_integer_parameter.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_file.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_file.php.inc new file mode 100644 index 00000000000..6a1d05ac86c --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_file.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_string_parameter.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_string_parameter.php.inc new file mode 100644 index 00000000000..89e44706f0e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/Validator/validation_string_parameter.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/param_converter_with_silent_key.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/param_converter_with_silent_key.php.inc new file mode 100644 index 00000000000..bbd39e52233 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/param_converter_with_silent_key.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/preserve_quotes.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/preserve_quotes.php.inc new file mode 100644 index 00000000000..5fcf505ab39 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/preserve_quotes.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/same_groups_class_name.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/same_groups_class_name.php.inc new file mode 100644 index 00000000000..ab17df145e2 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/same_groups_class_name.php.inc @@ -0,0 +1,48 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_route.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_route.php.inc new file mode 100644 index 00000000000..f4d7bbcd22e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_route.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_route_with_null_value.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_route_with_null_value.php.inc new file mode 100644 index 00000000000..f532ab6169c --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_route_with_null_value.php.inc @@ -0,0 +1,33 @@ + +----- + null])] + public function action() + { + } +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_security.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_security.php.inc new file mode 100755 index 00000000000..29731a775c1 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_security.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_security2.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_security2.php.inc new file mode 100755 index 00000000000..3c7afff8605 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Symfony/symfony_security2.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used.php.inc new file mode 100644 index 00000000000..d115feb2cae --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used_with_last_keyword.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used_with_last_keyword.php.inc new file mode 100644 index 00000000000..37f7c3fa843 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used_with_last_keyword.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used_with_partial_rename.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used_with_partial_rename.php.inc new file mode 100644 index 00000000000..a9c58cd1869 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/alias_used_with_partial_rename.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/aliased_with_multi_properties.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/aliased_with_multi_properties.php.inc new file mode 100644 index 00000000000..02e284851c9 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/aliased_with_multi_properties.php.inc @@ -0,0 +1,41 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/annotated_interface.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/annotated_interface.php.inc new file mode 100644 index 00000000000..11ef950f750 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/annotated_interface.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/annotation_converted_an_keep_docblock.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/annotation_converted_an_keep_docblock.php.inc deleted file mode 100644 index 56ff6ebf4cd..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/annotation_converted_an_keep_docblock.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/constant_as_array_key.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/constant_as_array_key.php.inc new file mode 100644 index 00000000000..e646b71a046 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/constant_as_array_key.php.inc @@ -0,0 +1,32 @@ + +----- + true, 'some::string' => 'some-value'])] +final class ConstantAsArrayKey +{ +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/first_param_is_silent.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/first_param_is_silent.php.inc new file mode 100644 index 00000000000..090ff6d5777 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/first_param_is_silent.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/from_implicit_to_name.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/from_implicit_to_name.php.inc index eb0392e95cd..24e8a185b4b 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/from_implicit_to_name.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/from_implicit_to_name.php.inc @@ -2,10 +2,10 @@ namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture; -use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericSingleImplicitAnnotation; /** - * @GenericAnnotation("/") + * @GenericSingleImplicitAnnotation("/") */ final class FromImplicitToName { @@ -17,9 +17,9 @@ final class FromImplicitToName namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture; -use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericSingleImplicitAnnotation; -#[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation(some: '/')] +#[GenericSingleImplicitAnnotation('/')] final class FromImplicitToName { } diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/from_simple_tag.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/from_simple_tag.php.inc new file mode 100644 index 00000000000..d7a67c6bb99 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/from_simple_tag.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/function_call_inside.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/function_call_inside.php.inc index 55d4f3f3819..4bfc95d4736 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/function_call_inside.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/function_call_inside.php.inc @@ -24,7 +24,7 @@ use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericA final class FunctionCallInside { - #[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation(some: "is_granted('ROLE_USER')")] + #[GenericAnnotation("is_granted('ROLE_USER')")] public function action() { } diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted.php.inc new file mode 100644 index 00000000000..d2b1de92e94 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted2.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted2.php.inc new file mode 100644 index 00000000000..f272dad8fdd --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted2.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted_key_value.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted_key_value.php.inc new file mode 100644 index 00000000000..88d8c721a02 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/keep_single_quoted_key_value.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/multi_nested_arrays.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/multi_nested_arrays.php.inc deleted file mode 100644 index 94f22ef2a46..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/multi_nested_arrays.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - ['mutation' => 'app.graphql.mutation_resolver.dummy_custom_only_persist', 'denormalization_context' => ['groups' => ['sum']], 'serialize' => false]])] -class ApiPlatformResource -{ -} - -?> diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/multiline_content.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/multiline_content.php.inc index 1bde5b85445..85eb9d379d0 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/multiline_content.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/multiline_content.php.inc @@ -21,9 +21,9 @@ namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture; use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation; -#[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation(some: ' - summary: Send webcam reward -')] +#[GenericAnnotation(' + summary: Send webcam reward + ')] final class MultilineContent { } diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_array_with_constants.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_array_with_constants.php.inc new file mode 100644 index 00000000000..0e1368876cf --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_array_with_constants.php.inc @@ -0,0 +1,43 @@ + +----- + ['foo' => ['bar']], ConstantReference::LAST_NAME])] +#[GenericAnnotation(some: [ConstantReference::LAST_NAME, 'trailingValue'])] +final class ArrayWithConstantAsKey +{ +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_nested_arrays.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_nested_arrays.php.inc new file mode 100644 index 00000000000..4c03d344042 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_nested_arrays.php.inc @@ -0,0 +1,27 @@ + +----- + true, 'parameters' => ['id' => '{id}']])] +final class NestedNestedArrays +{ +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_quote.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_quote.php.inc index 15dbfc7a8c1..3ad19b1b7c1 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_quote.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_quote.php.inc @@ -22,10 +22,10 @@ namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture; use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation; -#[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation(some: ' - summary: key - description: \'something `id => name`.\' -')] +#[GenericAnnotation(' + summary: key + description: \'something `id => name`.\' + ')] final class NestedQuote { } diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_quoted_asterisk.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_quoted_asterisk.php.inc index 73714d707cd..d8918e2fd88 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_quoted_asterisk.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_quoted_asterisk.php.inc @@ -24,12 +24,12 @@ namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture; use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation; -#[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation(some: ' - key: value - another_key: - another_value/*: - schema: 100 -')] +#[GenericAnnotation(' + key: value + another_key: + another_value/*: + schema: 100 + ')] final class NestedQuote { } diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_unwrap.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_unwrap.php.inc deleted file mode 100644 index c2675d39116..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/nested_unwrap.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/non_namespaced_class_with_annotation.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/non_namespaced_class_with_annotation.php.inc index aa4e9f91613..90ad1cd8252 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/non_namespaced_class_with_annotation.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/non_namespaced_class_with_annotation.php.inc @@ -2,7 +2,7 @@ /** * @Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation( - * routePrefix="/demo/" + * some="/demo/" * ) */ final class NonNamespacedClassWithAnnotation @@ -12,7 +12,7 @@ final class NonNamespacedClassWithAnnotation ----- +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/promoted_property.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/promoted_property.php.inc index 1ad61cd2577..68707d74ece 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/promoted_property.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/promoted_property.php.inc @@ -25,7 +25,8 @@ use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericA final class PromotedProperty { public function __construct( - #[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation] private string $property, + #[GenericAnnotation] + private string $property, ) {} } diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/simple_nested_arrays_and_alias_import.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/simple_nested_arrays_and_alias_import.php.inc index c83bac61460..aebd6240156 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/simple_nested_arrays_and_alias_import.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/simple_nested_arrays_and_alias_import.php.inc @@ -7,9 +7,9 @@ use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source as Asser final class EntityColumnAndAssertChoice { /** - * @Assert\GenericAnnotation({"php5", "php7", "php8"}) - * @Assert\GenericAnnotation(choices={"5.0", "5.1", "id"="5.2"}) - * @Assert\GenericAnnotation(choices={2, 3, 5, 7, 11, 13, 17, 19}) + * @Assert\GenericAnnotation({"php5", "php7"}) + * @Assert\GenericAnnotation(choices={"5.0", "id"="5.2"}) + * @Assert\GenericAnnotation(choices={2, 3, 5}) */ public $primeNumbers; } @@ -24,9 +24,9 @@ use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source as Asser final class EntityColumnAndAssertChoice { - #[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation(some: ['php5', 'php7', 'php8'])] - #[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation(choices: ['5.0', '5.1', 'id' => '5.2'])] - #[\Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation(choices: [2, 3, 5, 7, 11, 13, 17, 19])] + #[Assert\GenericAnnotation(['php5', 'php7'])] + #[Assert\GenericAnnotation(choices: ['5.0', 'id' => '5.2'])] + #[Assert\GenericAnnotation(choices: [2, 3, 5])] public $primeNumbers; } diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/skip_non_attribute_class.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/skip_non_attribute_class.php.inc new file mode 100644 index 00000000000..6a56cc52e73 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/skip_non_attribute_class.php.inc @@ -0,0 +1,15 @@ + ------ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/symfony_route.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/symfony_route.php.inc deleted file mode 100644 index c115042c4af..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/symfony_route.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/trailing_comma.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/trailing_comma.php.inc new file mode 100644 index 00000000000..735d7cbb656 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/trailing_comma.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/traing_comma_after_array.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/traing_comma_after_array.php.inc deleted file mode 100644 index 51d27068b8d..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/traing_comma_after_array.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/use_empty_attribute.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/use_empty_attribute.php.inc new file mode 100644 index 00000000000..960bf9e0543 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/use_empty_attribute.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/with_value_as_argument.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/with_value_as_argument.php.inc new file mode 100644 index 00000000000..f40de7f3b0a --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/with_value_as_argument.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/with_value_as_argument_and_description.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/with_value_as_argument_and_description.php.inc new file mode 100644 index 00000000000..608565a3b25 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/with_value_as_argument_and_description.php.inc @@ -0,0 +1,44 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_change_unique_entity_when_entity_attribute_not_exists.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_change_unique_entity_when_entity_attribute_not_exists.php.inc deleted file mode 100644 index a77ffe37c6a..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_change_unique_entity_when_entity_attribute_not_exists.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - \ No newline at end of file diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_import_collision.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_import_collision.php.inc deleted file mode 100644 index 620375d7713..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_import_collision.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_remove_assert_no_arg.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_remove_assert_no_arg.php.inc deleted file mode 100644 index a4334ca6b4f..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/do_not_remove_assert_no_arg.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/donot_reorder_annotation.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/donot_reorder_annotation.php.inc deleted file mode 100644 index 13a2cdc46da..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/donot_reorder_annotation.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/skip_unique_entity_when_entity_attribute_exists.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/skip_unique_entity_when_entity_attribute_exists.php.inc deleted file mode 100644 index 54fed78f026..00000000000 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureAutoImported/skip_unique_entity_when_entity_attribute_exists.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureMultipleCall/fixture.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureMultipleCall/fixture.php.inc new file mode 100644 index 00000000000..654a00f9356 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixtureMultipleCall/fixture.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/multiple_nested_attributes.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/multiple_nested_attributes.php.inc new file mode 100644 index 00000000000..d766b924d35 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/multiple_nested_attributes.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/multiple_nested_attributes_without_parentheses.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/multiple_nested_attributes_without_parentheses.php.inc new file mode 100644 index 00000000000..df7187c8c9e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/multiple_nested_attributes_without_parentheses.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/nested_attributes_with_brackets.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/nested_attributes_with_brackets.php.inc new file mode 100644 index 00000000000..a14e18d616f --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/nested_attributes_with_brackets.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/new_line_after_open_parentheses.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/new_line_after_open_parentheses.php.inc new file mode 100644 index 00000000000..81266381064 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/new_line_after_open_parentheses.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/skip_single_quoted.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/skip_single_quoted.php.inc new file mode 100644 index 00000000000..15170c07518 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/skip_single_quoted.php.inc @@ -0,0 +1,14 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/unique_constraints.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/unique_constraints.php.inc new file mode 100644 index 00000000000..1c50ede6ebf --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/FixturePhp81/unique_constraints.php.inc @@ -0,0 +1,26 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/MultipleCallAnnotationToAttributeRectorTest.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/MultipleCallAnnotationToAttributeRectorTest.php new file mode 100644 index 00000000000..d615c11344b --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/MultipleCallAnnotationToAttributeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureMultipleCall'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/multiple_call_configured_rule.php'; + } +} diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Php81NestedAttributesRectorTest.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Php81NestedAttributesRectorTest.php new file mode 100644 index 00000000000..457fe1c39c0 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Php81NestedAttributesRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp81'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/nested_attributes_php81.php'; + } +} diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Source/Annotation/OpenApi/Annotation/NestedPastAnnotation.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Source/Annotation/OpenApi/Annotation/NestedPastAnnotation.php new file mode 100644 index 00000000000..cd02a89885a --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Source/Annotation/OpenApi/Annotation/NestedPastAnnotation.php @@ -0,0 +1,9 @@ +parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(AnnotationToAttributeRector::class) - ->call('configure', [[ - AnnotationToAttributeRector::ANNOTATION_TO_ATTRIBUTE => ValueObjectInliner::inline([ - new AnnotationToAttribute('Doctrine\ORM\Mapping\Entity'), - new AnnotationToAttribute('Doctrine\ORM\Mapping\Id'), - new AnnotationToAttribute('Doctrine\ORM\Mapping\Column'), - - new AnnotationToAttribute(Apple::class, AppleAttribute::class), - ]), - ]]); -}; diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/configured_rule.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/configured_rule.php index 0a385fcac6a..a50fce55f02 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/configured_rule.php @@ -2,26 +2,82 @@ declare(strict_types=1); +use Behat\Step\Then; +use Behat\Step\When; +use Behat\Transformation\Transform; +use Rector\Config\RectorConfig; use Rector\Php80\Rector\Class_\AnnotationToAttributeRector; use Rector\Php80\ValueObject\AnnotationToAttribute; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi\Annotation\NestedPastAnnotation; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi\PastAnnotation; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\EmptyAttribute; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\NewName1; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\NewName2; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi\Attribute\NestedFutureAttribute; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi\FutureAttribute; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\SameName; use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation; -use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Response; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericSingleImplicitAnnotation; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\NotAnAttribute; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Doctrine\ORM\Mapping\Embeddable'), - $services->set(AnnotationToAttributeRector::class) - ->call('configure', [[ - AnnotationToAttributeRector::ANNOTATION_TO_ATTRIBUTE => ValueObjectInliner::inline([ - // use always this annotation to test inner part of annotation - arguments, arrays, calls... - new AnnotationToAttribute(GenericAnnotation::class), + // not an attribute yet + new AnnotationToAttribute(NotAnAttribute::class), - new AnnotationToAttribute('inject', 'Nette\DI\Attributes\Inject'), + new AnnotationToAttribute(PastAnnotation::class, FutureAttribute::class), + new AnnotationToAttribute(NestedPastAnnotation::class, NestedFutureAttribute::class), - new AnnotationToAttribute(Response::class), - new AnnotationToAttribute('Symfony\Component\Routing\Annotation\Route'), - ]), - ]]); + // use always this annotation to test inner part of annotation - arguments, arrays, calls... + new AnnotationToAttribute(GenericAnnotation::class), + new AnnotationToAttribute(GenericSingleImplicitAnnotation::class), + + new AnnotationToAttribute('Symfony\Component\Routing\Annotation\Route'), + + // doctrine + new AnnotationToAttribute('Doctrine\ORM\Mapping\Entity', null, ['repositoryClass']), + new AnnotationToAttribute('Doctrine\ORM\Mapping\DiscriminatorMap'), + new AnnotationToAttribute('Doctrine\ORM\Mapping\Column'), + + // validation + new AnnotationToAttribute('Symfony\Component\Validator\Constraints\Choice'), + new AnnotationToAttribute('Symfony\Component\Validator\Constraints\Length'), + new AnnotationToAttribute('Symfony\Component\Validator\Constraints\File'), + + // JMS + Symfony + new AnnotationToAttribute('Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter'), + + // test for alias used + new AnnotationToAttribute( + 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\UseAlias\TestSmth' + ), + new AnnotationToAttribute( + 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\UseAlias\TestOther' + ), + new AnnotationToAttribute('Sensio\Bundle\FrameworkExtraBundle\Configuration\Security'), + + new AnnotationToAttribute('Symfony\Component\Serializer\Attribute\Groups'), + + // special case with following comment becoming a inner value + new AnnotationToAttribute('When', When::class, useValueAsAttributeArgument: true), + new AnnotationToAttribute('Then', Then::class, useValueAsAttributeArgument: true), + new AnnotationToAttribute('Transform', Transform::class, useValueAsAttributeArgument: true), + + // simple tag to attribute + new AnnotationToAttribute('OldName1', NewName1::class), + new AnnotationToAttribute('OldName2', NewName2::class), + new AnnotationToAttribute('SameName', SameName::class), + + new AnnotationToAttribute( + 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi\Annotation\SomeProperty', + 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi\Attribute\SomeProperty' + ), + + // for testing allow numeric string keep as string + new AnnotationToAttribute('OpenApi\\Annotations\\Property', 'OpenApi\\Attributes\\Property'), + + new AnnotationToAttribute(EmptyAttribute::class, null, ['foo']), + ]); }; diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/multiple_call_configured_rule.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/multiple_call_configured_rule.php new file mode 100644 index 00000000000..013ec3b9de9 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/multiple_call_configured_rule.php @@ -0,0 +1,23 @@ +phpVersion(PhpVersionFeature::ATTRIBUTES); + + $rectorConfig + ->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Doctrine\ORM\Mapping\Entity'), + ]); + + // should merge with existing config + $rectorConfig + ->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Doctrine\ORM\Mapping\ManyToMany'), + ]); +}; diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/nested_attributes_php81.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/nested_attributes_php81.php new file mode 100644 index 00000000000..6935f12ecb5 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/nested_attributes_php81.php @@ -0,0 +1,34 @@ +phpVersion(PhpVersionFeature::NEW_INITIALIZERS); + + $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute(All::class), + new AnnotationToAttribute(Length::class), + new AnnotationToAttribute(NotNumber::class), + new AnnotationToAttribute(GenericAnnotation::class), + ]); + + $rectorConfig->ruleWithConfiguration(NestedAnnotationToAttributeRector::class, [ + new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\Table', [ + new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\Index', 'indexes'), + new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\UniqueConstraint', 'uniqueConstraints'), + ]), + ]); +}; diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/ClassPropertyAssignToConstructorPromotionPhp80RectorTest.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/ClassPropertyAssignToConstructorPromotionPhp80RectorTest.php new file mode 100644 index 00000000000..a28867666c9 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/ClassPropertyAssignToConstructorPromotionPhp80RectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php80.php'; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/ClassPropertyAssignToConstructorPromotionRectorTest.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/ClassPropertyAssignToConstructorPromotionRectorTest.php index d46e66691a2..c140dc4a9ef 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/ClassPropertyAssignToConstructorPromotionRectorTest.php +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/ClassPropertyAssignToConstructorPromotionRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ClassPropertyAssignToConstructorPromotionRectorTest extends AbstractRectorTestCase { - /** - * @requires PHP 7.4 - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/DisallowModelBasedClassesTest.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/DisallowModelBasedClassesTest.php new file mode 100644 index 00000000000..af1b8450493 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/DisallowModelBasedClassesTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureDisallowModelBasedClasses'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_disallow_model_based_classes.php'; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/add_default_false.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/add_default_false.php.inc new file mode 100644 index 00000000000..b7e286f12d0 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/add_default_false.php.inc @@ -0,0 +1,28 @@ +name = $name; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/add_default_nullable.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/add_default_nullable.php.inc new file mode 100644 index 00000000000..f73229d1ad8 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/add_default_nullable.php.inc @@ -0,0 +1,33 @@ +name = $name; + $this->surname = $surname; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/closure_type.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/closure_type.php.inc new file mode 100644 index 00000000000..36fdd064113 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/closure_type.php.inc @@ -0,0 +1,28 @@ +configureContainerBuilder = $configureContainerBuilder; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_generic_doc.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_generic_doc.php.inc new file mode 100644 index 00000000000..11d807c955b --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_generic_doc.php.inc @@ -0,0 +1,38 @@ + + */ + private array $map; + + public function __construct(array $map) + { + $this->map = $map; + } +} + +?> +----- + + */ + private array $map + ) + { + } +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_doc.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_doc.php.inc index 31b260503c5..ea2a1c0c942 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_doc.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_doc.php.inc @@ -2,30 +2,25 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; +use Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\ValidatedElement; use Symfony\Component\Validator\Constraints as Assert; -class Y {} -class Z {} - final class CopyDoc { - public Y $y; - - /** - * @var Z[] - * @Assert\Valid() - * @Assert\NotBlank() - */ - public array $z = []; - - /** - * @param Z[] $z - */ - public function __construct(Y $y, array $z = []) - { - $this->y = $y; - $this->z = $z; - } + /** + * @var ValidatedElement[] + * @Assert\Valid() + * @Assert\NotBlank() + */ + public array $z = []; + + /** + * @param ValidatedElement[] $z + */ + public function __construct(array $z = []) + { + $this->z = $z; + } } ?> ----- @@ -33,25 +28,22 @@ final class CopyDoc namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; +use Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\ValidatedElement; use Symfony\Component\Validator\Constraints as Assert; -class Y {} -class Z {} - final class CopyDoc { - /** - * @param Z[] $z - */ - public function __construct( - public Y $y, - /** - * @Assert\Valid() - * @Assert\NotBlank() - */ - public array $z = [] - ) - { - } + /** + * @param ValidatedElement[] $z + */ + public function __construct( + /** + * @Assert\Valid() + * @Assert\NotBlank() + */ + public array $z = [] + ) + { + } } ?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_integer_range_type_doc.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_integer_range_type_doc.php.inc new file mode 100644 index 00000000000..7c2c48df589 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_integer_range_type_doc.php.inc @@ -0,0 +1,32 @@ +count = $count; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/do_not_remove_parameter_attribute.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/do_not_remove_parameter_attribute.php.inc new file mode 100644 index 00000000000..ddf318dcaaf --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/do_not_remove_parameter_attribute.php.inc @@ -0,0 +1,34 @@ +password = $password; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/generic.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/generic.php.inc index 4172dfbc3ab..5141289357d 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/generic.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/generic.php.inc @@ -7,18 +7,18 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo */ final class Generic { - /** - * @var T - */ - public mixed $value; + /** + * @var T + */ + public mixed $value; - /** - * @param T $value - */ - public function __construct(mixed $value) - { - $this->value = $value; - } + /** + * @param T $value + */ + public function __construct(mixed $value) + { + $this->value = $value; + } } ?> ----- @@ -31,11 +31,11 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo */ final class Generic { - /** - * @param T $value - */ - public function __construct(public mixed $value) - { - } + /** + * @param T $value + */ + public function __construct(public mixed $value) + { + } } ?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/iterable_typed.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/iterable_typed.php.inc index 5caed2a2262..5c3f4c00130 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/iterable_typed.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/iterable_typed.php.inc @@ -4,18 +4,18 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo class IterableTyped { - /** - * @var iterable - */ - private iterable $property; + /** + * @var iterable + */ + private iterable $property; - /** - * @param iterable $property - */ - public function __construct(iterable $property) - { - $this->property = $property; - } + /** + * @param iterable $property + */ + public function __construct(iterable $property) + { + $this->property = $property; + } } ?> ----- @@ -25,11 +25,11 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo class IterableTyped { - /** - * @param iterable $property - */ - public function __construct(private iterable $property) - { - } + /** + * @param iterable $property + */ + public function __construct(private iterable $property) + { + } } ?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/mirror_additional_docblock.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/mirror_additional_docblock.php.inc new file mode 100644 index 00000000000..c0b28258bd8 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/mirror_additional_docblock.php.inc @@ -0,0 +1,44 @@ +name = $name; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/mix_non_attribute_and_attribute_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/mix_non_attribute_and_attribute_property.php.inc index 8cc64e9923c..77cc8fdb9d3 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/mix_non_attribute_and_attribute_property.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/mix_non_attribute_and_attribute_property.php.inc @@ -6,7 +6,7 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo use JetBrains\PhpStorm\Immutable; -final class Coordinate +final class MixNonAttributeAndAttributeProperty { #[Immutable] public float $latitude; @@ -29,9 +29,13 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo use JetBrains\PhpStorm\Immutable; -final class Coordinate +final class MixNonAttributeAndAttributeProperty { - public function __construct(#[Immutable] public float $latitude, public float $longitude) + public function __construct( + #[Immutable] + public float $latitude, + public float $longitude + ) { } } diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/nullable_type.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/nullable_type.php.inc index e00366ece65..85beeb7a898 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/nullable_type.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/nullable_type.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo use DateTimeInterface; -class NullableType +final class MergeWithNullableType { private ?DateTimeInterface $time; @@ -22,9 +22,9 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo use DateTimeInterface; -class NullableType +final class MergeWithNullableType { - public function __construct(private ?\DateTimeInterface $time = null) + public function __construct(private ?DateTimeInterface $time = null) { } } diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/param_and_var.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/param_and_var.php.inc index 1c272b1737a..48dcb1678ef 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/param_and_var.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/param_and_var.php.inc @@ -3,7 +3,7 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; use Rector\BetterPhpDocParser\Contract\BasePhpDocNodeVisitorInterface; -use Symplify\SimplePhpDocParser\Contract\PhpDocNodeVisitorInterface; +use Rector\PhpDocParser\PhpDocParser\Contract\PhpDocNodeVisitorInterface; final class ParamAndVar { @@ -28,7 +28,7 @@ final class ParamAndVar namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; use Rector\BetterPhpDocParser\Contract\BasePhpDocNodeVisitorInterface; -use Symplify\SimplePhpDocParser\Contract\PhpDocNodeVisitorInterface; +use Rector\PhpDocParser\PhpDocParser\Contract\PhpDocNodeVisitorInterface; final class ParamAndVar { diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/property_with_attribute.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/property_with_attribute.php.inc index 289ce025b77..9f393115a1d 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/property_with_attribute.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/property_with_attribute.php.inc @@ -4,16 +4,23 @@ declare(strict_types=1); namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; +use JetBrains\PhpStorm\Deprecated; use JetBrains\PhpStorm\Immutable; -final class Coordinate +final class PropertyWithAttribute { #[Immutable] public float $latitude; - public function __construct(float $latitude) + #[Immutable] + #[Deprecated] + public float $longitude; + + public function __construct(float $latitude, float $longitude) { $this->latitude = $latitude; + $this->longitude = $longitude; + } } ?> @@ -24,11 +31,18 @@ declare(strict_types=1); namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; +use JetBrains\PhpStorm\Deprecated; use JetBrains\PhpStorm\Immutable; -final class Coordinate +final class PropertyWithAttribute { - public function __construct(#[Immutable] public float $latitude) + public function __construct( + #[Immutable] + public float $latitude, + #[Immutable] + #[Deprecated] + public float $longitude + ) { } } diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/property_with_multiple_attributes.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/property_with_multiple_attributes.php.inc deleted file mode 100644 index 1e2f7e01d6c..00000000000 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/property_with_multiple_attributes.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -latitude = $latitude; - } -} -?> ------ - diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_accessed_variable_before_assign.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_accessed_variable_before_assign.php.inc index 8347f85c412..77118659e67 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_accessed_variable_before_assign.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_accessed_variable_before_assign.php.inc @@ -7,7 +7,7 @@ class SkipAccessedVariableBeforeAssign public $x; function __construct(string $x) { - $x = 'preprended-' . $x; + $x = 'prepended-' . $x; $this->x = $x; } } diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_callable_type.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_callable_type.php.inc index 5be42d4ad99..bd3c1eb33cf 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_callable_type.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_callable_type.php.inc @@ -7,13 +7,19 @@ final class SkipCallableType /** @var callable */ private $fallback; + /** @var callable|array */ + public $cb; + /** * @param mixed[] $cache */ - public function __construct(callable $fallback, private array $cache = []) + public function __construct( + callable $fallback, + private array $cache = [], + callable|array $cb = null + ) { $this->fallback = $fallback; + $this->cb = $cb; } } - -?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_callable_type_different_definition.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_callable_type_different_definition.php.inc new file mode 100644 index 00000000000..f640985545c --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_callable_type_different_definition.php.inc @@ -0,0 +1,13 @@ +fallback = $fallback; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_hook_with_indirect_set.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_hook_with_indirect_set.php.inc new file mode 100644 index 00000000000..7020c733b68 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_hook_with_indirect_set.php.inc @@ -0,0 +1,25 @@ +value; + } + set { + if ($this->optionUsedInValueSetter) { + $this->value = "changed value"; + } + $this->value = $value; + } + } + + public function __construct( + mixed $value, + bool $optionUsedInValueSetter = false, + ) { + $this->value = $value; + } +} \ No newline at end of file diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_merged_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_merged_property.php.inc new file mode 100644 index 00000000000..080229a29e5 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_merged_property.php.inc @@ -0,0 +1,50 @@ + [], + ]; + + /** + * @param array $metadata + */ + public function __construct(array $metadata) + { + $this->metadata = $metadata; + + if (!isset($this->metadata['permission'])) { + $this->metadata['permission'] = []; + } + + $permission = $metadata['permission']; + } +} + +?> +----- + $metadata + */ + public function __construct(private array $metadata) + { + if (!isset($this->metadata['permission'])) { + $this->metadata['permission'] = []; + } + + $permission = $this->metadata['permission']; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_not_match_property_hook_type.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_not_match_property_hook_type.php.inc new file mode 100644 index 00000000000..5d31dfdf104 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_not_match_property_hook_type.php.inc @@ -0,0 +1,17 @@ +origin = is_array($origin) ? (object) $origin : clone $origin; + } + } + + public function __construct( + array|\stdClass $origin + ) { + $this->origin = $origin; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_nullable_callable_without_typehint.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_nullable_callable_without_typehint.php.inc new file mode 100644 index 00000000000..b8f8eccfad5 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_nullable_callable_without_typehint.php.inc @@ -0,0 +1,14 @@ +cb = $cb; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_property_assign.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_property_assign.php.inc deleted file mode 100644 index b70aefea77e..00000000000 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_property_assign.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -command = $command; - } - - public function getCommand() - { - if (!$this->command instanceof \Closure) { - return $this->command; - } - - $this->command = ($this->command)(); - - return $this->command; - } -} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_var_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_var_property.php.inc new file mode 100644 index 00000000000..30dcff00c1e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_var_property.php.inc @@ -0,0 +1,12 @@ +id = $id; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/string_default_string.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/string_default_string.php.inc new file mode 100644 index 00000000000..8fb7cdb6443 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/string_default_string.php.inc @@ -0,0 +1,28 @@ +name = $name; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/union_fully_qualified.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/union_fully_qualified.php.inc index 1600844c9de..06eec3cd2a8 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/union_fully_qualified.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/union_fully_qualified.php.inc @@ -10,20 +10,20 @@ class Z {} final class UnionFullyQualified { - public Y $y; + public Y $y; - public Z $z; + public Z $z; - public function __construct(Y $y, Z $z) - { - $this->y = $y; - $this->z = $z; - } + public function __construct(Y $y, Z $z) + { + $this->y = $y; + $this->z = $z; + } - public function getX(): X - { - return $this->y; - } + public function getX(): X + { + return $this->y; + } } ?> ----- @@ -39,13 +39,13 @@ class Z {} final class UnionFullyQualified { - public function __construct(public Y $y, public Z $z) - { - } - - public function getX(): X - { - return $this->y; - } + public function __construct(public Y $y, public Z $z) + { + } + + public function getX(): X + { + return $this->y; + } } ?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/union_typed.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/union_typed.php.inc index c0ac8d55eaa..0502e85cab1 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/union_typed.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/union_typed.php.inc @@ -7,12 +7,12 @@ use DateTime; final class UnionTyped { - public stdClass|DateTime $property; + public stdClass|DateTime $property; - public function __construct(stdClass|DateTime $property) - { - $this->property = $property; - } + public function __construct(stdClass|DateTime $property) + { + $this->property = $property; + } } ?> @@ -26,9 +26,9 @@ use DateTime; final class UnionTyped { - public function __construct(public stdClass|DateTime $property) - { - } + public function __construct(public stdClass|DateTime $property) + { + } } ?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_property_hook.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_property_hook.php.inc new file mode 100644 index 00000000000..044c850a324 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_property_hook.php.inc @@ -0,0 +1,39 @@ + strtoupper($this->foo); + set { + $this->foo = $value; + } + } + + public function __construct( + string $foo, + ) { + $this->foo = $foo; + } +} + +?> +----- + strtoupper($this->foo); + set { + $this->foo = $value; + } + }) + { + } +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_jms_type_annotation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_jms_type_annotation.php.inc new file mode 100644 index 00000000000..5e7be7d1240 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_jms_type_annotation.php.inc @@ -0,0 +1,23 @@ +value = $value; + } + + public function getValue(): string + { + return $this->value; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_jms_type_attribute.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_jms_type_attribute.php.inc new file mode 100644 index 00000000000..cb1e31eed01 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_jms_type_attribute.php.inc @@ -0,0 +1,21 @@ +value = $value; + } + + public function getValue(): string + { + return $this->value; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_with_doctrine_entity_annotations.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_with_doctrine_entity_annotations.php.inc new file mode 100644 index 00000000000..fe8f8968b25 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_with_doctrine_entity_annotations.php.inc @@ -0,0 +1,26 @@ +content = $content; + } + + public function getContent(): string + { + return $this->content; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_with_doctrine_entity_attribute.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_with_doctrine_entity_attribute.php.inc new file mode 100644 index 00000000000..773d2fc5d02 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureDisallowModelBasedClasses/skip_with_doctrine_entity_attribute.php.inc @@ -0,0 +1,22 @@ +content = $content; + } + + public function getContent(): string + { + return $this->content; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/final_class_non_typed_protected_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/final_class_non_typed_protected_property.php.inc new file mode 100644 index 00000000000..86f5ab54cc6 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/final_class_non_typed_protected_property.php.inc @@ -0,0 +1,31 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/non_final_non_typed_private_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/non_final_non_typed_private_property.php.inc new file mode 100644 index 00000000000..74aea1f086e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/non_final_non_typed_private_property.php.inc @@ -0,0 +1,31 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/skip_non_typed_non_final_class_protected_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/skip_non_typed_non_final_class_protected_property.php.inc new file mode 100644 index 00000000000..766a5c81689 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/skip_non_typed_non_final_class_protected_property.php.inc @@ -0,0 +1,16 @@ +x = $x; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/typed_non_final_class_protected_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/typed_non_final_class_protected_property.php.inc new file mode 100644 index 00000000000..8138e122ad8 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureInlinePublicDisable/typed_non_final_class_protected_property.php.inc @@ -0,0 +1,28 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixturePhp80/add_default_false_to_bool.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixturePhp80/add_default_false_to_bool.php.inc new file mode 100644 index 00000000000..783dadda676 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixturePhp80/add_default_false_to_bool.php.inc @@ -0,0 +1,28 @@ +name = $name; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixturePhp80/nullable_string.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixturePhp80/nullable_string.php.inc new file mode 100644 index 00000000000..fdff2b1a311 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixturePhp80/nullable_string.php.inc @@ -0,0 +1,28 @@ +name = $name; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureRenamingDisabled/fixture.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureRenamingDisabled/fixture.php.inc new file mode 100644 index 00000000000..0dc580449f8 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureRenamingDisabled/fixture.php.inc @@ -0,0 +1,35 @@ +x = $x; + $this->y = $y; + $this->z = $z; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureRenamingDisabled/skip_if_different_names.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureRenamingDisabled/skip_if_different_names.php.inc new file mode 100644 index 00000000000..eea661ae2ba --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/FixtureRenamingDisabled/skip_if_different_names.php.inc @@ -0,0 +1,18 @@ +fixer = $notSameName; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/InlinePublicDisableTest.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/InlinePublicDisableTest.php new file mode 100644 index 00000000000..a4fb080bb1b --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/InlinePublicDisableTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureInlinePublicDisable'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_disable_inline_public.php'; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/RenamePropertyDisabledTest.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/RenamePropertyDisabledTest.php new file mode 100644 index 00000000000..17cdfddb158 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/RenamePropertyDisabledTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureRenamingDisabled'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_disabled_renaming.php'; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/ValidatedElement.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/ValidatedElement.php new file mode 100644 index 00000000000..4490a33655c --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/ValidatedElement.php @@ -0,0 +1,8 @@ +services(); - $services->set(ClassPropertyAssignToConstructorPromotionRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(ClassPropertyAssignToConstructorPromotionRector::class, [ + ClassPropertyAssignToConstructorPromotionRector::INLINE_PUBLIC => true, + ]); + + $rectorConfig->phpVersion(PhpVersion::PHP_10); }; diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disable_inline_public.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disable_inline_public.php new file mode 100644 index 00000000000..33343ce9ef9 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disable_inline_public.php @@ -0,0 +1,9 @@ +withRules([ClassPropertyAssignToConstructorPromotionRector::class]); diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disabled_renaming.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disabled_renaming.php new file mode 100644 index 00000000000..d964dbeca93 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disabled_renaming.php @@ -0,0 +1,12 @@ +ruleWithConfiguration(ClassPropertyAssignToConstructorPromotionRector::class, [ + ClassPropertyAssignToConstructorPromotionRector::RENAME_PROPERTY => false, + ]); +}; diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disallow_model_based_classes.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disallow_model_based_classes.php new file mode 100644 index 00000000000..7484f1b800d --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_disallow_model_based_classes.php @@ -0,0 +1,12 @@ +ruleWithConfiguration(ClassPropertyAssignToConstructorPromotionRector::class, [ + ClassPropertyAssignToConstructorPromotionRector::ALLOW_MODEL_BASED_CLASSES => false, + ]); +}; diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_php80.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_php80.php new file mode 100644 index 00000000000..aff3cb4cf1d --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/config/configured_rule_php80.php @@ -0,0 +1,12 @@ +rule(ClassPropertyAssignToConstructorPromotionRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_80); +}; diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/DoctrineAnnotationClassToAttributeRectorTest.php b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/DoctrineAnnotationClassToAttributeRectorTest.php deleted file mode 100644 index a34c0e5ed99..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/DoctrineAnnotationClassToAttributeRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/none_target.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/none_target.php.inc deleted file mode 100644 index 221f8b1a614..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/none_target.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/required_value.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/required_value.php.inc deleted file mode 100644 index 63e14c2dcd7..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/required_value.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/some_class.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/some_class.php.inc deleted file mode 100644 index 89a9e48bd03..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_method_only.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_method_only.php.inc deleted file mode 100644 index 83f2f771e40..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_method_only.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_method_property_class.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_method_property_class.php.inc deleted file mode 100644 index 8e03841811d..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_method_property_class.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_property.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_property.php.inc deleted file mode 100644 index 5913c8d379a..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/Fixture/target_property.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/required_keep_old.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/required_keep_old.php.inc deleted file mode 100644 index 595cc6175f8..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/required_keep_old.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ -requiredField = $requiredField; - } -} - -?> diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/skip_already_added.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/skip_already_added.php.inc deleted file mode 100644 index 873cc9eea95..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/skip_already_added.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureShouldNotRemoveAnnotation'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/should_not_remove_annotation.php'; - } -} diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/config/configured_rule.php b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/config/configured_rule.php deleted file mode 100644 index e96e8b579b1..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(DoctrineAnnotationClassToAttributeRector::class); -}; diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/config/should_not_remove_annotation.php b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/config/should_not_remove_annotation.php deleted file mode 100644 index 95da2df88b1..00000000000 --- a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/config/should_not_remove_annotation.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(DoctrineAnnotationClassToAttributeRector::class) - ->call('configure', [[ - DoctrineAnnotationClassToAttributeRector::REMOVE_ANNOTATIONS => false, - ]]); -}; diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/already_implements_stringable.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/already_implements_stringable.php.inc new file mode 100644 index 00000000000..18da322ea62 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/already_implements_stringable.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/always_throws.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/always_throws.php.inc new file mode 100644 index 00000000000..c3940a8068b --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/always_throws.php.inc @@ -0,0 +1,12 @@ + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/extends_with_interface.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/extends_with_interface.php.inc new file mode 100644 index 00000000000..fd142e5975e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/extends_with_interface.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/in_abstract_method.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/in_abstract_method.php.inc new file mode 100644 index 00000000000..7256d64a100 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/in_abstract_method.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/parent_child_return_string.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/parent_child_return_string.php.inc new file mode 100644 index 00000000000..560a4bb50f2 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/parent_child_return_string.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/possible_void.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/possible_void.php.inc new file mode 100644 index 00000000000..6801cf0882f --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/possible_void.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/possible_void2.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/possible_void2.php.inc new file mode 100644 index 00000000000..6eb800c49a9 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/possible_void2.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_empty_string_no_return_expr.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_empty_string_no_return_expr.php.inc new file mode 100644 index 00000000000..f0297b720b6 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_empty_string_no_return_expr.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_void.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_void.php.inc new file mode 100644 index 00000000000..76656e181d5 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_void.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_void2.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_void2.php.inc new file mode 100644 index 00000000000..a07af423501 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/return_void2.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/skip_already_implements_and_return_typed_string.php.inc b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/skip_already_implements_and_return_typed_string.php.inc new file mode 100644 index 00000000000..fd697de0b37 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/Fixture/skip_already_implements_and_return_typed_string.php.inc @@ -0,0 +1,13 @@ +template; + } +} + +class SkipOnFunctions implements \Stringable +{ + public function __construct(private array $items) + { + } + + public function __toString(): string + { + $rand = rand(1, 3); + + if ($rand === 1) { + return strtoupper('index'); + } + + if ($rand === 2) { + return 10; + } + + return View::make('index', [ + 'only_uppercase' => array_filter( + $this->items, + static function (string $item) { + return mb_strtoupper($item) === $item; + } + ), + ])->render(); + } +} + +?> +----- +template; + } +} + +class SkipOnFunctions implements \Stringable +{ + public function __construct(private array $items) + { + } + + public function __toString(): string + { + $rand = rand(1, 3); + + if ($rand === 1) { + return strtoupper('index'); + } + + if ($rand === 2) { + return (string) 10; + } + + return (string) View::make('index', [ + 'only_uppercase' => array_filter( + $this->items, + static function (string $item) { + return mb_strtoupper($item) === $item; + } + ), + ])->render(); + } +} + +?> diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/StringableForToStringRectorTest.php b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/StringableForToStringRectorTest.php index 6c88f67ef33..a34c5de3891 100644 --- a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/StringableForToStringRectorTest.php +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/StringableForToStringRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php80\Rector\Class_\StringableForToStringRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StringableForToStringRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/config/configured_rule.php b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/config/configured_rule.php index d54b48ad809..a0d81eaac5d 100644 --- a/rules-tests/Php80/Rector/Class_/StringableForToStringRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/Class_/StringableForToStringRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php80\Rector\Class_\StringableForToStringRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StringableForToStringRector::class); -}; +return RectorConfig::configure() + ->withRules([StringableForToStringRector::class]); diff --git a/rules-tests/Php80/Rector/FuncCall/ClassOnObjectRector/ClassOnObjectRectorTest.php b/rules-tests/Php80/Rector/FuncCall/ClassOnObjectRector/ClassOnObjectRectorTest.php index 154a6dce2ba..81ee998adb9 100644 --- a/rules-tests/Php80/Rector/FuncCall/ClassOnObjectRector/ClassOnObjectRectorTest.php +++ b/rules-tests/Php80/Rector/FuncCall/ClassOnObjectRector/ClassOnObjectRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php80\Rector\FuncCall\ClassOnObjectRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ClassOnObjectRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/FuncCall/ClassOnObjectRector/config/configured_rule.php b/rules-tests/Php80/Rector/FuncCall/ClassOnObjectRector/config/configured_rule.php index f28c1a4a6be..dccfb5cf650 100644 --- a/rules-tests/Php80/Rector/FuncCall/ClassOnObjectRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/FuncCall/ClassOnObjectRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php80\Rector\FuncCall\ClassOnObjectRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ClassOnObjectRector::class); -}; +return RectorConfig::configure() + ->withRules([ClassOnObjectRector::class]); diff --git a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/code_sniffer_case.php.inc b/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/code_sniffer_case.php.inc deleted file mode 100644 index be6c372fc87..00000000000 --- a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/code_sniffer_case.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ -is(T_VARIABLE)) { - $error = 'Variable "%s" not allowed in double quoted string; use concatenation instead'; - $data = [$token->text]; - return $data; - } - } - } -} - -?> diff --git a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/fixture.php.inc b/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/fixture.php.inc deleted file mode 100644 index f539d368845..00000000000 --- a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - ------ -getTokenName(); - $text = $token->text; - } - } -} - -?> diff --git a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/not_array_first.php.inc b/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/not_array_first.php.inc deleted file mode 100644 index 82cb9c339a0..00000000000 --- a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/not_array_first.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ -getTokenName(); - $text = $token->text; - } - } -} - -?> diff --git a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/process_token_get_all_but_keep_non_token_array.php.inc b/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/process_token_get_all_but_keep_non_token_array.php.inc deleted file mode 100644 index 516a4538502..00000000000 --- a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/Fixture/process_token_get_all_but_keep_non_token_array.php.inc +++ /dev/null @@ -1,49 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/TokenGetAllToObjectRectorTest.php b/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/TokenGetAllToObjectRectorTest.php deleted file mode 100644 index 9b06948e9c3..00000000000 --- a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/TokenGetAllToObjectRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/config/configured_rule.php b/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/config/configured_rule.php deleted file mode 100644 index 4cc90d4198b..00000000000 --- a/rules-tests/Php80/Rector/FuncCall/TokenGetAllToObjectRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(TokenGetAllToObjectRector::class); -}; diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/_remove_doc_array_typed.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/_remove_doc_array_typed.php.inc deleted file mode 100644 index 8d3f4d3cf42..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/_remove_doc_array_typed.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - - */ - public function normalizeNodeValue($value) - { - return $value; - } -} - -?> ------ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/_remove_doc_array_typed2.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/_remove_doc_array_typed2.php.inc deleted file mode 100644 index e33ee0c00c4..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/_remove_doc_array_typed2.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - $value - */ - public function normalizeNodeValue($value) - { - return $value; - } -} - -?> ------ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/double_array.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/double_array.php.inc deleted file mode 100644 index b36a7f6c72b..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/double_array.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/false_pseudo_type.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/false_pseudo_type.php.inc deleted file mode 100644 index b963c2b9f55..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/false_pseudo_type.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/fixture.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/fixture.php.inc deleted file mode 100644 index 02753782828..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/object_and_specific_class.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/object_and_specific_class.php.inc deleted file mode 100644 index cd8abe5d017..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/object_and_specific_class.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/param_doc.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/param_doc.php.inc deleted file mode 100644 index 671c8c4d935..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/param_doc.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/skip_object_and_string_type.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/skip_object_and_string_type.php.inc deleted file mode 100644 index 0822fff3f04..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/skip_object_and_string_type.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/skip_void.php.inc b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/skip_void.php.inc deleted file mode 100644 index 279c716cfaa..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Fixture/skip_void.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Source/vendor/ParentClassWithMethod.php b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Source/vendor/ParentClassWithMethod.php deleted file mode 100644 index c58510a379c..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/Source/vendor/ParentClassWithMethod.php +++ /dev/null @@ -1,12 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/config/configured_rule.php b/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/config/configured_rule.php deleted file mode 100644 index 1de3de858e0..00000000000 --- a/rules-tests/Php80/Rector/FunctionLike/UnionTypesRector/config/configured_rule.php +++ /dev/null @@ -1,16 +0,0 @@ -services(); - $services->set(UnionTypesRector::class); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES); -}; diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/fixture_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/fixture_equal.php.inc new file mode 100644 index 00000000000..8c7b7b41348 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/fixture_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/hardcoded_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/hardcoded_equal.php.inc new file mode 100644 index 00000000000..ac09bef8369 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/hardcoded_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/hardcoded_not_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/hardcoded_not_equal.php.inc new file mode 100644 index 00000000000..bcebe5ee705 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/hardcoded_not_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/include_case_sensitive.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/include_case_sensitive.php.inc new file mode 100644 index 00000000000..220aeb56d7e --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/include_case_sensitive.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_case_insensitive.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_case_insensitive.php.inc new file mode 100644 index 00000000000..261cdc9f94b --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_case_insensitive.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_equal_hardcoded.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_equal_hardcoded.php.inc new file mode 100644 index 00000000000..5172ae2998e --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_equal_hardcoded.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_not_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_not_equal.php.inc new file mode 100644 index 00000000000..95969e9f800 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_not_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_not_equal_hardcoded.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_not_equal_hardcoded.php.inc new file mode 100644 index 00000000000..c9ce4360983 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_compare_not_equal_hardcoded.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_not_equal_prefix.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_not_equal_prefix.php.inc new file mode 100644 index 00000000000..5453a79542c --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_not_equal_prefix.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/StrEndsWithRectorTest.php b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/StrEndsWithRectorTest.php index 0b7eac63361..8116b0c0606 100644 --- a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/StrEndsWithRectorTest.php +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/StrEndsWithRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php80\Rector\Identical\StrEndsWithRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StrEndsWithRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/config/configured_rule.php b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/config/configured_rule.php index e1f095123fd..b72b1c2f4ff 100644 --- a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php80\Rector\Identical\StrEndsWithRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StrEndsWithRector::class); -}; +return RectorConfig::configure() + ->withRules([StrEndsWithRector::class]); diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/fixture_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/fixture_equal.php.inc new file mode 100644 index 00000000000..5f87efcd522 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/fixture_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded.php.inc new file mode 100644 index 00000000000..e8dc94c4cc0 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_equal.php.inc new file mode 100644 index 00000000000..a7d8c7cdc0e --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_not_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_not_equal.php.inc new file mode 100644 index 00000000000..850e1550fed --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_not_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_not_identical.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_not_identical.php.inc new file mode 100644 index 00000000000..9c1091a38d9 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/hardcoded_not_identical.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/not_equal_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/not_equal_substr.php.inc new file mode 100644 index 00000000000..f48e15217a1 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/not_equal_substr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/other_side_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/other_side_equal.php.inc new file mode 100644 index 00000000000..70ca84ee897 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/other_side_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_equal_non_zero.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_equal_non_zero.php.inc new file mode 100644 index 00000000000..7ce386eabd8 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_equal_non_zero.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strncmp_hardcoded.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strncmp_hardcoded.php.inc new file mode 100644 index 00000000000..a00d7d75cc0 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strncmp_hardcoded.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strncmp_hardcoded_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strncmp_hardcoded_equal.php.inc new file mode 100644 index 00000000000..0a9defc142a --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strncmp_hardcoded_equal.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal.php.inc new file mode 100644 index 00000000000..cd671374ff5 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_not_prefix.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_not_prefix.php.inc new file mode 100644 index 00000000000..36f6112dbee --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_not_prefix.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_other_side.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_other_side.php.inc new file mode 100644 index 00000000000..1e959bb197d --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_other_side.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded.php.inc new file mode 100644 index 00000000000..3d7e91ded03 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded_equal.php.inc new file mode 100644 index 00000000000..a19bf55f32e --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/FixturePhp74/fixture.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/FixturePhp74/fixture.php.inc new file mode 100644 index 00000000000..6aa41d73c31 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/FixturePhp74/fixture.php.inc @@ -0,0 +1,12 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/polyfill_not_configured_php74.php'; + } +} diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/PolyfillRectorTest.php b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/PolyfillRectorTest.php new file mode 100644 index 00000000000..e942e52e083 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/PolyfillRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/polyfill_configured_rule.php'; + } +} diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/StrStartsWithRectorTest.php b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/StrStartsWithRectorTest.php index d21f49094ed..4df683b1559 100644 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/StrStartsWithRectorTest.php +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/StrStartsWithRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StrStartsWithRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/configured_rule.php b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/configured_rule.php index cdc04c4263b..1eb044d00bb 100644 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/configured_rule.php @@ -2,10 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php80\Rector\Identical\StrStartsWithRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StrStartsWithRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersion::PHP_80); + + $rectorConfig->rule(StrStartsWithRector::class); }; diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/polyfill_configured_rule.php b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/polyfill_configured_rule.php new file mode 100644 index 00000000000..ca57e0859a6 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/polyfill_configured_rule.php @@ -0,0 +1,16 @@ +rule(StrStartsWithRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_74); + + $rectorConfig->polyfillPackages([PolyfillPackage::PHP_80]); +}; diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/polyfill_not_configured_php74.php b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/polyfill_not_configured_php74.php new file mode 100644 index 00000000000..9d8c17c0e09 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/config/polyfill_not_configured_php74.php @@ -0,0 +1,12 @@ +rule(StrStartsWithRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_74); +}; diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/default_value.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/default_value.php.inc deleted file mode 100644 index a8ae18cd77a..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/default_value.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -endWeekDate !== null) { - $param = $this->endWeekDate->format(); - } else { - $param = "No data"; - } - return $param; - } -} -?> ------ -endWeekDate?->format() ?? "No data"; - return $param; - } -} -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture.php.inc deleted file mode 100644 index 93264ad30fb..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -mayFail1(); - if ($o2 === null) { - return null; - } - - return $o2->mayFail2(); - } -} - -?> ------ -mayFail1()?->mayFail2(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_multiple_if.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_multiple_if.php.inc deleted file mode 100644 index 902b272bc71..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_multiple_if.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -mayFail1(); - if ($o2 === null) { - return null; - } - - $mayFail2 = $o2->mayFail2(); - if ($mayFail2 === null) { - return null; - } - - $mayFail3 = $mayFail2->mayFail3(); - if ($mayFail3 === null) { - return null; - } - - $mayFail4 = $mayFail3->mayFail4(); - if ($mayFail4 === null) { - return null; - } - - return $mayFail4->mayFail5(); - } -} - -?> ------ -mayFail1()?->mayFail2()?->mayFail3()?->mayFail4()?->mayFail5(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_next_is_property.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_next_is_property.php.inc deleted file mode 100644 index 7008ceba6f2..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_next_is_property.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -property1; - if ($o2 === null) { - return null; - } - - return $o2->property2; - } -} - -?> ------ -property1?->property2; - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_no_return_last.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_no_return_last.php.inc deleted file mode 100644 index 7e32b7e3e7a..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_no_return_last.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -mayFail1(); - if ($o2 === null) { - return null; - } - - $o2->mayFail2(); - } -} - -?> ------ -mayFail1()?->mayFail2(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical.php.inc deleted file mode 100644 index 30bffeecc2f..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -user; - if ($user !== null) { - $address = $user->getAddress(); - if ($address !== null) { - $country = $address->getCountry(); - } - } - } - } -} - -?> ------ -user?->getAddress()?->getCountry(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical_process_only_direct_if_after_assign.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical_process_only_direct_if_after_assign.php.inc deleted file mode 100644 index 8e8ff47c8f8..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical_process_only_direct_if_after_assign.php.inc +++ /dev/null @@ -1,53 +0,0 @@ -user; - - echo 'STATEMENT HERE'; - - if ($user !== null) { - $address = $user->getAddress(); - - echo 'STATEMENT HERE'; - - if ($address !== null) { - $country = $address->getCountry(); - } - } - } - } -} - -?> ------ -user; - - echo 'STATEMENT HERE'; - - if ($user !== null) { - $address = $user->getAddress(); - - echo 'STATEMENT HERE'; - - $country = $address?->getCountry(); - } - } - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical_yoda.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical_yoda.php.inc deleted file mode 100644 index 31dea6d0f18..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_not_identical_yoda.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -user; - if (null !== $user) { - $address = $user->getAddress(); - if (null !== $address) { - $country = $address->getCountry(); - } - } - } - } -} - -?> ------ -user?->getAddress()?->getCountry(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_process_only_direct_usage_after_if.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_process_only_direct_usage_after_if.php.inc deleted file mode 100644 index 8eb1dff6236..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_process_only_direct_usage_after_if.php.inc +++ /dev/null @@ -1,51 +0,0 @@ -mayFail1(); - if ($o2 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - $mayFail2 = $o2->mayFail2(); - if ($mayFail2 === null) { - return null; - } - - $mayFail3 = $mayFail2->mayFail3(); - if ($mayFail3 === null) { - return null; - } - - return $mayFail3->mayFail4(); - } -} - -?> ------ -mayFail1(); - if ($o2 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - return $o2->mayFail2()?->mayFail3()?->mayFail4(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_ternary_is_not_null.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_ternary_is_not_null.php.inc deleted file mode 100644 index c54f96f26e6..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_ternary_is_not_null.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -getB() : null; - $c = $a !== null ? $a->c : null; - } -} -?> ------ -getB(); - $c = $a?->c; - } -} -?> \ No newline at end of file diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_ternary_is_null.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_ternary_is_null.php.inc deleted file mode 100644 index d344bc7e95d..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_ternary_is_null.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -getB(); - $c = $a === null ? null : $a->c; - } -} -?> ------ -getB(); - $c = $a?->c; - } -} -?> \ No newline at end of file diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_yoda.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_yoda.php.inc deleted file mode 100644 index a3521d07a26..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/fixture_yoda.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -mayFail1(); - if (null === $o2) { - return null; - } - - return $o2->mayFail2(); - } -} - -?> ------ -mayFail1()?->mayFail2(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/method_args.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/method_args.php.inc deleted file mode 100644 index bc9579b85bf..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/method_args.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -endWeekDate !== null) { - $param = $this->endWeekDate->format('Y-m-d'); - } else { - $param = null; - } - return $param; - } -} - -?> ------ -endWeekDate?->format('Y-m-d'); - return $param; - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/method_args_multi_call.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/method_args_multi_call.php.inc deleted file mode 100644 index 0a0b8a755cf..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/method_args_multi_call.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -mayFail1('a'); - if ($o2 === null) { - return null; - } - - return $o2->mayFail2('b'); - } -} - -?> ------ -mayFail1('a')?->mayFail2('b'); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_default_value_more_stmt.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_default_value_more_stmt.php.inc deleted file mode 100644 index b2fa93cff33..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_default_value_more_stmt.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -endWeekDate !== null) { - $param = $this->endWeekDate->format(); - } else { - $param = "No data"; - - echo 'STATEMENT HERE'; - } - return $param; - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_if_after_assignment.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_if_after_assignment.php.inc deleted file mode 100644 index f800c268854..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_if_after_assignment.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -mayFail1(); - - echo 'STATEMENT HERE'; - - if ($o2 === null) { - return null; - } - - $mayFail2 = $o2->mayFail2(); - - echo 'STATEMENT HERE'; - - if ($mayFail2 === null) { - return null; - } - - $mayFail3 = $mayFail2->mayFail3(); - - echo 'STATEMENT HERE'; - - if ($mayFail3 === null) { - return null; - } - - $mayFail4 = $mayFail3->mayFail4(); - - echo 'STATEMENT HERE'; - - if ($mayFail4 === null) { - return null; - } - - return $mayFail4->mayFail5(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if.php.inc deleted file mode 100644 index 1561f01b504..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -mayFail1(); - if ($o2 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - $mayFail2 = $o2->mayFail2(); - if ($mayFail2 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - $mayFail3 = $mayFail2->mayFail3(); - if ($mayFail3 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - $mayFail4 = $mayFail3->mayFail4(); - if ($mayFail4 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - return $mayFail4->mayFail5(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if_in_next.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if_in_next.php.inc deleted file mode 100644 index 7ad69bd3743..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if_in_next.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -mayFail1(); - if ($o2 === null) { - return null; - } - - $mayFail2 = $o2->mayFail2(); - if ($mayFail2 === null) { - return null; - } - - $mayFail3 = $mayFail2->mayFail3(); - if ($mayFail3 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - return $mayFail3->mayFail4(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if_in_prev_and_next.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if_in_prev_and_next.php.inc deleted file mode 100644 index 9f88e257269..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_no_direct_usage_after_if_in_prev_and_next.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -mayFail1(); - if ($o2 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - $mayFail2 = $o2->mayFail2(); - if ($mayFail2 === null) { - return null; - } - - $mayFail3 = $mayFail2->mayFail3(); - if ($mayFail3 === null) { - return null; - } - - echo 'STATEMENT HERE'; - - return $mayFail3->mayFail4(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_not_identical_no_direct_assign_after_if.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_not_identical_no_direct_assign_after_if.php.inc deleted file mode 100644 index 4520d56cbf4..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_not_identical_no_direct_assign_after_if.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -user; - } - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_not_identical_not_null.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_not_identical_not_null.php.inc deleted file mode 100644 index ac3398c1e38..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_not_identical_not_null.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -user; - } - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_null_safe_test.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_null_safe_test.php.inc deleted file mode 100644 index c8573ac1cc6..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_null_safe_test.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -getB(); - } -} -?> \ No newline at end of file diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_ternary_returns_non_null_in_if_and_else.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_ternary_returns_non_null_in_if_and_else.php.inc deleted file mode 100644 index 3a4f4e71eb9..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/skip_ternary_returns_non_null_in_if_and_else.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -getB(); - } -} \ No newline at end of file diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/sooo_many_null_checks.php.inc b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/sooo_many_null_checks.php.inc deleted file mode 100644 index a4492e78745..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/Fixture/sooo_many_null_checks.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -mayFail1(); - if ($o2 === null) { - return null; - } - - $o3 = $o2->mayFail2(); - if (null === $o3) { - return null; - } - - $o4 = $o3->mayFail3(); - if (null === $o4) { - return null; - } - - return $o4->mayFail4(); - } -} - -?> ------ -mayFail1()?->mayFail2()?->mayFail3()?->mayFail4(); - } -} - -?> diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/NullsafeOperatorRectorTest.php b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/NullsafeOperatorRectorTest.php deleted file mode 100644 index f1afe8eea76..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/NullsafeOperatorRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/config/configured_rule.php b/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/config/configured_rule.php deleted file mode 100644 index aa00b69b2ab..00000000000 --- a/rules-tests/Php80/Rector/If_/NullsafeOperatorRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(NullsafeOperatorRector::class); -}; diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..daef049f323 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture_equal.php.inc new file mode 100644 index 00000000000..044cda46f28 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/identical_mb_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/identical_mb_strpos.php.inc new file mode 100644 index 00000000000..583b2cb371d --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/identical_mb_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_equal_mb_strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_equal_mb_strstr.php.inc new file mode 100644 index 00000000000..8e1ca6f4c53 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_equal_mb_strstr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_strstr.php.inc new file mode 100644 index 00000000000..4e3499ebb67 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_strstr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_equal_strpos.php.inc new file mode 100644 index 00000000000..7a91da6a599 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_equal_strpos.php.inc new file mode 100644 index 00000000000..b57186ea9d6 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_mb_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_mb_strpos.php.inc new file mode 100644 index 00000000000..5202f15e2d8 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_mb_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_equal_strpos.php.inc new file mode 100644 index 00000000000..22c0564cbd9 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_strpos.php.inc new file mode 100644 index 00000000000..5f928270ad7 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_strpos.php.inc new file mode 100644 index 00000000000..6f2aadf4607 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_equal_strpos.php.inc new file mode 100644 index 00000000000..defaf7f1515 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_strpos.php.inc new file mode 100644 index 00000000000..9b4945425dc --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc new file mode 100644 index 00000000000..ff3e1bc7e9c --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_strpos.php.inc new file mode 100644 index 00000000000..c7cac64b729 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_equal_strpos.php.inc new file mode 100644 index 00000000000..54cf829f71e --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_strpos.php.inc new file mode 100644 index 00000000000..7938e5ce99e --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strpos_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strpos_equal.php.inc new file mode 100644 index 00000000000..a6129f96ced --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strpos_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr.php.inc new file mode 100644 index 00000000000..613b9fb4480 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr_equal.php.inc new file mode 100644 index 00000000000..039c4b8949e --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way.php.inc new file mode 100644 index 00000000000..f61ee8b3b4a --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way_equal.php.inc new file mode 100644 index 00000000000..2e7ef24f8ad --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/MbStrContainsRectorTest.php b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/MbStrContainsRectorTest.php new file mode 100644 index 00000000000..6c7f78b1f46 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/MbStrContainsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/config/configured_rule.php b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/config/configured_rule.php new file mode 100644 index 00000000000..559d668df47 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/config/configured_rule.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersion::PHP_80); + + $rectorConfig->rule(MbStrContainsRector::class); +}; diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/fixture.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/fixture.php.inc index 52a810b46ff..cf80044c1a5 100644 --- a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/fixture.php.inc +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/fixture.php.inc @@ -6,7 +6,7 @@ class Fixture { public function run() { - return strpos('abc', 'a') !== false; + $isMatch = strpos('abc', 'a') !== false; } } @@ -20,7 +20,7 @@ class Fixture { public function run() { - return str_contains('abc', 'a'); + $isMatch = str_contains('abc', 'a'); } } diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/fixture_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/fixture_equal.php.inc new file mode 100644 index 00000000000..e345230dfb5 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/fixture_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/identical_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/identical_strpos.php.inc index e62ee7f230a..609d1c8eaa5 100644 --- a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/identical_strpos.php.inc +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/identical_strpos.php.inc @@ -6,7 +6,7 @@ class IdenticalStrpos { public function run() { - return strpos('abc', 'a') === false; + $isMatch = strpos('abc', 'a') === false; } } @@ -20,7 +20,7 @@ class IdenticalStrpos { public function run() { - return !str_contains('abc', 'a'); + $isMatch = !str_contains('abc', 'a'); } } diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/identical_strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/identical_strstr.php.inc deleted file mode 100644 index e8f9d9c132b..00000000000 --- a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/identical_strstr.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/not_equal_strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/not_equal_strstr.php.inc new file mode 100644 index 00000000000..2467d364c17 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/not_equal_strstr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/not_strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/not_strstr.php.inc new file mode 100644 index 00000000000..ac92f9db0f9 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/not_strstr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_equal_strpos.php.inc new file mode 100644 index 00000000000..95cda89f4eb --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_expression_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_expression_equal_strpos.php.inc new file mode 100644 index 00000000000..f86c7683193 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_expression_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_expression_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_expression_strpos.php.inc new file mode 100644 index 00000000000..c2e2c7ea05f --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_expression_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_negative_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_negative_equal_strpos.php.inc new file mode 100644 index 00000000000..209324612af --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_negative_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_negative_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_negative_strpos.php.inc new file mode 100644 index 00000000000..3054576fcbc --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_negative_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_strpos.php.inc new file mode 100644 index 00000000000..d37fa271755 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_equal_strpos.php.inc new file mode 100644 index 00000000000..3557b7de24b --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_strpos.php.inc new file mode 100644 index 00000000000..fdcdb3475f2 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc new file mode 100644 index 00000000000..9d891068846 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_zero_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_zero_strpos.php.inc new file mode 100644 index 00000000000..c88698e3a49 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_variable_zero_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_zero_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_zero_equal_strpos.php.inc new file mode 100644 index 00000000000..771e9a02a83 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_zero_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_zero_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_zero_strpos.php.inc new file mode 100644 index 00000000000..ec727682d5a --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/offset_zero_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strpos_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strpos_equal.php.inc new file mode 100644 index 00000000000..db6d9ee1e93 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strpos_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strstr.php.inc index b46621bc176..384001ccda2 100644 --- a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strstr.php.inc +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strstr.php.inc @@ -6,7 +6,7 @@ class Strstr { public function run() { - return strstr('abc', 'a') !== false; + $isMatch = strstr('abc', 'a') !== false; } } @@ -20,7 +20,7 @@ class Strstr { public function run() { - return str_contains('abc', 'a'); + $isMatch = str_contains('abc', 'a'); } } diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strstr_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strstr_equal.php.inc new file mode 100644 index 00000000000..0d65c0c8977 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/strstr_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way.php.inc index d3a878035d4..6e9ad77f2f5 100644 --- a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way.php.inc +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way.php.inc @@ -6,7 +6,7 @@ class TheOtherWay { public function run() { - return false !== strpos('abc', 'a'); + $isMatch = false !== strpos('abc', 'a'); } } @@ -20,7 +20,7 @@ class TheOtherWay { public function run() { - return str_contains('abc', 'a'); + $isMatch = str_contains('abc', 'a'); } } diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way_equal.php.inc new file mode 100644 index 00000000000..b706a1e6f91 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/StrContainsRectorTest.php b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/StrContainsRectorTest.php index 8f84774de7b..5e6bbb11552 100644 --- a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/StrContainsRectorTest.php +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/StrContainsRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php80\Rector\NotIdentical\StrContainsRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StrContainsRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/config/configured_rule.php b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/config/configured_rule.php index a5fc7462e57..009267fe010 100644 --- a/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/NotIdentical/StrContainsRector/config/configured_rule.php @@ -2,10 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php80\Rector\NotIdentical\StrContainsRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StrContainsRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersion::PHP_80); + + $rectorConfig->rule(StrContainsRector::class); }; diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_join_column_nested_in_join_table.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_join_column_nested_in_join_table.php.inc new file mode 100644 index 00000000000..f26552de233 --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_join_column_nested_in_join_table.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns.php.inc new file mode 100644 index 00000000000..745183d13dc --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns_promoted_property.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns_promoted_property.php.inc new file mode 100644 index 00000000000..c0048a22c69 --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns_promoted_property.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_table.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_table.php.inc new file mode 100644 index 00000000000..c3be9463270 --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_table.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_unique_constraints.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_unique_constraints.php.inc new file mode 100644 index 00000000000..28fb06047cb --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_unique_constraints.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/fully_qualified_use.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/fully_qualified_use.php.inc new file mode 100644 index 00000000000..88cf0c8a40e --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/fully_qualified_use.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/merge_with_existing_attribute.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/merge_with_existing_attribute.php.inc new file mode 100644 index 00000000000..0d33ee69d1d --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/merge_with_existing_attribute.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/multiple_inversed_join_columns.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/multiple_inversed_join_columns.php.inc new file mode 100644 index 00000000000..3ad16431be0 --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/multiple_inversed_join_columns.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/some_class.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..b3795e3559b --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/some_class.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/string_annotation_value.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/string_annotation_value.php.inc new file mode 100644 index 00000000000..3c6d47001ba --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/string_annotation_value.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/NestedAnnotationToAttributeRectorTest.php b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/NestedAnnotationToAttributeRectorTest.php new file mode 100644 index 00000000000..906bb0b166a --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/NestedAnnotationToAttributeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/config/configured_rule.php b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/config/configured_rule.php new file mode 100644 index 00000000000..58db49d1142 --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/config/configured_rule.php @@ -0,0 +1,35 @@ +phpVersion(PhpVersion::PHP_81); + + $rectorConfig->ruleWithConfiguration(NestedAnnotationToAttributeRector::class, [ + /** @see https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/reference/attributes-reference.html#joincolumn-inversejoincolumn */ + new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\JoinTable', [ + new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\JoinColumn', 'joinColumns'), + new AnnotationPropertyToAttributeClass( + 'Doctrine\ORM\Mapping\InverseJoinColumn', + 'inverseJoinColumns', + true + ), + ]), + + new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\Table', [ + new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\Index', 'indexes'), + new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\UniqueConstraint', 'uniqueConstraints'), + ]), + + /** @see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#joincolumns */ + new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\JoinColumns', [ + new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\JoinColumn'), + ], true), + ]); +}; diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/ChangeSwitchToMatchRectorSkippedTest.php b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/ChangeSwitchToMatchRectorSkippedTest.php new file mode 100644 index 00000000000..9b32931b98e --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/ChangeSwitchToMatchRectorSkippedTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureSkipped'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php_version_74.php'; + } +} diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/ChangeSwitchToMatchRectorTest.php b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/ChangeSwitchToMatchRectorTest.php index f5b74a044d1..2011d5b24ed 100644 --- a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/ChangeSwitchToMatchRectorTest.php +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/ChangeSwitchToMatchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php80\Rector\Switch_\ChangeSwitchToMatchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ChangeSwitchToMatchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/assign_and_use_as_parameter.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/assign_and_use_as_parameter.php.inc new file mode 100644 index 00000000000..30b695830f8 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/assign_and_use_as_parameter.php.inc @@ -0,0 +1,52 @@ + +----- + 'BAD REQUEST', + 2 => 'CHANGE STATUS FAILED', + 3 => 'NOT FOUND', + default => 'FAILED', + }; + + return new SomeResponse($content, 400); + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/before_else.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/before_else.php.inc new file mode 100644 index 00000000000..4dc5c7fb684 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/before_else.php.inc @@ -0,0 +1,47 @@ + +----- + "B", + 2 => "C", + default => "D", + }; + } + + return $value; +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default.php.inc deleted file mode 100644 index ed488abd9ca..00000000000 --- a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - 'foo', - default => 'bar', - }; - } -} - -?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default_in_first_case_no_prev.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default_in_first_case_no_prev.php.inc new file mode 100644 index 00000000000..8fc4d383da9 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default_in_first_case_no_prev.php.inc @@ -0,0 +1,37 @@ + +----- + 'bar', + default => 'foo', + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default_in_middle_case.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default_in_middle_case.php.inc new file mode 100644 index 00000000000..8bfbd8651cd --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cases_mix_up_default_in_middle_case.php.inc @@ -0,0 +1,42 @@ + +----- + 'A', + 'other' => 'bar', + default => 'foo', + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cast_int_for_string.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cast_int_for_string.php.inc new file mode 100644 index 00000000000..98ce58fca0e --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cast_int_for_string.php.inc @@ -0,0 +1,38 @@ + +----- + 1, + 88 => 2, + default => 4, + };; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cast_string_for_int.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cast_string_for_int.php.inc new file mode 100644 index 00000000000..759894220af --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/cast_string_for_int.php.inc @@ -0,0 +1,38 @@ + +----- + '1', + '88' => '2', + default => '4', + };; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/copy_switch_comment.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/copy_switch_comment.php.inc new file mode 100644 index 00000000000..4f14f4353a0 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/copy_switch_comment.php.inc @@ -0,0 +1,40 @@ +lexer->lookahead['type']) { + case Lexer::T_DELETE: + $statement = $this->DeleteStatement(); + break; + + default: + $statement = $this->syntaxError('SELECT, UPDATE or DELETE'); + break; + } + } +} + +?> +----- +lexer->lookahead['type']) { + Lexer::T_DELETE => $this->DeleteStatement(), + default => $this->syntaxError('SELECT, UPDATE or DELETE'), + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case.php.inc new file mode 100644 index 00000000000..3c0ebcb294f --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case.php.inc @@ -0,0 +1,50 @@ + +----- + 'a', + $b => 'b', + default => throw new \InvalidArgumentException(), + }; + + var_dump($result); + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case2.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case2.php.inc new file mode 100644 index 00000000000..8997505e2cc --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case2.php.inc @@ -0,0 +1,50 @@ + +----- + 'a', + self::B => 'b', + default => throw new \InvalidArgumentException(), + }; + + var_dump($result); + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case3.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case3.php.inc new file mode 100644 index 00000000000..a750042afc3 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case3.php.inc @@ -0,0 +1,50 @@ + +----- + 'a', + DefinedCase3::B => 'b', + default => throw new \InvalidArgumentException(), + }; + + var_dump($result); + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case4.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case4.php.inc new file mode 100644 index 00000000000..66c83373a4d --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/defined_case4.php.inc @@ -0,0 +1,44 @@ + +----- + 'a', + $variable2 => 'b', + default => throw new \InvalidArgumentException(), + }; + + var_dump($result); + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/double_default.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/double_default.php.inc index 8d12afbfbc9..52bb842b995 100644 --- a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/double_default.php.inc +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/double_default.php.inc @@ -30,10 +30,13 @@ final class DoubleDefault { public function run($input) { + $value = null; + $value = match ($input) { 100 => 1000, default => throw new \InvalidArgumentException('Not defined'), }; + return $value; } } diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/dynamic_bool_expr.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/dynamic_bool_expr.php.inc new file mode 100644 index 00000000000..f344e83adb4 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/dynamic_bool_expr.php.inc @@ -0,0 +1,44 @@ + +----- + new \ReflectionFunction($filter), + is_object($filter) => new \ReflectionMethod($filter, '__invoke'), + default => throw new \InvalidArgumentException( + sprintf('Expected Closure or invokable object on callable filter, %s given', gettype($filter)) + ), + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/dynamic_bool_expr2.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/dynamic_bool_expr2.php.inc new file mode 100644 index 00000000000..480d52c0134 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/dynamic_bool_expr2.php.inc @@ -0,0 +1,44 @@ + +----- + new \ReflectionFunction($filter), + is_object($filter) && method_exists($filter, '__invoke') => new \ReflectionMethod($filter, '__invoke'), + default => throw new \InvalidArgumentException( + sprintf('Expected Closure or invokable object on callable filter, %s given', gettype($filter)) + ), + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/fixture.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/fixture.php.inc deleted file mode 100644 index 8632ae1b033..00000000000 --- a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -lexer->lookahead['type']) { - case Lexer::T_DELETE: - $statement = $this->DeleteStatement(); - break; - - default: - $statement = $this->syntaxError('SELECT, UPDATE or DELETE'); - break; - } - } -} - -?> ------ -lexer->lookahead['type']) { - Lexer::T_DELETE => $this->DeleteStatement(), - default => $this->syntaxError('SELECT, UPDATE or DELETE'), - }; - } -} - -?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_assign_comment.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_assign_comment.php.inc new file mode 100644 index 00000000000..17246d1d6c1 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_assign_comment.php.inc @@ -0,0 +1,39 @@ + +----- + 'get', + default => 'error', + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_comment_on_unrelated_return.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_comment_on_unrelated_return.php.inc new file mode 100644 index 00000000000..b533a6ca520 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_comment_on_unrelated_return.php.inc @@ -0,0 +1,46 @@ +getValue()) { + case ProductService::SPECIAL_GESTION_CAPSULE: + // comment 1 + $proxyQuery->getQueryBuilder()->andWhere($alias . '.collectionCapsule = true'); + break; + case ProductService::SPECIAL_GESTION_PRESTIGE: + // comment 2 + $proxyQuery->getQueryBuilder()->andWhere($alias . '.editionPrestige = true'); + break; + } + + return true; + } +} + +?> +----- +getValue()) { + // comment 1 + ProductService::SPECIAL_GESTION_CAPSULE => $proxyQuery->getQueryBuilder()->andWhere($alias . '.collectionCapsule = true'), + // comment 2 + ProductService::SPECIAL_GESTION_PRESTIGE => $proxyQuery->getQueryBuilder()->andWhere($alias . '.editionPrestige = true'), + default => true, + }; + + return true; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_return_comment.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_return_comment.php.inc new file mode 100644 index 00000000000..edf3abdd951 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_return_comment.php.inc @@ -0,0 +1,40 @@ + +----- + false, + 'EDIT' => true, + // Comment + default => true, + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_return_comment_with_default.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_return_comment_with_default.php.inc new file mode 100644 index 00000000000..cb2108559f0 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_return_comment_with_default.php.inc @@ -0,0 +1,44 @@ + +----- + false, + // Comment 1 + 'EDIT' => true, + // Comment 2 + default => true, + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_throw_comment_with_default.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_throw_comment_with_default.php.inc new file mode 100644 index 00000000000..86f28e868f1 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirror_throw_comment_with_default.php.inc @@ -0,0 +1,40 @@ + +----- + 'yes', + 200 => 'no', + // comment + default => throw new \RuntimeException, + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirrow_comment_with_default_exception_with_next_stmt.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirrow_comment_with_default_exception_with_next_stmt.php.inc new file mode 100644 index 00000000000..1fe636ce023 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mirrow_comment_with_default_exception_with_next_stmt.php.inc @@ -0,0 +1,47 @@ + +----- + 100, + // comment 1 + default => throw new \InvalidArgumentException(), + }; + + // comment 2 + return $statement; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/multi_next_stmt.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/multi_next_stmt.php.inc new file mode 100644 index 00000000000..df8ed7e779a --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/multi_next_stmt.php.inc @@ -0,0 +1,46 @@ + +----- + SORT_NUMERIC, + 'string', 'list', 'text' => SORT_NATURAL | SORT_FLAG_CASE, + default => SORT_REGULAR, + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mutiple_cases2.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/multiple_cases2.php.inc similarity index 100% rename from rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mutiple_cases2.php.inc rename to rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/multiple_cases2.php.inc diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mutiple_cases.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mutiple_cases.php.inc deleted file mode 100644 index 9abeb48fb82..00000000000 --- a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/mutiple_cases.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - 100, - default => 1000, - }; - } -} - -?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/no_default.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/no_default.php.inc new file mode 100644 index 00000000000..3d34a4442a4 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/no_default.php.inc @@ -0,0 +1,22 @@ + +----- + 'AB', + 'C' => 'D', + default => $value, + }; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_2_and_more_stmts.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_2_and_more_stmts.php.inc deleted file mode 100644 index abc009f40bd..00000000000 --- a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_2_and_more_stmts.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -lexer->lookahead['type']) { - case Lexer::T_DELETE: - $statement = $this->DeleteStatement(); - $statement = $this->DeleteStatement(); - break; - } - } -} diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_array_append_different_keys_in_case.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_array_append_different_keys_in_case.php.inc new file mode 100644 index 00000000000..769f937f682 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_array_append_different_keys_in_case.php.inc @@ -0,0 +1,22 @@ + diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_array_assign_and_dim_fetch.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_array_assign_and_dim_fetch.php.inc index fabbc37b437..fc731d6161d 100644 --- a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_array_assign_and_dim_fetch.php.inc +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_array_assign_and_dim_fetch.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Php80\Rector\Switch_\ChangeSwitchToMatchRector\Fixture; -final class SkipArrayAssignAdnDimFetch +final class SkipArrayAssignAndDimFetch { public function run($value) { diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_assign_on_its_var_not_directly_used_in_next_return.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_assign_on_its_var_not_directly_used_in_next_return.php.inc new file mode 100644 index 00000000000..7be0bd262c0 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_assign_on_its_var_not_directly_used_in_next_return.php.inc @@ -0,0 +1,26 @@ +status) { + case self::STATUS_B: + case self::STATUS_C: + $class[] = 'red'; + break; + } + + return execute($class); + }; + } +} diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_by_ref_method.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_by_ref_method.php.inc new file mode 100644 index 00000000000..07aaa8fbb08 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_by_ref_method.php.inc @@ -0,0 +1,31 @@ +monday; + case 1: + return $this->tuesday; + // ... + default: + throw new LogicException(); + } + } + + public function setAtIndex(int $index, int $value) { + $ref = &$this->getRef($index); + $ref = $value; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_case_after_default_empty.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_case_after_default_empty.php.inc new file mode 100644 index 00000000000..7ff1bacd742 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_case_after_default_empty.php.inc @@ -0,0 +1,21 @@ +assignedExpr = 1000; - break; - - default: - $this->another = 2000; - break; - } - } -} diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_different_type_cases.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_different_type_cases.php.inc new file mode 100644 index 00000000000..a5e20515ee7 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_different_type_cases.php.inc @@ -0,0 +1,22 @@ + diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_no_default_case_collection_assign.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_no_default_case_collection_assign.php.inc new file mode 100644 index 00000000000..7883f61bf46 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_no_default_case_collection_assign.php.inc @@ -0,0 +1,20 @@ +name) { + case 'a': + $factor = 0.95; + break; + case 'b': + case 'c': + $factor = 0.8; + break; + } + + return $price * $factor; + } +} diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_no_default_next_no_return.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_no_default_next_no_return.php.inc new file mode 100644 index 00000000000..cbbefd5fc87 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_no_default_next_no_return.php.inc @@ -0,0 +1,20 @@ +result = 0; + break; + case 'b': + $this->result = 1; + break; + } + + return $this; + } +} diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_numeric_string.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_numeric_string.php.inc new file mode 100644 index 00000000000..6ba3ca6c6e5 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_numeric_string.php.inc @@ -0,0 +1,26 @@ +firstCollection = 1000; - } - } -} diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_throwen_exception.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_throwen_exception.php.inc deleted file mode 100644 index 4d2e03ba1a0..00000000000 --- a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/skip_throwen_exception.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -a(); + $this->a(); break; case "b": - $ths->b(); + $this->b(); break; } } diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/unrelated_return.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/unrelated_return.php.inc new file mode 100644 index 00000000000..3c7b61bd926 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Fixture/unrelated_return.php.inc @@ -0,0 +1,42 @@ +getValue()) { + case ProductService::SPECIAL_GESTION_CAPSULE: + $proxyQuery->getQueryBuilder()->andWhere($alias . '.collectionCapsule = true'); + break; + case ProductService::SPECIAL_GESTION_PRESTIGE: + $proxyQuery->getQueryBuilder()->andWhere($alias . '.editionPrestige = true'); + break; + } + + return true; + } +} + +?> +----- +getValue()) { + ProductService::SPECIAL_GESTION_CAPSULE => $proxyQuery->getQueryBuilder()->andWhere($alias . '.collectionCapsule = true'), + ProductService::SPECIAL_GESTION_PRESTIGE => $proxyQuery->getQueryBuilder()->andWhere($alias . '.editionPrestige = true'), + default => true, + }; + + return true; + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/FixtureSkipped/skipped.php.inc b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/FixtureSkipped/skipped.php.inc new file mode 100644 index 00000000000..94d3d3c5db1 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/FixtureSkipped/skipped.php.inc @@ -0,0 +1,23 @@ +assignedExpr = 1000; + break; + + default: + $this->assignedExpr = 2000; + break; + } + } +} + +?> diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Source/SomeResponse.php b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Source/SomeResponse.php new file mode 100644 index 00000000000..d5f3ed8023f --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/Source/SomeResponse.php @@ -0,0 +1,17 @@ +services(); - $services->set(ChangeSwitchToMatchRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersion::PHP_80); + $rectorConfig->rule(ChangeSwitchToMatchRector::class); }; diff --git a/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/config/configured_rule_php_version_74.php b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/config/configured_rule_php_version_74.php new file mode 100644 index 00000000000..08654616209 --- /dev/null +++ b/rules-tests/Php80/Rector/Switch_/ChangeSwitchToMatchRector/config/configured_rule_php_version_74.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersion::PHP_74); + $rectorConfig->rule(ChangeSwitchToMatchRector::class); +}; diff --git a/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/Fixture/class_const_fetch_if.php.inc b/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/Fixture/class_const_fetch_if.php.inc new file mode 100644 index 00000000000..6e5dba1dd49 --- /dev/null +++ b/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/Fixture/class_const_fetch_if.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/Fixture/skip_class_const_fetch_if_different_name.php.inc b/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/Fixture/skip_class_const_fetch_if_different_name.php.inc new file mode 100644 index 00000000000..0320ac063c9 --- /dev/null +++ b/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/Fixture/skip_class_const_fetch_if_different_name.php.inc @@ -0,0 +1,11 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/config/configured_rule.php b/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/config/configured_rule.php index 3d9424d037a..bd7ef6752d5 100644 --- a/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/Ternary/GetDebugTypeRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php80\Rector\Ternary\GetDebugTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(GetDebugTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([GetDebugTypeRector::class]); diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/ArrayToFirstClassCallableRectorTest.php b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/ArrayToFirstClassCallableRectorTest.php new file mode 100644 index 00000000000..a8e18eece44 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/ArrayToFirstClassCallableRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/make_other_closure_pass.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/make_other_closure_pass.php.inc new file mode 100644 index 00000000000..f125926e8bd --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/make_other_closure_pass.php.inc @@ -0,0 +1,27 @@ +services() + ->factory([SomeExternalObject::class, 'sleepStatic']); +}; + +?> +----- +services() + ->factory(SomeExternalObject::sleepStatic(...)); +}; + +?> diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_dynamic_variable2.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_dynamic_variable2.php.inc new file mode 100644 index 00000000000..31112d81b14 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_dynamic_variable2.php.inc @@ -0,0 +1,8 @@ + null, +); diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_in_attribute_class.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_in_attribute_class.php.inc new file mode 100644 index 00000000000..166946693a8 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_in_attribute_class.php.inc @@ -0,0 +1,8 @@ +name('verification.notice'); diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_non_existing_method.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_non_existing_method.php.inc new file mode 100644 index 00000000000..3538905b777 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_non_existing_method.php.inc @@ -0,0 +1,15 @@ + [ + [SomeExternalObject::class, 'sleepPrivateStatic'], + ], + ]; + } +} diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_non_static_with_other_object_const_fetch.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_non_static_with_other_object_const_fetch.php.inc new file mode 100644 index 00000000000..e9411b4426e --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_non_static_with_other_object_const_fetch.php.inc @@ -0,0 +1,17 @@ + [ + [SomeExternalObject::class, 'sleepOver'], + ], + ]; + } +} diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_on_class_const.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_on_class_const.php.inc new file mode 100644 index 00000000000..c079e0ed5b3 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_on_class_const.php.inc @@ -0,0 +1,16 @@ + [SkipOnClassConst::class, 'size'] + ]; + + public function size() + { + } +} diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_on_param.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_on_param.php.inc new file mode 100644 index 00000000000..4cdfe7e2d4c --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_on_param.php.inc @@ -0,0 +1,14 @@ + [SkipOnProperty::class, 'size'] + ]; + + public function size() + { + } +} diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_private_method_by_instantiation.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_private_method_by_instantiation.php.inc new file mode 100644 index 00000000000..44b5e176d95 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_private_method_by_instantiation.php.inc @@ -0,0 +1,15 @@ +setHandler([$thing, 'defaultHandler']); + $thing->doTheThing(); + } +} diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_private_method_by_property_fetch.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_private_method_by_property_fetch.php.inc new file mode 100644 index 00000000000..81397547168 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_private_method_by_property_fetch.php.inc @@ -0,0 +1,18 @@ +thing->setHandler([$this->thing, 'defaultHandler']); + $this->thing->doTheThing(); + } +} diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_simple_yield_array.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_simple_yield_array.php.inc new file mode 100644 index 00000000000..d43f5f2b62a --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/skip_simple_yield_array.php.inc @@ -0,0 +1,13 @@ +services() + ->factory([SomeExternalObject::class, 'sleepStatic']); +}; diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_class.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..56211a23e9d --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_class.php.inc @@ -0,0 +1,35 @@ + +----- +name(...); + } + + public function name() + { + } +} + +?> diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_class_with_own_private.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_class_with_own_private.php.inc new file mode 100644 index 00000000000..f7563cda6f1 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_class_with_own_private.php.inc @@ -0,0 +1,35 @@ + +----- +name(...); + } + + private function name() + { + } +} + +?> diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_static_call.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_static_call.php.inc new file mode 100644 index 00000000000..b914a503f86 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/some_static_call.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/static_with_other_object_const_fetch.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/static_with_other_object_const_fetch.php.inc new file mode 100644 index 00000000000..0bb4efb661d --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/static_with_other_object_const_fetch.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/with_property_fetch.php.inc b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/with_property_fetch.php.inc new file mode 100644 index 00000000000..f2b28d9474a --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Fixture/with_property_fetch.php.inc @@ -0,0 +1,45 @@ +someExternalObject = $someExternalObject; + } + + public function run() + { + $name = [$this->someExternalObject, 'sleepOver']; + } +} + +?> +----- +someExternalObject = $someExternalObject; + } + + public function run() + { + $name = $this->someExternalObject->sleepOver(...); + } +} + +?> diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Source/SomeExternalObject.php b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Source/SomeExternalObject.php new file mode 100644 index 00000000000..236514e0bd3 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/Source/SomeExternalObject.php @@ -0,0 +1,24 @@ +handler = $handler; + } + + public function doTheThing(): void { + ($this->handler)(); + } + + private function defaultHandler(): void { + echo 'default handler'; + } +} diff --git a/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/config/configured_rule.php b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/config/configured_rule.php new file mode 100644 index 00000000000..d6b7a1e2e03 --- /dev/null +++ b/rules-tests/Php81/Rector/Array_/ArrayToFirstClassCallableRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(ArrayToFirstClassCallableRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_81); +}; diff --git a/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/FinalizePublicClassConstantRectorTest.php b/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/FinalizePublicClassConstantRectorTest.php deleted file mode 100644 index f77a19ed01a..00000000000 --- a/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/FinalizePublicClassConstantRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/Fixture/skip_already_final.php.inc b/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/Fixture/skip_already_final.php.inc deleted file mode 100644 index 15b4b17b482..00000000000 --- a/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/Fixture/skip_already_final.php.inc +++ /dev/null @@ -1,8 +0,0 @@ - ------ - diff --git a/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/config/configured_rule.php b/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/config/configured_rule.php deleted file mode 100644 index 09b8f8c4e83..00000000000 --- a/rules-tests/Php81/Rector/ClassConst/FinalizePublicClassConstantRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(FinalizePublicClassConstantRector::class); -}; diff --git a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/keep_methods.php.inc b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/keep_methods.php.inc new file mode 100644 index 00000000000..622a54ad411 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/keep_methods.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/keep_used_trait.php.inc b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/keep_used_trait.php.inc new file mode 100644 index 00000000000..6e5751e3baa --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/keep_used_trait.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/private_constant_with_static_method.php.inc b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/private_constant_with_static_method.php.inc index d8b76649db9..a83e6ef05af 100644 --- a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/private_constant_with_static_method.php.inc +++ b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/private_constant_with_static_method.php.inc @@ -23,7 +23,7 @@ namespace Rector\Tests\Php81\Rector\Class_\MyCLabsClassToEnumRector\Fixture; use MyCLabs\Enum\Enum; -enum PrivateConstWithStaticMethod +enum PrivateConstWithStaticMethod : string { /** * Some comment diff --git a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/some_class.php.inc b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/some_class.php.inc index 147d371f4d5..07f705db1a1 100644 --- a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/some_class.php.inc +++ b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Fixture/some_class.php.inc @@ -22,7 +22,7 @@ namespace Rector\Tests\Php81\Rector\Class_\MyCLabsClassToEnumRector\Fixture; use MyCLabs\Enum\Enum; -enum SomeClass +enum SomeClass : string { /** * Some comment diff --git a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/MyCLabsClassToEnumRectorTest.php b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/MyCLabsClassToEnumRectorTest.php index 9dd0b4f310c..9f470cd1047 100644 --- a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/MyCLabsClassToEnumRectorTest.php +++ b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/MyCLabsClassToEnumRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php81\Rector\Class_\MyCLabsClassToEnumRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class MyCLabsClassToEnumRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Source/ComparingTrait.php b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Source/ComparingTrait.php new file mode 100644 index 00000000000..ecb78886b37 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/Source/ComparingTrait.php @@ -0,0 +1,18 @@ +getValue() === $this->getValue(); + } +} diff --git a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/config/configured_rule.php b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/config/configured_rule.php index fb34271040b..04841abee67 100644 --- a/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/config/configured_rule.php +++ b/rules-tests/Php81/Rector/Class_/MyCLabsClassToEnumRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php81\Rector\Class_\MyCLabsClassToEnumRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(MyCLabsClassToEnumRector::class); -}; +return RectorConfig::configure() + ->withRules([MyCLabsClassToEnumRector::class]); diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/enum_with_camel_case_names.php.inc b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/enum_with_camel_case_names.php.inc new file mode 100644 index 00000000000..40c5b82381b --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/enum_with_camel_case_names.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/enum_with_values.php.inc b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/enum_with_values.php.inc new file mode 100644 index 00000000000..c7a7d38d38c --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/enum_with_values.php.inc @@ -0,0 +1,39 @@ + 1, + 'published' => 2, + 'archived' => 3, + ]; + } +} + +?> +----- + diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc index 7e083403f76..fa02ecb7d78 100644 --- a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc @@ -21,11 +21,11 @@ namespace Rector\Tests\Php81\Rector\Class_\SpatieEnumClassToEnumRector\Fixture; use Spatie\Enum\Enum; -enum StatusEnum +enum StatusEnum : string { - case draft = 'draft'; - case published = 'published'; - case archived = 'archived'; + case DRAFT = 'draft'; + case PUBLISHED = 'published'; + case ARCHIVED = 'archived'; } ?> diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/FixtureUpperCaseSnakeCase/enum_with_camel_case_names.php.inc b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/FixtureUpperCaseSnakeCase/enum_with_camel_case_names.php.inc new file mode 100644 index 00000000000..4d0f4d06ff0 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/FixtureUpperCaseSnakeCase/enum_with_camel_case_names.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/SpatieEnumClassToEnumRectorTest.php b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/SpatieEnumClassToEnumRectorTest.php index e62309966cf..6e97ec21451 100644 --- a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/SpatieEnumClassToEnumRectorTest.php +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/SpatieEnumClassToEnumRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php81\Rector\Class_\SpatieEnumClassToEnumRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class SpatieEnumClassToEnumRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/UpperCaseSnakeCaseTest.php b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/UpperCaseSnakeCaseTest.php new file mode 100644 index 00000000000..5fb53a48397 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/UpperCaseSnakeCaseTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureUpperCaseSnakeCase'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_uppercase_snake_case.php'; + } +} diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule.php b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule.php index 298e7c9f5cf..075e76b6e9c 100644 --- a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule.php +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(SpatieEnumClassToEnumRector::class); -}; +return RectorConfig::configure() + ->withRules([SpatieEnumClassToEnumRector::class]); diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule_uppercase_snake_case.php b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule_uppercase_snake_case.php new file mode 100644 index 00000000000..67341b53445 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule_uppercase_snake_case.php @@ -0,0 +1,12 @@ +ruleWithConfiguration(SpatieEnumClassToEnumRector::class, [ + SpatieEnumClassToEnumRector::TO_UPPER_SNAKE_CASE => true, + ]); +}; diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/different_named_arg.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/different_named_arg.php.inc new file mode 100644 index 00000000000..2b1fb39b51e --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/different_named_arg.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/fixture.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..ee7ae7a818b --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/skip_no_limit.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/skip_no_limit.php.inc new file mode 100644 index 00000000000..f0ca2ac91fa --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/Fixture/skip_no_limit.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/config/configured_rule.php b/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/config/configured_rule.php new file mode 100644 index 00000000000..7a7a0f036fa --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([NullToStrictIntPregSlitFuncCallLimitArgRector::class]); diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/after_exit.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/after_exit.php.inc new file mode 100644 index 00000000000..7bbbda63b41 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/after_exit.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/after_exit_namespaced.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/after_exit_namespaced.php.inc new file mode 100644 index 00000000000..fd2ec86f5b3 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/after_exit_namespaced.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/case_insensitive_function_name.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/case_insensitive_function_name.php.inc new file mode 100644 index 00000000000..51e3661d8b9 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/case_insensitive_function_name.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/crypt_one_arg.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/crypt_one_arg.php.inc new file mode 100644 index 00000000000..84bc987c53c --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/crypt_one_arg.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/different_named.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/different_named.php.inc new file mode 100644 index 00000000000..7426af4e5e3 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/different_named.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/explicit_mixed_in_trait.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/explicit_mixed_in_trait.php.inc new file mode 100644 index 00000000000..6817eee40a7 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/explicit_mixed_in_trait.php.inc @@ -0,0 +1,29 @@ +title = $title; + return trim($this->title); + } +} + +?> +----- +title = $title; + return trim((string) $this->title); + } +} + +?> diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/named_argument.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/named_argument.php.inc new file mode 100644 index 00000000000..ff15e03be88 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/named_argument.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/non_string_param_type.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/non_string_param_type.php.inc new file mode 100644 index 00000000000..c992daa9e76 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/non_string_param_type.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/non_string_param_type_in_use_closure.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/non_string_param_type_in_use_closure.php.inc new file mode 100644 index 00000000000..23c7a9c89cb --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/non_string_param_type_in_use_closure.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/normal_variable_in_trait.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/normal_variable_in_trait.php.inc new file mode 100644 index 00000000000..ba5719c1455 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/normal_variable_in_trait.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/null_defined_in_variable_first.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/null_defined_in_variable_first.php.inc new file mode 100644 index 00000000000..cbf966b378a --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/null_defined_in_variable_first.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/pass_null.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/pass_null.php.inc new file mode 100644 index 00000000000..5718741472a --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/pass_null.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/pass_null_multi.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/pass_null_multi.php.inc new file mode 100644 index 00000000000..1fee0e4a96c --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/pass_null_multi.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_after_is_string_on_object_call.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_after_is_string_on_object_call.php.inc new file mode 100644 index 00000000000..250871baa20 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_after_is_string_on_object_call.php.inc @@ -0,0 +1,14 @@ +stringNull) && trim($demo->stringNull) !== '') { + echo $demo->stringNull; +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_always_string_if_else_dom_element.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_always_string_if_else_dom_element.php.inc new file mode 100644 index 00000000000..1a3b8610916 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_always_string_if_else_dom_element.php.inc @@ -0,0 +1,16 @@ +loadHTML($html); + $titleElement = $dom->getElementsByTagName('title')->item(0); + if ($titleElement?->nodeValue !== null) { + return trim($titleElement->nodeValue); + } else { + return 'Unnamed document'; + } + } +} \ No newline at end of file diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_array_of_strings_on_array_map.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_array_of_strings_on_array_map.php.inc new file mode 100644 index 00000000000..e6dd64e9ded --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_array_of_strings_on_array_map.php.inc @@ -0,0 +1,19 @@ + + strtolower($part), + $parts + ) + ); + } +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_class_string_on_trait.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_class_string_on_trait.php.inc new file mode 100644 index 00000000000..ed96b8c35d2 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_class_string_on_trait.php.inc @@ -0,0 +1,11 @@ + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_error_type.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_error_type.php.inc new file mode 100644 index 00000000000..9b9f9e37853 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_error_type.php.inc @@ -0,0 +1,27 @@ +delimiter); + + if ($positionDelimiter > 0) { + $innerPattern = str_replace($this->delimiter, '\\' . $this->delimiter, $innerPattern); + } + + // change delimiter + if (strlen($innerPattern) > 2 && $innerPattern[0] === $innerPattern[strlen($innerPattern) - 1]) { + return 'foo'; + } + }); + } +} \ No newline at end of file diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..855cff83902 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,11 @@ +value; + + if (\str_contains($numericValueAsString, '.')) { + [$mainPart, $decimalPart] = explode('.', $numericValueAsString); + } + } +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_method_call_on_trait.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_method_call_on_trait.php.inc new file mode 100644 index 00000000000..ab1f6beb3c5 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_method_call_on_trait.php.inc @@ -0,0 +1,12 @@ +foo->getBar(); + preg_split('#a#', $obj->getValue()); + } +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_mixed_array_dimfetch.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_mixed_array_dimfetch.php.inc new file mode 100644 index 00000000000..3d6ada2856c --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_mixed_array_dimfetch.php.inc @@ -0,0 +1,17 @@ +find, $this->replace, $this->items[$index]); + } +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_not_registered_func_call.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_not_registered_func_call.php.inc new file mode 100644 index 00000000000..a8798415178 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_not_registered_func_call.php.inc @@ -0,0 +1,11 @@ +value = (string) $node->value; + if ($node instanceof Float_ && ! \str_contains($node->value, '.')) { + + } + } +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_str_replace_pass_array_subject.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_str_replace_pass_array_subject.php.inc new file mode 100644 index 00000000000..70dca47a694 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/skip_str_replace_pass_array_subject.php.inc @@ -0,0 +1,11 @@ +title === null) { + return null; + } + + return trim($this->title); + } +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/ternary_with_ints.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/ternary_with_ints.php.inc new file mode 100644 index 00000000000..fa9ae6738ce --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/ternary_with_ints.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/with_existing_cast_on_ternary.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/with_existing_cast_on_ternary.php.inc new file mode 100644 index 00000000000..5c589a3ad5c --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/with_existing_cast_on_ternary.php.inc @@ -0,0 +1,51 @@ +Get('account_code') ?: $this->Get('customer_id')); + } + + public function run2() + { + return $relpath + . '/' . rawurlencode((string) $this->Get('account_code') ?: null); + } + + public function Get($fieldname) + { + return $this->{$fieldname} ?? null; + } +} + +?> +----- +Get('account_code') ?: (string) $this->Get('customer_id')); + } + + public function run2() + { + return $relpath + . '/' . rawurlencode((string) $this->Get('account_code') ?: ''); + } + + public function Get($fieldname) + { + return $this->{$fieldname} ?? null; + } +} + +?> diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/with_nullable_docblock.php.inc b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/with_nullable_docblock.php.inc new file mode 100644 index 00000000000..b48bc1f704a --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/Fixture/with_nullable_docblock.php.inc @@ -0,0 +1,45 @@ +importedId) > 0) { + $this->importedId = $importedId; + } + } +} + +?> +----- +importedId) > 0) { + $this->importedId = $importedId; + } + } +} + +?> diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/NullToStrictStringFuncCallArgRectorTest.php b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/NullToStrictStringFuncCallArgRectorTest.php new file mode 100644 index 00000000000..a7036c64111 --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/NullToStrictStringFuncCallArgRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/config/configured_rule.php b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/config/configured_rule.php new file mode 100644 index 00000000000..cb3c0502d6f --- /dev/null +++ b/rules-tests/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([NullToStrictStringFuncCallArgRector::class]); diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/fully_qualified_class_name.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/fully_qualified_class_name.php.inc new file mode 100644 index 00000000000..d39759a1634 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/fully_qualified_class_name.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_enum_static_method_call.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_enum_static_method_call.php.inc new file mode 100644 index 00000000000..d481a3bff33 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_enum_static_method_call.php.inc @@ -0,0 +1,21 @@ +getKey(); + SomeEnum::from($value)->getValue(); + $compare = SomeEnum::from($value); + $compare = SomeEnum::values(); + $compare = SomeEnum::keys(); + $compare = SomeEnum::isValid($value); + $compare = SomeEnum::search($value); + $compare = SomeEnum::toArray(); + SomeEnum::assertValidValue($value); + } +} diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_magic_calls.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_magic_calls.php.inc new file mode 100644 index 00000000000..ef788a8c307 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_magic_calls.php.inc @@ -0,0 +1,13 @@ +getKey(); + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_usage_of_get_value.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_usage_of_get_value.php.inc new file mode 100644 index 00000000000..35b1d96760e --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/skip_usage_of_get_value.php.inc @@ -0,0 +1,17 @@ +getValue(); + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/static_method_call.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/static_method_call.php.inc new file mode 100644 index 00000000000..c730b7776f1 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/static_method_call.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_constant.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_constant.php.inc deleted file mode 100644 index c2ff24449a8..00000000000 --- a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_constant.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -getKey(); - } -} - -?> ------ - diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_equals.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_equals.php.inc new file mode 100644 index 00000000000..b7858789b69 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_equals.php.inc @@ -0,0 +1,113 @@ +prop = $var = SomeEnum::USED_TO_BE_CONST(); + + $compare = SomeEnum::USED_TO_BE_CONST()->equals(SomeEnum::USED_TO_BE_CONST()); + $compare = SomeEnum::USED_TO_BE_CONST()->equals($var); + $compare = SomeEnum::USED_TO_BE_CONST()->equals($this->prop); + $compare = SomeEnum::USED_TO_BE_CONST()->equals($this->getEnum()); + $compare = SomeEnum::USED_TO_BE_CONST()->equals(self::getStaticEnum()); + $compare = SomeEnum::create()->equals(self::getStaticEnum()); + + $compare = $var->equals(SomeEnum::USED_TO_BE_CONST()); + $compare = $var->equals($var); + $compare = $var->equals($this->prop); + + $compare = $this->prop->equals(SomeEnum::USED_TO_BE_CONST()); + $compare = $this->prop->equals($var); + $compare = $this->prop->equals($this->prop); + + $compare = $this->getEnum()->equals(SomeEnum::USED_TO_BE_CONST()); + $compare = $this->getEnum()->equals($var); + $compare = $this->getEnum()->equals($this->prop); + + $compare = self::getStaticEnum()->equals(SomeEnum::USED_TO_BE_CONST()); + $compare = self::getStaticEnum()->equals($var); + $compare = self::getStaticEnum()->equals($this->prop); + + $compare = $var->equals($this->getEnum()); + $compare = $this->prop->equals($this->getEnum()); + + $compare = $var->equals(self::getStaticEnum()); + $compare = $this->prop->equals(self::getStaticEnum()); + } + + public function getEnum(): SomeEnum + { + return SomeEnum::USED_TO_BE_CONST(); + } + + public static function getStaticEnum(): SomeEnum + { + return SomeEnum::USED_TO_BE_CONST(); + } +} + +?> +----- +prop = $var = \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + + $compare = \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST === \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + $compare = \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST === $var; + $compare = \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST === $this->prop; + $compare = \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST === $this->getEnum(); + $compare = \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST === self::getStaticEnum(); + $compare = SomeEnum::create() === self::getStaticEnum(); + + $compare = $var === \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + $compare = $var === $var; + $compare = $var === $this->prop; + + $compare = $this->prop === \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + $compare = $this->prop === $var; + $compare = $this->prop === $this->prop; + + $compare = $this->getEnum() === \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + $compare = $this->getEnum() === $var; + $compare = $this->getEnum() === $this->prop; + + $compare = self::getStaticEnum() === \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + $compare = self::getStaticEnum() === $var; + $compare = self::getStaticEnum() === $this->prop; + + $compare = $var === $this->getEnum(); + $compare = $this->prop === $this->getEnum(); + + $compare = $var === self::getStaticEnum(); + $compare = $this->prop === self::getStaticEnum(); + } + + public function getEnum(): SomeEnum + { + return \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + } + + public static function getStaticEnum(): SomeEnum + { + return \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_get_key.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_get_key.php.inc new file mode 100644 index 00000000000..e6a6e65fde1 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_get_key.php.inc @@ -0,0 +1,37 @@ +getKey(); + $enum = SomeEnum::USED_TO_BE_CONST(); + $name2 = $enum->getKey(); + } +} + +?> +----- +name; + $enum = \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + $name2 = $enum->name; + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_get_value.php.inc b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_get_value.php.inc new file mode 100644 index 00000000000..3cf1de151d8 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Fixture/usage_of_get_value.php.inc @@ -0,0 +1,37 @@ +getValue(); + $enum = SomeEnum::USED_TO_BE_CONST(); + $value2 = $enum->getValue(); + } +} + +?> +----- +value; + $enum = \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\Source\SomeEnum::USED_TO_BE_CONST; + $value2 = $enum->value; + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/MyCLabsMethodCallToEnumConstRectorTest.php b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/MyCLabsMethodCallToEnumConstRectorTest.php index a5417bd9763..13dafc71770 100644 --- a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/MyCLabsMethodCallToEnumConstRectorTest.php +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/MyCLabsMethodCallToEnumConstRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class MyCLabsMethodCallToEnumConstRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Source/SomeClass.php b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Source/SomeClass.php new file mode 100644 index 00000000000..569bdb8c904 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector/Source/SomeClass.php @@ -0,0 +1,17 @@ +services(); - $services->set(MyCLabsMethodCallToEnumConstRector::class); -}; +return RectorConfig::configure() + ->withRules([MyCLabsMethodCallToEnumConstRector::class]); diff --git a/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/Fixture/fixture.php.inc b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..c5ed1800193 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/Fixture/fixture.php.inc @@ -0,0 +1,43 @@ +setAccessible(true); + $value = $reflectionProperty->getValue($this); + + $reflectionMethod = new ReflectionMethod($this, 'privateMethod'); + $reflectionMethod->setAccessible(false); + $reflectionMethod->invoke($this); + } +} + +?> +----- +getValue($this); + + $reflectionMethod = new ReflectionMethod($this, 'privateMethod'); + $reflectionMethod->invoke($this); + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php new file mode 100644 index 00000000000..b41e85eda5c --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + /** + * @return Iterator> + */ + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/config/configured_rule.php b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/config/configured_rule.php new file mode 100644 index 00000000000..c15b8139eea --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(RemoveReflectionSetAccessibleCallsRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/name_or_getName_to_name_property.php.inc b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/name_or_getName_to_name_property.php.inc new file mode 100644 index 00000000000..bdefd8e9ab0 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/name_or_getName_to_name_property.php.inc @@ -0,0 +1,33 @@ +getName(); + $draftName = SpatieEnumMethodCallToEnumConstRector\Source\StatusEnum::draft()->name; + } +} + +?> +----- +name; + $draftName = \Rector\Tests\Php81\Rector\MethodCall\SpatieEnumMethodCallToEnumConstRector\Source\StatusEnum::DRAFT->name; + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/static_method_call_to_static_property.php.inc b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/static_method_call_to_static_property.php.inc new file mode 100644 index 00000000000..531b27a12a7 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/static_method_call_to_static_property.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/value_or_getValue_to_value_property.php.inc b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/value_or_getValue_to_value_property.php.inc new file mode 100644 index 00000000000..d71395d6e33 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Fixture/value_or_getValue_to_value_property.php.inc @@ -0,0 +1,33 @@ +getValue(); + $draftName = SpatieEnumMethodCallToEnumConstRector\Source\StatusEnum::draft()->value; + } +} + +?> +----- +value; + $draftName = \Rector\Tests\Php81\Rector\MethodCall\SpatieEnumMethodCallToEnumConstRector\Source\StatusEnum::DRAFT->value; + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Source/StatusEnum.php b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Source/StatusEnum.php new file mode 100644 index 00000000000..d9a0ce1212c --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/Source/StatusEnum.php @@ -0,0 +1,16 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/config/configured_rule.php b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/config/configured_rule.php new file mode 100644 index 00000000000..fad50c95ea3 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([SpatieEnumMethodCallToEnumConstRector::class]); diff --git a/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/constructor_self_call.php.inc b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/constructor_self_call.php.inc new file mode 100644 index 00000000000..585c773f40c --- /dev/null +++ b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/constructor_self_call.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/default_constructor_call.php.inc b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/default_constructor_call.php.inc new file mode 100644 index 00000000000..1af8cd37bf6 --- /dev/null +++ b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/default_constructor_call.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/skip_constructor_call_for_custom_constructor.php.inc b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/skip_constructor_call_for_custom_constructor.php.inc new file mode 100644 index 00000000000..52ca9b57649 --- /dev/null +++ b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/skip_constructor_call_for_custom_constructor.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/skip_constructor_call_for_non_enum.php.inc b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/skip_constructor_call_for_non_enum.php.inc new file mode 100644 index 00000000000..596beec7076 --- /dev/null +++ b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Fixture/skip_constructor_call_for_non_enum.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/MyCLabsConstructorCallToEnumFromRectorTest.php b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/MyCLabsConstructorCallToEnumFromRectorTest.php new file mode 100644 index 00000000000..08f11c60b5a --- /dev/null +++ b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/MyCLabsConstructorCallToEnumFromRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Source/SomeClass.php b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Source/SomeClass.php new file mode 100644 index 00000000000..52e09cd2213 --- /dev/null +++ b/rules-tests/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector/Source/SomeClass.php @@ -0,0 +1,9 @@ +withRules([MyCLabsConstructorCallToEnumFromRector::class]); diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/keep_readonly_doc_on_property_promotion_with_description.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/keep_readonly_doc_on_property_promotion_with_description.php.inc new file mode 100644 index 00000000000..8120575ff52 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/keep_readonly_doc_on_property_promotion_with_description.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/keep_readonly_doc_on_property_with_description.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/keep_readonly_doc_on_property_with_description.php.inc new file mode 100644 index 00000000000..53530fd5cda --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/keep_readonly_doc_on_property_with_description.php.inc @@ -0,0 +1,47 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> +----- +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/on_assign_op.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/on_assign_op.php.inc new file mode 100644 index 00000000000..3294f023fe0 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/on_assign_op.php.inc @@ -0,0 +1,49 @@ +foo = $string; + } + + public function do(): void + { + $bar = ''; + + if ($this->foo !== null) { + $bar .= $this->foo; + } + } +} + +?> +----- +foo = $string; + } + + public function do(): void + { + $bar = ''; + + if ($this->foo !== null) { + $bar .= $this->foo; + } + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/redis_set.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/redis_set.php.inc new file mode 100644 index 00000000000..a8fe67cdf83 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/redis_set.php.inc @@ -0,0 +1,47 @@ +redis->set( + 'key', + 'value', + $this->ttl + ); + } +} + +?> +----- +redis->set( + 'key', + 'value', + $this->ttl + ); + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/remove_readonly_doc_on_property.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/remove_readonly_doc_on_property.php.inc new file mode 100644 index 00000000000..66ee2e0794f --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/remove_readonly_doc_on_property.php.inc @@ -0,0 +1,44 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> +----- +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/remove_readonly_doc_on_property_promotion.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/remove_readonly_doc_on_property_promotion.php.inc new file mode 100644 index 00000000000..3a7d7cfa4db --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/remove_readonly_doc_on_property_promotion.php.inc @@ -0,0 +1,26 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/set_method_in_trait.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/set_method_in_trait.php.inc new file mode 100644 index 00000000000..942279e905f --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/set_method_in_trait.php.inc @@ -0,0 +1,20 @@ +property; + } + + public function setProperty(stdClass $property): void + { + $this->property = $property; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_api-platform-api-resource1.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_api-platform-api-resource1.php.inc new file mode 100644 index 00000000000..b87bf681ba3 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_api-platform-api-resource1.php.inc @@ -0,0 +1,11 @@ +array = new ArrayObject(); + } + + public function getOrSet(int|string $key, mixed $value = null): mixed + { + return $this->array[$key] ??= $value; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assign_op.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assign_op.php.inc new file mode 100644 index 00000000000..7c2fe7f1859 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assign_op.php.inc @@ -0,0 +1,23 @@ +name = $name; + + if ($flag) { + $this->name .= 'changed'; + } + + $this->count = 0; + if ($flag) { + ++$this->count; + } + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assign_plus_call_by_ref_global.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assign_plus_call_by_ref_global.php.inc new file mode 100644 index 00000000000..5a8df7d2e61 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assign_plus_call_by_ref_global.php.inc @@ -0,0 +1,12 @@ +fruits = ['banana', 'pear', 'apple']; + \sort($this->fruits); + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assigned_in_named_constructor.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assigned_in_named_constructor.php.inc new file mode 100644 index 00000000000..01a8d1e93ba --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assigned_in_named_constructor.php.inc @@ -0,0 +1,20 @@ +date = new \DateTime(); + } + + public static function create(\DateTimeInterface $date): self + { + $ack = new self(); + $ack->date = $date; + + return $ack; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assignment_constructor_promotion.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assignment_constructor_promotion.php.inc new file mode 100644 index 00000000000..87c82863697 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_assignment_constructor_promotion.php.inc @@ -0,0 +1,11 @@ +value = 'any value'; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_by_ref_param.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_by_ref_param.php.inc new file mode 100644 index 00000000000..c024a051e16 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_by_ref_param.php.inc @@ -0,0 +1,10 @@ +doSomething($this->someArray); + } + + public function doSomething(array &$someArray = null): void + { + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_call_by_ref_method_scope.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_call_by_ref_method_scope.php.inc new file mode 100644 index 00000000000..5711d8d669e --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_call_by_ref_method_scope.php.inc @@ -0,0 +1,21 @@ +install($this->output); + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_changed_promoted_property.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_changed_promoted_property.php.inc new file mode 100644 index 00000000000..d73827a0cf0 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_changed_promoted_property.php.inc @@ -0,0 +1,16 @@ +converter instanceof CacheableConverter) { + $this->converter = clone $this->converter; + $this->converter->reset(); + } + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_default_array.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_default_array.php.inc new file mode 100644 index 00000000000..69f2ad79d84 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_default_array.php.inc @@ -0,0 +1,16 @@ +posts = $postDataProvider->provide(); + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_doctrine_embeddable.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_doctrine_embeddable.php.inc new file mode 100644 index 00000000000..a2fd42d6a82 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_doctrine_embeddable.php.inc @@ -0,0 +1,17 @@ +amount = $amount; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_doctrine_odm_mongodb_annotations_document.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_doctrine_odm_mongodb_annotations_document.php.inc new file mode 100644 index 00000000000..468aa626b01 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_doctrine_odm_mongodb_annotations_document.php.inc @@ -0,0 +1,11 @@ +id; + } + + /** + * Set title. + * + * @param string $title + * + * @return self + */ + public function setTitle($title) + { + $this->title = $title; + + return $this; + } + + /** + * Get title. + * + * @return string + */ + public function getTitle() + { + return $this->title; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_entity2.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_entity2.php.inc new file mode 100644 index 00000000000..f5347cb2087 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_entity2.php.inc @@ -0,0 +1,63 @@ +id; + } + + /** + * Set title. + * + * @param string $title + * + * @return self + */ + public function setTitle($title) + { + $this->title = $title; + + return $this; + } + + /** + * Get title. + * + * @return string + */ + public function getTitle() + { + return $this->title; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_entity_mapped_superclass.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_entity_mapped_superclass.php.inc new file mode 100644 index 00000000000..9e99556a150 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_entity_mapped_superclass.php.inc @@ -0,0 +1,17 @@ +id = $id; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_has_default_value.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_has_default_value.php.inc new file mode 100644 index 00000000000..bde1eb011af --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_has_default_value.php.inc @@ -0,0 +1,18 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_increment.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_increment.php.inc new file mode 100644 index 00000000000..802b496e7d2 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_increment.php.inc @@ -0,0 +1,17 @@ +maxRetriesLeft--; + } +} + + diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_jms_type.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_jms_type.php.inc new file mode 100644 index 00000000000..7ad32b3b7ee --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_jms_type.php.inc @@ -0,0 +1,16 @@ +value; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_multiple_assign.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_multiple_assign.php.inc new file mode 100644 index 00000000000..33136ccb98d --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_multiple_assign.php.inc @@ -0,0 +1,21 @@ +name = $name; + + if ($flag) { + $this->name = 'change'; + } + + if ($flag) { + $this->name = 'change again'; + } + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_no_type.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_no_type.php.inc new file mode 100644 index 00000000000..48221146bd8 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_no_type.php.inc @@ -0,0 +1,18 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_no_type_param.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_no_type_param.php.inc new file mode 100644 index 00000000000..5d86889899d --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_no_type_param.php.inc @@ -0,0 +1,22 @@ +data; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_non_private_property.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_non_private_property.php.inc new file mode 100644 index 00000000000..90af9531f8c --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_non_private_property.php.inc @@ -0,0 +1,18 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_non_private_property2.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_non_private_property2.php.inc new file mode 100644 index 00000000000..21d3dbeb3c4 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_non_private_property2.php.inc @@ -0,0 +1,16 @@ +name; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_on_read_only_class.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_on_read_only_class.php.inc new file mode 100644 index 00000000000..f1042a363b2 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_on_read_only_class.php.inc @@ -0,0 +1,16 @@ +name; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_param_hook.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_param_hook.php.inc new file mode 100644 index 00000000000..479df9a4173 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_param_hook.php.inc @@ -0,0 +1,14 @@ +foo; + } + }) + { + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_param_reassign.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_param_reassign.php.inc new file mode 100644 index 00000000000..8bc6a114f66 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_param_reassign.php.inc @@ -0,0 +1,13 @@ +foo = $foo; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_property_hook.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_property_hook.php.inc new file mode 100644 index 00000000000..e3baedbf359 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_property_hook.php.inc @@ -0,0 +1,16 @@ + random_int(1, 100); + set => 1; + } + + public function bar(): int + { + return $this->propertyHook * 1000; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_property_on_array_destructuring.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_property_on_array_destructuring.php.inc new file mode 100644 index 00000000000..5d51b8000a6 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_property_on_array_destructuring.php.inc @@ -0,0 +1,24 @@ +accessToken = $value; + [$clone->owner, $clone->name] = explode('/', $value, 2); + + return $clone; + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_re_assign_with_assign_op.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_re_assign_with_assign_op.php.inc new file mode 100644 index 00000000000..0ccd91effbc --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_re_assign_with_assign_op.php.inc @@ -0,0 +1,18 @@ +value |= $flag; + } + +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_readonly_new_static.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_readonly_new_static.php.inc new file mode 100644 index 00000000000..c8bfd2a58cf --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_readonly_new_static.php.inc @@ -0,0 +1,18 @@ +method = 'hi'; + $self->method = 'hello'; + return $self; + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_reassign_on_array_destruct.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_reassign_on_array_destruct.php.inc new file mode 100644 index 00000000000..70f3b823729 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_reassign_on_array_destruct.php.inc @@ -0,0 +1,15 @@ +selectedFrom, $this->selectedTo] = [0, 1]; + [$this->selectedFrom, $this->selectedTo] = [0, 1]; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_static_property.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_static_property.php.inc new file mode 100644 index 00000000000..359fcc657db --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_static_property.php.inc @@ -0,0 +1,20 @@ +data['item']); + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_array_dim_fetch_in_side_effect_call_arg.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_array_dim_fetch_in_side_effect_call_arg.php.inc new file mode 100644 index 00000000000..a7dc50f950d --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_array_dim_fetch_in_side_effect_call_arg.php.inc @@ -0,0 +1,19 @@ +> $items + */ + public function __construct(private array $items) + {} + + public function popFooItem(): void + { + if (!empty($this->items) && array_key_exists('foo', $this->items)) { + array_pop($this->items['foo']); + } + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_array_dim_fetch_in_side_effect_call_arg2.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_array_dim_fetch_in_side_effect_call_arg2.php.inc new file mode 100644 index 00000000000..337210f7d8d --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_array_dim_fetch_in_side_effect_call_arg2.php.inc @@ -0,0 +1,23 @@ +> $items + */ + public function __construct(array $items) + { + $this->items = $items; + } + + public function popFooItem(): void + { + if (!empty($this->items) && array_key_exists('foo', $this->items)) { + array_pop($this->items['foo']); + } + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_assign_expr_by_ref.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_assign_expr_by_ref.php.inc new file mode 100644 index 00000000000..2bc1736dfd0 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_assign_expr_by_ref.php.inc @@ -0,0 +1,17 @@ +foo; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_pass_by_reference_falsy.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_pass_by_reference_falsy.php.inc new file mode 100644 index 00000000000..dd6e5f0da1d --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_used_as_pass_by_reference_falsy.php.inc @@ -0,0 +1,25 @@ +prepare('SELECT * FROM users WHERE id = :id'); + $sth->bindParam('id', $this->id, PDO::PARAM_INT); + $sth->execute(); + } +} \ No newline at end of file diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_write_also_cloned_property.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_write_also_cloned_property.php.inc new file mode 100644 index 00000000000..9c2b21d1575 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_write_also_cloned_property.php.inc @@ -0,0 +1,26 @@ +name = $name; + } + + public function withName(string $name): self + { + $clone = clone $this; + $clone->name = $name; + + return $clone; + } + + public function getName() + { + return $this->name; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_write_in_trait_property.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_write_in_trait_property.php.inc new file mode 100644 index 00000000000..7ac3ad95826 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/skip_write_in_trait_property.php.inc @@ -0,0 +1,22 @@ +myProperty = $myProperty; + } +} diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/used_as_assign_expr.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/used_as_assign_expr.php.inc new file mode 100644 index 00000000000..f736447e589 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/used_as_assign_expr.php.inc @@ -0,0 +1,33 @@ +foo; + } +} + +?> +----- +foo; + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute.php.inc new file mode 100644 index 00000000000..6ccb2901179 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute.php.inc @@ -0,0 +1,43 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> +----- +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_inline.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_inline.php.inc new file mode 100644 index 00000000000..2cf0dfee487 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_inline.php.inc @@ -0,0 +1,41 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> +----- +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_new_line.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_new_line.php.inc new file mode 100644 index 00000000000..1989b058429 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_new_line.php.inc @@ -0,0 +1,43 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> +----- +name = $name; + } + + public function getName() + { + return $this->name; + } +} + +?> diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_on_property_promotion_inline.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_on_property_promotion_inline.php.inc new file mode 100644 index 00000000000..8adce000d93 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_on_property_promotion_inline.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_on_property_promotion_new_line.php.inc b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_on_property_promotion_new_line.php.inc new file mode 100644 index 00000000000..1a8128f4dca --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Fixture/with_attribute_on_property_promotion_new_line.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/ReadOnlyPropertyRectorTest.php b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/ReadOnlyPropertyRectorTest.php index dfeccc4b223..d68c3cc1572 100644 --- a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/ReadOnlyPropertyRectorTest.php +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/ReadOnlyPropertyRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Php81\Rector\Property\ReadOnlyPropertyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReadOnlyPropertyRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Source/WithByRefMethod.php b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Source/WithByRefMethod.php new file mode 100644 index 00000000000..dbcfa393377 --- /dev/null +++ b/rules-tests/Php81/Rector/Property/ReadOnlyPropertyRector/Source/WithByRefMethod.php @@ -0,0 +1,12 @@ +services(); - $services->set(ReadOnlyPropertyRector::class); -}; +return RectorConfig::configure() + ->withRules([ReadOnlyPropertyRector::class]); diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/child_extends_parent_already_readonly.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/child_extends_parent_already_readonly.php.inc new file mode 100644 index 00000000000..a5ee64c9da0 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/child_extends_parent_already_readonly.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_with_attribute_inline.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_with_attribute_inline.php.inc new file mode 100644 index 00000000000..3db05763049 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_with_attribute_inline.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_with_attribute_new_line.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_with_attribute_new_line.php.inc new file mode 100644 index 00000000000..f1d67ec70c4 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_with_attribute_new_line.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_without_attribute.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_without_attribute.php.inc new file mode 100644 index 00000000000..777cae2863a --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/class_without_attribute.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/combine_promo_and_property_readonly.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/combine_promo_and_property_readonly.php.inc new file mode 100644 index 00000000000..dd0bb48785b --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/combine_promo_and_property_readonly.php.inc @@ -0,0 +1,35 @@ +b = $b ?? 'foo'; + } +} + +?> +----- +b = $b ?? 'foo'; + } +} + +?> diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/implicit_public_readonly_property.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/implicit_public_readonly_property.php.inc new file mode 100644 index 00000000000..e741830bff4 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/implicit_public_readonly_property.php.inc @@ -0,0 +1,31 @@ +foo = 'bar'; + } +} + +?> +----- +foo = 'bar'; + } +} + +?> diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/implicit_public_readonly_property_promotion.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/implicit_public_readonly_property_promotion.php.inc new file mode 100644 index 00000000000..ed752f855db --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/implicit_public_readonly_property_promotion.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc new file mode 100644 index 00000000000..66c29134d1a --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc new file mode 100644 index 00000000000..a43997d96c0 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc new file mode 100644 index 00000000000..fa6e6368a05 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc @@ -0,0 +1,9 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc new file mode 100644 index 00000000000..3d0d13296f8 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc @@ -0,0 +1,8 @@ +property; + } +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_missing_promoted_property_type.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_missing_promoted_property_type.php.inc new file mode 100644 index 00000000000..65ca8151c90 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_missing_promoted_property_type.php.inc @@ -0,0 +1,11 @@ +b = $b ?? 'foo'; + } +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc new file mode 100644 index 00000000000..543d00b9d1d --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc @@ -0,0 +1,10 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/with_attribute_on_property_promotion.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/with_attribute_on_property_promotion.php.inc new file mode 100644 index 00000000000..c9eff6216d0 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/with_attribute_on_property_promotion.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/ReadOnlyClassRectorTest.php b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/ReadOnlyClassRectorTest.php new file mode 100644 index 00000000000..16e0c56322d --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/ReadOnlyClassRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Source/ParentAlreadyReadonly.php b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Source/ParentAlreadyReadonly.php new file mode 100644 index 00000000000..f15b0c5b6c7 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Source/ParentAlreadyReadonly.php @@ -0,0 +1,7 @@ +rule(ReadOnlyClassRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::READONLY_CLASS); +}; diff --git a/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/array_dimfetch_variable.php.inc b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/array_dimfetch_variable.php.inc new file mode 100644 index 00000000000..fcb29c6a428 --- /dev/null +++ b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/array_dimfetch_variable.php.inc @@ -0,0 +1,25 @@ + 'basketball', +]; +echo "I like playing ${array['game']}"; + +?> +----- + 'basketball', +]; +echo "I like playing {$array['game']}"; + +?> diff --git a/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/fixture.php.inc b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..635b945a5d0 --- /dev/null +++ b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/fixture.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/func_call_as_variable_in_string_statement.php.inc b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/func_call_as_variable_in_string_statement.php.inc new file mode 100644 index 00000000000..d38a966fee2 --- /dev/null +++ b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/func_call_as_variable_in_string_statement.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/skip_already_valid_variable.php.inc b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/skip_already_valid_variable.php.inc new file mode 100644 index 00000000000..aa608a66b8f --- /dev/null +++ b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/Fixture/skip_already_valid_variable.php.inc @@ -0,0 +1,7 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/VariableInStringInterpolationFixerRectorTest.php b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/VariableInStringInterpolationFixerRectorTest.php new file mode 100644 index 00000000000..e4201230788 --- /dev/null +++ b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/VariableInStringInterpolationFixerRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/config/configured_rule.php b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/config/configured_rule.php new file mode 100644 index 00000000000..ec46eb1ce1f --- /dev/null +++ b/rules-tests/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector/config/configured_rule.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersionFeature::DEPRECATE_VARIABLE_IN_STRING_INTERPOLATION); + + $rectorConfig->rule(VariableInStringInterpolationFixerRector::class); +}; diff --git a/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Fixture/skip_different_function_name.php.inc b/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Fixture/skip_different_function_name.php.inc new file mode 100644 index 00000000000..88c3caa9b6d --- /dev/null +++ b/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Fixture/skip_different_function_name.php.inc @@ -0,0 +1,7 @@ + +----- + diff --git a/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Fixture/some_utf8_encode.php.inc b/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Fixture/some_utf8_encode.php.inc new file mode 100644 index 00000000000..c665435214a --- /dev/null +++ b/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Fixture/some_utf8_encode.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Utf8DecodeEncodeToMbConvertEncodingRectorTest.php b/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Utf8DecodeEncodeToMbConvertEncodingRectorTest.php new file mode 100644 index 00000000000..4497026900e --- /dev/null +++ b/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/Utf8DecodeEncodeToMbConvertEncodingRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/config/configured_rule.php b/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/config/configured_rule.php new file mode 100644 index 00000000000..afd80430aca --- /dev/null +++ b/rules-tests/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector/config/configured_rule.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersionFeature::DEPRECATE_UTF8_DECODE_ENCODE_FUNCTION); + + $rectorConfig->rule(Utf8DecodeEncodeToMbConvertEncodingRector::class); +}; diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/FilesystemIteratorSkipDotsRectorTest.php b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/FilesystemIteratorSkipDotsRectorTest.php new file mode 100644 index 00000000000..d1e54fc9b12 --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/FilesystemIteratorSkipDotsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_flag.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_flag.php.inc new file mode 100644 index 00000000000..e81a906f1bb --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_flag.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_multiple_flags.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_multiple_flags.php.inc new file mode 100644 index 00000000000..2e2434cf69d --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_multiple_flags.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_multiple_flags_with_fqn.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_multiple_flags_with_fqn.php.inc new file mode 100644 index 00000000000..cb8e2bf0347 --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/append_constant_to_multiple_flags_with_fqn.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present.php.inc new file mode 100644 index 00000000000..1bc7ea7d835 --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present_in_flags.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present_in_flags.php.inc new file mode 100644 index 00000000000..fdb2063f452 --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present_in_flags.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present_with_fqn.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present_with_fqn.php.inc new file mode 100644 index 00000000000..b64e2a40062 --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_constant_present_with_fqn.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_non_filesystem_iterator_instance.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_non_filesystem_iterator_instance.php.inc new file mode 100644 index 00000000000..349bea67f92 --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_non_filesystem_iterator_instance.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_use_variable.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_use_variable.php.inc new file mode 100644 index 00000000000..0e5faa315e0 --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_use_variable.php.inc @@ -0,0 +1,16 @@ + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_with_default_value.php.inc b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_with_default_value.php.inc new file mode 100644 index 00000000000..5315639d807 --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/Fixture/skip_with_default_value.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/config/configured_rule.php b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/config/configured_rule.php new file mode 100644 index 00000000000..14f98e2326e --- /dev/null +++ b/rules-tests/Php82/Rector/New_/FilesystemIteratorSkipDotsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([FilesystemIteratorSkipDotsRector::class]); diff --git a/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/AddSensitiveParameterAttributeRectorTest.php b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/AddSensitiveParameterAttributeRectorTest.php new file mode 100644 index 00000000000..b2f8001902e --- /dev/null +++ b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/AddSensitiveParameterAttributeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/apply_attribute_to_functions.php.inc b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/apply_attribute_to_functions.php.inc new file mode 100644 index 00000000000..29779005428 --- /dev/null +++ b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/apply_attribute_to_functions.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/apply_attribute_to_methods.php.inc b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/apply_attribute_to_methods.php.inc new file mode 100644 index 00000000000..5c8827f5413 --- /dev/null +++ b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/apply_attribute_to_methods.php.inc @@ -0,0 +1,26 @@ + +----- + diff --git a/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/skip_sensitive_parameter_attribute_exists.php.inc b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/skip_sensitive_parameter_attribute_exists.php.inc new file mode 100644 index 00000000000..1176c32a4ba --- /dev/null +++ b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/Fixture/skip_sensitive_parameter_attribute_exists.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/config/configured_rule.php b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/config/configured_rule.php new file mode 100644 index 00000000000..f59f03dbde3 --- /dev/null +++ b/rules-tests/Php82/Rector/Param/AddSensitiveParameterAttributeRector/config/configured_rule.php @@ -0,0 +1,15 @@ +phpVersion(PhpVersionFeature::SENSITIVE_PARAMETER_ATTRIBUTE); + + $rectorConfig->ruleWithConfiguration(AddSensitiveParameterAttributeRector::class, [ + AddSensitiveParameterAttributeRector::SENSITIVE_PARAMETERS => ['password'], + ]); +}; diff --git a/rules-tests/Php83/Rector/BooleanAnd/.DS_Store b/rules-tests/Php83/Rector/BooleanAnd/.DS_Store new file mode 100644 index 00000000000..080b4b70e0a Binary files /dev/null and b/rules-tests/Php83/Rector/BooleanAnd/.DS_Store differ diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/elseif.php.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/elseif.php.inc new file mode 100644 index 00000000000..be3aa80a610 --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/elseif.php.inc @@ -0,0 +1,17 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate.php.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate.php.inc new file mode 100644 index 00000000000..87748e3d6f4 --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate.php.inc @@ -0,0 +1,17 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_base.php.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_base.php.inc new file mode 100644 index 00000000000..6e3cd1767eb --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_base.php.inc @@ -0,0 +1,17 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_named.php.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_named.php.inc new file mode 100644 index 00000000000..68fa4cffc32 --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_named.php.inc @@ -0,0 +1,15 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_named_base.php.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_named_base.php.inc new file mode 100644 index 00000000000..08ebe7d8454 --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_named_base.php.inc @@ -0,0 +1,15 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_yoda.php.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_yoda.php.inc new file mode 100644 index 00000000000..6f29ea59a90 --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_yoda.php.inc @@ -0,0 +1,17 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_yoda_base.php.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_yoda_base.php.inc new file mode 100644 index 00000000000..60629369fbc --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/json_validate_yoda_base.php.inc @@ -0,0 +1,17 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/skip_json_validate_invalid_depth.php copy.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/skip_json_validate_invalid_depth.php copy.inc new file mode 100644 index 00000000000..6e7e9a9f274 --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/skip_json_validate_invalid_depth.php copy.inc @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/skip_json_validate_invalid_flag.php.inc b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/skip_json_validate_invalid_flag.php.inc new file mode 100644 index 00000000000..8359236e996 --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/Fixture/skip_json_validate_invalid_flag.php.inc @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/JsonValidateRectorTest.php b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/JsonValidateRectorTest.php new file mode 100644 index 00000000000..27333d5d312 --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/JsonValidateRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/config/configured_rule.php b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/config/configured_rule.php new file mode 100644 index 00000000000..dd7fc250a5d --- /dev/null +++ b/rules-tests/Php83/Rector/BooleanAnd/JsonValidateRector/config/configured_rule.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersion::PHP_83); + + $rectorConfig->rule(JsonValidateRector::class); +}; diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/AddTypeToConstRectorTest.php b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/AddTypeToConstRectorTest.php new file mode 100644 index 00000000000..5e5f3f3d694 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/AddTypeToConstRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_only_to_private_const_when_public_available.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_only_to_private_const_when_public_available.php.inc new file mode 100644 index 00000000000..08d194cbe33 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_only_to_private_const_when_public_available.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_class_final.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_class_final.php.inc new file mode 100644 index 00000000000..6f4ccb5d039 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_class_final.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_final.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_final.php.inc new file mode 100644 index 00000000000..ab30f16a86b --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_final.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_protected.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_protected.php.inc new file mode 100644 index 00000000000..68386b5e0ba --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_protected.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_references_another_const.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_references_another_const.php.inc new file mode 100644 index 00000000000..aa572011278 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/apply_type_to_class_const_when_const_references_another_const.php.inc @@ -0,0 +1,53 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/default_native_constant.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/default_native_constant.php.inc new file mode 100644 index 00000000000..1dab5102cc8 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/default_native_constant.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/handle_private_abstract.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/handle_private_abstract.php.inc new file mode 100644 index 00000000000..8aaefb59ebc --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/handle_private_abstract.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/multiple_const_same_type.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/multiple_const_same_type.php.inc new file mode 100644 index 00000000000..8fa92854705 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/multiple_const_same_type.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/plus_or_minus.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/plus_or_minus.php.inc new file mode 100644 index 00000000000..ea8a719800a --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/plus_or_minus.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/remove_docblock_as_useless.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/remove_docblock_as_useless.php.inc new file mode 100644 index 00000000000..a67e6aa03a5 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/remove_docblock_as_useless.php.inc @@ -0,0 +1,24 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_multiple_const_different_types.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_multiple_const_different_types.php.inc new file mode 100644 index 00000000000..cd13cfecab8 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_multiple_const_different_types.php.inc @@ -0,0 +1,8 @@ + + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_interface_defines_const.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_interface_defines_const.php.inc new file mode 100644 index 00000000000..5115b448254 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_interface_defines_const.php.inc @@ -0,0 +1,11 @@ + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_parent_class_const_defines_type.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_parent_class_const_defines_type.php.inc new file mode 100644 index 00000000000..00609882815 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_parent_class_const_defines_type.php.inc @@ -0,0 +1,10 @@ + + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_type_already_set.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_type_already_set.php.inc new file mode 100644 index 00000000000..d10afc08c16 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Fixture/skip_when_type_already_set.php.inc @@ -0,0 +1,10 @@ + diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/FixtureMarkedAsFinal/normal_class_as_final.php.inc b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/FixtureMarkedAsFinal/normal_class_as_final.php.inc new file mode 100644 index 00000000000..2494b49b096 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/FixtureMarkedAsFinal/normal_class_as_final.php.inc @@ -0,0 +1,21 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Source/ParentClass.php b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Source/ParentClass.php new file mode 100644 index 00000000000..58325c04722 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/Source/ParentClass.php @@ -0,0 +1,8 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureMarkedAsFinal'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/marked_classes_as_final.php'; + } +} diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/configured_rule.php b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/configured_rule.php new file mode 100644 index 00000000000..688629c6bf0 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(AddTypeToConstRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_83); +}; diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/marked_classes_as_final.php b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/marked_classes_as_final.php new file mode 100644 index 00000000000..9fe7068313f --- /dev/null +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/marked_classes_as_final.php @@ -0,0 +1,14 @@ +rule(AddTypeToConstRector::class); + $rectorConfig->treatClassesAsFinal(); + + $rectorConfig->phpVersion(PhpVersion::PHP_83); +}; diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/AddOverrideAttributeToOverriddenMethodsRectorTest.php b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/AddOverrideAttributeToOverriddenMethodsRectorTest.php new file mode 100644 index 00000000000..cb1bb9e537c --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/AddOverrideAttributeToOverriddenMethodsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/AddToInterfaceMethodsTest.php b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/AddToInterfaceMethodsTest.php new file mode 100644 index 00000000000..c82b8a9d105 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/AddToInterfaceMethodsTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureAddToInterfaceMethods'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/add_to_interface_methods_configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_overridden_methods.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_overridden_methods.php.inc new file mode 100644 index 00000000000..40386f76494 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_overridden_methods.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_overridden_methods_from_grandparent.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_overridden_methods_from_grandparent.php.inc new file mode 100644 index 00000000000..8a4506ef1d6 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_overridden_methods_from_grandparent.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/extends_parent_consume_trait.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/extends_parent_consume_trait.php.inc new file mode 100644 index 00000000000..1b066a10590 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/extends_parent_consume_trait.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/indirect_override_abstract_method.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/indirect_override_abstract_method.php.inc new file mode 100644 index 00000000000..9aa0c0b4a0b --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/indirect_override_abstract_method.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/override_trait_with_extends_non_empty_method.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/override_trait_with_extends_non_empty_method.php.inc new file mode 100644 index 00000000000..3f90c355fb1 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/override_trait_with_extends_non_empty_method.php.inc @@ -0,0 +1,36 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_countable.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_countable.php.inc new file mode 100644 index 00000000000..1f71f307d3e --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_countable.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_method_that_is_not_overridden.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_method_that_is_not_overridden.php.inc new file mode 100644 index 00000000000..0600d2f861f --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_method_that_is_not_overridden.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_construct.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_construct.php.inc new file mode 100644 index 00000000000..82a16832531 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_construct.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_non_inheriting_class.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_non_inheriting_class.php.inc new file mode 100644 index 00000000000..6d0c8a31e42 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_non_inheriting_class.php.inc @@ -0,0 +1,11 @@ + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_parent_return_null.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_parent_return_null.php.inc new file mode 100644 index 00000000000..23b48279748 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_on_parent_return_null.php.inc @@ -0,0 +1,16 @@ + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_override_only_trait_with_non_empty_method.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_override_only_trait_with_non_empty_method.php.inc new file mode 100644 index 00000000000..53a9d0debf4 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_override_only_trait_with_non_empty_method.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_setup_override.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_setup_override.php.inc new file mode 100644 index 00000000000..5e1298a3263 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_setup_override.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/implement_built_in.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/implement_built_in.php.inc new file mode 100644 index 00000000000..63b8e932a2d --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/implement_built_in.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/indirect_totring_override.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/indirect_totring_override.php.inc new file mode 100644 index 00000000000..64de191e620 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/indirect_totring_override.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/interface_implement.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/interface_implement.php.inc new file mode 100644 index 00000000000..43c06bc50a1 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/interface_implement.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/interface_implement_multiple.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/interface_implement_multiple.php.inc new file mode 100644 index 00000000000..0c073882323 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/interface_implement_multiple.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/normal_extends.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/normal_extends.php.inc new file mode 100644 index 00000000000..5bcadeeaf4d --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/normal_extends.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/totring_override.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/totring_override.php.inc new file mode 100644 index 00000000000..e59f9e9ae02 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureAddToInterfaceMethods/totring_override.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/indirect_totring_override.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/indirect_totring_override.php.inc new file mode 100644 index 00000000000..e1ec98fa4b2 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/indirect_totring_override.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/override_abstract_from_trait.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/override_abstract_from_trait.php.inc new file mode 100644 index 00000000000..565b3501b18 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/override_abstract_from_trait.php.inc @@ -0,0 +1,42 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/override_parent_from_trait.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/override_parent_from_trait.php.inc new file mode 100644 index 00000000000..b23d82ceedd --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/override_parent_from_trait.php.inc @@ -0,0 +1,44 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/setup_override.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/setup_override.php.inc new file mode 100644 index 00000000000..25e2618fc60 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/setup_override.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/skip_countable.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/skip_countable.php.inc new file mode 100644 index 00000000000..3a4449df10c --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/skip_countable.php.inc @@ -0,0 +1,14 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/skip_implement_interface.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/skip_implement_interface.php.inc new file mode 100644 index 00000000000..11e53b749d1 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/skip_implement_interface.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/totring_override_with_interface.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/totring_override_with_interface.php.inc new file mode 100644 index 00000000000..93c5a143367 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/FixtureOverrideEmptyMethod/totring_override_with_interface.php.inc @@ -0,0 +1,48 @@ + +----- + diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/OverrideEmptyMethodTest.php b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/OverrideEmptyMethodTest.php new file mode 100644 index 00000000000..52ceb69ec0f --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/OverrideEmptyMethodTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureOverrideEmptyMethod'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/override_empty_method_configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Source/AbstractParentWithInterface.php b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Source/AbstractParentWithInterface.php new file mode 100644 index 00000000000..b005e14c4d2 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Source/AbstractParentWithInterface.php @@ -0,0 +1,10 @@ +ruleWithConfiguration(AddOverrideAttributeToOverriddenMethodsRector::class, [ + AddOverrideAttributeToOverriddenMethodsRector::ADD_TO_INTERFACE_METHODS => true, + ]); + + $rectorConfig->phpVersion(PhpVersion::PHP_83); +}; diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/config/configured_rule.php b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/config/configured_rule.php new file mode 100644 index 00000000000..2e69c043eb7 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(AddOverrideAttributeToOverriddenMethodsRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_83); +}; diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/config/override_empty_method_configured_rule.php b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/config/override_empty_method_configured_rule.php new file mode 100644 index 00000000000..b3f70c6bb46 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/config/override_empty_method_configured_rule.php @@ -0,0 +1,15 @@ +ruleWithConfiguration(AddOverrideAttributeToOverriddenMethodsRector::class, [ + AddOverrideAttributeToOverriddenMethodsRector::ALLOW_OVERRIDE_EMPTY_METHOD => true, + ]); + + $rectorConfig->phpVersion(PhpVersion::PHP_83); +}; diff --git a/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/extends_readonly_class.php.inc b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/extends_readonly_class.php.inc new file mode 100644 index 00000000000..c86363ef8ed --- /dev/null +++ b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/extends_readonly_class.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/parenthesized_with_anonymous_class.php.inc b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/parenthesized_with_anonymous_class.php.inc new file mode 100644 index 00000000000..a48d4a474ec --- /dev/null +++ b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/parenthesized_with_anonymous_class.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/skip_named_class.php.inc b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/skip_named_class.php.inc new file mode 100644 index 00000000000..78913fb7b1b --- /dev/null +++ b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Fixture/skip_named_class.php.inc @@ -0,0 +1,8 @@ + +----- + diff --git a/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/ReadOnlyAnonymousClassRectorTest.php b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/ReadOnlyAnonymousClassRectorTest.php new file mode 100644 index 00000000000..90e50cff133 --- /dev/null +++ b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/ReadOnlyAnonymousClassRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Source/ParentAlreadyReadonly.php b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Source/ParentAlreadyReadonly.php new file mode 100644 index 00000000000..1d2d28d9d1a --- /dev/null +++ b/rules-tests/Php83/Rector/Class_/ReadOnlyAnonymousClassRector/Source/ParentAlreadyReadonly.php @@ -0,0 +1,9 @@ +rule(ReadOnlyAnonymousClassRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::READONLY_ANONYMOUS_CLASS); +}; diff --git a/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/CombineHostPortLdapUriRectorTest.php b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/CombineHostPortLdapUriRectorTest.php new file mode 100644 index 00000000000..0324ebcd161 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/CombineHostPortLdapUriRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/dynamic_value.php.inc b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/dynamic_value.php.inc new file mode 100644 index 00000000000..ed27763da4a --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/dynamic_value.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/fixture.php.inc b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..11ccb2caf18 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/skip_different_func_call.php.inc b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/skip_different_func_call.php.inc new file mode 100644 index 00000000000..37a6cbd18e0 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/Fixture/skip_different_func_call.php.inc @@ -0,0 +1,11 @@ +protocol . $this->server, $this->port); + } +} + +?> +----- +protocol . $this->server . ':' . $this->port); + } +} + +?> diff --git a/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/config/configured_rule.php b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/config/configured_rule.php new file mode 100644 index 00000000000..e686826d158 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/CombineHostPortLdapUriRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(CombineHostPortLdapUriRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_83); +}; diff --git a/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/DynamicClassConstFetchRectorTest.php b/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/DynamicClassConstFetchRectorTest.php new file mode 100644 index 00000000000..d5d62d051fa --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/DynamicClassConstFetchRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/Fixture/fixture.php.inc b/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..37c0a1801c9 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/Fixture/fixture.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/Fixture/skip_dynamic_class_const_fetch.php.inc b/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/Fixture/skip_dynamic_class_const_fetch.php.inc new file mode 100644 index 00000000000..76accbddd13 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/DynamicClassConstFetchRector/Fixture/skip_dynamic_class_const_fetch.php.inc @@ -0,0 +1,14 @@ +rule(DynamicClassConstFetchRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_83); +}; diff --git a/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/fixture_get_class.php.inc b/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/fixture_get_class.php.inc new file mode 100644 index 00000000000..4102c849034 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/fixture_get_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/fixture_get_parent_class.php.inc b/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/fixture_get_parent_class.php.inc new file mode 100644 index 00000000000..daf6b9e8d83 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/fixture_get_parent_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/skip_different_func_call.php.inc b/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/skip_different_func_call.php.inc new file mode 100644 index 00000000000..cdc82280ef8 --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/Fixture/skip_different_func_call.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/config/configured_rule.php b/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/config/configured_rule.php new file mode 100644 index 00000000000..67f3b08201c --- /dev/null +++ b/rules-tests/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(RemoveGetClassGetParentClassNoArgsRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_83); +}; diff --git a/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/DeprecatedAnnotationToDeprecatedAttributeRectorTest.php b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/DeprecatedAnnotationToDeprecatedAttributeRectorTest.php new file mode 100644 index 00000000000..4f96d80c260 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/DeprecatedAnnotationToDeprecatedAttributeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/fixture.php.inc b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..c256555cdea --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/fixture.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/gracefully_removes_annotation.php.inc b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/gracefully_removes_annotation.php.inc new file mode 100644 index 00000000000..f0708b2d7ed --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/gracefully_removes_annotation.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/multiline_text.php.inc b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/multiline_text.php.inc new file mode 100644 index 00000000000..c16527a22fa --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/multiline_text.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/single_line_text.php.inc b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/single_line_text.php.inc new file mode 100644 index 00000000000..50990bdd1e5 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/single_line_text.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/with_quote.php.inc b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/with_quote.php.inc new file mode 100644 index 00000000000..4fc44e382ed --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/with_quote.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/works_with_functions.php.inc b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/works_with_functions.php.inc new file mode 100644 index 00000000000..4a7becf5ab5 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/Fixture/works_with_functions.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/config/configured_rule.php b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/config/configured_rule.php new file mode 100644 index 00000000000..236335dd794 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(DeprecatedAnnotationToDeprecatedAttributeRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_different_variable.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_different_variable.php.inc new file mode 100644 index 00000000000..30cb25da195 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_different_variable.php.inc @@ -0,0 +1,20 @@ +surname; + } + + public function setName(string $name): void + { + $this->surname = ucfirst($name); + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_get_set_magic.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_get_set_magic.php.inc new file mode 100644 index 00000000000..e5b35b1204e --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_get_set_magic.php.inc @@ -0,0 +1,26 @@ +name; + } + + public function setName(string $name): void + { + $this->name = ucfirst($name); + } + + public function __get($name) + { + } + + public function __set($name, $value) + { + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_getter_and_readonly.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_getter_and_readonly.php.inc new file mode 100644 index 00000000000..a8dd2371297 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_getter_and_readonly.php.inc @@ -0,0 +1,18 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_method_with_attributes.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_method_with_attributes.php.inc new file mode 100644 index 00000000000..4741a2e3eb7 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_method_with_attributes.php.inc @@ -0,0 +1,22 @@ +name; + } + + #[Required] + public function setName(string $name): void + { + $this->name = ucfirst($name); + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_mismatching_getter_setter_names.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_mismatching_getter_setter_names.php.inc new file mode 100644 index 00000000000..e5bc9900851 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_mismatching_getter_setter_names.php.inc @@ -0,0 +1,18 @@ +name; + } + + public function setAnotherName(string $name): void + { + $this->name = ucfirst($name); + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_multi_stmts_getter.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_multi_stmts_getter.php.inc new file mode 100644 index 00000000000..82bf0145be7 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_multi_stmts_getter.php.inc @@ -0,0 +1,15 @@ +name; + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_non_final_class_to_avoid_child_break.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_non_final_class_to_avoid_child_break.php.inc new file mode 100644 index 00000000000..651b06fa9e6 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_non_final_class_to_avoid_child_break.php.inc @@ -0,0 +1,18 @@ +name; + } + + public function setName(string $name): void + { + $this->name = ucfirst($name); + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_parent_contract.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_parent_contract.php.inc new file mode 100644 index 00000000000..287c336b6af --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_parent_contract.php.inc @@ -0,0 +1,15 @@ +name; + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_class.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_class.php.inc new file mode 100644 index 00000000000..4e4c98fe329 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_class.php.inc @@ -0,0 +1,18 @@ +name; + } + + public function setName(string $name): void + { + $this->name = ucfirst($name); + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_getter_and_setter.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_getter_and_setter.php.inc new file mode 100644 index 00000000000..2dce01eb3dc --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_getter_and_setter.php.inc @@ -0,0 +1,18 @@ +name; + } + + public function setName(string $name): void + { + $this->name = ucfirst($name); + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_promoted_property.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_promoted_property.php.inc new file mode 100644 index 00000000000..0e1c8210636 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/skip_readonly_promoted_property.php.inc @@ -0,0 +1,17 @@ +value; + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/some_fixture.php.inc b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/some_fixture.php.inc new file mode 100644 index 00000000000..051d0840fb9 --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Fixture/some_fixture.php.inc @@ -0,0 +1,36 @@ +name; + } + + public function setName(string $name): void + { + $this->name = ucfirst($name); + } +} + +?> +----- + $this->name; + set(string $name) { + $this->name = ucfirst($name); + } + } +} + +?> diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/PropertyHookRectorTest.php b/rules-tests/Php84/Rector/Class_/PropertyHookRector/PropertyHookRectorTest.php new file mode 100644 index 00000000000..9a3360ed62b --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/PropertyHookRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/Class_/PropertyHookRector/Source/SomeParentContractInterface.php b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Source/SomeParentContractInterface.php new file mode 100644 index 00000000000..ab4920f92cf --- /dev/null +++ b/rules-tests/Php84/Rector/Class_/PropertyHookRector/Source/SomeParentContractInterface.php @@ -0,0 +1,8 @@ +rule(PropertyHookRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/basic_usage.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/basic_usage.php.inc new file mode 100644 index 00000000000..2c91f512425 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/basic_usage.php.inc @@ -0,0 +1,71 @@ + 10)) { + $allGreater = false; + break; + } + } + return $allGreater; + } + + public function checkAllItemsEqualTarget(array $items) + { + $allMatch = true; + foreach ($items as $key => $value) { + if (!($value === 'target')) { + $allMatch = false; + break; + } + } + return $allMatch; + } +} + +?> +----- + str_starts_with($animal, 'c')); + return $found; + } + + public function checkAllNumbersGreaterThanTen(array $numbers) + { + $allGreater = array_all($numbers, fn($number) => $number > 10); + return $allGreater; + } + + public function checkAllItemsEqualTarget(array $items) + { + $allMatch = array_all($items, fn($value) => $value === 'target'); + return $allMatch; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_basic_usage.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_basic_usage.php.inc new file mode 100644 index 00000000000..fcc73afcb43 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_basic_usage.php.inc @@ -0,0 +1,62 @@ + 10)) { + return false; + } + } + return true; + } + + public function checkAllItemsEqualTarget(array $items) + { + foreach ($items as $key => $value) { + if (!($value === 'target')) { + return false; + } + } + return true; + } +} + +?> +----- + str_starts_with($animal, 'c')); + } + + public function checkAllNumbersGreaterThanTen(array $numbers) + { + return array_all($numbers, fn($number) => $number > 10); + } + + public function checkAllItemsEqualTarget(array $items) + { + return array_all($items, fn($value) => $value === 'target'); + } +} + +?> diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_missing_return_true.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_missing_return_true.php.inc new file mode 100644 index 00000000000..b1c4cedff81 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_missing_return_true.php.inc @@ -0,0 +1,17 @@ + diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_multiple_statements_after_foreach.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_multiple_statements_after_foreach.php.inc new file mode 100644 index 00000000000..812362336a3 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_multiple_statements_after_foreach.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_multiple_statements_within_if_statement.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_multiple_statements_within_if_statement.php.inc new file mode 100644 index 00000000000..98bdf075a4b --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_multiple_statements_within_if_statement.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_nested_if_statements.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_nested_if_statements.php.inc new file mode 100644 index 00000000000..89a0b47f702 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_nested_if_statements.php.inc @@ -0,0 +1,20 @@ + diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_non_array.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_non_array.php.inc new file mode 100644 index 00000000000..0e99c69ffad --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_non_array.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_unexpected_return_type.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_unexpected_return_type.php.inc new file mode 100644 index 00000000000..d97e9a2f02e --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_skip_unexpected_return_type.php.inc @@ -0,0 +1,17 @@ + diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_with_key.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_with_key.php.inc new file mode 100644 index 00000000000..225ada3af3f --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/early_return_with_key.php.inc @@ -0,0 +1,32 @@ + $v) { + if (!isset($params[$k]) || (string) $params[$k] !== (string) $v) { + return false; + } + } + return true; + } +} + +?> +----- + !(!isset($params[$k]) || (string) $params[$k] !== (string) $v)); + } +} + +?> diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/skip_multiple_statements.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/skip_multiple_statements.php.inc new file mode 100644 index 00000000000..0b34512887e --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/skip_multiple_statements.php.inc @@ -0,0 +1,19 @@ + $currentType]) { + if ($currentType !== $type) { + return false; + } + } + + return true; + } +} diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/skip_variable_reuse_after_foreach.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/skip_variable_reuse_after_foreach.php.inc new file mode 100644 index 00000000000..6702adb4712 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/Fixture/skip_variable_reuse_after_foreach.php.inc @@ -0,0 +1,23 @@ + $v) { + if (!isset($params[$k]) || (string) $params[$k] !== (string) $v) { + $match = false; + break; + } + } + return $match; + } +} + +?> +----- + !(!isset($params[$k]) || (string) $params[$k] !== (string) $v)); + return $match; + } +} + +?> diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/ForeachToArrayAllRectorTest.php b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/ForeachToArrayAllRectorTest.php new file mode 100644 index 00000000000..fe47076e6a9 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/ForeachToArrayAllRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/config/configured_rule.php b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/config/configured_rule.php new file mode 100644 index 00000000000..043d63ae465 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAllRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ForeachToArrayAllRector::class); +}; diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/basic_usage.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/basic_usage.php.inc new file mode 100644 index 00000000000..e392146c228 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/basic_usage.php.inc @@ -0,0 +1,71 @@ + 10) { + $exists = true; + break; + } + } + return $exists; + } + + public function checkWithKey(array $items) + { + $hasMatch = false; + foreach ($items as $key => $value) { + if ($value === 'target') { + $hasMatch = true; + break; + } + } + return $hasMatch; + } +} + +?> +----- + str_starts_with($animal, 'c')); + return $found; + } + + public function checkNumber(array $numbers) + { + $exists = array_any($numbers, fn($number) => $number > 10); + return $exists; + } + + public function checkWithKey(array $items) + { + $hasMatch = array_any($items, fn($value) => $value === 'target'); + return $hasMatch; + } +} + +?> diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/early_return_basic_usage.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/early_return_basic_usage.php.inc new file mode 100644 index 00000000000..ba71a7e1c5d --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/early_return_basic_usage.php.inc @@ -0,0 +1,60 @@ + 10) { + return true; + } + } + return false; + } + + public function checkWithKey(array $items) + { + foreach ($items as $key => $value) { + if ($value === 'target') { + return true; + } + } + return false; + } +} +?> +----- + str_starts_with($animal, 'c')); + } + + public function checkNumber(array $numbers) + { + return array_any($numbers, fn($number) => $number > 10); + } + + public function checkWithKey(array $items) + { + return array_any($items, fn($value) => $value === 'target'); + } +} +?> diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/early_return_skip_missing_return_false.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/early_return_skip_missing_return_false.php.inc new file mode 100644 index 00000000000..9717f2e9b74 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/early_return_skip_missing_return_false.php.inc @@ -0,0 +1,15 @@ + $animal) { + if (str_starts_with($animal, 'c') && $key === 1) { + return true; + } + } + return false; + } +} + +?> +----- + str_starts_with($animal, 'c') && $key === 1); + } +} + +?> diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/skip_multiple_statements.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/skip_multiple_statements.php.inc new file mode 100644 index 00000000000..6d5c0f3573c --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/skip_multiple_statements.php.inc @@ -0,0 +1,19 @@ + 'foo'], + ]; + + foreach ($data as ['type' => $currentType]) { + if ($currentType === $type) { + return true; + } + } + + return false; + } +} diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/skip_variable_reuse_after_foreach.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/skip_variable_reuse_after_foreach.php.inc new file mode 100644 index 00000000000..b5f8f12d9fa --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/Fixture/skip_variable_reuse_after_foreach.php.inc @@ -0,0 +1,23 @@ + $animal) { + if (str_starts_with($animal, 'c') && $key === 1) { + $found = true; + break; + } + } + return $found; + } +} + +?> +----- + str_starts_with($animal, 'c') && $key === 1); + return $found; + } +} + +?> diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/ForeachToArrayAnyRectorTest.php b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/ForeachToArrayAnyRectorTest.php new file mode 100644 index 00000000000..acdf3c48c3a --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/ForeachToArrayAnyRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/config/configured_rule.php b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/config/configured_rule.php new file mode 100644 index 00000000000..aef591ab108 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayAnyRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ForeachToArrayAnyRector::class); +}; diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/basic_usage.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/basic_usage.php.inc new file mode 100644 index 00000000000..7168b5ef97e --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/basic_usage.php.inc @@ -0,0 +1,53 @@ + $animal) { + if (str_starts_with($animal, 'c')) { + $found = $idx; + break; + } + } + return $found; + } + + public function findNumberKey(array $numbers) + { + $result = null; + foreach ($numbers as $key => $number) { + if ($number > 10) { + $result = $key; + break; + } + } + return $result; + } +} + +?> +----- + str_starts_with($animal, 'c')); + return $found; + } + + public function findNumberKey(array $numbers) + { + $result = array_find_key($numbers, fn($number) => $number > 10); + return $result; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_multiple_statements.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_multiple_statements.php.inc new file mode 100644 index 00000000000..747c53ae12d --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_multiple_statements.php.inc @@ -0,0 +1,19 @@ + $animal) { + if (str_starts_with($animal, 'c')) { + $found = $idx; + echo "Found: " . $animal; + break; + } + } + return $found; + } +} \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_no_break.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_no_break.php.inc new file mode 100644 index 00000000000..4d72d90568e --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_no_break.php.inc @@ -0,0 +1,17 @@ + $animal) { + if (str_starts_with($animal, 'c')) { + $found = $idx; + } + } + return $found; + } +} \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_no_key_var.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_no_key_var.php.inc new file mode 100644 index 00000000000..cace3bf8396 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_no_key_var.php.inc @@ -0,0 +1,18 @@ + $animal) { + if (str_starts_with($animal, 'c')) { + $found = $idx; + break; + } + } + return $found; + } +} diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_variable_reuse_after_foreach.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_variable_reuse_after_foreach.php.inc new file mode 100644 index 00000000000..042e355e1b6 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/skip_variable_reuse_after_foreach.php.inc @@ -0,0 +1,23 @@ + $animal) { + if (str_starts_with($animal, 'c')) { + $found = $idx; + break; + } + } + + if (isset($animal)) { + echo 'hit'; + } + + return $found; + } +} \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/with_key.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/with_key.php.inc new file mode 100644 index 00000000000..afd32e10034 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/Fixture/with_key.php.inc @@ -0,0 +1,35 @@ + $animal) { + if (str_starts_with($animal, 'c') && $idx === 1) { + $found = $idx; + break; + } + } + return $found; + } +} + +?> +----- + str_starts_with($animal, 'c') && $idx === 1); + return $found; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/ForeachToArrayFindKeyRectorTest.php b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/ForeachToArrayFindKeyRectorTest.php new file mode 100644 index 00000000000..0870d1214d9 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/ForeachToArrayFindKeyRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/config/configured_rule.php b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/config/configured_rule.php new file mode 100644 index 00000000000..fe83f386ac0 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ForeachToArrayFindKeyRector::class]); diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/basic_usage.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/basic_usage.php.inc new file mode 100644 index 00000000000..a81d58906bf --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/basic_usage.php.inc @@ -0,0 +1,53 @@ + 10) { + $result = $number; + break; + } + } + return $result; + } +} + +?> +----- + str_starts_with($animal, 'c')); + return $found; + } + + public function findNumber(array $numbers) + { + $result = array_find($numbers, fn($number) => $number > 10); + return $result; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/skip_multiple_statements.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/skip_multiple_statements.php.inc new file mode 100644 index 00000000000..9b4cd99c474 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/skip_multiple_statements.php.inc @@ -0,0 +1,19 @@ + $urls + */ + public function create(array $urls, string $hash): ?Url + { + $url = null; + foreach ($urls as $urlToCheck) { + if ($urlToCheck->getPathHash() === $hash) { + $url = $urlToCheck; + break; + } + } + + if (isset($urlToCheck)) { + echo 'hit'; + } + + return $url; + } +} \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/with_key.php.inc b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/with_key.php.inc new file mode 100644 index 00000000000..1e84dbbae8a --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/Fixture/with_key.php.inc @@ -0,0 +1,35 @@ + $animal) { + if (str_starts_with($animal, 'c') && $key === 1) { + $found = $animal; + break; + } + } + return $found; + } +} + +?> +----- + str_starts_with($animal, 'c') && $key === 1); + return $found; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/ForeachToArrayFindRectorTest.php b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/ForeachToArrayFindRectorTest.php new file mode 100644 index 00000000000..2ed019ba383 --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/ForeachToArrayFindRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/config/configured_rule.php b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/config/configured_rule.php new file mode 100644 index 00000000000..c650631700b --- /dev/null +++ b/rules-tests/Php84/Rector/Foreach_/ForeachToArrayFindRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ForeachToArrayFindRector::class]); diff --git a/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/AddEscapeArgumentRectorTest.php b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/AddEscapeArgumentRectorTest.php new file mode 100644 index 00000000000..8d50b7fcf5e --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/AddEscapeArgumentRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/fixture.php.inc b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..b0affec2b57 --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/fixture.php.inc @@ -0,0 +1,33 @@ +fputcsv($a, $b, $c); + } +} + +?> +----- +fputcsv($a, $b, $c, escape: '\\'); + } +} + +?> diff --git a/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/skip_pass_escape.php.inc b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/skip_pass_escape.php.inc new file mode 100644 index 00000000000..c8b9454d702 --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/skip_pass_escape.php.inc @@ -0,0 +1,14 @@ +fputcsv($a, $b, $c, $d); + } +} diff --git a/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/skip_pass_escape_named.php.inc b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/skip_pass_escape_named.php.inc new file mode 100644 index 00000000000..82237dc41fe --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/Fixture/skip_pass_escape_named.php.inc @@ -0,0 +1,14 @@ +fputcsv($a, $b, $c, escape: '\\'); + } +} diff --git a/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/config/configured_rule.php b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/config/configured_rule.php new file mode 100644 index 00000000000..8638d5287ed --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/AddEscapeArgumentRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(AddEscapeArgumentRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/Fixture/rounding_constants.php.inc b/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/Fixture/rounding_constants.php.inc new file mode 100644 index 00000000000..2a70b9d247d --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/Fixture/rounding_constants.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/Fixture/skip_indirect_use_of_constants.php.inc b/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/Fixture/skip_indirect_use_of_constants.php.inc new file mode 100644 index 00000000000..be3441a4fa3 --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/Fixture/skip_indirect_use_of_constants.php.inc @@ -0,0 +1,10 @@ + diff --git a/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/RoundingModeEnumRectorTest.php b/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/RoundingModeEnumRectorTest.php new file mode 100644 index 00000000000..51c2e48eb1f --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/RoundingModeEnumRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/config/configured_rule.php b/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/config/configured_rule.php new file mode 100644 index 00000000000..68ca3b74849 --- /dev/null +++ b/rules-tests/Php84/Rector/FuncCall/RoundingModeEnumRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(RoundingModeEnumRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/fixture.php.inc b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..7b12b614ee4 --- /dev/null +++ b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ +withMethod('GET')->withUri('/hello-world'); + } +} + +?> +----- +withMethod('GET')->withUri('/hello-world'); + } +} + +?> diff --git a/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/skip_with_parentheses_on_class.php.inc b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/skip_with_parentheses_on_class.php.inc new file mode 100644 index 00000000000..6742e17faf7 --- /dev/null +++ b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/skip_with_parentheses_on_class.php.inc @@ -0,0 +1,12 @@ +newQuery() + ->orderByDesc('received_at'); + } +} \ No newline at end of file diff --git a/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/skip_without_parentheses.php.inc b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/skip_without_parentheses.php.inc new file mode 100644 index 00000000000..35a02e0965a --- /dev/null +++ b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/Fixture/skip_without_parentheses.php.inc @@ -0,0 +1,12 @@ +withMethod('GET')->withUri('/hello-world'); + } +} + diff --git a/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/NewMethodCallWithoutParenthesesRectorTest.php b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/NewMethodCallWithoutParenthesesRectorTest.php new file mode 100644 index 00000000000..db77e209cfb --- /dev/null +++ b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/NewMethodCallWithoutParenthesesRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/config/configured_rule.php b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/config/configured_rule.php new file mode 100644 index 00000000000..87e810fb0fd --- /dev/null +++ b/rules-tests/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(NewMethodCallWithoutParenthesesRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/ExplicitNullableParamTypeRectorTest.php b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/ExplicitNullableParamTypeRectorTest.php new file mode 100644 index 00000000000..e07af2b9f7f --- /dev/null +++ b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/ExplicitNullableParamTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type.php.inc b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type.php.inc new file mode 100644 index 00000000000..5bd78426f24 --- /dev/null +++ b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type2.php.inc b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type2.php.inc new file mode 100644 index 00000000000..2800a0b93f1 --- /dev/null +++ b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type2.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type3.php.inc b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type3.php.inc new file mode 100644 index 00000000000..78617601e01 --- /dev/null +++ b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/do_not_reprint_node_type3.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/fixture.php.inc b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..97f28bcbfd7 --- /dev/null +++ b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/fixture.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/skip_already_nullable.php.inc b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/skip_already_nullable.php.inc new file mode 100644 index 00000000000..714814ba4d3 --- /dev/null +++ b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/Fixture/skip_already_nullable.php.inc @@ -0,0 +1,10 @@ + +----- + diff --git a/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/config/configured_rule.php b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..0cc5d96eba7 --- /dev/null +++ b/rules-tests/Php84/Rector/Param/ExplicitNullableParamTypeRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(ExplicitNullableParamTypeRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/ArrayFirstLastRectorTest.php b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/ArrayFirstLastRectorTest.php new file mode 100644 index 00000000000..cf1c6cb8e87 --- /dev/null +++ b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/ArrayFirstLastRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/array_values_pattern.php.inc b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/array_values_pattern.php.inc new file mode 100644 index 00000000000..8931a04fd8f --- /dev/null +++ b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/array_values_pattern.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/fixture.php.inc b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..2c3825bb2c6 --- /dev/null +++ b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/fixture.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_array_values_different_variable.php.inc b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_array_values_different_variable.php.inc new file mode 100644 index 00000000000..21f7abf72cb --- /dev/null +++ b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_array_values_different_variable.php.inc @@ -0,0 +1,13 @@ + ['label'=> ['show'=> false]] + ]; + + $myArray[array_key_last($myArray)]['label']['show'] = true; + var_dump($myArray); + } +} diff --git a/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_different_variable.php.inc b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_different_variable.php.inc new file mode 100644 index 00000000000..15febdf74fa --- /dev/null +++ b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_different_variable.php.inc @@ -0,0 +1,14 @@ +rule(ArrayFirstLastRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/skip_sleep_empty.php.inc b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/skip_sleep_empty.php.inc new file mode 100644 index 00000000000..c0aba35565e --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/skip_sleep_empty.php.inc @@ -0,0 +1,15 @@ + +?> diff --git a/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/skip_sleep_null.php.inc b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/skip_sleep_null.php.inc new file mode 100644 index 00000000000..93efd11e174 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/skip_sleep_null.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/sleep.php.inc b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/sleep.php.inc new file mode 100644 index 00000000000..d41ff9bef18 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/Fixture/sleep.php.inc @@ -0,0 +1,29 @@ + +----- + $this->id, 'name' => $this->name]; + } +} +?> diff --git a/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/SleepToSerializeRectorTest.php b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/SleepToSerializeRectorTest.php new file mode 100644 index 00000000000..5234b35a750 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/SleepToSerializeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/config/configured_rule.php b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/config/configured_rule.php new file mode 100644 index 00000000000..56ac5753670 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/SleepToSerializeRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(SleepToSerializeRector::class); +}; diff --git a/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/Fixture/wakeup.php.inc b/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/Fixture/wakeup.php.inc new file mode 100644 index 00000000000..c8abafca716 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/Fixture/wakeup.php.inc @@ -0,0 +1,33 @@ +id = 1; + } +} +?> +----- + $value) { + if (property_exists($this, $property)) { + $this->{$property} = $value; + } + } + } +} +?> diff --git a/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/WakeupToUnserializeRectorTest.php b/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/WakeupToUnserializeRectorTest.php new file mode 100644 index 00000000000..e242647f5d3 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/WakeupToUnserializeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/config/configured_rule.php b/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/config/configured_rule.php new file mode 100644 index 00000000000..659c7717a7c --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/WakeupToUnserializeRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(WakeupToUnserializeRector::class); +}; diff --git a/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/ConstAndTraitDeprecatedAttributeRectorTest.php b/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/ConstAndTraitDeprecatedAttributeRectorTest.php new file mode 100644 index 00000000000..2b8d76deaf3 --- /dev/null +++ b/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/ConstAndTraitDeprecatedAttributeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/basic.php.inc b/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/basic.php.inc new file mode 100644 index 00000000000..a8c27ca64ef --- /dev/null +++ b/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/basic.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/handle_trait.php.inc b/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/handle_trait.php.inc new file mode 100644 index 00000000000..9acfc944786 --- /dev/null +++ b/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/handle_trait.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/skip_on_class_const.php.inc b/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/skip_on_class_const.php.inc new file mode 100644 index 00000000000..162b9de3c62 --- /dev/null +++ b/rules-tests/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector/Fixture/skip_on_class_const.php.inc @@ -0,0 +1,16 @@ +rule(ConstAndTraitDeprecatedAttributeRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/nested_functions.php.inc b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/nested_functions.php.inc new file mode 100644 index 00000000000..07fb0dd8d8e --- /dev/null +++ b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/nested_functions.php.inc @@ -0,0 +1,30 @@ + +----- + htmlspecialchars(...) + |> strtolower(...) + |> trim(...); + } +} + +?> diff --git a/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/return_nested.php.inc b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/return_nested.php.inc new file mode 100644 index 00000000000..dd9cff8fb74 --- /dev/null +++ b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/return_nested.php.inc @@ -0,0 +1,30 @@ + +----- + htmlspecialchars(...) + |> strtolower(...) + |> trim(...); + } +} + +?> diff --git a/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/skip_sole.php.inc b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/skip_sole.php.inc new file mode 100644 index 00000000000..c136ac0857d --- /dev/null +++ b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/Fixture/skip_sole.php.inc @@ -0,0 +1,11 @@ + +----- + array_unique(...); + +?> diff --git a/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/NestedFuncCallsToPipeOperatorRectorTest.php b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/NestedFuncCallsToPipeOperatorRectorTest.php new file mode 100644 index 00000000000..4d880a4a12c --- /dev/null +++ b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/NestedFuncCallsToPipeOperatorRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/config/configured_rule.php b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/config/configured_rule.php new file mode 100644 index 00000000000..3034785d7e6 --- /dev/null +++ b/rules-tests/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(NestedFuncCallsToPipeOperatorRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/ArrayKeyExistsNullToEmptyStringRectorTest.php b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/ArrayKeyExistsNullToEmptyStringRectorTest.php new file mode 100644 index 00000000000..99ac55e8f0b --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/ArrayKeyExistsNullToEmptyStringRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null.php.inc b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null.php.inc new file mode 100644 index 00000000000..a67a072f816 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null.php.inc @@ -0,0 +1,15 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null_named_flip.php.inc b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null_named_flip.php.inc new file mode 100644 index 00000000000..b8606d38af2 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null_named_flip.php.inc @@ -0,0 +1,15 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null_var.php.inc b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null_var.php.inc new file mode 100644 index 00000000000..dde06a0d90c --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/key_null_var.php.inc @@ -0,0 +1,15 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/skip_defined_int_or_string.php.inc b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/skip_defined_int_or_string.php.inc new file mode 100644 index 00000000000..45222b039b5 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/skip_defined_int_or_string.php.inc @@ -0,0 +1,23 @@ + + */ + private array $shownTodos = [ + 0 => false, + 'fallback_todo' => true, + ]; + + public function run(string|int $index): void + { + if (! array_key_exists($index, $this->shownTodos)) { + return; + } + + $this->shownTodos[$index] = true; + } +} diff --git a/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/skip_integer_key.php.inc b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/skip_integer_key.php.inc new file mode 100644 index 00000000000..8718ea6efd8 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector/Fixture/skip_integer_key.php.inc @@ -0,0 +1,5 @@ +rule(ArrayKeyExistsNullToEmptyStringRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/ChrArgModuloRectorTest.php b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/ChrArgModuloRectorTest.php new file mode 100644 index 00000000000..093a907ccf2 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/ChrArgModuloRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/chr.php.inc b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/chr.php.inc new file mode 100644 index 00000000000..320ac129c3a --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/chr.php.inc @@ -0,0 +1,11 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/chr_var.php.inc b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/chr_var.php.inc new file mode 100644 index 00000000000..2e9bae1ede9 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/chr_var.php.inc @@ -0,0 +1,13 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/skip_chr_inbond.php.inc b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/skip_chr_inbond.php.inc new file mode 100644 index 00000000000..c303cdffff7 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/Fixture/skip_chr_inbond.php.inc @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/config/configured_rule.php b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/config/configured_rule.php new file mode 100644 index 00000000000..15d2a0b412f --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/ChrArgModuloRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(ChrArgModuloRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/multibyte_int.php.inc b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/multibyte_int.php.inc new file mode 100644 index 00000000000..aaffcdbfa77 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/multibyte_int.php.inc @@ -0,0 +1,9 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/multibyte_str.php.inc b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/multibyte_str.php.inc new file mode 100644 index 00000000000..af2fe155cb8 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/multibyte_str.php.inc @@ -0,0 +1,11 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_already_dim_fetched_value.php.inc b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_already_dim_fetched_value.php.inc new file mode 100644 index 00000000000..69092b8865a --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_already_dim_fetched_value.php.inc @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_multibyte_int_var.php.inc b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_multibyte_int_var.php.inc new file mode 100644 index 00000000000..7cc95c53a58 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_multibyte_int_var.php.inc @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_no_arg.php.inc b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_no_arg.php.inc new file mode 100644 index 00000000000..ada5fef1675 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/Fixture/skip_no_arg.php.inc @@ -0,0 +1,5 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/config/configured_rule.php b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/config/configured_rule.php new file mode 100644 index 00000000000..6989be8e1f1 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/OrdSingleByteRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(OrdSingleByteRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/Fixture/fixture.php.inc b/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..f45896a4263 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/Fixture/fixture.php.inc @@ -0,0 +1,61 @@ +buffer($fileContents, FILEINFO_NONE, $context); + $finfo->buffer($fileContents, FILEINFO_NONE, context: $context); + $finfo->buffer($fileContents, flags: FILEINFO_NONE, context: $context); + $finfo->buffer($fileContents, context: $context); + $finfo->buffer($fileContents, context: $context, flags: FILEINFO_NONE); + $finfo->buffer(...[$fileContents]); + $finfo->buffer(...[$fileContents, $context]); + $finfo->buffer(...[$fileContents, FILEINFO_NONE, $context]); +} + +?> +----- +buffer($fileContents, FILEINFO_NONE); + $finfo->buffer($fileContents, FILEINFO_NONE); + $finfo->buffer($fileContents, flags: FILEINFO_NONE); + $finfo->buffer($fileContents); + $finfo->buffer($fileContents, flags: FILEINFO_NONE); + $finfo->buffer(...[$fileContents]); + $finfo->buffer(...[$fileContents, $context]); + $finfo->buffer(...[$fileContents, FILEINFO_NONE, $context]); +} + +?> diff --git a/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/Fixture/skip_different_cases.php.inc b/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/Fixture/skip_different_cases.php.inc new file mode 100644 index 00000000000..cbc6fc21a85 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/Fixture/skip_different_cases.php.inc @@ -0,0 +1,23 @@ +file($fileContents, FILEINFO_NONE, $context); + $finfo->file($fileContents, FILEINFO_NONE, context: $context); + $finfo->file($fileContents, flags: FILEINFO_NONE, context: $context); +} + +function bar($otherObject): void +{ + $finfo->buffer($fileContents, FILEINFO_NONE, $context); + $finfo->buffer($fileContents, FILEINFO_NONE, context: $context); + $finfo->buffer($fileContents, flags: FILEINFO_NONE, context: $context); +} diff --git a/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/RemoveFinfoBufferContextArgRectorTest.php b/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/RemoveFinfoBufferContextArgRectorTest.php new file mode 100644 index 00000000000..3f811dfff45 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/RemoveFinfoBufferContextArgRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/config/configured_rule.php b/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/config/configured_rule.php new file mode 100644 index 00000000000..61a845d4f75 --- /dev/null +++ b/rules-tests/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(RemoveFinfoBufferContextArgRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/explicit_null_return.php.inc b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/explicit_null_return.php.inc new file mode 100644 index 00000000000..ff335572230 --- /dev/null +++ b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/explicit_null_return.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/implicit_null_return.php.inc b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/implicit_null_return.php.inc new file mode 100644 index 00000000000..0046487199b --- /dev/null +++ b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/implicit_null_return.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/skip_different_method.php.inc b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/skip_different_method.php.inc new file mode 100644 index 00000000000..9839326a596 --- /dev/null +++ b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/Fixture/skip_different_method.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/NullDebugInfoReturnRectorTest.php b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/NullDebugInfoReturnRectorTest.php new file mode 100644 index 00000000000..4458581f490 --- /dev/null +++ b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/NullDebugInfoReturnRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/config/configured_rule.php b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/config/configured_rule.php new file mode 100644 index 00000000000..2f632ba1861 --- /dev/null +++ b/rules-tests/Php85/Rector/MethodCall/NullDebugInfoReturnRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(NullDebugInfoReturnRector::class); +}; diff --git a/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/AddOverrideAttributeToOverriddenPropertiesRectorTest.php b/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/AddOverrideAttributeToOverriddenPropertiesRectorTest.php new file mode 100644 index 00000000000..219fba364ee --- /dev/null +++ b/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/AddOverrideAttributeToOverriddenPropertiesRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/Fixture/fixture.php.inc b/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..bbaa5743fd7 --- /dev/null +++ b/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/Fixture/fixture.php.inc @@ -0,0 +1,26 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/Fixture/skip_already_has_attribute.php.inc b/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/Fixture/skip_already_has_attribute.php.inc new file mode 100644 index 00000000000..b6acbb30e0e --- /dev/null +++ b/rules-tests/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector/Fixture/skip_already_has_attribute.php.inc @@ -0,0 +1,11 @@ +rule(AddOverrideAttributeToOverriddenPropertiesRector::class); +}; diff --git a/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/fixture.php.inc b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..cbec9525569 --- /dev/null +++ b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/fixture.php.inc @@ -0,0 +1,29 @@ +$output"; + } +} + +?> +----- +$output"; + } +} + +?> diff --git a/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/multi_variables.php.inc b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/multi_variables.php.inc new file mode 100644 index 00000000000..519a1971922 --- /dev/null +++ b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/multi_variables.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/skip_empty_backticks.php.inc b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/skip_empty_backticks.php.inc new file mode 100644 index 00000000000..16118ad59d7 --- /dev/null +++ b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/Fixture/skip_empty_backticks.php.inc @@ -0,0 +1,11 @@ +$output"; + echo "
$output2
"; + } +} + +?> +----- +$output"; + echo "
$output2
"; + } +} + +?> diff --git a/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/ShellExecFunctionCallOverBackticksRectorTest.php b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/ShellExecFunctionCallOverBackticksRectorTest.php new file mode 100644 index 00000000000..988002eff25 --- /dev/null +++ b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/ShellExecFunctionCallOverBackticksRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/config/configured_rule.php b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/config/configured_rule.php new file mode 100644 index 00000000000..ba0bc248b6f --- /dev/null +++ b/rules-tests/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(ShellExecFunctionCallOverBackticksRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/basic.php.inc b/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/basic.php.inc new file mode 100644 index 00000000000..4b03f35742b --- /dev/null +++ b/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/basic.php.inc @@ -0,0 +1,22 @@ + +----- + function3(...) + |> function2(...) + |> function1(...); + +?> diff --git a/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/inside_class_method.php.inc b/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/inside_class_method.php.inc new file mode 100644 index 00000000000..82052119e7a --- /dev/null +++ b/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/inside_class_method.php.inc @@ -0,0 +1,34 @@ + +----- + function3(...) + |> function2(...) + |> function1(...); + } +} + +?> diff --git a/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/skip_if_used_elsewhere.php.inc b/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/skip_if_used_elsewhere.php.inc new file mode 100644 index 00000000000..851bae6d480 --- /dev/null +++ b/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/Fixture/skip_if_used_elsewhere.php.inc @@ -0,0 +1,9 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/config/configured_rule.php b/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/config/configured_rule.php new file mode 100644 index 00000000000..12414980873 --- /dev/null +++ b/rules-tests/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(SequentialAssignmentsToPipeOperatorRector::class); +}; diff --git a/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/ColonAfterSwitchCaseRectorTest.php b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/ColonAfterSwitchCaseRectorTest.php new file mode 100644 index 00000000000..be28a0bdf15 --- /dev/null +++ b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/ColonAfterSwitchCaseRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/fixture.php.inc b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..07dfd311e52 --- /dev/null +++ b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/fixture.php.inc @@ -0,0 +1,53 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/skip_with_colon.php.inc b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/skip_with_colon.php.inc new file mode 100644 index 00000000000..63ea8da70e2 --- /dev/null +++ b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/skip_with_colon.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/config/configured_rule.php b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/config/configured_rule.php new file mode 100644 index 00000000000..6f357c18365 --- /dev/null +++ b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ColonAfterSwitchCaseRector::class); +}; diff --git a/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/fixture.php.inc b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..cc9f9d54d0b --- /dev/null +++ b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/known_variable_and_constant_bounds.php.inc b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/known_variable_and_constant_bounds.php.inc new file mode 100644 index 00000000000..c7a12750fa3 --- /dev/null +++ b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/known_variable_and_constant_bounds.php.inc @@ -0,0 +1,27 @@ + max(0, min(100, $value)); +fn (int $value) => max(0, min($limit, $value)); +fn (int $value) => max(0, min($value, $limit)); +fn (int $value) => max(PHP_INT_MIN, min(PHP_INT_MAX, $value)); +fn (string $value) => max('a', min('z', $value)); + +?> +----- + clamp($value, 0, 100); +fn (int $value) => clamp($value, 0, $limit); +fn (int $value) => clamp($value, 0, $limit); +fn (int $value) => clamp($value, PHP_INT_MIN, PHP_INT_MAX); +fn (string $value) => clamp($value, 'a', 'z'); + +?> diff --git a/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/outer_variable_inner_literal.php.inc b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/outer_variable_inner_literal.php.inc new file mode 100644 index 00000000000..618d00b524d --- /dev/null +++ b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/outer_variable_inner_literal.php.inc @@ -0,0 +1,17 @@ + max($min, min(100, $value)); +fn (int $value, int $max) => min($max, max(0, $value)); + +?> +----- + clamp($value, $min, 100); +fn (int $value, int $max) => clamp($value, 0, $max); + +?> diff --git a/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/skip_ambiguous_inner_order.php.inc b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/skip_ambiguous_inner_order.php.inc new file mode 100644 index 00000000000..ade283b8b48 --- /dev/null +++ b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/skip_ambiguous_inner_order.php.inc @@ -0,0 +1,5 @@ + max($min, min($value, $max)); diff --git a/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/skip_same_function.php.inc b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/skip_same_function.php.inc new file mode 100644 index 00000000000..7e20510fc80 --- /dev/null +++ b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/Fixture/skip_same_function.php.inc @@ -0,0 +1,6 @@ + max($min, max($max, $value)); +fn (int $value, int $min, int $max) => min($min, min($max, $value)); diff --git a/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/MinMaxToClampRectorTest.php b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/MinMaxToClampRectorTest.php new file mode 100644 index 00000000000..995eba1d345 --- /dev/null +++ b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/MinMaxToClampRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/config/configured_rule.php b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/config/configured_rule.php new file mode 100644 index 00000000000..5148a78825d --- /dev/null +++ b/rules-tests/Php86/Rector/FuncCall/MinMaxToClampRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(MinMaxToClampRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_86); +}; diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector/Fixture/some_file_Spec.php b/rules-tests/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector/Fixture/some_file_Spec.php deleted file mode 100644 index 92af628f9cb..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector/Fixture/some_file_Spec.php +++ /dev/null @@ -1,11 +0,0 @@ -doTestFileInfo($fileInfo); - - // test file is moved - $isFileRemoved = $this->removedAndAddedFilesCollector->isFileRemoved($this->originalTempFileInfo); - $this->assertTrue($isFileRemoved); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.php'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector/config/configured_rule.php b/rules-tests/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector/config/configured_rule.php deleted file mode 100644 index 79fc4a5d22f..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RenameSpecFileToTestFileRector::class); -}; diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/Blabla.php b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/Blabla.php deleted file mode 100644 index 0771adc7139..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/Blabla.php +++ /dev/null @@ -1,10 +0,0 @@ -get()->shouldBeCalled()->willReturn( - '08.12.2017 #237 - země|měna|množství|kód|kurz - Austrálie|dolar|1|AUD|16,362 - Velká Británie|libra|1|GBP|29,194' - ); - $this->beConstructedWith($provider); - } - - public function it_is_initializable() - { - $this->shouldHaveType(Rates::class); - } - - public function it_should_load_current_rates(Provider $provider) - { - $provider->get()->shouldBeCalled()->willReturn( - '08.12.2017 #237 - země|měna|množství|kód|kurz - Austrálie|dolar|1|AUD|16,362 - Velká Británie|libra|1|GBP|29,194' - ); - - $this->load()->shouldReturnRates(new ArrayCollection([ - new Rate('AUD', '16.362'), - new Rate('GBP', '29.194'), - ])); - } - - public function getMatchers(): array - { - return [ - 'returnRates' => function (ArrayCollection $rates, ArrayCollection $expectedRates) { - foreach ($rates as $index => $rate) { - if ($rate->rate !== $expectedRates[$index]->rate || $rate->currency !== $expectedRates[$index]->currency) { - return false; - } - } - - return true; - }, - ]; - } -} - -?> ------ -provider = $this->createMock(Provider::class); - $this->provider->expects($this->atLeastOnce())->method('get')->willReturn( - '08.12.2017 #237 - země|měna|množství|kód|kurz - Austrálie|dolar|1|AUD|16,362 - Velká Británie|libra|1|GBP|29,194' - ); - $this->rates = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Rates($this->provider); - } - - public function testInitializable() - { - $this->assertInstanceOf(Rates::class, $this->rates); - } - - public function testLoadCurrentRates() - { - $this->provider->expects($this->atLeastOnce())->method('get')->willReturn( - '08.12.2017 #237 - země|měna|množství|kód|kurz - Austrálie|dolar|1|AUD|16,362 - Velká Británie|libra|1|GBP|29,194' - ); - $matcherCallable = $this->getMatchers()['returnRates']; - $matcherCallable(new ArrayCollection([ - new Rate('AUD', '16.362'), - new Rate('GBP', '29.194'), - ]), $this->rates->load()); - } - - public function getMatchers(): array - { - return [ - 'returnRates' => function (ArrayCollection $rates, ArrayCollection $expectedRates) { - foreach ($rates as $index => $rate) { - if ($rate->rate !== $expectedRates[$index]->rate || $rate->currency !== $expectedRates[$index]->currency) { - return false; - } - } - - return true; - }, - ]; - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/exception.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/exception.php.inc deleted file mode 100644 index f0a403080c4..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/exception.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -beConstructedWith('https://non-existent-rates.example.com/denni_kurz.txt'); - - $this->shouldThrow(RatesNotLoadedException::class)->during('get'); - } -} - -?> ------ -exception = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Exception(); - } - public function testThrowExceptionsWhereRatesAreNotLoaded() - { - $this->exception = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Exception('https://non-existent-rates.example.com/denni_kurz.txt'); - - $this->expectException(RatesNotLoadedException::class); - $this->exception->get(); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/fixture.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/fixture.php.inc deleted file mode 100644 index 74173d7edc0..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,93 +0,0 @@ -beConstructedWith(5); - } - - public function it_returns_id() - { - $this->id()->shouldReturn(5); - } - - public function it_blows() - { - $this->shouldThrow('SomeException')->during('item', [5]); - } - - public function it_should_be_called(Cart $cart) - { - $cart->price()->shouldBeCalled()->willReturn(5); - $cart->shippingAddress(Argument::type(Address::class))->shouldBeCalled(); - $cart->shippingAddress(Argument::type('DateTime'))->shouldBeCalled(); - $cart->shippingAddress(Argument::type('string'))->shouldBeCalled(); - } - - public function is_bool_check() - { - $this->hasFailed()->shouldBe(false); - $this->hasFailed()->shouldNotBe(false); - } - - public function is_array_type() - { - $this->shippingAddresses()->shouldBeArray(); - } -} - -?> ------ -createMe = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\CreateMe(5); - } - - public function testReturnsId() - { - $this->assertSame(5, $this->createMe->id()); - } - - public function testBlows() - { - $this->expectException('SomeException'); - $this->createMe->item(5); - } - - public function testCalled() - { - /** @var Cart|\PHPUnit\Framework\MockObject\MockObject $cart */ - $cart = $this->createMock(Cart::class); - $cart->expects($this->atLeastOnce())->method('price')->willReturn(5); - $cart->expects($this->atLeastOnce())->method('shippingAddress')->with($this->isInstanceOf(Address::class)); - $cart->expects($this->atLeastOnce())->method('shippingAddress')->with($this->isInstanceOf('DateTime')); - $cart->expects($this->atLeastOnce())->method('shippingAddress')->with($this->isType('string')); - } - - public function testBoolCheck() - { - $this->assertFalse($this->createMe->hasFailed()); - $this->assertNotFalse($this->createMe->hasFailed()); - } - - public function testArrayType() - { - $this->assertIsIterable($this->createMe->shippingAddresses()); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/full_blown.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/full_blown.php.inc deleted file mode 100644 index 201ae2c50f1..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/full_blown.php.inc +++ /dev/null @@ -1,352 +0,0 @@ -beConstructedWith(Uuid::uuid1()); - } - - public function it_is_initializable() - { - $this->shouldHaveType(Cart::class); - } - - public function it_should_add_new_item() - { - $id = Uuid::uuid1(); - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->add(new \Ecommerce\Cart\Item($id, 'Lorem ipsum', 4, new Price(671, $taxRate, $currency))); - - $item = $this->item($id); - - $item->id()->shouldReturn($id); - $item->quantity()->shouldReturn(4); - } - - public function it_should_provide_item_by_its_identifier() - { - $id = Uuid::uuid1(); - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->add(new \Ecommerce\Cart\Item($id, 'Lorem ipsum', 3, new Price(671, $taxRate, $currency))); - - $this->item($id)->shouldBeAnInstanceOf(\Ecommerce\Cart\Contract\Item::class); - } - - public function it_should_throw_an_exception_when_item_is_not_in_cart() - { - $id = Uuid::uuid1(); - - $this->shouldThrow(NotFoundException::class)->during('item', [$id]); - } - - public function it_should_summ_up_quantities_when_adding_same_item_multiple_times() - { - PriceType::set(PriceType::WITH_VAT); - - $id = Uuid::uuid1(); - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->add(new \Ecommerce\Cart\Item($id, 'Lorem ipsum', 3, new Price(671, $taxRate, $currency))); - $this->add(new \Ecommerce\Cart\Item($id, 'Dolor sit amet', 7, new Price(671, $taxRate, $currency))); - - $item = $this->item($id); - - $item->quantity()->shouldReturn(10); - } - - public function it_should_remove_item() - { - PriceType::set(PriceType::WITH_VAT); - - $id = Uuid::uuid1(); - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->add(new \Ecommerce\Cart\Item($id, 'Lorem ipsum', 3, new Price(671, $taxRate, $currency))); - $this->remove($id); - - $this->shouldThrow(NotFoundException::class)->during('item', [$id]); - } - - public function it_should_calculate_zero_price_when_cart_is_empty() - { - $price = $this->price(); - - $price->shouldBeAnInstanceOf(CalculatedPrice::class); - $price->withVat()->shouldReturn(0.0); - $price->withoutVat()->shouldReturn(0.0); - $price->vat()->shouldReturn(0.0); - } - - public function it_should_calculate_total_price_of_all_items_when_not_empty() - { - PriceType::set(PriceType::WITH_VAT); - - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->add(new \Ecommerce\Cart\Item(Uuid::uuid1(), 'Lorem ipsum', 3, new Price(671, $taxRate, $currency))); - - $price = $this->price(); - - $price->shouldReturnAnInstanceOf(CalculatedPrice::class); - - $price->withVat()->shouldReturn(2013.00); - $price->withoutVat()->shouldReturn(1663.54); - $price->vat()->shouldReturn(349.46); - } - - public function it_should_contain_old_items_when_merged_with_empty_cart() - { - PriceType::set(PriceType::WITH_VAT); - - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $cart = new Cart(Uuid::uuid1()); - - $item1 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f49c-af76-11e8-8981-529269fb1459'), 'Lorem ipsum', 2, new Price(671, $taxRate, $currency)); - $item2 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f80c-af76-11e8-8981-529269fb1459'), 'Dolor sit amet', 3, new Price(475, $taxRate, $currency)); - - $this->add($item1); - $this->add($item2); - - $this->merge($cart); - - $items = $this->items(); - $items->shouldHaveCount(2); - - $this->item($item1->key())->quantity()->shouldBe(2); - $this->item($item2->key())->quantity()->shouldBe(3); - } - - public function it_should_contain_only_items_from_second_cart_when_merged_with_not_empty_cart() - { - PriceType::set(PriceType::WITH_VAT); - - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $cart = new Cart(Uuid::uuid1()); - - $item1 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f49c-af76-11e8-8981-529269fb1459'), 'Lorem ipsum', 2, new Price(671, $taxRate, $currency)); - $item2 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f80c-af76-11e8-8981-529269fb1459'), 'Dolor sit amet', 3, new Price(475, $taxRate, $currency)); - $item3 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f49c-af76-11e8-8981-529269fb1459'), 'Lorem ipsum', 4, new Price(671, $taxRate, $currency)); - $item4 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9fa0a-af76-11e8-8981-529269fb1459'), 'Lorem ipsum', 5, new Price(671, $taxRate, $currency)); - - $cart->add($item1); - $cart->add($item2); - - $this->add($item3); - $this->add($item4); - - $this->merge($cart); - - $items = $this->items(); - $items->shouldHaveCount(2); - - $this->item($item1->key())->quantity()->shouldBe(2); - $this->item($item2->key())->quantity()->shouldBe(3); - } -} - -?> ------ -cart = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Cart(Uuid::uuid1()); - } - - public function testInitializable() - { - $this->assertInstanceOf(Cart::class, $this->cart); - } - - public function testAddNewItem() - { - $id = Uuid::uuid1(); - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->cart->add(new \Ecommerce\Cart\Item($id, 'Lorem ipsum', 4, new Price(671, $taxRate, $currency))); - - $item = $this->cart->item($id); - - $this->assertSame($id, $item->id()); - $this->assertSame(4, $item->quantity()); - } - - public function testProvideItemByItsIdentifier() - { - $id = Uuid::uuid1(); - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->cart->add(new \Ecommerce\Cart\Item($id, 'Lorem ipsum', 3, new Price(671, $taxRate, $currency))); - - $this->assertInstanceOf(\Ecommerce\Cart\Contract\Item::class, $this->cart->item($id)); - } - - public function testThrowAnExceptionWhenItemIsNotInCart() - { - $id = Uuid::uuid1(); - - $this->expectException(NotFoundException::class); - $this->cart->item($id); - } - - public function testSummUpQuantitiesWhenAddingSameItemMultipleTimes() - { - PriceType::set(PriceType::WITH_VAT); - - $id = Uuid::uuid1(); - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->cart->add(new \Ecommerce\Cart\Item($id, 'Lorem ipsum', 3, new Price(671, $taxRate, $currency))); - $this->cart->add(new \Ecommerce\Cart\Item($id, 'Dolor sit amet', 7, new Price(671, $taxRate, $currency))); - - $item = $this->cart->item($id); - - $this->assertSame(10, $item->quantity()); - } - - public function testRemoveItem() - { - PriceType::set(PriceType::WITH_VAT); - - $id = Uuid::uuid1(); - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->cart->add(new \Ecommerce\Cart\Item($id, 'Lorem ipsum', 3, new Price(671, $taxRate, $currency))); - $this->cart->remove($id); - - $this->expectException(NotFoundException::class); - $this->cart->item($id); - } - - public function testCalculateZeroPriceWhenCartIsEmpty() - { - $price = $this->cart->price(); - - $this->assertInstanceOf(CalculatedPrice::class, $price); - $this->assertSame(0.0, $price->withVat()); - $this->assertSame(0.0, $price->withoutVat()); - $this->assertSame(0.0, $price->vat()); - } - - public function testCalculateTotalPriceOfAllItemsWhenNotEmpty() - { - PriceType::set(PriceType::WITH_VAT); - - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $this->cart->add(new \Ecommerce\Cart\Item(Uuid::uuid1(), 'Lorem ipsum', 3, new Price(671, $taxRate, $currency))); - - $price = $this->cart->price(); - - $this->assertInstanceOf(CalculatedPrice::class, $price); - - $this->assertSame(2013.00, $price->withVat()); - $this->assertSame(1663.54, $price->withoutVat()); - $this->assertSame(349.46, $price->vat()); - } - - public function testContainOldItemsWhenMergedWithEmptyCart() - { - PriceType::set(PriceType::WITH_VAT); - - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $cart = new Cart(Uuid::uuid1()); - - $item1 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f49c-af76-11e8-8981-529269fb1459'), 'Lorem ipsum', 2, new Price(671, $taxRate, $currency)); - $item2 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f80c-af76-11e8-8981-529269fb1459'), 'Dolor sit amet', 3, new Price(475, $taxRate, $currency)); - - $this->cart->add($item1); - $this->cart->add($item2); - - $this->cart->merge($cart); - - $items = $this->cart->items(); - $this->assertCount(2, $items); - - $this->assertSame(2, $this->cart->item($item1->key())->quantity()); - $this->assertSame(3, $this->cart->item($item2->key())->quantity()); - } - - public function testContainOnlyItemsFromSecondCartWhenMergedWithNotEmptyCart() - { - PriceType::set(PriceType::WITH_VAT); - - $taxRate = EntityFactory::make('tax_rate_21'); - $currency = EntityFactory::make('currency_czk'); - - $cart = new Cart(Uuid::uuid1()); - - $item1 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f49c-af76-11e8-8981-529269fb1459'), 'Lorem ipsum', 2, new Price(671, $taxRate, $currency)); - $item2 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f80c-af76-11e8-8981-529269fb1459'), 'Dolor sit amet', 3, new Price(475, $taxRate, $currency)); - $item3 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9f49c-af76-11e8-8981-529269fb1459'), 'Lorem ipsum', 4, new Price(671, $taxRate, $currency)); - $item4 = new \Ecommerce\Cart\Item(Uuid::fromString('3cf9fa0a-af76-11e8-8981-529269fb1459'), 'Lorem ipsum', 5, new Price(671, $taxRate, $currency)); - - $cart->add($item1); - $cart->add($item2); - - $this->cart->add($item3); - $this->cart->add($item4); - - $this->cart->merge($cart); - - $items = $this->cart->items(); - $this->assertCount(2, $items); - - $this->assertSame(2, $this->cart->item($item1->key())->quantity()); - $this->assertSame(3, $this->cart->item($item2->key())->quantity()); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/get_wrapped_object.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/get_wrapped_object.php.inc deleted file mode 100644 index 1ba4a5205bf..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/get_wrapped_object.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -getWrappedObject(); - } -} - -?> ------ -blabla = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Blabla(); - } - public function testMe() - { - /** @var SomeType|\PHPUnit\Framework\MockObject\MockObject $someType */ - $someType = $this->createMock(SomeType::class); - $assignMe = $someType; - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/issue_3931.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/issue_3931.php.inc deleted file mode 100644 index 9ae14ac5698..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/issue_3931.php.inc +++ /dev/null @@ -1,103 +0,0 @@ -shouldHaveType(PageLinksGenerator::class); - } - - public function let(Router $router) - { - $this->beConstructedWith($router); - } - - public function it_should_return_only_self_link_when_no_results(Router $router) - { - $paginator = new Paginator( - 10, - 0, - 1, - 0, - 'route_name' - ); - - $router->generate($paginator->getRouteName(), [], UrlGeneratorInterface::ABSOLUTE_URL) - ->shouldBeCalled()->willReturn('http://api.foo.loc/bar/1.0.0/'); - - $expectations = [ - 'self' => new Link('http://api.foo.loc/bar/1.0.0/'), - 'first' => null, - 'prev' => null, - 'next' => null, - 'last' => null, - ]; - - $this->generateLinks($paginator)->shouldBeLike($expectations); - } -} -?> ------ -assertInstanceOf(PageLinksGenerator::class, $this->pageLinksGenerator); - } - - protected function setUp() - { - $this->router = $this->createMock(Router::class); - $this->pageLinksGenerator = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\PageLinksGenerator($this->router); - } - - public function testReturnOnlySelfLinkWhenNoResults() - { - $paginator = new Paginator( - 10, - 0, - 1, - 0, - 'route_name' - ); - $this->router->expects($this->atLeastOnce()) - ->method('generate')->with($this->equalTo($paginator->getRouteName()))->willReturn('http://api.foo.loc/bar/1.0.0/'); - $expectations = [ - 'self' => new Link('http://api.foo.loc/bar/1.0.0/'), - 'first' => null, - 'prev' => null, - 'next' => null, - 'last' => null, - ]; - $this->pageLinksGenerator->generateLinks($paginator)->shouldBeLike($expectations); - } -} -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/keep_method.php b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/keep_method.php deleted file mode 100644 index b79a89e02c7..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/keep_method.php +++ /dev/null @@ -1,10 +0,0 @@ -code = 'CZK'; - - $this->beConstructedThrough('create', [$data]); - } - - public function it_should_not_be_constructed_without_code(CurrencyData $data) - { - $data->code = ''; - - $this->beConstructedThrough('create', [$data]); - $this->shouldThrow(ValidationException::class)->duringInstantiation(); - } -} - -?> ------ -data = $this->createMock(CurrencyData::class); - $this->data->code = 'CZK'; - $this->currency = \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Currency::create($this->data); - } - - public function testNotBeConstructedWithoutCode() - { - $this->data->code = ''; - $this->expectException(ValidationException::class); - $this->currency = \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Currency::create($this->data); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/method.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/method.php.inc deleted file mode 100644 index d8cf4c4d10d..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/method.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -beConstructedWith(5); - - $result = $this->getSomeNumbers(); - } - - private function getSomeNumbers() - { - return [1, 2, 3]; - } -} - -?> ------ -keepMethod = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\KeepMethod(5); - - $result = $this->getSomeNumbers(); - } - - private function getSomeNumbers() - { - return [1, 2, 3]; - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_method_call_arguments.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_method_call_arguments.php.inc deleted file mode 100644 index 8c2aae1d283..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_method_call_arguments.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -createShippingMethodFor(5) - ->shouldBeCalled() - ->willReturn($shippingMethod); - } -} - -?> ------ -createMock(DeliveryFactory::class); - /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ - $shippingMethod = $this->createMock(ShippingMethod::class); - $factory->expects($this->atLeastOnce()) - ->method('createShippingMethodFor')->with($this->equalTo(5)) - ->willReturn($shippingMethod); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_properties.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_properties.php.inc deleted file mode 100644 index 49aacda26bd..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_properties.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -beConstructedWith($factory); - } - - public function let_it_go(OrderFactory $factory) - { - $factory->someMethod()->shouldBeCalled(); - $this->run(); - } -} - -?> ------ -factory = $this->createMock(OrderFactory::class); - $this->mockProperties = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\MockProperties($this->factory); - } - - public function testLetItGo() - { - $this->factory->expects($this->atLeastOnce())->method('someMethod'); - $this->mockProperties->run(); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_properties_non_local.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_properties_non_local.php.inc deleted file mode 100644 index 8aa55dbb11a..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mock_properties_non_local.php.inc +++ /dev/null @@ -1,62 +0,0 @@ -beConstructedWith($factory); - } - - public function let_it_go(AnotherMock $anotherMock) - { - $anotherMock->setName('Nummy'); - $this->addAnotherMock($anotherMock); - } - - public function let_it_go_again(AnotherMock $anotherMock) - { - $anotherMock->setName('Nummy2'); - $this->addAnotherMock($anotherMock); - } -} - -?> ------ -createMock(OrderFactory::class); - $this->mockPropertiesNonLocal = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\MockPropertiesNonLocal($factory); - } - - public function testLetItGo() - { - /** @var AnotherMock|\PHPUnit\Framework\MockObject\MockObject $anotherMock */ - $anotherMock = $this->createMock(AnotherMock::class); - $anotherMock->setName('Nummy'); - $this->mockPropertiesNonLocal->addAnotherMock($anotherMock); - } - - public function testLetItGoAgain() - { - /** @var AnotherMock|\PHPUnit\Framework\MockObject\MockObject $anotherMock */ - $anotherMock = $this->createMock(AnotherMock::class); - $anotherMock->setName('Nummy2'); - $this->mockPropertiesNonLocal->addAnotherMock($anotherMock); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mocking.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mocking.php.inc deleted file mode 100644 index 9dbb113f671..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/mocking.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -createShippingMethodFor(Argument::any())->shouldBeCalled()->willReturn($shippingMethod); - $factory->anotherMethod(25)->shouldBeCalled()->willReturn($shippingMethod); - } -} - -?> ------ -createMock(OrderFactory::class); - /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ - $shippingMethod = $this->createMock(ShippingMethod::class); - $factory->expects($this->atLeastOnce())->method('createShippingMethodFor')->willReturn($shippingMethod); - $factory->expects($this->atLeastOnce())->method('anotherMethod')->with($this->equalTo(25))->willReturn($shippingMethod); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/no_self_type_test.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/no_self_type_test.php.inc deleted file mode 100644 index 0cf6b3c7279..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/no_self_type_test.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -shouldHaveType(ItIsMe::class); - } -} - -?> ------ -itIsMe = new \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\ItIsMe(); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/setup_private_ctor.php.inc b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/setup_private_ctor.php.inc deleted file mode 100644 index e13f672a90d..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Fixture/setup_private_ctor.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -beConstructedThrough('success'); - $this->shouldHaveType(Result::class); - } - - public function it_should_succeed() - { - $this->beConstructedThrough('fail'); - $this->hasFailed()->shouldReturn(false); - } -} - -?> ------ -result = \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Result::success(); - $this->assertInstanceOf(Result::class, $this->result); - } - - public function testSucceed() - { - $this->result = \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\Fixture\Result::fail(); - $this->assertFalse($this->result->hasFailed()); - } -} - -?> diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/PhpSpecToPHPUnitRectorTest.php b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/PhpSpecToPHPUnitRectorTest.php deleted file mode 100644 index 9f03f4a6165..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/PhpSpecToPHPUnitRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Source/OrderFactory.php b/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Source/OrderFactory.php deleted file mode 100644 index cfd33202a6f..00000000000 --- a/rules-tests/PhpSpecToPHPUnit/Rector/Variable/PhpSpecToPHPUnitRector/Source/OrderFactory.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(PhpSpecMocksToPHPUnitMocksRector::class); - $services->set(PhpSpecPromisesToPHPUnitAssertRector::class); - $services->set(PhpSpecMethodToPHPUnitMethodRector::class); - $services->set(PhpSpecClassToPHPUnitClassRector::class); - $services->set(AddMockPropertiesRector::class); - $services->set(MockVariableToPropertyFetchRector::class); -}; diff --git a/rules-tests/Privatization/NodeManipulator/VisibilityManipulatorTest.php b/rules-tests/Privatization/NodeManipulator/VisibilityManipulatorTest.php new file mode 100644 index 00000000000..7dbe3df34d1 --- /dev/null +++ b/rules-tests/Privatization/NodeManipulator/VisibilityManipulatorTest.php @@ -0,0 +1,24 @@ +make(VisibilityManipulator::class); + + $classMethod = new ClassMethod('SomeClass'); + $classMethod->flags = Visibility::PUBLIC | Visibility::STATIC; + + $visibilityManipulator->changeNodeVisibility($classMethod, Visibility::PROTECTED); + $this->assertSame(Visibility::PROTECTED | Visibility::STATIC, $classMethod->flags); + } +} diff --git a/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/change_parent_without_care.php.inc b/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/change_parent_without_care.php.inc new file mode 100644 index 00000000000..38dabfa068c --- /dev/null +++ b/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/change_parent_without_care.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/fixture.php.inc b/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..0e17b2dcd98 --- /dev/null +++ b/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/fixture.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/keep_parent_protected_const.php.inc b/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/keep_parent_protected_const.php.inc new file mode 100644 index 00000000000..1105d0adb9d --- /dev/null +++ b/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Fixture/keep_parent_protected_const.php.inc @@ -0,0 +1,10 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Source/AbstractClassWithProtectedClassConst.php b/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Source/AbstractClassWithProtectedClassConst.php new file mode 100644 index 00000000000..a8b52f50858 --- /dev/null +++ b/rules-tests/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector/Source/AbstractClassWithProtectedClassConst.php @@ -0,0 +1,10 @@ +withRules([PrivatizeFinalClassConstantRector::class]); diff --git a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/ChangeGlobalVariablesToPropertiesRectorTest.php b/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/ChangeGlobalVariablesToPropertiesRectorTest.php deleted file mode 100644 index f33fd81d1bd..00000000000 --- a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/ChangeGlobalVariablesToPropertiesRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/fixture.php.inc b/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/fixture.php.inc deleted file mode 100644 index 79dccafc222..00000000000 --- a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ -variable = 5; - } - - public function run() - { - var_dump($this->variable); - } -} - -?> diff --git a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/global_after.php.inc b/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/global_after.php.inc deleted file mode 100644 index 69b27e27cdf..00000000000 --- a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/global_after.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - ------ -variable = 5; - return $this->variable; - } - - public function run() - { - var_dump($this->variable); - } -} - -?> diff --git a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/skip_read_only_global.php.inc b/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/skip_read_only_global.php.inc deleted file mode 100644 index f23ca2486f5..00000000000 --- a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/Fixture/skip_read_only_global.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/config/configured_rule.php b/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/config/configured_rule.php deleted file mode 100644 index 0222c0d4814..00000000000 --- a/rules-tests/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ChangeGlobalVariablesToPropertiesRector::class); -}; diff --git a/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Fixture/keep_magic_parent_called_method.php.inc b/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Fixture/keep_magic_parent_called_method.php.inc new file mode 100644 index 00000000000..03414a5a4cf --- /dev/null +++ b/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Fixture/keep_magic_parent_called_method.php.inc @@ -0,0 +1,14 @@ +{'p'} method + protected function pFileNode(FileNode $fileNode) + { + } +} diff --git a/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Fixture/skip_construct.php.inc b/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Fixture/skip_construct.php.inc new file mode 100644 index 00000000000..49fbae0fa8b --- /dev/null +++ b/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Fixture/skip_construct.php.inc @@ -0,0 +1,18 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Source/SomeParentClass.php b/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Source/SomeParentClass.php new file mode 100644 index 00000000000..7b04a0142d4 --- /dev/null +++ b/rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Source/SomeParentClass.php @@ -0,0 +1,8 @@ +services(); - $services->set(PrivatizeFinalClassMethodRector::class); -}; +return RectorConfig::configure() + ->withRules([PrivatizeFinalClassMethodRector::class]); diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/ChangeLocalPropertyToVariableRectorTest.php b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/ChangeLocalPropertyToVariableRectorTest.php deleted file mode 100644 index 7a507df3b20..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/ChangeLocalPropertyToVariableRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/fixture.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/fixture.php.inc deleted file mode 100644 index cce39f36f24..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -count = 5; - return $this->count; - } -} - -?> ------ - diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/no_assign_in_construct.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/no_assign_in_construct.php.inc deleted file mode 100644 index b1d0ac83f20..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/no_assign_in_construct.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -name = $aName; - return "Hello {$this->name}"; - } -} - -?> ------ - diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_construct_assign.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_construct_assign.php.inc deleted file mode 100644 index bcc69ec62bd..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_construct_assign.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -someService = $someService; - } - - public function run() - { - if ($this->someService->whatever()) { - return true; - } - - return false; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_construct_assign_outside_condition.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_construct_assign_outside_condition.php.inc deleted file mode 100644 index e609489e725..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_construct_assign_outside_condition.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -someService = $someService; - } - - public function run() - { - return $this->someService->whatever(); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use.php.inc deleted file mode 100644 index 74a2548a0d2..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -count = 5; - return $this->count; - } - - public function go() - { - $this->count = 5; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use_in_one_method.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use_in_one_method.php.inc deleted file mode 100644 index 431782e2436..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use_in_one_method.php.inc +++ /dev/null @@ -1,22 +0,0 @@ -run() return 5 - // second ->run() return 10 - public function run() - { - if ($this->count === 5) { - $this->count = 10; - return $this->count; - } - - $this->count = 5; - - return $this->count; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use_in_one_method_without_change.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use_in_one_method_without_change.php.inc deleted file mode 100644 index ff700dc0cab..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_double_use_in_one_method_without_change.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -run() return 5 - // second ->run() return 10 - public function run() - { - if ($this->count === 5) { - return 10; - } - - $this->count = 5; - - return $this->count; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_never_assigned.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_never_assigned.php.inc deleted file mode 100644 index fc5685221f9..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_never_assigned.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -name}"; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_no_assign_in_construct_has_default_value.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_no_assign_in_construct_has_default_value.php.inc deleted file mode 100644 index 91a266af7c9..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_no_assign_in_construct_has_default_value.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -name}"; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_property_promotion.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_property_promotion.php.inc deleted file mode 100644 index ee4591f1866..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_property_promotion.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -name}"; - } -} - -?> diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_public.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_public.php.inc deleted file mode 100644 index 7712c4ec378..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_public.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -count = 5; - return $this->count; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_set_once_double_used.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_set_once_double_used.php.inc deleted file mode 100644 index fa8da969da5..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Fixture/skip_set_once_double_used.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -nodeReplacementIsRestricted = false; - - $joinedNode = $this->joinConcatIfStrings($node); - if ($this->nodeReplacementIsRestricted) { - return null; - } - } - - /** - * @return Concat|String_ - */ - private function joinConcatIfStrings(Concat $node): Expr - { - if ($node) { - $this->nodeReplacementIsRestricted = true; - return $node; - } - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Source/SomeService.php b/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Source/SomeService.php deleted file mode 100644 index b8bae0709d7..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector/Source/SomeService.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(ChangeLocalPropertyToVariableRector::class); -}; diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/ChangeReadOnlyVariableWithDefaultValueToConstantRectorTest.php b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/ChangeReadOnlyVariableWithDefaultValueToConstantRectorTest.php deleted file mode 100644 index f44200eae13..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/ChangeReadOnlyVariableWithDefaultValueToConstantRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/add_missing_const.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/add_missing_const.php.inc deleted file mode 100644 index 5662044fb01..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/add_missing_const.php.inc +++ /dev/null @@ -1,85 +0,0 @@ -"authors": \[\s+)(?.*?)(?\s+\](,))#ms'; - - return Strings::replace($jsonContent, $pattern, function (array $match): string { - $inlined = Strings::replace($match['content'], '#\s+#', ' '); - $inlined = trim($inlined); - $inlined = Strings::replace($inlined, '#},#', "},\n "); - - return $match['start'] . $inlined . $match['end']; - }); - } -} - -?> ------ -"authors": \[\s+)(?.*?)(?\s+\](,))#ms'; - /** - * @param string[] $sections - */ - public function inlineSections(string $jsonContent, array $sections): string - { - foreach ($sections as $section) { - $pattern = '#("' . preg_quote($section, '#') . '": )\[(.*?)\](,)#ms'; - $jsonContent = Strings::replace($jsonContent, $pattern, function (array $match): string { - $inlined = Strings::replace($match[2], '#\s+#', ' '); - $inlined = trim($inlined); - $inlined = '[' . $inlined . ']'; - return $match[1] . $inlined . $match[3]; - }); - } - - return $jsonContent; - } - - public function inlineAuthors(string $jsonContent): string - { - return Strings::replace($jsonContent, self::PATTERN, function (array $match): string { - $inlined = Strings::replace($match['content'], '#\s+#', ' '); - $inlined = trim($inlined); - $inlined = Strings::replace($inlined, '#},#', "},\n "); - - return $match['start'] . $inlined . $match['end']; - }); - } -} - -?> diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/fixture.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/fixture.php.inc deleted file mode 100644 index d08bc8e2385..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - 'expectNotice', - 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', - ]; - - foreach ($replacements as $class => $method) { - } - } -} - -?> ------ - - */ - private const REPLACEMENTS = [ - 'PHPUnit\Framework\TestCase\Notice' => 'expectNotice', - 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', - ]; - public function run() - { - foreach (self::REPLACEMENTS as $class => $method) { - } - } -} - -?> diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/multiple_usage.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/multiple_usage.php.inc deleted file mode 100644 index a741bf29bc4..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/multiple_usage.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - 'expectNotice', - 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', - ]; - - foreach ($replacements as $class => $method) { - } - - foreach ($replacements as $class => $method) { - } - } -} - -?> ------ - - */ - private const REPLACEMENTS = [ - 'PHPUnit\Framework\TestCase\Notice' => 'expectNotice', - 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', - ]; - public function run() - { - foreach (self::REPLACEMENTS as $class => $method) { - } - - foreach (self::REPLACEMENTS as $class => $method) { - } - } -} - -?> diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/replace_in_args.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/replace_in_args.php.inc deleted file mode 100644 index dd2ae2c7581..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/replace_in_args.php.inc +++ /dev/null @@ -1,59 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/scalar.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/scalar.php.inc deleted file mode 100644 index c1393291d75..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/scalar.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_array_assign.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_array_assign.php.inc deleted file mode 100644 index 8a20f1f873d..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_array_assign.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -isName($label, '__')) { - $labelFilters[] = 'trans'; - $label = $label->args[0]->value; - } - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_array_push.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_array_push.php.inc deleted file mode 100644 index a2cb66d43ee..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_array_push.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -renamedClassesCollector->getOldToNewClassesSortedByHighestParentsAsString(); - - $data = [ - 'services' => [ - RenameClassRector::class => [ - RenameClassRector::OLD_TO_NEW_CLASSES => $oldToNewClasses, - ], - ], - ]; - - return Yaml::dump($data, 10); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_assign_equals.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_assign_equals.php.inc deleted file mode 100644 index b68ca543951..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_assign_equals.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -getTag(); - } - - $item .= (string)$tagValueNode; - - $itemsAsStrings[] = $item; - } - - return implode(', ', $itemsAsStrings); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_by_ref_param.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_by_ref_param.php.inc deleted file mode 100644 index 0024e1c5240..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_by_ref_param.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -byRef($toInsert); - } - - public function byRef(array & $data) - { - } -} - -?> diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_double_assign.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_double_assign.php.inc deleted file mode 100644 index 56f1017e6ac..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_double_assign.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -testSuite->rootPath}/tests"; - - return $testsBaseDir; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_nested.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_nested.php.inc deleted file mode 100644 index b838f79e4dc..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_nested.php.inc +++ /dev/null @@ -1,49 +0,0 @@ -currentTokenType() === Lexer::TOKEN_OPEN_PARENTHESES) { - ++$unclosedOpenedBracketCount; - } - - if ($tokenIterator->currentTokenType() === Lexer::TOKEN_CLOSE_PARENTHESES) { - --$unclosedOpenedBracketCount; - } - - if ($unclosedOpenedBracketCount === 0 && $tokenIterator->currentTokenType() === Lexer::TOKEN_PHPDOC_EOL) { - break; - } - - // remove new line "*" - if (Strings::contains($tokenIterator->currentTokenValue(), '*')) { - $tokenValueWithoutAsterisk = Strings::replace($tokenIterator->currentTokenValue(), '#\*#', ''); - $annotationContent .= $tokenValueWithoutAsterisk; - } else { - $annotationContent .= $tokenIterator->currentTokenValue(); - } - - // this is the end of single-line comment - if ($tokenIterator->currentTokenType() === Lexer::TOKEN_END) { - break; - } - - $tokenIterator->next(); - } - - return $this->cleanMultilineAnnotationContent($annotationContent); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_overriden.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_overriden.php.inc deleted file mode 100644 index 07c93fcd1c1..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_overriden.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - 'expectNotice', - 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', - ]; - - foreach ($replacements as $class => $method) { - } - - $replacements = []; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_reference_direct.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_reference_direct.php.inc deleted file mode 100644 index dfaf6bede0b..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_reference_direct.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable($class->stmts, function (Node $node) use ( - &$formerVariablesByMethods, - $skipSetUpMethod - ): ?PropertyFetch { - $this->processAssign($node, $parentNode, $type, $formerVariablesByMethods); - }); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_reference_in_service_configurator.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_reference_in_service_configurator.php.inc deleted file mode 100644 index 3680fec11b2..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_reference_in_service_configurator.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -createContainerBuilder(); - $phpFileLoader = $this->createPhpFileLoader($containerBuilder); - - $instanceOf = []; - return new ServicesConfigurator($containerBuilder, $phpFileLoader, $instanceOf); - } - - private function createPhpFileLoader(ContainerBuilder $containerBuilder): PhpFileLoader - { - return new PhpFileLoader($containerBuilder, new FileLocator()); - } - - private function createContainerBuilder(): ContainerBuilder - { - return new ContainerBuilder(); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_referenced.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_referenced.php.inc deleted file mode 100644 index ffadbc346e1..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_referenced.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -process($value); - } - - private function process(array &$value) - { - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_referenced_in_constructor.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_referenced_in_constructor.php.inc deleted file mode 100644 index dafe37dccaa..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_referenced_in_constructor.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -filesFinder->findInDirectoriesAndFiles([__DIR__ . '/FilesFinderSource'], ['yaml', 'yml']); - $this->assertCount(2, $foundFiles); - - $foundFileNames = []; - foreach ($foundFiles as $foundFile) { - $foundFileNames[] = $foundFile->getFilename(); - } - - $expectedFoundFileNames = ['some_config.yml', 'other_config.yaml']; - - sort($foundFileNames); - sort($expectedFoundFileNames); - $this->assertSame($expectedFoundFileNames, $foundFileNames); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_test_variable_test.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_test_variable_test.php.inc deleted file mode 100644 index 032b57f33ef..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_test_variable_test.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -assertSame($expectedValue, $result); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_trait.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_trait.php.inc deleted file mode 100644 index 2516421485d..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_trait.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - 'expectNotice', - 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', - ]; - - foreach ($replacements as $class => $method) { - } - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_unpack_array.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_unpack_array.php.inc deleted file mode 100644 index 585af880e9a..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_unpack_array.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -addImportToNamespaceIfMissing($node, $import, $alias); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_unpack_list.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_unpack_list.php.inc deleted file mode 100644 index 7110ef094e3..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_unpack_list.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -addImportToNamespaceIfMissing($node, $import, $alias); - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_used_as_parameter.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_used_as_parameter.php.inc deleted file mode 100644 index b409fd9796d..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_used_as_parameter.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - $value) { - if ($value === null) { - $value = []; - } - - $this->runSomething($value); - } - } - - private function runSomething(?array $value) - { - } -} diff --git a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_variable_assign.php.inc b/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_variable_assign.php.inc deleted file mode 100644 index 1c10941dc85..00000000000 --- a/rules-tests/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector/Fixture/skip_variable_assign.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(ChangeReadOnlyVariableWithDefaultValueToConstantRector::class); -}; diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/FinalizeClassesWithoutChildrenRectorTest.php b/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/FinalizeClassesWithoutChildrenRectorTest.php deleted file mode 100644 index bb2a7d61354..00000000000 --- a/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/FinalizeClassesWithoutChildrenRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/Fixture/fixture.php.inc b/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/Fixture/fixture.php.inc deleted file mode 100644 index 77b1e8ca30c..00000000000 --- a/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/Fixture/skip_embedable.php.inc b/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/Fixture/skip_embedable.php.inc deleted file mode 100644 index 57d00cd2305..00000000000 --- a/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/Fixture/skip_embedable.php.inc +++ /dev/null @@ -1,12 +0,0 @@ -expense = $expense; - $this->tagId = $tagId; - } - - public function tagId(): Snowflake - { - return $this->tagId; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/config/configured_rule.php b/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/config/configured_rule.php deleted file mode 100644 index 290907417b1..00000000000 --- a/rules-tests/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(FinalizeClassesWithoutChildrenRector::class); -}; diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/FinalizeTestCaseClassRectorTest.php b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/FinalizeTestCaseClassRectorTest.php new file mode 100644 index 00000000000..7ffffd48f94 --- /dev/null +++ b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/FinalizeTestCaseClassRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/skip_suffix_test_case.php.inc b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/skip_suffix_test_case.php.inc new file mode 100644 index 00000000000..3c39be3b2ed --- /dev/null +++ b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/skip_suffix_test_case.php.inc @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/some_class.php.inc b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..957997bb7dc --- /dev/null +++ b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/some_class.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/with_attribute_new_line.php.inc b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/with_attribute_new_line.php.inc new file mode 100644 index 00000000000..d44c6e64f13 --- /dev/null +++ b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/Fixture/with_attribute_new_line.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/config/configured_rule.php b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/config/configured_rule.php new file mode 100644 index 00000000000..a57ddffe853 --- /dev/null +++ b/rules-tests/Privatization/Rector/Class_/FinalizeTestCaseClassRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([FinalizeTestCaseClassRector::class]); diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/beginning_with_number.php.inc b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/beginning_with_number.php.inc deleted file mode 100644 index f8d502b0152..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/beginning_with_number.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/camel_case.php.inc b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/camel_case.php.inc deleted file mode 100644 index cf5a288f8df..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/camel_case.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/dash_to_underscore.php.inc b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/dash_to_underscore.php.inc deleted file mode 100644 index eeefcf2cf35..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/dash_to_underscore.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/fixture.php.inc b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/fixture.php.inc deleted file mode 100644 index 1f68ad02a5d..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/skip_keywords.php.inc b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/skip_keywords.php.inc deleted file mode 100644 index f811be0796c..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/skip_keywords.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - 'string', - 'seocnd' => 'string', - 'thirdValue' => 'string', - ]; -} diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/skip_regex_patterns.php.inc b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/skip_regex_patterns.php.inc deleted file mode 100644 index 02a28ed8243..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/skip_regex_patterns.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/with_non_local_constant.php.inc b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/with_non_local_constant.php.inc deleted file mode 100644 index 97d6e836e5e..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/with_non_local_constant.php.inc +++ /dev/null @@ -1,51 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/with_slash.php.inc b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/with_slash.php.inc deleted file mode 100644 index 35e01072b56..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/Fixture/with_slash.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/RepeatedLiteralToClassConstantRectorTest.php b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/RepeatedLiteralToClassConstantRectorTest.php deleted file mode 100644 index 4e3bcff7e96..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/RepeatedLiteralToClassConstantRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/config/configured_rule.php b/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/config/configured_rule.php deleted file mode 100644 index 5062b374d3b..00000000000 --- a/rules-tests/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RepeatedLiteralToClassConstantRector::class); -}; diff --git a/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_differnt_object_getter.php.inc b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_different_object_getter.php.inc similarity index 100% rename from rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_differnt_object_getter.php.inc rename to rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_different_object_getter.php.inc diff --git a/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..c7f0f67679e --- /dev/null +++ b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,18 @@ +some(...); + } + + private function some() + { + return $this->some; + } +} diff --git a/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_non_final_class_call_non_private_method.php.inc b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_non_final_class_call_non_private_method.php.inc new file mode 100644 index 00000000000..7f57ed09005 --- /dev/null +++ b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_non_final_class_call_non_private_method.php.inc @@ -0,0 +1,19 @@ +getSome() + 5; + } + + // public can be overridden by child + public function getSome() + { + return $this->some; + } +} diff --git a/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_with_parameter.php.inc b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_with_parameter.php.inc new file mode 100644 index 00000000000..139b47041f8 --- /dev/null +++ b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/Fixture/skip_with_parameter.php.inc @@ -0,0 +1,19 @@ +getSome($var); + } + + private function getSome(string $parameter) + { + return (new SkipWithParameter())->$parameter; + } +} \ No newline at end of file diff --git a/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/PrivatizeLocalGetterToPropertyRectorTest.php b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/PrivatizeLocalGetterToPropertyRectorTest.php index cc8177c0f58..2d8c718208c 100644 --- a/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/PrivatizeLocalGetterToPropertyRectorTest.php +++ b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/PrivatizeLocalGetterToPropertyRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Privatization\Rector\MethodCall\PrivatizeLocalGetterToPropertyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class PrivatizeLocalGetterToPropertyRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/config/configured_rule.php b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/config/configured_rule.php index fd839be153a..33e54a6b734 100644 --- a/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/config/configured_rule.php +++ b/rules-tests/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Privatization\Rector\MethodCall\PrivatizeLocalGetterToPropertyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(PrivatizeLocalGetterToPropertyRector::class); -}; +return RectorConfig::configure() + ->withRules([PrivatizeLocalGetterToPropertyRector::class]); diff --git a/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Fixture/replace_with_constant.php.inc b/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Fixture/replace_with_constant.php.inc deleted file mode 100644 index 75941c5144b..00000000000 --- a/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Fixture/replace_with_constant.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -call('name'); - } - - public function call(string $value) - { - } -} - -?> ------ -call(\Rector\Tests\Privatization\Rector\MethodCall\ReplaceStringWithClassConstantRector\Source\Placeholder::NAME); - } - - public function call(string $value) - { - } -} - -?> diff --git a/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Fixture/skip_different_position.php.inc b/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Fixture/skip_different_position.php.inc deleted file mode 100644 index 32fff192ba8..00000000000 --- a/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Fixture/skip_different_position.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -call(1000, 'name'); - } -} diff --git a/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/ReplaceStringWithClassConstantRectorTest.php b/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/ReplaceStringWithClassConstantRectorTest.php deleted file mode 100644 index b7e9e1a4718..00000000000 --- a/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/ReplaceStringWithClassConstantRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_config.php'; - } -} diff --git a/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Source/Placeholder.php b/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Source/Placeholder.php deleted file mode 100644 index 2b5f0dd4857..00000000000 --- a/rules-tests/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector/Source/Placeholder.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(ReplaceStringWithClassConstantRector::class) - ->call('configure', [[ - ReplaceStringWithClassConstantRector::REPLACE_STRING_WITH_CLASS_CONSTANT => ValueObjectInliner::inline([ - new ReplaceStringWithClassConstant( - 'Rector\Tests\Privatization\Rector\MethodCall\ReplaceStringWithClassConstantRector\Fixture\ReplaceWithConstant', - 'call', - 0, - Placeholder::class - ), - ]), - ]]); -}; diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/ChangeReadOnlyPropertyWithDefaultValueToConstantRectorTest.php b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/ChangeReadOnlyPropertyWithDefaultValueToConstantRectorTest.php deleted file mode 100644 index 6e4234a89ea..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/ChangeReadOnlyPropertyWithDefaultValueToConstantRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/fixture.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/fixture.php.inc deleted file mode 100644 index 85811bc48de..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -magicMethods as $magicMethod) { - echo $magicMethod; - } - } -} - -?> ------ - diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_assigned_in_ctor.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_assigned_in_ctor.php.inc deleted file mode 100644 index 9a0f5bb7409..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_assigned_in_ctor.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -isAllowed = (bool) Strings::match($originalContent, '#\d+#'); - } - - public function go() - { - return $this->isAllowed; - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_end_function.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_end_function.php.inc deleted file mode 100644 index a1618a6112a..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_end_function.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -values); - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_multiple_properties.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_multiple_properties.php.inc deleted file mode 100644 index a68e4722315..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_multiple_properties.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -magicMethods as $magicMethod) { - echo $magicMethod; - } - - $this->magicMethods = 123; - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced.php.inc deleted file mode 100644 index d6f2cdde140..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -process($this->value); - } - - private function process(array &$value) - { - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced2.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced2.php.inc deleted file mode 100644 index c4160064281..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced2.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -process($this->value); - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_method_call_with_another_class.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_method_call_with_another_class.php.inc deleted file mode 100644 index 3b2d90bfe02..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_method_call_with_another_class.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -process($this->value); - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_static_call.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_static_call.php.inc deleted file mode 100644 index 5f995da5647..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_static_call.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -value); - } - - private static function process(array &$value) - { - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_static_call_with_another_class.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_static_call_with_another_class.php.inc deleted file mode 100644 index 5955ecfcdb9..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_referenced_in_static_call_with_another_class.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -value); - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_sniff.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_sniff.php.inc deleted file mode 100644 index f0fa353129c..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_sniff.php.inc +++ /dev/null @@ -1,32 +0,0 @@ -magicMethods as $magicMethod) { - echo $magicMethod; - } - } - - public function register() - { - // TODO: Implement register() method. - } - - public function process(File $phpcsFile, $stackPtr) - { - // TODO: Implement process() method. - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_trait.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_trait.php.inc deleted file mode 100644 index 9c5fd20d5f3..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/skip_trait.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -magicMethods as $magicMethod) { - echo $magicMethod; - } - } -} diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/static_property.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/static_property.php.inc deleted file mode 100644 index 7d2fa600223..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/static_property.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - ------ - diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/symfony_example.php.inc b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/symfony_example.php.inc deleted file mode 100644 index 32f3cef94d3..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Fixture/symfony_example.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ', self::$bgColor[1]); - } -} - -?> ------ - ', self::BG_COLOR[1]); - } -} - -?> diff --git a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Source/ReferencedInMethodCall.php b/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Source/ReferencedInMethodCall.php deleted file mode 100644 index 31891109418..00000000000 --- a/rules-tests/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector/Source/ReferencedInMethodCall.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(ChangeReadOnlyPropertyWithDefaultValueToConstantRector::class); -}; diff --git a/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/do_not_remove_readonly_promoted_property.php.inc b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/do_not_remove_readonly_promoted_property.php.inc new file mode 100644 index 00000000000..ef68d23e583 --- /dev/null +++ b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/do_not_remove_readonly_promoted_property.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/keep_parent_not_autoloaded.php.inc b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/keep_parent_not_autoloaded.php.inc new file mode 100644 index 00000000000..1ee3746e73a --- /dev/null +++ b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/keep_parent_not_autoloaded.php.inc @@ -0,0 +1,8 @@ + [], + ]; + + public function __construct() + { + if (isset($this->options)) { + $this->abstractOptions['options'] = $this->options; + } + } +} + +final class KeepUsedProtected extends AbstractKeepUsedProtected +{ + protected $options = [ + 'someSetting' => true + ]; +} +?> diff --git a/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/promoted_property.php.inc b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/promoted_property.php.inc new file mode 100644 index 00000000000..faa913f622c --- /dev/null +++ b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/promoted_property.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/skip_from_parent_promoted_property.php.inc b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/skip_from_parent_promoted_property.php.inc new file mode 100644 index 00000000000..1adebea5695 --- /dev/null +++ b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/skip_from_parent_promoted_property.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/skip_trait_reserved.php.inc b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/skip_trait_reserved.php.inc new file mode 100644 index 00000000000..d887bbcec88 --- /dev/null +++ b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Fixture/skip_trait_reserved.php.inc @@ -0,0 +1,12 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Source/AbstractClassWithProtectedProperty.php b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Source/AbstractClassWithProtectedProperty.php index fe622ad5b46..227fbcd386f 100644 --- a/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Source/AbstractClassWithProtectedProperty.php +++ b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Source/AbstractClassWithProtectedProperty.php @@ -10,4 +10,19 @@ abstract class AbstractClassWithProtectedProperty * @var int */ protected $value = 1000; + + public function run() + { + static::$valueStatic = 1000; + } + + public function run2() + { + self::$valueStatic2 = 1000; + } + + public function run3() + { + \Rector\Tests\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector\Fixture\KeepParentStaticProtectedUsedByParent::$valueStatic3 = 1000; + } } diff --git a/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Source/SomeTraitWithProtectedProperty.php b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Source/SomeTraitWithProtectedProperty.php new file mode 100644 index 00000000000..1062cc4f591 --- /dev/null +++ b/rules-tests/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector/Source/SomeTraitWithProtectedProperty.php @@ -0,0 +1,9 @@ +services(); - $services->set(PrivatizeFinalClassPropertyRector::class); -}; +return RectorConfig::configure() + ->withRules([PrivatizeFinalClassPropertyRector::class]); diff --git a/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/ArgumentRemoverRectorTest.php b/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/ArgumentRemoverRectorTest.php index fd81950083f..a33e78c02b0 100644 --- a/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/ArgumentRemoverRectorTest.php +++ b/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/ArgumentRemoverRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Removing\Rector\ClassMethod\ArgumentRemoverRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ArgumentRemoverRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/Fixture/fixture2.php.inc b/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/Fixture/fixture2.php.inc index c3c0627748f..8cf5d08a37b 100644 --- a/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/Fixture/fixture2.php.inc +++ b/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/Fixture/fixture2.php.inc @@ -1,49 +1,49 @@ ----- diff --git a/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/Source/Yaml.php b/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/Source/Yaml.php new file mode 100644 index 00000000000..2f52ac5f27f --- /dev/null +++ b/rules-tests/Removing/Rector/ClassMethod/ArgumentRemoverRector/Source/Yaml.php @@ -0,0 +1,12 @@ +services(); - $services->set(ArgumentRemoverRector::class) - ->call('configure', [[ - ArgumentRemoverRector::REMOVED_ARGUMENTS => ValueObjectInliner::inline([ - new ArgumentRemover(Persister::class, 'getSelectJoinColumnSQL', 4, null), new ArgumentRemover( - Yaml::class, - 'parse', - 1, - ['Symfony\Component\Yaml\Yaml::PARSE_KEYS_AS_STRINGS', 'hey', 55, 5.5] - ), - new ArgumentRemover(RemoveInTheMiddle::class, 'run', 1, [ - 'name' => 'second', - ]), +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(ArgumentRemoverRector::class, [ + new ArgumentRemover(Persister::class, 'getSelectJoinColumnSQL', 4, null), + new ArgumentRemover( + Yaml::class, + 'parse', + 1, + ['Symfony\Component\Yaml\Yaml::PARSE_KEYS_AS_STRINGS', 'hey', 55, 5.5] + ), + new ArgumentRemover(RemoveInTheMiddle::class, 'run', 1, [ + 'name' => 'second', ]), - ]]); + ]); }; diff --git a/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/Fixture/remove_from_interface.php.inc b/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/Fixture/remove_from_interface.php.inc new file mode 100644 index 00000000000..0e6d97547ba --- /dev/null +++ b/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/Fixture/remove_from_interface.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/RemoveInterfacesRectorTest.php b/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/RemoveInterfacesRectorTest.php index dfe237fd911..c73768adf9f 100644 --- a/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/RemoveInterfacesRectorTest.php +++ b/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/RemoveInterfacesRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Removing\Rector\Class_\RemoveInterfacesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveInterfacesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/config/configured_rule.php b/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/config/configured_rule.php index 05f79a743b6..3a3ffe3f3cd 100644 --- a/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/config/configured_rule.php +++ b/rules-tests/Removing/Rector/Class_/RemoveInterfacesRector/config/configured_rule.php @@ -2,14 +2,11 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Removing\Rector\Class_\RemoveInterfacesRector; use Rector\Tests\Removing\Rector\Class_\RemoveInterfacesRector\Source\SomeInterface; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveInterfacesRector::class) - ->call('configure', [[ - RemoveInterfacesRector::INTERFACES_TO_REMOVE => [SomeInterface::class], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RemoveInterfacesRector::class, [SomeInterface::class]); }; diff --git a/rules-tests/Removing/Rector/Class_/RemoveParentRector/Fixture/fixture.php.inc b/rules-tests/Removing/Rector/Class_/RemoveParentRector/Fixture/fixture.php.inc deleted file mode 100644 index 15f4186bc0f..00000000000 --- a/rules-tests/Removing/Rector/Class_/RemoveParentRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/Removing/Rector/Class_/RemoveParentRector/RemoveParentRectorTest.php b/rules-tests/Removing/Rector/Class_/RemoveParentRector/RemoveParentRectorTest.php deleted file mode 100644 index ba5e0c3fd67..00000000000 --- a/rules-tests/Removing/Rector/Class_/RemoveParentRector/RemoveParentRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Removing/Rector/Class_/RemoveParentRector/Source/ParentTypeToBeRemoved.php b/rules-tests/Removing/Rector/Class_/RemoveParentRector/Source/ParentTypeToBeRemoved.php deleted file mode 100644 index fb6f541b8b7..00000000000 --- a/rules-tests/Removing/Rector/Class_/RemoveParentRector/Source/ParentTypeToBeRemoved.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(RemoveParentRector::class) - ->call('configure', [[ - RemoveParentRector::PARENT_TYPES_TO_REMOVE => [ParentTypeToBeRemoved::class], - ]]); -}; diff --git a/rules-tests/Removing/Rector/Class_/RemoveTraitUseRector/RemoveTraitUseRectorTest.php b/rules-tests/Removing/Rector/Class_/RemoveTraitUseRector/RemoveTraitUseRectorTest.php index ef991331486..20d2d1a02ce 100644 --- a/rules-tests/Removing/Rector/Class_/RemoveTraitUseRector/RemoveTraitUseRectorTest.php +++ b/rules-tests/Removing/Rector/Class_/RemoveTraitUseRector/RemoveTraitUseRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Removing\Rector\Class_\RemoveTraitUseRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveTraitUseRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Removing/Rector/Class_/RemoveTraitUseRector/config/configured_rule.php b/rules-tests/Removing/Rector/Class_/RemoveTraitUseRector/config/configured_rule.php index e0454aa66ca..8168d5a7a98 100644 --- a/rules-tests/Removing/Rector/Class_/RemoveTraitUseRector/config/configured_rule.php +++ b/rules-tests/Removing/Rector/Class_/RemoveTraitUseRector/config/configured_rule.php @@ -2,14 +2,11 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Removing\Rector\Class_\RemoveTraitUseRector; use Rector\Tests\Removing\Rector\Class_\RemoveTraitUseRector\Source\TraitToBeRemoved; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveTraitUseRector::class) - ->call('configure', [[ - RemoveTraitUseRector::TRAITS_TO_REMOVE => [TraitToBeRemoved::class], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RemoveTraitUseRector::class, [TraitToBeRemoved::class]); }; diff --git a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallArgRector/RemoveFuncCallArgRectorTest.php b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallArgRector/RemoveFuncCallArgRectorTest.php index 5163acbdd35..064222f3993 100644 --- a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallArgRector/RemoveFuncCallArgRectorTest.php +++ b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallArgRector/RemoveFuncCallArgRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\Removing\Rector\FuncCall\RemoveFuncCallArgRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use SplFileInfo; -use Symplify\SmartFileSystem\SmartFileInfo; final class RemoveFuncCallArgRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallArgRector/config/configured_rule.php b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallArgRector/config/configured_rule.php index 3e116c1547d..ebe60921d13 100644 --- a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallArgRector/config/configured_rule.php +++ b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallArgRector/config/configured_rule.php @@ -2,19 +2,11 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector; use Rector\Removing\ValueObject\RemoveFuncCallArg; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveFuncCallArgRector::class) - ->call('configure', [[ - RemoveFuncCallArgRector::REMOVED_FUNCTION_ARGUMENTS => ValueObjectInliner::inline([ - - new RemoveFuncCallArg('ldap_first_attribute', 2), - - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RemoveFuncCallArgRector::class, [new RemoveFuncCallArg('ldap_first_attribute', 2)]); }; diff --git a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/remove_listed.php.inc b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/remove_listed.php.inc deleted file mode 100644 index 1dd340d786c..00000000000 --- a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/remove_listed.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - ------ - diff --git a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/remove_var_dump.php.inc b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/remove_var_dump.php.inc new file mode 100644 index 00000000000..c5416ca1d2d --- /dev/null +++ b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/remove_var_dump.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/skip_assign.php.inc b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/skip_assign.php.inc deleted file mode 100644 index 7d1b8e687fd..00000000000 --- a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/skip_assign.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - + diff --git a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/skip_other.php.inc b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/skip_other.php.inc deleted file mode 100644 index 563f2828e66..00000000000 --- a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/Fixture/skip_other.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/config/configured_rule.php b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/config/configured_rule.php index 2353f844487..0b1fb14924b 100644 --- a/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/config/configured_rule.php +++ b/rules-tests/Removing/Rector/FuncCall/RemoveFuncCallRector/config/configured_rule.php @@ -2,22 +2,10 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Removing\Rector\FuncCall\RemoveFuncCallRector; -use Rector\Removing\ValueObject\RemoveFuncCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveFuncCallRector::class) - ->call('configure', [[ - RemoveFuncCallRector::REMOVE_FUNC_CALLS => ValueObjectInliner::inline([ - new RemoveFuncCall('ini_get', [ - 0 => ['y2k_compliance', 'safe_mode', 'magic_quotes_runtime'], - ]), - new RemoveFuncCall('ini_set', [ - 0 => ['y2k_compliance', 'safe_mode', 'magic_quotes_runtime'], - ]), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RemoveFuncCallRector::class, ['var_dump']); }; diff --git a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/case_with_parent.php.inc b/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/case_with_parent.php.inc deleted file mode 100644 index 33a84c588a9..00000000000 --- a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/case_with_parent.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ -someStatic(); - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/double_static_method.php.inc b/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/double_static_method.php.inc deleted file mode 100644 index 390815b4c6b..00000000000 --- a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/double_static_method.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ -someStatic(); - } - - private function someStatic() - { - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/first_method_then_call.php.inc b/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/first_method_then_call.php.inc deleted file mode 100644 index 372f798a5b1..00000000000 --- a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/first_method_then_call.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ -someStatic(); - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/fixture.php.inc b/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/fixture.php.inc deleted file mode 100644 index b5adeaf5880..00000000000 --- a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ -someStatic(); - } - - private function someStatic() - { - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_else_class.php.inc b/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_else_class.php.inc deleted file mode 100644 index f092d19119a..00000000000 --- a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_else_class.php.inc +++ /dev/null @@ -1,18 +0,0 @@ - 'time']; - } -} diff --git a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_local_and_else_class.php.inc b/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_local_and_else_class.php.inc deleted file mode 100644 index 6dd81ba6d11..00000000000 --- a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Fixture/skip_local_and_else_class.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Source/SomeParent.php b/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Source/SomeParent.php deleted file mode 100644 index bd70fb4b2cf..00000000000 --- a/rules-tests/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector/Source/SomeParent.php +++ /dev/null @@ -1,9 +0,0 @@ -services(); - $services->set(LocallyCalledStaticMethodToNonStaticRector::class); -}; diff --git a/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/DesiredClassTypeToDynamicRectorTest.php b/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/DesiredClassTypeToDynamicRectorTest.php deleted file mode 100644 index f8812a23ca1..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/DesiredClassTypeToDynamicRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/Fixture/another_class.php.inc b/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/Fixture/another_class.php.inc deleted file mode 100644 index 6467687e968..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/Fixture/another_class.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/Source/FirstStaticClass.php b/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/Source/FirstStaticClass.php deleted file mode 100644 index ec82490a72e..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector/Source/FirstStaticClass.php +++ /dev/null @@ -1,12 +0,0 @@ -parameters(); - $parameters->set(Option::TYPES_TO_REMOVE_STATIC_FROM, [FirstStaticClass::class]); - - $services = $containerConfigurator->services(); - $services->set(DesiredClassTypeToDynamicRector::class); -}; diff --git a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/FixtureWithMultipleArguments/multiple_args.php.inc b/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/FixtureWithMultipleArguments/multiple_args.php.inc deleted file mode 100644 index 653c795d03f..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/FixtureWithMultipleArguments/multiple_args.php.inc +++ /dev/null @@ -1,64 +0,0 @@ -number = $number; - } - - public function someFun() - { - return TurnMeToService::someStaticCall(5, $this->number); - } -} - -?> ------ -anotherClassWithMoreArgumentsFactory->create(10); - } -} - -class AnotherClassWithMoreArguments -{ - private $number; - - public function __construct($number, private \Rector\Tests\RemovingStatic\Rector\Class_\PassFactoryToEntityRector\Source\TurnMeToService $turnMeToService) - { - $this->number = $number; - } - - public function someFun() - { - return $this->turnMeToService->someStaticCall(5, $this->number); - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/PassFactoryToEntityRectorTest.php b/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/PassFactoryToEntityRectorTest.php deleted file mode 100644 index 8a4263db636..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/PassFactoryToEntityRectorTest.php +++ /dev/null @@ -1,42 +0,0 @@ -doTestFileInfo($fileInfo); - - $expectedFactoryFilePath = StaticFixtureSplitter::getTemporaryPath() . '/AnotherClassWithMoreArgumentsFactory.php'; - - $this->assertFileExists($expectedFactoryFilePath); - $this->assertFileEquals( - __DIR__ . '/Source/ExpectedAnotherClassWithMoreArgumentsFactory.php', - $expectedFactoryFilePath - ); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureWithMultipleArguments'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/ExpectedAnotherClassFactory.php b/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/ExpectedAnotherClassFactory.php deleted file mode 100644 index 3209948e6cf..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/ExpectedAnotherClassFactory.php +++ /dev/null @@ -1,19 +0,0 @@ -turnMeToService = $turnMeToService; - } - public function create(): \Rector\Tests\RemovingStatic\Rector\Class_\PassFactoryToEntityRector\Fixture\AnotherClass - { - return new \Rector\Tests\RemovingStatic\Rector\Class_\PassFactoryToEntityRector\Fixture\AnotherClass($this->turnMeToService); - } -} diff --git a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/ExpectedAnotherClassWithMoreArgumentsFactory.php b/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/ExpectedAnotherClassWithMoreArgumentsFactory.php deleted file mode 100644 index 80a244b7ec8..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/ExpectedAnotherClassWithMoreArgumentsFactory.php +++ /dev/null @@ -1,19 +0,0 @@ -turnMeToService = $turnMeToService; - } - public function create($number): \Rector\Tests\RemovingStatic\Rector\Class_\PassFactoryToEntityRector\Fixture\AnotherClassWithMoreArguments - { - return new \Rector\Tests\RemovingStatic\Rector\Class_\PassFactoryToEntityRector\Fixture\AnotherClassWithMoreArguments($number, $this->turnMeToService); - } -} diff --git a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/TurnMeToService.php b/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/TurnMeToService.php deleted file mode 100644 index 422445bb466..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/PassFactoryToEntityRector/Source/TurnMeToService.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - - $typesToServices = [TurnMeToService::class, AnotherClassWithMoreArguments::class]; - - $services->set(PassFactoryToUniqueObjectRector::class) - ->call('configure', [[ - PassFactoryToUniqueObjectRector::TYPES_TO_SERVICES => $typesToServices, - ]]); - - $services->set(NewUniqueObjectToEntityFactoryRector::class) - ->call('configure', [[ - NewUniqueObjectToEntityFactoryRector::TYPES_TO_SERVICES => $typesToServices, - ]]); -}; diff --git a/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Fixture/fixture.php.inc b/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Fixture/fixture.php.inc deleted file mode 100644 index 9b2aaf8c8d9..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ -genericEntityFactory = $genericEntityFactory; - } - public function run() - { - return $this->genericEntityFactory->make(); - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Fixture/fixture_with_implements.php.inc b/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Fixture/fixture_with_implements.php.inc deleted file mode 100644 index 3ad5d49de02..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Fixture/fixture_with_implements.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ -genericEntityFactoryWith = $genericEntityFactoryWith; - } - public function run() - { - return $this->genericEntityFactoryWith->make(); - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Source/GenericEntityFactory.php b/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Source/GenericEntityFactory.php deleted file mode 100644 index 1e25629c9b0..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/Source/GenericEntityFactory.php +++ /dev/null @@ -1,15 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/config/configured_rule.php b/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/config/configured_rule.php deleted file mode 100644 index 96ca3f994bd..00000000000 --- a/rules-tests/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector/config/configured_rule.php +++ /dev/null @@ -1,20 +0,0 @@ -services(); - $services->set(StaticTypeToSetterInjectionRector::class) - ->call('configure', [[ - StaticTypeToSetterInjectionRector::STATIC_TYPES => [ - GenericEntityFactory::class, - // with adding a parent interface to the class - 'ParentSetterEnforcingInterface' => GenericEntityFactoryWithInterface::class, - ], - ]]); -}; diff --git a/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/DesiredPropertyClassMethodTypeToDynamicRectorTest.php b/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/DesiredPropertyClassMethodTypeToDynamicRectorTest.php deleted file mode 100644 index 0c0309e5a79..00000000000 --- a/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/DesiredPropertyClassMethodTypeToDynamicRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/Fixture/static_property.php.inc b/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/Fixture/static_property.php.inc deleted file mode 100644 index 3f5032c005d..00000000000 --- a/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/Fixture/static_property.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/config/some_config.php b/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/config/some_config.php deleted file mode 100644 index cbcd2dbe9d9..00000000000 --- a/rules-tests/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector/config/some_config.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::TYPES_TO_REMOVE_STATIC_FROM, [StaticProperty::class]); - - $services = $containerConfigurator->services(); - $services->set(DesiredPropertyClassMethodTypeToDynamicRector::class); -}; diff --git a/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/DesiredStaticCallTypeToDynamicRectorTest.php b/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/DesiredStaticCallTypeToDynamicRectorTest.php deleted file mode 100644 index baa9d1ed717..00000000000 --- a/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/DesiredStaticCallTypeToDynamicRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/Fixture/some_static_call.php.inc b/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/Fixture/some_static_call.php.inc deleted file mode 100644 index 1358de333a4..00000000000 --- a/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/Fixture/some_static_call.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ -someStaticMethod->go(); - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/Source/SomeStaticMethod.php b/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/Source/SomeStaticMethod.php deleted file mode 100644 index da2353037cf..00000000000 --- a/rules-tests/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector/Source/SomeStaticMethod.php +++ /dev/null @@ -1,12 +0,0 @@ -parameters(); - $parameters->set(Option::TYPES_TO_REMOVE_STATIC_FROM, [SomeStaticMethod::class]); - - $services = $containerConfigurator->services(); - $services->set(DesiredStaticCallTypeToDynamicRector::class); -}; diff --git a/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/DesiredStaticPropertyFetchTypeToDynamicRectorTest.php b/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/DesiredStaticPropertyFetchTypeToDynamicRectorTest.php deleted file mode 100644 index c7bf73d4ef4..00000000000 --- a/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/DesiredStaticPropertyFetchTypeToDynamicRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/Fixture/some_static_property_fetch.php.inc b/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/Fixture/some_static_property_fetch.php.inc deleted file mode 100644 index b4a6c540dea..00000000000 --- a/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/Fixture/some_static_property_fetch.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ -someStaticType->someProperty; - } -} - -?> diff --git a/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/Source/SomeStaticType.php b/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/Source/SomeStaticType.php deleted file mode 100644 index c40191837c1..00000000000 --- a/rules-tests/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector/Source/SomeStaticType.php +++ /dev/null @@ -1,10 +0,0 @@ -parameters(); - $parameters->set(Option::TYPES_TO_REMOVE_STATIC_FROM, [SomeStaticType::class]); - - $services = $containerConfigurator->services(); - $services->set(DesiredStaticPropertyFetchTypeToDynamicRector::class); -}; diff --git a/rules-tests/Renaming/Rector/Cast/RenameCastRector/Fixture/fixture.php.inc b/rules-tests/Renaming/Rector/Cast/RenameCastRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..a8c1e9a4925 --- /dev/null +++ b/rules-tests/Renaming/Rector/Cast/RenameCastRector/Fixture/fixture.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Cast/RenameCastRector/Fixture/keep_parentheses.php.inc b/rules-tests/Renaming/Rector/Cast/RenameCastRector/Fixture/keep_parentheses.php.inc new file mode 100644 index 00000000000..f1d864b911d --- /dev/null +++ b/rules-tests/Renaming/Rector/Cast/RenameCastRector/Fixture/keep_parentheses.php.inc @@ -0,0 +1,37 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Renaming/Rector/Cast/RenameCastRector/RenameCastRectorTest.php b/rules-tests/Renaming/Rector/Cast/RenameCastRector/RenameCastRectorTest.php new file mode 100644 index 00000000000..e6d60a6166e --- /dev/null +++ b/rules-tests/Renaming/Rector/Cast/RenameCastRector/RenameCastRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Renaming/Rector/Cast/RenameCastRector/config/configured_rule.php b/rules-tests/Renaming/Rector/Cast/RenameCastRector/config/configured_rule.php new file mode 100644 index 00000000000..57e0a5dec33 --- /dev/null +++ b/rules-tests/Renaming/Rector/Cast/RenameCastRector/config/configured_rule.php @@ -0,0 +1,18 @@ +ruleWithConfiguration(RenameCastRector::class, [ + new RenameCast(Double::class, Double::KIND_REAL, Double::KIND_FLOAT), + new RenameCast(Double::class, Double::KIND_DOUBLE, Double::KIND_FLOAT), + new RenameCast(Int_::class, Int_::KIND_INTEGER, Int_::KIND_INT), + ]); +}; diff --git a/rules-tests/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector/RenameClassConstFetchRectorTest.php b/rules-tests/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector/RenameClassConstFetchRectorTest.php index 10d9dcfb593..77e75a097a1 100644 --- a/rules-tests/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector/RenameClassConstFetchRectorTest.php +++ b/rules-tests/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector/RenameClassConstFetchRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameClassConstFetchRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector/config/configured_rule.php b/rules-tests/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector/config/configured_rule.php index 1a8f4b4dbb6..f802d179656 100644 --- a/rules-tests/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector/config/configured_rule.php +++ b/rules-tests/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector/config/configured_rule.php @@ -2,28 +2,19 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector; use Rector\Renaming\ValueObject\RenameClassAndConstFetch; use Rector\Renaming\ValueObject\RenameClassConstFetch; use Rector\Tests\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector\Source\DifferentClass; use Rector\Tests\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector\Source\LocalFormEvents; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameClassConstFetchRector::class) - ->call('configure', [[ - RenameClassConstFetchRector::CLASS_CONSTANT_RENAME => ValueObjectInliner::inline([ - new RenameClassConstFetch(LocalFormEvents::class, 'PRE_BIND', 'PRE_SUBMIT'), - new RenameClassConstFetch(LocalFormEvents::class, 'BIND', 'SUBMIT'), - new RenameClassConstFetch(LocalFormEvents::class, 'POST_BIND', 'POST_SUBMIT'), - new RenameClassAndConstFetch( - LocalFormEvents::class, - 'OLD_CONSTANT', - DifferentClass::class, - 'NEW_CONSTANT' - ), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenameClassConstFetchRector::class, [ + new RenameClassConstFetch(LocalFormEvents::class, 'PRE_BIND', 'PRE_SUBMIT'), + new RenameClassConstFetch(LocalFormEvents::class, 'BIND', 'SUBMIT'), + new RenameClassConstFetch(LocalFormEvents::class, 'POST_BIND', 'POST_SUBMIT'), + new RenameClassAndConstFetch(LocalFormEvents::class, 'OLD_CONSTANT', DifferentClass::class, 'NEW_CONSTANT'), + ]); }; diff --git a/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/Fixture/rename_everywhere.php.inc b/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/Fixture/rename_everywhere.php.inc new file mode 100644 index 00000000000..d516f0b9cc9 --- /dev/null +++ b/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/Fixture/rename_everywhere.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/RenameAnnotationRectorTest.php b/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/RenameAnnotationRectorTest.php index b97140e7217..e83237b853f 100644 --- a/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/RenameAnnotationRectorTest.php +++ b/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/RenameAnnotationRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Renaming\Rector\ClassMethod\RenameAnnotationRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameAnnotationRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/config/configured_rule.php b/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/config/configured_rule.php index abd32a2b6df..7557046253c 100644 --- a/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/config/configured_rule.php +++ b/rules-tests/Renaming/Rector/ClassMethod/RenameAnnotationRector/config/configured_rule.php @@ -2,19 +2,14 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\ClassMethod\RenameAnnotationRector; use Rector\Renaming\ValueObject\RenameAnnotation; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; +use Rector\Renaming\ValueObject\RenameAnnotationByType; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameAnnotationRector::class) - ->call('configure', [[ - RenameAnnotationRector::RENAMED_ANNOTATIONS_IN_TYPES => ValueObjectInliner::inline([ - - new RenameAnnotation('PHPUnit\Framework\TestCase', 'scenario', 'test'), - - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(RenameAnnotationRector::class, [ + new RenameAnnotationByType('PHPUnit\Framework\TestCase', 'scenario', 'test'), + new RenameAnnotation('psalm-ignore', 'phpstan-ignore'), + ]); }; diff --git a/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/fixture.php.inc b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..84eaeebce43 --- /dev/null +++ b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/fixture.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/multiple-places.php.inc b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/multiple-places.php.inc new file mode 100644 index 00000000000..d829c458e74 --- /dev/null +++ b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/multiple-places.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/on_param.php.inc b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/on_param.php.inc new file mode 100644 index 00000000000..7641af939ae --- /dev/null +++ b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Fixture/on_param.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/RenameAttributeRectorTest.php b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/RenameAttributeRectorTest.php new file mode 100644 index 00000000000..7ff30edac8a --- /dev/null +++ b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/RenameAttributeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Source/NextParamAttribute.php b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Source/NextParamAttribute.php new file mode 100644 index 00000000000..91469438a8f --- /dev/null +++ b/rules-tests/Renaming/Rector/Class_/RenameAttributeRector/Source/NextParamAttribute.php @@ -0,0 +1,8 @@ +ruleWithConfiguration(RenameAttributeRector::class, [ + new RenameAttribute( + 'Rector\Tests\Renaming\Rector\Class_\RenameAttributeRector\Source\SimpleRoute', + 'Rector\Tests\Renaming\Rector\Class_\RenameAttributeRector\Source\NextRoute', + ), + new RenameAttribute( + 'Rector\Tests\Renaming\Rector\Class_\RenameAttributeRector\Source\SimpleParamAttribute', + 'Rector\Tests\Renaming\Rector\Class_\RenameAttributeRector\Source\NextParamAttribute', + ), + ]); +}; diff --git a/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/Fixture/spaghetti.php.inc b/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/Fixture/spaghetti.php.inc deleted file mode 100644 index 821baeb5d21..00000000000 --- a/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/Fixture/spaghetti.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/RenameConstantRectorTest.php b/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/RenameConstantRectorTest.php index 14844350552..57f8f07958e 100644 --- a/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/RenameConstantRectorTest.php +++ b/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/RenameConstantRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Renaming\Rector\ConstFetch\RenameConstantRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameConstantRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/config/configured_rule.php b/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/config/configured_rule.php index c794fae39d0..5e23ef80134 100644 --- a/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/config/configured_rule.php +++ b/rules-tests/Renaming/Rector/ConstFetch/RenameConstantRector/config/configured_rule.php @@ -2,16 +2,13 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\ConstFetch\RenameConstantRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameConstantRector::class) - ->call('configure', [[ - RenameConstantRector::OLD_TO_NEW_CONSTANTS => [ - 'MYSQL_ASSOC' => 'MYSQLI_ASSOC', - 'OLD_CONSTANT' => 'NEW_CONSTANT', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenameConstantRector::class, [ + 'MYSQL_ASSOC' => 'MYSQLI_ASSOC', + 'OLD_CONSTANT' => 'NEW_CONSTANT', + ]); }; diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/namespace_split.php.inc b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/namespace_split.php.inc deleted file mode 100644 index e7adc590c4c..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/namespace_split.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_rename.php.inc b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_rename.php.inc deleted file mode 100644 index dfe9556e965..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_rename.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_special_class.php.inc b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_special_class.php.inc deleted file mode 100644 index 329bc5c48d8..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_special_class.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_test_case_split.php.inc b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_test_case_split.php.inc deleted file mode 100644 index 5ed285ca4b1..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/phpunit_test_case_split.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/skip_class_from_same_namespace.php.inc b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/skip_class_from_same_namespace.php.inc deleted file mode 100644 index 2af400d112e..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/skip_class_from_same_namespace.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -getOffset(); - } -} diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/skip_excluded_classes.php.inc b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/skip_excluded_classes.php.inc deleted file mode 100644 index 73c52225717..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/skip_excluded_classes.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/use_statement.php.inc b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/use_statement.php.inc deleted file mode 100644 index 80fba1d455d..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Fixture/use_statement.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - ------ - diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/PseudoNamespaceToNamespaceRectorTest.php b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/PseudoNamespaceToNamespaceRectorTest.php deleted file mode 100644 index adbb67d6ce7..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/PseudoNamespaceToNamespaceRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Source/ChangeMeAnotherNamespace.php b/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Source/ChangeMeAnotherNamespace.php deleted file mode 100644 index 17c583f9e76..00000000000 --- a/rules-tests/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Source/ChangeMeAnotherNamespace.php +++ /dev/null @@ -1,8 +0,0 @@ -services(); - $services->set(PseudoNamespaceToNamespaceRector::class) - ->call('configure', [[ - PseudoNamespaceToNamespaceRector::NAMESPACE_PREFIXES_WITH_EXCLUDED_CLASSES => ValueObjectInliner::inline([ - new PseudoNamespaceToNamespace('PHPUnit_', ['PHPUnit_Framework_MockObject_MockObject']), - new PseudoNamespaceToNamespace('ChangeMe_', ['KeepMe_']), - new PseudoNamespaceToNamespace( - 'Rector_Tests_Renaming_Rector_FileWithoutNamespace_PseudoNamespaceToNamespaceRector_Fixture_' - ), - ]), - ]]); -}; diff --git a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/Fixture/fixture.php.inc b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/Fixture/fixture.php.inc deleted file mode 100644 index 3136f720c56..00000000000 --- a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/RenameFunctionAutoImportRectorTest.php b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/RenameFunctionAutoImportRectorTest.php deleted file mode 100644 index 2a758ccc7d3..00000000000 --- a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/RenameFunctionAutoImportRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/config/configured_rule.php b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/config/configured_rule.php deleted file mode 100644 index c4436440b0f..00000000000 --- a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionAutoImportRector/config/configured_rule.php +++ /dev/null @@ -1,20 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'service' => 'Symfony\Component\DependencyInjection\Loader\Configurator\service', - ], - ]]); -}; diff --git a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/in_for.php.inc b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/in_for.php.inc new file mode 100644 index 00000000000..6e803f39647 --- /dev/null +++ b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/in_for.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/with_die_and_session.php.inc b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/with_die_and_session.php.inc new file mode 100644 index 00000000000..9a12f0d4322 --- /dev/null +++ b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/with_die_and_session.php.inc @@ -0,0 +1,65 @@ +delivery_tracking_url) + ) { + $trackingUrl = $deliveryRequestResponse->delivery_tracking_url; + $_SESSION['checkout_success_tracking_url'] = $trackingUrl; + } + return; + } +} else { + die('Sorry, this order may already have been placed. If you did not receive a confirmation email, please give us a call to confirm your order.'); +} + +function insertorder() +{ + + $result = abc(''); + if (!$result) { + die(abc2()); + } +} + +unset($_SESSION['cart']); + +?> +----- +delivery_tracking_url) + ) { + $trackingUrl = $deliveryRequestResponse->delivery_tracking_url; + $_SESSION['checkout_success_tracking_url'] = $trackingUrl; + } + return; + } +} else { + die('Sorry, this order may already have been placed. If you did not receive a confirmation email, please give us a call to confirm your order.'); +} + +function insertorder() +{ + + $result = abc(''); + if (!$result) { + die(abc()); + } +} + +unset($_SESSION['cart']); + +?> diff --git a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/with_die_and_session2.php.inc b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/with_die_and_session2.php.inc new file mode 100644 index 00000000000..4dda0348e5c --- /dev/null +++ b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/Fixture/with_die_and_session2.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/RenameFunctionRectorTest.php b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/RenameFunctionRectorTest.php index 53b6f2136b5..74e7e569831 100644 --- a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/RenameFunctionRectorTest.php +++ b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/RenameFunctionRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Renaming\Rector\FuncCall\RenameFunctionRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameFunctionRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/config/configured_rule.php b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/config/configured_rule.php index 206f0ef9fcd..dfa9d6dda16 100644 --- a/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/config/configured_rule.php +++ b/rules-tests/Renaming/Rector/FuncCall/RenameFunctionRector/config/configured_rule.php @@ -2,16 +2,15 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'view' => 'Laravel\Templating\render', - 'sprintf' => 'Safe\sprintf', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenameFunctionRector::class, [ + 'view' => 'Laravel\Templating\render', + 'sprintf' => 'Safe\sprintf', + 'abc2' => 'abc', + 'sizeof' => 'count', + ]); }; diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/Enum/rename_enum_method.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/Enum/rename_enum_method.php.inc new file mode 100644 index 00000000000..7a3116ad184 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/Enum/rename_enum_method.php.inc @@ -0,0 +1,31 @@ +oldEnumMethod(); + } +} + +?> +----- +newEnumMethod(); + } +} + +?> diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_class_method_and_parent_call.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_class_method_and_parent_call.php.inc index ae0b375d7be..ca68db4c5dc 100644 --- a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_class_method_and_parent_call.php.inc +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_class_method_and_parent_call.php.inc @@ -12,14 +12,6 @@ class MyCustomType extends AbstractType } } -class AnotherClass extends \DateTime -{ - public function setDefaultOptions(SomeResolver $resolver) - { - parent::setDefaultOptions($resolver); - } -} - ?> ----- diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_method_and_method_call.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_method_and_method_call.php.inc new file mode 100644 index 00000000000..78c4b4c4a38 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_method_and_method_call.php.inc @@ -0,0 +1,43 @@ +notify(); + } +} +?> +----- +__invoke(); + } +} +?> diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_method_call_when_class_method_exists.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_method_call_when_class_method_exists.php.inc new file mode 100644 index 00000000000..ac45d0495e8 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_method_call_when_class_method_exists.php.inc @@ -0,0 +1,33 @@ +old(); + } +} + +?> +----- +new(); + } +} + +?> diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_nullsafe_method_call.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_nullsafe_method_call.php.inc new file mode 100644 index 00000000000..7e83b5dae99 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_nullsafe_method_call.php.inc @@ -0,0 +1,35 @@ +add('someContent'); + + $anotherHtml = $html; + $anotherHtml?->add('someContent'); + } +} + +?> +----- +addHtml('someContent'); + + $anotherHtml = $html; + $anotherHtml?->addHtml('someContent'); + } +} + +?> diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_reflection_method.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_reflection_method.php.inc new file mode 100644 index 00000000000..29ce6796168 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_reflection_method.php.inc @@ -0,0 +1,27 @@ +getTentativeReturnType(); + } +} + +?> +----- +getReturnType(); + } +} + +?> diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method.php.inc new file mode 100644 index 00000000000..8e6d17046ec --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method_call.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method_call.php.inc new file mode 100644 index 00000000000..c27095c8911 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method_call.php.inc @@ -0,0 +1,47 @@ +method1(); + } + + public function withStatic() + { + self::method1(); + static::method1(); + } +} + +?> +----- +method3(); + } + + public function withStatic() + { + self::method3(); + static::method3(); + } +} + +?> diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method_which_is_overwritten.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method_which_is_overwritten.php.inc new file mode 100644 index 00000000000..c75b59dbb16 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_trait_method_which_is_overwritten.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_class_method_rename_by_different_parent.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_class_method_rename_by_different_parent.php.inc new file mode 100644 index 00000000000..aeeff2a705c --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_class_method_rename_by_different_parent.php.inc @@ -0,0 +1,11 @@ +getBar(); + $bat = $bar->some_old(); + } +} diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_never.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_never.php.inc new file mode 100644 index 00000000000..17aac78892c --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_never.php.inc @@ -0,0 +1,14 @@ +messageBus->reset(); + } +} diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_old_new_exists_implement_interface.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_old_new_exists_implement_interface.php.inc new file mode 100644 index 00000000000..abcc9753dda --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_old_new_exists_implement_interface.php.inc @@ -0,0 +1,14 @@ +some_old(); + } +} diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_rename_method_which_is_not_in_trait.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_rename_method_which_is_not_in_trait.php.inc new file mode 100644 index 00000000000..3747d9ef8dc --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/skip_rename_method_which_is_not_in_trait.php.inc @@ -0,0 +1,14 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/when_parent_interface.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/when_parent_interface.php.inc new file mode 100644 index 00000000000..7c60de3bf4f --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/when_parent_interface.php.inc @@ -0,0 +1,22 @@ +old(); + } +} + +final class SomeSubscriber implements SubscriberInterface +{ + public function old(): int + { + return 5; + } +} diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/RenameMethodRectorTest.php b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/RenameMethodRectorTest.php index 5100b548d4a..6f47a07af4a 100644 --- a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/RenameMethodRectorTest.php +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/RenameMethodRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameMethodRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Source/AClass.php b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Source/AClass.php new file mode 100644 index 00000000000..d42d9da29c9 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Source/AClass.php @@ -0,0 +1,14 @@ +services(); - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - new MethodCallRename(AbstractType::class, 'setDefaultOptions', 'configureOptions'), - new MethodCallRename('Nette\Utils\Html', 'add', 'addHtml'), - new MethodCallRename( - 'Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Fixture\DemoFile', - 'notify', - '__invoke' - ), - new MethodCallRename( - 'Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Fixture\SomeSubscriber', - 'old', - 'new' - ), - // with array key - new MethodCallRenameWithArrayKey('Nette\Utils\Html', 'addToArray', 'addToHtmlArray', 'hey'), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [ + new MethodCallRename(AbstractType::class, 'setDefaultOptions', 'configureOptions'), + new MethodCallRename('Nette\Utils\Html', 'add', 'addHtml'), + new MethodCallRename(CustomType::class, 'notify', '__invoke'), + new MethodCallRename( + \Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Fixture\CustomType::class, + 'notify', + '__invoke' + ), + new MethodCallRename(SomeSubscriber::class, 'old', 'new'), + new MethodCallRename(Foo::class, 'old', 'new'), + new MethodCallRename(NewInterface::class, 'some_old', 'some_new'), + new MethodCallRename(DifferentInterface::class, 'renameMe', 'toNewVersion'), + + new MethodCallRename(ReflectionFunctionAbstract::class, 'getTentativeReturnType', 'getReturnType'), + + new MethodCallRenameWithArrayKey('Nette\Utils\Html', 'addToArray', 'addToHtmlArray', 'hey'), + new MethodCallRename('Symfony\\Component\\Workflow\\DefinitionBuilder', 'reset', 'clear'), + + new MethodCallRename(SomeEnumWithMethod::class, 'oldEnumMethod', 'newEnumMethod'), + new MethodCallRename(SomeTrait::class, '_test', 'test'), + new MethodCallRename(RenameTraitMethod::class, 'run', 'execute'), + new MethodCallRename(RenameTraitMethodCall::class, 'method1', 'method3'), + ]); }; diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/AutoImportNamesPhp74Test.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/AutoImportNamesPhp74Test.php deleted file mode 100644 index 0fa5c88dc19..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/AutoImportNamesPhp74Test.php +++ /dev/null @@ -1,37 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureAutoImportNamesPhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/auto_import_names.php'; - } -} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/AutoImportNamesTest.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/AutoImportNamesTest.php index e2997062e3f..633d1732c18 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/AutoImportNamesTest.php +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/AutoImportNamesTest.php @@ -5,28 +5,23 @@ namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * @see \Rector\PostRector\Rector\NameImportingPostRector */ final class AutoImportNamesTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureAutoImportNames'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureAutoImportNames'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/ComplexFixture/rename_class_then_rename_method.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/ComplexFixture/rename_class_then_rename_method.php.inc deleted file mode 100644 index 6d5ac1b4dc4..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/ComplexFixture/rename_class_then_rename_method.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -someMethod(); - -?> ------ -someNewMethod(); - -?> diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/ComplexRenameClassRectorTest.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/ComplexRenameClassRectorTest.php deleted file mode 100644 index 0dbf3977d7c..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/ComplexRenameClassRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/ComplexFixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/complex_rename.php'; - } -} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_annotations_serializer_type.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_annotations_serializer_type.php.inc index 66aef552f1c..744cb5f24b2 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_annotations_serializer_type.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_annotations_serializer_type.php.inc @@ -10,6 +10,11 @@ class ClassAnnotationsSerializerIterableType * @Serializer\Type("array") */ public $flights = []; + + /** + * @Serializer\Type("Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass") + */ + public $time; } ?> @@ -26,6 +31,11 @@ class ClassAnnotationsSerializerIterableType * @Serializer\Type("array") */ public $flights = []; + + /** + * @Serializer\Type("Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass") + */ + public $time; } ?> diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_annotations_type.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_annotations_type.php.inc deleted file mode 100644 index fb39e9651c0..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_annotations_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_to_interface.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_to_interface.php.inc index bd1278c0221..82213ab846b 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_to_interface.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_to_interface.php.inc @@ -2,26 +2,12 @@ namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture; -class ClassToInterface extends \DateTime -{ - public function run(\DateTime $dateTime): \DateTime - { - $oldClassWithTypo = new \DateTime(); - } -} +use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\InterfaceAndClass\SomeBasicDateTime; -?> ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_to_new.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_to_new.php.inc index f571d951345..c48f8bcc09b 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_to_new.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/class_to_new.php.inc @@ -1,6 +1,6 @@ +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_implement_twice.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_implement_twice.php.inc deleted file mode 100644 index c07c2c29b81..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_implement_twice.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_remove_existing_target_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_remove_existing_target_namespace.php.inc index df0da3e76cb..53be3cfe062 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_remove_existing_target_namespace.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_remove_existing_target_namespace.php.inc @@ -37,8 +37,6 @@ namespace Acme\Bar { +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_replace_interface_static_call.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_replace_interface_static_call.php.inc new file mode 100644 index 00000000000..9c0e03f8258 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/do_not_replace_interface_static_call.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_aliased_subname.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_aliased_subname.php.inc new file mode 100644 index 00000000000..f1b257a435e --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_aliased_subname.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_shortname.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_shortname.php.inc new file mode 100644 index 00000000000..c2813918303 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_shortname.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_shortname_subname.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_shortname_subname.php.inc new file mode 100644 index 00000000000..b1d930ee888 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/docblock_shortname_subname.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/fqnize_namespaced.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/fqnize_namespaced.php.inc deleted file mode 100644 index 012608edc80..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/fqnize_namespaced.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/keep_comment.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/keep_comment.php.inc new file mode 100644 index 00000000000..5727f25af01 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/keep_comment.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/name_insensitive.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/name_insensitive.php.inc index 828c318b162..6f5c1f8556c 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/name_insensitive.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/name_insensitive.php.inc @@ -9,6 +9,8 @@ class NameInsensitive extends OldClassWithTypO public function run(): OLDClassWithTYPO { $oldClassWithTypo = new OldClassWithTYPO; + + return $oldClassWithTypo; } } @@ -18,13 +20,13 @@ class NameInsensitive extends OldClassWithTypO namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture; -use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClassWithTypo; - class NameInsensitive extends \Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClassWithoutTypo { public function run(): \Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClassWithoutTypo { $oldClassWithTypo = new \Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClassWithoutTypo; + + return $oldClassWithTypo; } } diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class.php.inc deleted file mode 100644 index 3545d85df04..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_to_class_without_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_to_class_without_namespace.php.inc deleted file mode 100644 index 2d88f48cc48..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_to_class_without_namespace.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_without_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_without_namespace.php.inc deleted file mode 100644 index 4c3b7713f42..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_without_namespace.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_without_namespace_to_class_without_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_without_namespace_to_class_without_namespace.php.inc deleted file mode 100644 index 1d26aa2c002..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_class_without_namespace_to_class_without_namespace.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_generic_template_of_fixture.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_generic_template_of_fixture.php.inc new file mode 100644 index 00000000000..3575c369253 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_generic_template_of_fixture.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_interface.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_interface.php.inc deleted file mode 100644 index 92366a00652..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_interface.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_property_read.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_property_read.php.inc index 196c2a3810f..468081d86a9 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_property_read.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_property_read.php.inc @@ -19,8 +19,6 @@ class RenamePropertyRead namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture; -use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass; - /** * @property \Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass $some * @property-read \Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass $someRead diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_trait.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_trait.php.inc deleted file mode 100644 index e1af9fc39d0..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/rename_trait.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/skip_docblock_rename_different_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/skip_docblock_rename_different_namespace.php.inc new file mode 100644 index 00000000000..f464cbc294e --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/skip_docblock_rename_different_namespace.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/SomeShort.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/SomeShort.php new file mode 100644 index 00000000000..a9bbd6152d3 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/SomeShort.php @@ -0,0 +1,9 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/auto_import_conflict_name_all_fqcn_same_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/auto_import_conflict_name_all_fqcn_same_namespace.php.inc new file mode 100644 index 00000000000..80b668559ae --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/auto_import_conflict_name_all_fqcn_same_namespace.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/class_imported_as_alias.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/class_imported_as_alias.php.inc index 10cf96c6951..f741e1423ca 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/class_imported_as_alias.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/class_imported_as_alias.php.inc @@ -16,7 +16,6 @@ namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\FixtureAutoImportN use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\SecondNamespace\SecondOriginalClass; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\FirstNamespace\FirstOriginalClass as AliasedClass; -use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\SecondNamespace; $aliasedClass = new AliasedClass(); $secondClass = new SecondOriginalClass(); diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/conflict_with_use_statement_last_name.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/conflict_with_use_statement_last_name.php.inc new file mode 100644 index 00000000000..8a4ad2e12d9 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/conflict_with_use_statement_last_name.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/do_not_rename_class_with_same_name_inside_different_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/do_not_rename_class_with_same_name_inside_different_namespace.php.inc new file mode 100644 index 00000000000..fd8b342355d --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/do_not_rename_class_with_same_name_inside_different_namespace.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/do_not_rename_class_with_same_name_inside_different_namespace_class_not_exists.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/do_not_rename_class_with_same_name_inside_different_namespace_class_not_exists.php.inc new file mode 100644 index 00000000000..9fed9d89ff4 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/do_not_rename_class_with_same_name_inside_different_namespace_class_not_exists.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/in_const_fetch_conflict_use_statement_last_name.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/in_const_fetch_conflict_use_statement_last_name.php.inc new file mode 100644 index 00000000000..4a8ee527863 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/in_const_fetch_conflict_use_statement_last_name.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/partial_expression.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/partial_expression.php.inc index ed1f2b57d55..dd0368a73bf 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/partial_expression.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/partial_expression.php.inc @@ -19,7 +19,6 @@ class PartialExpression namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\FixtureAutoImportNames; use PhpParser\Node\Stmt\Expression; -use PhpParser\Node; class PartialExpression { diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_from_inner_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_from_inner_namespace.php.inc new file mode 100644 index 00000000000..67edc75f745 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_from_inner_namespace.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_from_inner_namespace_with_import_target_class_exists.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_from_inner_namespace_with_import_target_class_exists.php.inc new file mode 100644 index 00000000000..84255e8340e --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_from_inner_namespace_with_import_target_class_exists.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_not_inside_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_not_inside_namespace.php.inc new file mode 100644 index 00000000000..bdbe40c5178 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_not_inside_namespace.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace.php.inc index e054ce07a52..dde941d06ca 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace.php.inc +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace.php.inc @@ -4,7 +4,13 @@ namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\FixtureAutoImportN use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\FirstNamespace\SomeServiceClass; -$someService = new SomeServiceClass(); +final class RenameClassWithSameNameButDifferentNamespace +{ + public function run(): void + { + $someService = new SomeServiceClass(); + } +} ?> ----- @@ -14,6 +20,12 @@ namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\FixtureAutoImportN use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\SecondNamespace\SomeServiceClass; -$someService = new SomeServiceClass(); +final class RenameClassWithSameNameButDifferentNamespace +{ + public function run(): void + { + $someService = new SomeServiceClass(); + } +} ?> diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace_in_class.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace_in_class.php.inc deleted file mode 100644 index c40f2748b21..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace_in_class.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace_in_var_doc4.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace_in_var_doc4.php.inc new file mode 100644 index 00000000000..f51f079c96c --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/rename_class_with_same_name_but_different_namespace_in_var_doc4.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/skip_conflict_with_namespace_last_name_and_different_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/skip_conflict_with_namespace_last_name_and_different_namespace.php.inc new file mode 100644 index 00000000000..591144543df --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNames/skip_conflict_with_namespace_last_name_and_different_namespace.php.inc @@ -0,0 +1,7 @@ + diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNamesPhp74/skip_already_in_use.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNamesPhp74/skip_already_in_use.php.inc deleted file mode 100644 index 1bfbde99037..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureAutoImportNamesPhp74/skip_already_in_use.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -date; - } -} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameClassAndMethod/rename_class_then_rename_method.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameClassAndMethod/rename_class_then_rename_method.php.inc new file mode 100644 index 00000000000..8f51e4a0b47 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameClassAndMethod/rename_class_then_rename_method.php.inc @@ -0,0 +1,21 @@ +someMethod(); + +?> +----- +someNewMethod(); + +?> diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.neon b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.neon deleted file mode 100644 index fccc925fa14..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.neon +++ /dev/null @@ -1,7 +0,0 @@ -services: - - - class: Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass ------ -services: - - - class: Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.xml b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.xml deleted file mode 100644 index e9cbed58aa2..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - ------ - - - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.yaml b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.yaml deleted file mode 100644 index e3fdbba9a76..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -services: - Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass: null ------ -services: - Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass: null diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.yml b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.yml deleted file mode 100644 index e3fdbba9a76..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameNonPhp/config/local_config.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass: null ------ -services: - Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass: null diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameParent/longer_class_name_with_slashes.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameParent/longer_class_name_with_slashes.php.inc deleted file mode 100644 index d5c70c3e056..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameParent/longer_class_name_with_slashes.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameParent/whatever_parent_is_missing.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameParent/whatever_parent_is_missing.php.inc deleted file mode 100644 index 2b61df1c1c7..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureRenameParent/whatever_parent_is_missing.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureSkipAfterDefined/skip_after_defined.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureSkipAfterDefined/skip_after_defined.php.inc new file mode 100644 index 00000000000..783d625e9f9 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/FixtureSkipAfterDefined/skip_after_defined.php.inc @@ -0,0 +1,7 @@ +doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory( - __DIR__ . '/FixtureRenameNonPhp', - StaticNonPhpFileSuffixes::getSuffixRegexPattern() - ); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/non_php_config.php'; - } -} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameClassAndMethodTest.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameClassAndMethodTest.php new file mode 100644 index 00000000000..9155cfaa3ee --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameClassAndMethodTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureRenameClassAndMethod'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/complex_rename.php'; + } +} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameClassRectorTest.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameClassRectorTest.php index 9cac48fc6bd..e05a87a4329 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameClassRectorTest.php +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameClassRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameClassRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameParentTest.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameParentTest.php deleted file mode 100644 index 75da14bb8c1..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/RenameParentTest.php +++ /dev/null @@ -1,37 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureRenameParent'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/rename_parent.php'; - } -} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/SkipAfterDefinedTest.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/SkipAfterDefinedTest.php new file mode 100644 index 00000000000..776cb94670d --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/SkipAfterDefinedTest.php @@ -0,0 +1,32 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureSkipAfterDefined'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/skip_after_defined_configured_rule.php'; + } +} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/EnforceExceptionSuffixCallback.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/EnforceExceptionSuffixCallback.php new file mode 100644 index 00000000000..ae4c33bc7c7 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/EnforceExceptionSuffixCallback.php @@ -0,0 +1,31 @@ +getName($class); + $classReflection = $reflectionProvider->getClass($fullyQualifiedClassName); + if (! $classReflection->is(Exception::class)) { + return null; + } + + if (!str_ends_with($fullyQualifiedClassName, 'Exception')) { + return $fullyQualifiedClassName . 'Exception'; + } + + return null; + } +} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/EnforceInterfaceSuffixCallback.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/EnforceInterfaceSuffixCallback.php new file mode 100644 index 00000000000..6bf08d44c95 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/EnforceInterfaceSuffixCallback.php @@ -0,0 +1,24 @@ +getName($class); + if ( + $class instanceof Interface_ && + !str_ends_with($fullyQualifiedClassName, 'Interface') + ) { + return $fullyQualifiedClassName . 'Interface'; + } + + return null; + } +} diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/Foo2/Bar/Storage.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/Foo2/Bar/Storage.php new file mode 100644 index 00000000000..9b54175986d --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Source/Foo2/Bar/Storage.php @@ -0,0 +1,8 @@ +parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->importNames(); + $rectorConfig->importShortClasses(); + $rectorConfig->removeUnusedImports(); - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - OldClass::class => NewClass::class, - SomeServiceClassFirstNamespace::class => SomeServiceClass::class, - ], - ]]); + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + OldClass::class => NewClass::class, + SomeServiceClassFirstNamespace::class => SomeServiceClass::class, + 'Storage' => 'Illuminate\Support\Facades\Storage', + 'Queue' => 'Illuminate\Support\Facades\Queue', + 'Some\Storage' => 'Illuminate\Support\Facades\Storage', + 'Some2\Storage' => 'Foo2\Storage', + ]); }; diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/complex_rename.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/complex_rename.php index 334f810eefc..fd5b99e7e8e 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/complex_rename.php +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/complex_rename.php @@ -2,31 +2,25 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\MethodCall\RenameMethodRector; use Rector\Renaming\Rector\Name\RenameClassRector; use Rector\Renaming\ValueObject\MethodCallRename; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClassWithNewMethod; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClassWithMethod; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->importNames(); + $rectorConfig->removeUnusedImports(); - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - OldClassWithMethod::class => NewClassWithNewMethod::class, - ], - ]]); + $rectorConfig + ->ruleWithConfiguration(RenameClassRector::class, [ + OldClassWithMethod::class => NewClassWithNewMethod::class, + ]); - $services->set(RenameMethodRector::class) - ->call('configure', [[ - RenameMethodRector::METHOD_CALL_RENAMES => ValueObjectInliner::inline([ - new MethodCallRename(NewClassWithNewMethod::class, 'someMethod', 'someNewMethod'), - ]), - ]]); + $rectorConfig + ->ruleWithConfiguration( + RenameMethodRector::class, + [new MethodCallRename(NewClassWithNewMethod::class, 'someMethod', 'someNewMethod')] + ); }; diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/configured_rule.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/configured_rule.php index 4cb3af44381..576f5c9f96e 100644 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/configured_rule.php +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/configured_rule.php @@ -3,48 +3,72 @@ declare(strict_types=1); use Acme\Bar\DoNotUpdateExistingTargetNamespace; -use Manual\Twig\TwigFilter; +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture\DuplicatedClass; -use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\AbstractManualExtension; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\Contract\FirstInterface; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\Contract\SecondInterface; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\Contract\ThirdInterface; +use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\InterfaceAndClass\SomeBasicDateTime; +use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\InterfaceAndClass\SomeBasicDateTimeInterface; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClassWithoutTypo; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClassWithTypo; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\SomeFinalClass; use Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\SomeNonFinalClass; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'FqnizeNamespaced' => 'Abc\FqnizeNamespaced', - OldClass::class => NewClass::class, - OldClassWithTypo::class => NewClassWithoutTypo::class, - 'DateTime' => 'DateTimeInterface', - 'Countable' => 'stdClass', - Manual_Twig_Filter::class => TwigFilter::class, - 'Twig_AbstractManualExtension' => AbstractManualExtension::class, - 'Twig_Extension_Sandbox' => 'Twig\Extension\SandboxExtension', - // Renaming class itself and its namespace - 'MyNamespace\MyClass' => 'MyNewNamespace\MyNewClass', - 'MyNamespace\MyTrait' => 'MyNewNamespace\MyNewTrait', - 'MyNamespace\MyInterface' => 'MyNewNamespace\MyNewInterface', - 'MyOldClass' => 'MyNamespace\MyNewClass', - 'AnotherMyOldClass' => 'AnotherMyNewClass', - 'MyNamespace\AnotherMyClass' => 'MyNewClassWithoutNamespace', - // test duplicated class - @see https://github.com/rectorphp/rector/issues/1438 - 'Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture\SingularClass' => DuplicatedClass::class, - // test duplicated class - @see https://github.com/rectorphp/rector/issues/5389 - FirstInterface::class => ThirdInterface::class, - SecondInterface::class => ThirdInterface::class, - \Acme\Foo\DoNotUpdateExistingTargetNamespace::class => DoNotUpdateExistingTargetNamespace::class, - SomeNonFinalClass::class => SomeFinalClass::class, - ], - ]]); + +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->removeUnusedImports(); + + $rectorConfig + ->ruleWithConfiguration(RenameClassRector::class, [ + OldClass::class => NewClass::class, + // interface to class + SomeBasicDateTime::class => + SomeBasicDateTimeInterface::class, + + // test casing + OldClassWithTypo::class => NewClassWithoutTypo::class, + 'Countable' => 'stdClass', + 'Twig_Extension_Sandbox' => 'Twig\Extension\SandboxExtension', + // Renaming class itself and its namespace + 'MyNamespace\MylegacyClass' => 'MyNewNamespace\MyNewClass', + // test duplicated class - @see https://github.com/rectorphp/rector/issues/1438 + 'Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture\SingularClass' => DuplicatedClass::class, + // test duplicated class - @see https://github.com/rectorphp/rector/issues/5389 + FirstInterface::class => ThirdInterface::class, + SecondInterface::class => ThirdInterface::class, + \Acme\Foo\DoNotUpdateExistingTargetNamespace::class => DoNotUpdateExistingTargetNamespace::class, + SomeNonFinalClass::class => SomeFinalClass::class, + + 'Doctrine\DBAL\DBALException' => 'Doctrine\DBAL\Exception', + 'NotExistsClass' => 'NewClass', + + foo::class => Bar\Foo::class, + + /** + * This test never renamed as it is annotation @IsGranted + * + * Only Assert, Doctrine, and Serializer type annotation that currently supported + * + * For @IsGranted, it needs to be changed to Attribute instead + * + * @see https://github.com/rectorphp/rector-src/blob/290f2a03d53d0b8da35beb973d724f95a77983cb/tests/Issues/AnnotationToAttributeRenameAutoImport/config/configured_rule.php#L13-L22 + * @see https://github.com/rectorphp/rector-symfony/issues/535 + */ + 'Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted' => 'Symfony\Component\Security\Http\Attribute\IsGranted', + + // test skip rename class const fetch when target rename is interface + // and interface class const fetch not found + 'Symfony\Component\Serializer\Normalizer\ObjectNormalizer' => 'Symfony\Component\Serializer\Normalizer\NormalizerInterface', + + /** + * // avoid error extends itself + * namespace MyNamespace; + * + * class MyMigration extends \MyNamespace\BaseMigration {} + */ + 'MyNamespace\AbstractMigration' => 'MyNamespace\BaseMigration', + ]); }; diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/files/renamed_classes.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/files/renamed_classes.php deleted file mode 100644 index 0c92d4d82bf..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/files/renamed_classes.php +++ /dev/null @@ -1,14 +0,0 @@ - NewClass::class, - // Laravel - 'Session' => 'Illuminate\Support\Facades\Session', - 'Form' => 'Collective\Html\FormFacade', - 'Html' => 'Collective\Html\HtmlFacade', -]; diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/non_php_config.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/non_php_config.php deleted file mode 100644 index 4208bacb594..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/non_php_config.php +++ /dev/null @@ -1,15 +0,0 @@ -services(); - - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::CLASS_MAP_FILES => [__DIR__ . '/files/renamed_classes.php'], - ]]); -}; diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/rename_parent.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/rename_parent.php deleted file mode 100644 index ba9551e9bcc..00000000000 --- a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/rename_parent.php +++ /dev/null @@ -1,18 +0,0 @@ -services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'ThisClassDoesNotExistAnymore' => 'NewClassThatDoesNotExistEither', - 'App\NotHereClass\AndNamespace' => 'NewClassThatDoesNotExistEither', - ], - ]]); -}; diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/config/skip_after_defined_configured_rule.php b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/skip_after_defined_configured_rule.php new file mode 100644 index 00000000000..4ab81ef9905 --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/config/skip_after_defined_configured_rule.php @@ -0,0 +1,22 @@ +removeUnusedImports(); + + $rectorConfig + ->ruleWithConfiguration(RenameClassRector::class, [ + OldClass::class => NewClass::class, + ]); + + // skip the path after defined + $rectorConfig->skip([ + RenameClassRector::class => [__DIR__ . '/../FixtureSkipAfterDefined'], + ]); +}; diff --git a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture.php.inc b/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture.php.inc deleted file mode 100644 index df8f8aaa423..00000000000 --- a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture2.php.inc b/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture2.php.inc deleted file mode 100644 index b5c8d138eba..00000000000 --- a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture3.php.inc b/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture3.php.inc deleted file mode 100644 index 0ca435556d4..00000000000 --- a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture4.php.inc b/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture4.php.inc deleted file mode 100644 index fb469a941bf..00000000000 --- a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture4.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture5.php.inc b/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture5.php.inc deleted file mode 100644 index 212b843ab7b..00000000000 --- a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/Fixture/fixture5.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/RenameNamespaceRectorTest.php b/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/RenameNamespaceRectorTest.php deleted file mode 100644 index fe34c876e35..00000000000 --- a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/RenameNamespaceRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/config/configured_rule.php b/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/config/configured_rule.php deleted file mode 100644 index 44630d296cc..00000000000 --- a/rules-tests/Renaming/Rector/Namespace_/RenameNamespaceRector/config/configured_rule.php +++ /dev/null @@ -1,19 +0,0 @@ -services(); - $services->set(RenameNamespaceRector::class) - ->call('configure', [[ - RenameNamespaceRector::OLD_TO_NEW_NAMESPACES => [ - 'OldNamespace' => 'NewNamespace', - 'OldNamespaceWith\OldSplitNamespace' => 'NewNamespaceWith\NewSplitNamespace', - 'Old\Long\AnyNamespace' => 'Short\AnyNamespace', - 'PHPUnit_Framework_' => 'PHPUnit\Framework\\', - ], - ]]); -}; diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/child_class_with_old_property.php.inc b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/child_class_with_old_property.php.inc new file mode 100644 index 00000000000..a4261a8fcc4 --- /dev/null +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/child_class_with_old_property.php.inc @@ -0,0 +1,55 @@ +oldProperty; + } +} + +class PropertyFetchFromChildClass +{ + public ChildClassWithOldProperty $classWithOldProperty; + + public function run() + { + return $this->classWithOldProperty->oldProperty; + } +} + +?> +----- +newProperty; + } +} + +class PropertyFetchFromChildClass +{ + public ChildClassWithOldProperty $classWithOldProperty; + + public function run() + { + return $this->classWithOldProperty->newProperty; + } +} + +?> diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/class_with_old_property.php.inc b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/class_with_old_property.php.inc new file mode 100644 index 00000000000..057e700a7a6 --- /dev/null +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/class_with_old_property.php.inc @@ -0,0 +1,31 @@ +oldProperty; + } +} + +?> +----- +newProperty; + } +} + +?> diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/class_with_old_property2.php.inc b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/class_with_old_property2.php.inc new file mode 100644 index 00000000000..c3427a1fc7a --- /dev/null +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/class_with_old_property2.php.inc @@ -0,0 +1,41 @@ +classWithOldProperty->oldProperty; + } +} + +?> +----- +classWithOldProperty->newProperty; + } +} + +?> diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/do_not_change_to_property_exists.php.inc b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/do_not_change_to_property_exists.php.inc new file mode 100644 index 00000000000..12d402234d7 --- /dev/null +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/do_not_change_to_property_exists.php.inc @@ -0,0 +1,33 @@ +oldProperty; + } +} + +?> +----- +newProperty; + } +} + +?> diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/rename_self_static.php.inc b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/rename_self_static.php.inc new file mode 100644 index 00000000000..51b49d428cd --- /dev/null +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/rename_self_static.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/skip_not_configured_class.php.inc b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/skip_not_configured_class.php.inc new file mode 100644 index 00000000000..cb236eaf1b7 --- /dev/null +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Fixture/skip_not_configured_class.php.inc @@ -0,0 +1,15 @@ +oldProperty; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/RenamePropertyRectorTest.php b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/RenamePropertyRectorTest.php index d62629379cc..aebc2ecba35 100644 --- a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/RenamePropertyRectorTest.php +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/RenamePropertyRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Renaming\Rector\PropertyFetch\RenamePropertyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenamePropertyRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Source/ParentClassWithOldProperty.php b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Source/ParentClassWithOldProperty.php new file mode 100644 index 00000000000..f8cdd5bcc6e --- /dev/null +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/Source/ParentClassWithOldProperty.php @@ -0,0 +1,15 @@ +oldProperty; + } +} diff --git a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/config/configured_rule.php b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/config/configured_rule.php index b5d74363870..c49d413530f 100644 --- a/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/config/configured_rule.php +++ b/rules-tests/Renaming/Rector/PropertyFetch/RenamePropertyRector/config/configured_rule.php @@ -2,21 +2,42 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\PropertyFetch\RenamePropertyRector; use Rector\Renaming\ValueObject\RenameProperty; use Rector\Tests\Renaming\Rector\PropertyFetch\RenamePropertyRector\Source\ClassWithProperties; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenamePropertyRector::class) - ->call('configure', [[ - RenamePropertyRector::RENAMED_PROPERTIES => ValueObjectInliner::inline([ +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenamePropertyRector::class, [ - new RenameProperty(ClassWithProperties::class, 'oldProperty', 'newProperty'), - new RenameProperty(ClassWithProperties::class, 'anotherOldProperty', 'anotherNewProperty'), + new RenameProperty(ClassWithProperties::class, 'oldProperty', 'newProperty'), + new RenameProperty(ClassWithProperties::class, 'anotherOldProperty', 'anotherNewProperty'), - ]), - ]]); + new RenameProperty( + 'Rector\Tests\Renaming\Rector\PropertyFetch\RenamePropertyRector\Fixture\ClassWithOldProperty', + 'oldProperty', + 'newProperty' + ), + new RenameProperty( + 'Rector\Tests\Renaming\Rector\PropertyFetch\RenamePropertyRector\Fixture\ClassWithOldProperty2', + 'oldProperty', + 'newProperty' + ), + new RenameProperty( + 'Rector\Tests\Renaming\Rector\PropertyFetch\RenamePropertyRector\Fixture\DoNotChangeToPropertyExists', + 'oldProperty', + 'newProperty' + ), + new RenameProperty( + 'Rector\Tests\Renaming\Rector\PropertyFetch\RenamePropertyRector\Source\ParentClassWithOldProperty', + 'oldProperty', + 'newProperty' + ), + new RenameProperty( + 'Rector\Tests\Renaming\Rector\PropertyFetch\RenamePropertyRector\Fixture\MyClass', + '_config', + 'config' + ), + ]); }; diff --git a/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/Fixture/fixture.php.inc b/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/Fixture/fixture.php.inc index d609f00fc24..abf7c2f3d3f 100644 --- a/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/Fixture/fixture.php.inc +++ b/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/Fixture/fixture.php.inc @@ -1,17 +1,21 @@ ----- diff --git a/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/Fixture/fixture2.php.inc b/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/Fixture/fixture2.php.inc index 63ba55d42e1..7da36e09ee4 100644 --- a/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/Fixture/fixture2.php.inc +++ b/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/Fixture/fixture2.php.inc @@ -1,5 +1,7 @@ doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/config/configured_rule.php b/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/config/configured_rule.php index 51c2748e7bc..9557397b384 100644 --- a/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/config/configured_rule.php +++ b/rules-tests/Renaming/Rector/StaticCall/RenameStaticMethodRector/config/configured_rule.php @@ -3,26 +3,22 @@ declare(strict_types=1); use Nette\Utils\Html; +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector; use Rector\Renaming\ValueObject\RenameStaticMethod; use Rector\Tests\Renaming\Rector\StaticCall\RenameStaticMethodRector\Source\FormMacros; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameStaticMethodRector::class) - ->call('configure', [[ - RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => ValueObjectInliner::inline([ +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenameStaticMethodRector::class, [ - new RenameStaticMethod(Html::class, 'add', Html::class, 'addHtml'), - new RenameStaticMethod( - FormMacros::class, - 'renderFormBegin', - 'Nette\Bridges\FormsLatte\Runtime', - 'renderFormBegin' - ), + new RenameStaticMethod(Html::class, 'add', Html::class, 'addHtml'), + new RenameStaticMethod( + FormMacros::class, + 'renderFormBegin', + 'Nette\Bridges\FormsLatte\Runtime', + 'renderFormBegin' + ), - ]), - ]]); + ]); }; diff --git a/rules-tests/Renaming/Rector/String_/RenameStringRector/RenameStringRectorTest.php b/rules-tests/Renaming/Rector/String_/RenameStringRector/RenameStringRectorTest.php index bde52095cc9..fd306d3a639 100644 --- a/rules-tests/Renaming/Rector/String_/RenameStringRector/RenameStringRectorTest.php +++ b/rules-tests/Renaming/Rector/String_/RenameStringRector/RenameStringRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Renaming\Rector\String_\RenameStringRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class RenameStringRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Renaming/Rector/String_/RenameStringRector/config/configured_rule.php b/rules-tests/Renaming/Rector/String_/RenameStringRector/config/configured_rule.php index c5f7d936f72..25606c1a791 100644 --- a/rules-tests/Renaming/Rector/String_/RenameStringRector/config/configured_rule.php +++ b/rules-tests/Renaming/Rector/String_/RenameStringRector/config/configured_rule.php @@ -2,15 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\String_\RenameStringRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameStringRector::class) - ->call('configure', [[ - RenameStringRector::STRING_CHANGES => [ - 'ROLE_PREVIOUS_ADMIN' => 'IS_IMPERSONATOR', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(RenameStringRector::class, [ + 'ROLE_PREVIOUS_ADMIN' => 'IS_IMPERSONATOR', + ]); }; diff --git a/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/Fixture/fixture.php.inc b/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/Fixture/fixture.php.inc deleted file mode 100644 index 70056def323..00000000000 --- a/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/MissingClassConstantReferenceToStringRectorTest.php b/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/MissingClassConstantReferenceToStringRectorTest.php deleted file mode 100644 index ddd2336ea21..00000000000 --- a/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/MissingClassConstantReferenceToStringRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/config/configured_rule.php b/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/config/configured_rule.php deleted file mode 100644 index 182ec3a83f9..00000000000 --- a/rules-tests/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(MissingClassConstantReferenceToStringRector::class); -}; diff --git a/rules-tests/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector/Fixture/skip_different_class_name.php.inc b/rules-tests/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector/Fixture/skip_different_class_name.php.inc deleted file mode 100644 index 4c566f48ef0..00000000000 --- a/rules-tests/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector/Fixture/skip_different_class_name.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -doTestFileInfo($smartFileInfo); - - $expectedAddedFileWithContent = new AddedFileWithContent( - $this->originalTempFileInfo->getRealPathDirectory() . '/SkipDifferentClassName.php', - FileSystem::read(__DIR__ . '/Fixture/skip_different_class_name.php.inc') - ); - $this->assertFileWasAdded($expectedAddedFileWithContent); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector/config/configured_rule.php b/rules-tests/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector/config/configured_rule.php deleted file mode 100644 index 1efd61941ae..00000000000 --- a/rules-tests/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(UpdateFileNameByClassNameFileSystemRector::class); -}; diff --git a/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Fixture/fixture.php.inc b/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Fixture/fixture.php.inc deleted file mode 100644 index b3b8aaf045d..00000000000 --- a/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - ------ - diff --git a/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Fixture/skip_alias_already.php.inc b/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Fixture/skip_alias_already.php.inc deleted file mode 100644 index 14b14a9bc92..00000000000 --- a/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Fixture/skip_alias_already.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/InferParamFromClassMethodReturnRectorTest.php b/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/InferParamFromClassMethodReturnRectorTest.php deleted file mode 100644 index dff72efdb96..00000000000 --- a/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/InferParamFromClassMethodReturnRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Source/SomeType.php b/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Source/SomeType.php deleted file mode 100644 index 3727b0b0f0e..00000000000 --- a/rules-tests/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Source/SomeType.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - - $configuration = ValueObjectInliner::inline([ - new InferParamFromClassMethodReturn(SomeType::class, 'process', 'getNodeTypes'), - ]); - - $services->set(InferParamFromClassMethodReturnRector::class) - ->call('configure', [[ - InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => $configuration, - ]]); -}; diff --git a/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/Fixture/fixture.php.inc b/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/Fixture/fixture.php.inc deleted file mode 100644 index 3b98ebb8b3a..00000000000 --- a/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/RemoveFinalFromEntityRectorTest.php b/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/RemoveFinalFromEntityRectorTest.php deleted file mode 100644 index 2bc78522dca..00000000000 --- a/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/RemoveFinalFromEntityRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/config/configured_rule.php b/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/config/configured_rule.php deleted file mode 100644 index a6278b20ed5..00000000000 --- a/rules-tests/Restoration/Rector/Class_/RemoveFinalFromEntityRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(RemoveFinalFromEntityRector::class); -}; diff --git a/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/CompleteImportForPartialAnnotationRectorTest.php b/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/CompleteImportForPartialAnnotationRectorTest.php deleted file mode 100644 index dac2619d9bd..00000000000 --- a/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/CompleteImportForPartialAnnotationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/Fixture/fixture.php.inc b/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/Fixture/fixture.php.inc deleted file mode 100644 index 5c0eeb0845e..00000000000 --- a/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/Fixture/keep_non_annotations.php.inc b/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/Fixture/keep_non_annotations.php.inc deleted file mode 100644 index 6f1a9eb234c..00000000000 --- a/rules-tests/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector/Fixture/keep_non_annotations.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(CompleteImportForPartialAnnotationRector::class) - ->call( - 'configure', - [[ - CompleteImportForPartialAnnotationRector::USE_IMPORTS_TO_RESTORE => ValueObjectInliner::inline([ - new CompleteImportForPartialAnnotation('Doctrine\ORM\Mapping', 'ORM'), - ]), - ]] - ); -}; diff --git a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/boolean_not_on_property.php.inc b/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/boolean_not_on_property.php.inc deleted file mode 100644 index fbdc68bfc5f..00000000000 --- a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/boolean_not_on_property.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -propertyTypeClass) { - $this->propertyTypeClass = new PropertyTypeClass(); - } - } -} - -?> ------ -propertyTypeClass) { - $this->propertyTypeClass = new PropertyTypeClass(); - } - } -} - -?> diff --git a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/fixture.php.inc b/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/fixture.php.inc deleted file mode 100644 index 74a21729caa..00000000000 --- a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -propertyTypeClass === null) { - $this->propertyTypeClass = new PropertyTypeClass; - } - - if (null === $this->propertyTypeClass) { - $this->propertyTypeClass = new PropertyTypeClass; - } - } -} - -?> ------ -propertyTypeClass === null) { - $this->propertyTypeClass = new PropertyTypeClass; - } - - if (null === $this->propertyTypeClass) { - $this->propertyTypeClass = new PropertyTypeClass; - } - } -} - -?> diff --git a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/not_identical_to_null.php.inc b/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/not_identical_to_null.php.inc deleted file mode 100644 index 78b66dea919..00000000000 --- a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/not_identical_to_null.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -propertyTypeClass !== null) { - return $this->propertyTypeClass; - } - } -} - -?> ------ -propertyTypeClass !== null) { - return $this->propertyTypeClass; - } - } -} - -?> diff --git a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/skip_nullable_type.php.inc b/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/skip_nullable_type.php.inc deleted file mode 100644 index 499e5d1a705..00000000000 --- a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Fixture/skip_nullable_type.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -propertyTypeClass === null) { - $this->propertyTypeClass = new PropertyTypeClass; - } - } -} diff --git a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/MakeTypedPropertyNullableIfCheckedRectorTest.php b/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/MakeTypedPropertyNullableIfCheckedRectorTest.php deleted file mode 100644 index 50309557b35..00000000000 --- a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/MakeTypedPropertyNullableIfCheckedRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Source/PropertyTypeClass.php b/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Source/PropertyTypeClass.php deleted file mode 100644 index fcb5b7bd3dc..00000000000 --- a/rules-tests/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector/Source/PropertyTypeClass.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(MakeTypedPropertyNullableIfCheckedRector::class); -}; diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/DisallowedEmptyRuleFixerRectorTest.php b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/DisallowedEmptyRuleFixerRectorTest.php new file mode 100644 index 00000000000..53dadc79bd2 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/DisallowedEmptyRuleFixerRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/empty_string_nullable.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/empty_string_nullable.php.inc new file mode 100644 index 00000000000..91cf79b8dd0 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/empty_string_nullable.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/from_return_method.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/from_return_method.php.inc new file mode 100644 index 00000000000..9db99b55af6 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/from_return_method.php.inc @@ -0,0 +1,47 @@ +getProperty()); + } + + public function run2() + { + return ! empty($this->getProperty()); + } + + public function getProperty(): string + { + return ''; + } +} + +?> +----- +getProperty(), ['', '0'], true); + } + + public function run2() + { + return !in_array($this->getProperty(), ['', '0'], true); + } + + public function getProperty(): string + { + return ''; + } +} + +?> diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/may_uninitialized_property_no_default_value.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/may_uninitialized_property_no_default_value.php.inc new file mode 100644 index 00000000000..8a9b065ad96 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/may_uninitialized_property_no_default_value.php.inc @@ -0,0 +1,41 @@ +items); + } + + public function isNotEmpty() + { + return ! empty($this->items); + } +} + +?> +----- +items) || $this->items === []; + } + + public function isNotEmpty() + { + return isset($this->items) && $this->items !== []; + } +} + +?> diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/may_uninitialized_static_property_no_default_value.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/may_uninitialized_static_property_no_default_value.php.inc new file mode 100644 index 00000000000..214d7b9909c --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/may_uninitialized_static_property_no_default_value.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/negated_empty_array.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/negated_empty_array.php.inc new file mode 100644 index 00000000000..0b24045da75 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/negated_empty_array.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/negation_empty_string_nullable.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/negation_empty_string_nullable.php.inc new file mode 100644 index 00000000000..81e067fc40e --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/negation_empty_string_nullable.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/nullable_bool.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/nullable_bool.php.inc new file mode 100644 index 00000000000..bcfe8ea38f3 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/nullable_bool.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/property_with_assign_in_constructor.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/property_with_assign_in_constructor.php.inc new file mode 100644 index 00000000000..bc42a451dbb --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/property_with_assign_in_constructor.php.inc @@ -0,0 +1,51 @@ +items = []; + } + + public function isEmpty() + { + return empty($this->items); + } + + public function isNotEmpty() + { + return ! empty($this->items); + } +} + +?> +----- +items = []; + } + + public function isEmpty() + { + return $this->items === []; + } + + public function isNotEmpty() + { + return $this->items !== []; + } +} + +?> diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/property_with_default_value.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/property_with_default_value.php.inc new file mode 100644 index 00000000000..334414cd21d --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/property_with_default_value.php.inc @@ -0,0 +1,41 @@ +items); + } + + public function isNotEmpty() + { + return ! empty($this->items); + } +} + +?> +----- +items === []; + } + + public function isNotEmpty() + { + return $this->items !== []; + } +} + +?> diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_arg_array_dim_fetch_from_property_fetch.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_arg_array_dim_fetch_from_property_fetch.php.inc new file mode 100644 index 00000000000..6cce8cd69e9 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_arg_array_dim_fetch_from_property_fetch.php.inc @@ -0,0 +1,19 @@ + + */ + protected array $labels = []; + + public function getLabel(string $value): string + { + if (empty($this->labels[$value])) { + throw new \InvalidArgumentException(sprintf('%s is missing label for value "%s"', static::class, $value)); + } + + return $this->labels[$value]; + } +} diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_arg_array_dim_fetch_from_static_property_fetch.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_arg_array_dim_fetch_from_static_property_fetch.php.inc new file mode 100644 index 00000000000..855de842375 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_arg_array_dim_fetch_from_static_property_fetch.php.inc @@ -0,0 +1,19 @@ + + */ + protected static array $labels = []; + + public static function getLabel(string $value): string + { + if (empty(static::$labels[$value])) { + throw new \InvalidArgumentException(sprintf('%s is missing label for value "%s"', static::class, $value)); + } + + return static::$labels[$value]; + } +} diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_empty_from_param_doc.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_empty_from_param_doc.php.inc new file mode 100644 index 00000000000..ccbcb021e13 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_empty_from_param_doc.php.inc @@ -0,0 +1,16 @@ +getProperty()); + } + + public function run2() + { + return ! empty($this->getProperty()); + } + + /** + * @return string + */ + public function getProperty() + { + } +} diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_multi_union_types_from_docs.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_multi_union_types_from_docs.php.inc new file mode 100644 index 00000000000..5183bf50816 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/skip_multi_union_types_from_docs.php.inc @@ -0,0 +1,14 @@ + +----- + diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/union_object_nullable.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/union_object_nullable.php.inc new file mode 100644 index 00000000000..762e36fbe49 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/union_object_nullable.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/config/configured_rule.php b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/config/configured_rule.php new file mode 100644 index 00000000000..a230bbec5b5 --- /dev/null +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([DisallowedEmptyRuleFixerRector::class]); diff --git a/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/ArrayDimFetchToMethodCallRectorTest.php b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/ArrayDimFetchToMethodCallRectorTest.php new file mode 100644 index 00000000000..c7cbeead72f --- /dev/null +++ b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/ArrayDimFetchToMethodCallRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..918de0ed276 --- /dev/null +++ b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/fixture.php.inc @@ -0,0 +1,23 @@ + +----- +get('key'); +$object->set('key', 42); +$object->has('key'); +$object->unset('key'); + +?> diff --git a/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/handle_container_get.php.inc b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/handle_container_get.php.inc new file mode 100644 index 00000000000..603557ba726 --- /dev/null +++ b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/handle_container_get.php.inc @@ -0,0 +1,17 @@ + +----- +get('key'); + +?> diff --git a/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/skip_assign_ops.php.inc b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/skip_assign_ops.php.inc new file mode 100644 index 00000000000..25313e4ee49 --- /dev/null +++ b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/skip_assign_ops.php.inc @@ -0,0 +1,8 @@ +get() + +?> diff --git a/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_multiple_issets.php.inc b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_multiple_issets.php.inc new file mode 100644 index 00000000000..70525585214 --- /dev/null +++ b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_multiple_issets.php.inc @@ -0,0 +1,23 @@ + +----- +has('key1') && $object->has('key2')) { + echo 'Keys are set'; +} + +?> diff --git a/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_multiple_unsets.php.inc b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_multiple_unsets.php.inc new file mode 100644 index 00000000000..6974b05f432 --- /dev/null +++ b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_multiple_unsets.php.inc @@ -0,0 +1,21 @@ + +----- +unset('key1'); +$object->unset('key2'); + +?> diff --git a/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_property_fetch.php.inc b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_property_fetch.php.inc new file mode 100644 index 00000000000..fb7d9fd2671 --- /dev/null +++ b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Fixture/transforms_property_fetch.php.inc @@ -0,0 +1,37 @@ +something['key']; + $this->something['key'] = 42; + isset($this->something['key']); + unset($this->something['key']); + } +} + +?> +----- +something->get('key'); + $this->something->set('key', 42); + $this->something->has('key'); + $this->something->unset('key'); + } +} + +?> diff --git a/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Source/SomeExtendedClass.php b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Source/SomeExtendedClass.php new file mode 100644 index 00000000000..a29665e194e --- /dev/null +++ b/rules-tests/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector/Source/SomeExtendedClass.php @@ -0,0 +1,10 @@ +ruleWithConfiguration(ArrayDimFetchToMethodCallRector::class, [ + new ArrayDimFetchToMethodCall(new ObjectType('SomeClass'), 'get', 'set', 'has', 'unset'), + new ArrayDimFetchToMethodCall(new ObjectType('Container'), 'get'), + ]); +}; diff --git a/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/DimFetchAssignToMethodCallRectorTest.php b/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/DimFetchAssignToMethodCallRectorTest.php deleted file mode 100644 index a77ed8ef798..00000000000 --- a/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/DimFetchAssignToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index 3b66ca0b572..00000000000 --- a/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -//[/]', 'Homepage:default'); - return $routeList; - } -} - -?> ------ -addRoute('//[/]', 'Homepage:default'); - return $routeList; - } -} - -?> diff --git a/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/Source/SomeRoute.php b/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/Source/SomeRoute.php deleted file mode 100644 index be7442b223d..00000000000 --- a/rules-tests/Transform/Rector/Assign/DimFetchAssignToMethodCallRector/Source/SomeRoute.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(DimFetchAssignToMethodCallRector::class) - ->call('configure', [[ - DimFetchAssignToMethodCallRector::DIM_FETCH_ASSIGN_TO_METHOD_CALL => ValueObjectInliner::inline([ - new DimFetchAssignToMethodCall(SomeRouteList::class, SomeRoute::class, 'addRoute'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/fixture2.php.inc b/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/fixture2.php.inc deleted file mode 100644 index 01d6f5e0cfd..00000000000 --- a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -someService = new SomeService; - - $parameters = $container->parameters; -} - -?> ------ -addService('someService', new SomeService); - - $parameters = $container->parameters; -} - -?> diff --git a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/get.php.inc b/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/get.php.inc deleted file mode 100644 index c32aaf437b7..00000000000 --- a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/get.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -someService; - - $parameters = $container->parameters; - - if ($container->someService) { - - } -} - -?> ------ -getService('someService'); - - $parameters = $container->parameters; - - if ($container->getService('someService')) { - - } -} - -?> diff --git a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/klarka.php.inc b/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/klarka.php.inc deleted file mode 100644 index 297087c3489..00000000000 --- a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Fixture/klarka.php.inc +++ /dev/null @@ -1,55 +0,0 @@ -leafBreadcrumbCategory) { - $category = $this->leafBreadcrumbCategory; - } - - while ($this->leafLet !== 5) { - } - - while ($this->existingProperty !== 5) { - } - while ($this->existingProperty) { - } - } -} - -?> ------ -get('leafBreadcrumbCategory')) { - $category = $this->get('leafBreadcrumbCategory'); - } - - while ($this->get('leafLet') !== 5) { - } - - while ($this->existingProperty !== 5) { - } - while ($this->existingProperty) { - } - } -} - -?> diff --git a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/GetAndSetToMethodCallRectorTest.php b/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/GetAndSetToMethodCallRectorTest.php deleted file mode 100644 index fa8bc9eb6ed..00000000000 --- a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/GetAndSetToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Source/Klarka.php b/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Source/Klarka.php deleted file mode 100644 index 8c4da821b88..00000000000 --- a/rules-tests/Transform/Rector/Assign/GetAndSetToMethodCallRector/Source/Klarka.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(GetAndSetToMethodCallRector::class) - ->call('configure', [[ - GetAndSetToMethodCallRector::TYPE_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new GetAndSetToMethodCall(SomeContainer::class, 'getService', 'addService'), - new GetAndSetToMethodCall(Klarka::class, 'get', 'set'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index 77754fe9dd9..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -checkAllowedValues = false; - } -} - -?> ------ -checkDefaultValue(false); - } -} - -?> diff --git a/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Fixture/skip_invalid_type.php.inc b/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Fixture/skip_invalid_type.php.inc deleted file mode 100644 index 553f390e5b5..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Fixture/skip_invalid_type.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -checkAllowedValues = false; - } -} diff --git a/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/PropertyAssignToMethodCallRectorTest.php b/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/PropertyAssignToMethodCallRectorTest.php deleted file mode 100644 index 845f382e89e..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/PropertyAssignToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Source/ChoiceControl.php b/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Source/ChoiceControl.php deleted file mode 100644 index 71d1bbc4c94..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyAssignToMethodCallRector/Source/ChoiceControl.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(PropertyAssignToMethodCallRector::class) - ->call('configure', [[ - PropertyAssignToMethodCallRector::PROPERTY_ASSIGNS_TO_METHODS_CALLS => ValueObjectInliner::inline([ - new PropertyAssignToMethodCall(ChoiceControl::class, 'checkAllowedValues', 'checkDefaultValue'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/factory.php.inc b/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/factory.php.inc deleted file mode 100644 index a46652ba460..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/factory.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - $generator->word - ]; - } -} - -?> ------ - $generator->word() - ]; - } -} - -?> diff --git a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index fabd7fc47c2..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -locale; - - // update locale - $this->locale = $locale; - } -} - -?> ------ -getLocale(); - - // update locale - $this->setLocale($locale); - } -} - -?> diff --git a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/fixture2.php.inc b/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/fixture2.php.inc deleted file mode 100644 index 10343ac6977..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -parameter; - } -} - -?> ------ -getConfig('parameter'); - } -} - -?> diff --git a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/PropertyFetchToMethodCallRectorTest.php b/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/PropertyFetchToMethodCallRectorTest.php deleted file mode 100644 index 139b3175657..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/PropertyFetchToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Source/Generator.php b/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Source/Generator.php deleted file mode 100644 index 0ab60a78102..00000000000 --- a/rules-tests/Transform/Rector/Assign/PropertyFetchToMethodCallRector/Source/Generator.php +++ /dev/null @@ -1,16 +0,0 @@ -services(); - $services->set(PropertyFetchToMethodCallRector::class) - ->call('configure', [[ - PropertyFetchToMethodCallRector::PROPERTIES_TO_METHOD_CALLS => ValueObjectInliner::inline( - [ - - new PropertyFetchToMethodCall(Translator::class, 'locale', 'getLocale', 'setLocale'), - new PropertyFetchToMethodCall(Generator::class, 'word', 'word'), - new PropertyFetchToMethodCall( - 'Rector\Tests\Transform\Rector\Assign\PropertyFetchToMethodCallRector\Fixture\Fixture2', - 'parameter', - 'getConfig', - null, - ['parameter'] - ), - ] - ), - ]]); -}; diff --git a/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/AttributeKeyToClassConstFetchRectorTest.php b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/AttributeKeyToClassConstFetchRectorTest.php new file mode 100644 index 00000000000..cc6802e5379 --- /dev/null +++ b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/AttributeKeyToClassConstFetchRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/not_replacing_existing_constant.php.inc b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/not_replacing_existing_constant.php.inc new file mode 100644 index 00000000000..5c7de7610ea --- /dev/null +++ b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/not_replacing_existing_constant.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/skip_already.php.inc b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/skip_already.php.inc new file mode 100644 index 00000000000..292b812e69a --- /dev/null +++ b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/skip_already.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/some_class.php.inc b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..3423936f438 --- /dev/null +++ b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Fixture/some_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Source/Constant.php b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Source/Constant.php new file mode 100644 index 00000000000..83a9cfc9f4a --- /dev/null +++ b/rules-tests/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector/Source/Constant.php @@ -0,0 +1,10 @@ +ruleWithConfiguration(AttributeKeyToClassConstFetchRector::class, [ + new AttributeKeyToClassConstFetch('Doctrine\ORM\Mapping\Column', 'type', 'Doctrine\DBAL\Types\Types', [ + 'string' => 'STRING', + ]), + new AttributeKeyToClassConstFetch('Rector\Tests\Transform\Rector\Attribute\AttributeKeyToClassConstFetchRector\Source\TestAttribute', 'type', 'Rector\Tests\Transform\Rector\Attribute\AttributeKeyToClassConstFetchRector\Source\Constant', [ + 'value' => 'VALUE', + ]), + ]); +}; diff --git a/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/ClassConstFetchToValueRectorTest.php b/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/ClassConstFetchToValueRectorTest.php deleted file mode 100644 index ad381d08e3b..00000000000 --- a/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/ClassConstFetchToValueRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/Fixture/fixture.php.inc deleted file mode 100644 index 46affc58880..00000000000 --- a/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/Source/OldClassWithConstants.php b/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/Source/OldClassWithConstants.php deleted file mode 100644 index ad6b8a73b12..00000000000 --- a/rules-tests/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector/Source/OldClassWithConstants.php +++ /dev/null @@ -1,18 +0,0 @@ -services(); - $services->set(ClassConstFetchToValueRector::class) - ->call('configure', [[ - ClassConstFetchToValueRector::CLASS_CONST_FETCHES_TO_VALUES => ValueObjectInliner::inline([ - new ClassConstFetchToValue(OldClassWithConstants::class, 'DEVELOPMENT', 'development'), - new ClassConstFetchToValue(OldClassWithConstants::class, 'PRODUCTION', 'production'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/fixture.php.inc deleted file mode 100644 index 7efe02ef704..00000000000 --- a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - - */ - public function getNode(): string - { - return 'Echo_'; - } -} - -?> ------ - diff --git a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/multi_return.php.inc b/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/multi_return.php.inc deleted file mode 100644 index 02410ccf416..00000000000 --- a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/multi_return.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - - */ - public function getNode(): string - { - if (true) { - return 'Echo_'; - } - - return 'Exit_'; - } -} - -?> ------ - diff --git a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/no_return_type.php.inc b/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/no_return_type.php.inc deleted file mode 100644 index 8e43af99cea..00000000000 --- a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/no_return_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/return_class_const_fetch.php.inc b/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/return_class_const_fetch.php.inc deleted file mode 100644 index 62d72573516..00000000000 --- a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Fixture/return_class_const_fetch.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - - */ - public function getNode(): string - { - return self::class; - } -} - -?> ------ - diff --git a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/SingleToManyMethodRectorTest.php b/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/SingleToManyMethodRectorTest.php deleted file mode 100644 index 5d9301d93d3..00000000000 --- a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/SingleToManyMethodRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Source/OneToManyInterface.php b/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Source/OneToManyInterface.php deleted file mode 100644 index 2f0dfa3eea8..00000000000 --- a/rules-tests/Transform/Rector/ClassMethod/SingleToManyMethodRector/Source/OneToManyInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(SingleToManyMethodRector::class) - ->call('configure', [[ - SingleToManyMethodRector::SINGLES_TO_MANY_METHODS => ValueObjectInliner::inline([ - - new SingleToManyMethod(OneToManyInterface::class, 'getNode', 'getNodes'), - - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/ClassMethod/WrapReturnRector/WrapReturnRectorTest.php b/rules-tests/Transform/Rector/ClassMethod/WrapReturnRector/WrapReturnRectorTest.php index 92a5c8fcb76..a7b18a483ca 100644 --- a/rules-tests/Transform/Rector/ClassMethod/WrapReturnRector/WrapReturnRectorTest.php +++ b/rules-tests/Transform/Rector/ClassMethod/WrapReturnRector/WrapReturnRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\ClassMethod\WrapReturnRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class WrapReturnRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/ClassMethod/WrapReturnRector/config/configured_rule.php b/rules-tests/Transform/Rector/ClassMethod/WrapReturnRector/config/configured_rule.php index 6b1ae323476..7c2587fdfeb 100644 --- a/rules-tests/Transform/Rector/ClassMethod/WrapReturnRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/ClassMethod/WrapReturnRector/config/configured_rule.php @@ -2,18 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Tests\Transform\Rector\ClassMethod\WrapReturnRector\Source\SomeReturnClass; use Rector\Transform\Rector\ClassMethod\WrapReturnRector; use Rector\Transform\ValueObject\WrapReturn; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(WrapReturnRector::class) - ->call('configure', [[ - WrapReturnRector::TYPE_METHOD_WRAPS => ValueObjectInliner::inline([ - new WrapReturn(SomeReturnClass::class, 'getItem', true), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(WrapReturnRector::class, [new WrapReturn(SomeReturnClass::class, 'getItem', true)]); }; diff --git a/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/AddInterfaceByParentRectorTest.php b/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/AddInterfaceByParentRectorTest.php deleted file mode 100644 index 33307dc9d8f..00000000000 --- a/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/AddInterfaceByParentRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/Fixture/fixture.php.inc deleted file mode 100644 index 0fcff687865..00000000000 --- a/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/Fixture/skip_existing.php.inc b/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/Fixture/skip_existing.php.inc deleted file mode 100644 index 43222b739c7..00000000000 --- a/rules-tests/Transform/Rector/Class_/AddInterfaceByParentRector/Fixture/skip_existing.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(AddInterfaceByParentRector::class) - ->call('configure', [[ - AddInterfaceByParentRector::INTERFACE_BY_PARENT => [ - SomeParent::class => SomeInterface::class, - ], - ]]); -}; diff --git a/rules-tests/Transform/Rector/Class_/AddInterfaceByTraitRector/AddInterfaceByTraitRectorTest.php b/rules-tests/Transform/Rector/Class_/AddInterfaceByTraitRector/AddInterfaceByTraitRectorTest.php index 0535b247f8a..5ff498279c9 100644 --- a/rules-tests/Transform/Rector/Class_/AddInterfaceByTraitRector/AddInterfaceByTraitRectorTest.php +++ b/rules-tests/Transform/Rector/Class_/AddInterfaceByTraitRector/AddInterfaceByTraitRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\Class_\AddInterfaceByTraitRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AddInterfaceByTraitRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/Class_/AddInterfaceByTraitRector/Fixture/skip_transitional_interface.php.inc b/rules-tests/Transform/Rector/Class_/AddInterfaceByTraitRector/Fixture/skip_transitional_interface.php.inc new file mode 100644 index 00000000000..fa3b2acffad --- /dev/null +++ b/rules-tests/Transform/Rector/Class_/AddInterfaceByTraitRector/Fixture/skip_transitional_interface.php.inc @@ -0,0 +1,11 @@ +services(); - $services->set(AddInterfaceByTraitRector::class) - ->call('configure', [[ - AddInterfaceByTraitRector::INTERFACE_BY_TRAIT => [ - SomeTrait::class => SomeInterface::class, - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(AddInterfaceByTraitRector::class, [ + SomeTrait::class => SomeInterface::class, + AnotherTrait::class => TopMostInterface::class, + ]); }; diff --git a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/ChangeSingletonToServiceRectorTest.php b/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/ChangeSingletonToServiceRectorTest.php deleted file mode 100644 index 8f312beb12a..00000000000 --- a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/ChangeSingletonToServiceRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/fixture.php.inc deleted file mode 100644 index 2c5a39a0ac3..00000000000 --- a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/non_empty_protected_construct.php.inc b/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/non_empty_protected_construct.php.inc deleted file mode 100644 index 3fc12e0207e..00000000000 --- a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/non_empty_protected_construct.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/protected_construct.php.inc b/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/protected_construct.php.inc deleted file mode 100644 index f1abaa82a2f..00000000000 --- a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/protected_construct.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/static_variable.php.inc b/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/static_variable.php.inc deleted file mode 100644 index 99526e6850b..00000000000 --- a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/Fixture/static_variable.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/config/configured_rule.php b/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/config/configured_rule.php deleted file mode 100644 index cbf2069cf8a..00000000000 --- a/rules-tests/Transform/Rector/Class_/ChangeSingletonToServiceRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ChangeSingletonToServiceRector::class); -}; diff --git a/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/Fixture/fixture2.php.inc b/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/Fixture/fixture2.php.inc index a85bbd78a90..61d51360768 100644 --- a/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/Fixture/fixture2.php.inc +++ b/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/Fixture/fixture2.php.inc @@ -17,7 +17,7 @@ namespace Rector\Tests\Transform\Rector\Class_\MergeInterfacesRector\Fixture; use Rector\Tests\Transform\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface; -class SomeClass2 implements Rector\Tests\Transform\Rector\Class_\MergeInterfacesRector\Source\SomeInterface +class SomeClass2 implements \Rector\Tests\Transform\Rector\Class_\MergeInterfacesRector\Source\SomeInterface { } diff --git a/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/Fixture/fqcn.php.inc b/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/Fixture/fqcn.php.inc new file mode 100644 index 00000000000..757cfeda779 --- /dev/null +++ b/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/Fixture/fqcn.php.inc @@ -0,0 +1,23 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/MergeInterfacesRectorTest.php b/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/MergeInterfacesRectorTest.php index 25bc9cd1ca5..14851c2b07a 100644 --- a/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/MergeInterfacesRectorTest.php +++ b/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/MergeInterfacesRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\Class_\MergeInterfacesRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class MergeInterfacesRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/config/configured_rule.php b/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/config/configured_rule.php index eb43fab2ccb..c56297db8c0 100644 --- a/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/Class_/MergeInterfacesRector/config/configured_rule.php @@ -2,17 +2,15 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Tests\Transform\Rector\Class_\MergeInterfacesRector\Source\SomeInterface; use Rector\Tests\Transform\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface; use Rector\Transform\Rector\Class_\MergeInterfacesRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(MergeInterfacesRector::class) - ->call('configure', [[ - MergeInterfacesRector::OLD_TO_NEW_INTERFACES => [ - SomeOldInterface::class => SomeInterface::class, - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(MergeInterfacesRector::class, [ + SomeOldInterface::class => SomeInterface::class, + 'App\Interfaces\SomeInterface' => 'App\Interfaces\SomeOtherInterface', + ]); }; diff --git a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture.php.inc deleted file mode 100644 index 12ed6a3479b..00000000000 --- a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture2.php.inc b/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture2.php.inc deleted file mode 100644 index 3626193be7a..00000000000 --- a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture3.php.inc b/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture3.php.inc deleted file mode 100644 index 1cfa8ed227a..00000000000 --- a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture4.php.inc b/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture4.php.inc deleted file mode 100644 index 3293fc0f3fa..00000000000 --- a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture4.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture5.php.inc b/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture5.php.inc deleted file mode 100644 index 9a48cda6b10..00000000000 --- a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Fixture/fixture5.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/ParentClassToTraitsRectorTest.php b/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/ParentClassToTraitsRectorTest.php deleted file mode 100644 index d4f488e7757..00000000000 --- a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/ParentClassToTraitsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Source/AnotherParentObject.php b/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Source/AnotherParentObject.php deleted file mode 100644 index 074bea7b570..00000000000 --- a/rules-tests/Transform/Rector/Class_/ParentClassToTraitsRector/Source/AnotherParentObject.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(ParentClassToTraitsRector::class) - ->call('configure', [[ - ParentClassToTraitsRector::PARENT_CLASS_TO_TRAITS => ValueObjectInliner::inline([ - new ParentClassToTraits(ParentObject::class, [SomeTrait::class]), - new ParentClassToTraits(AnotherParentObject::class, [SomeTrait::class, SecondTrait::class]), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector/ConstFetchToClassConstFetchTest.php b/rules-tests/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector/ConstFetchToClassConstFetchTest.php new file mode 100644 index 00000000000..ddffb95a70a --- /dev/null +++ b/rules-tests/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector/ConstFetchToClassConstFetchTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector/Fixture/fixture1.php.inc b/rules-tests/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector/Fixture/fixture1.php.inc new file mode 100644 index 00000000000..bab293eeed8 --- /dev/null +++ b/rules-tests/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector/Fixture/fixture1.php.inc @@ -0,0 +1,11 @@ +ruleWithConfiguration(ConstFetchToClassConstFetchRector::class, [ + new ConstFetchToClassConstFetch('CONTEXT_COURSE', 'core\context\course', 'LEVEL'), + ]); +}; diff --git a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/ArgumentFuncCallToMethodCallRectorTest.php b/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/ArgumentFuncCallToMethodCallRectorTest.php deleted file mode 100644 index 0586f54a90d..00000000000 --- a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/ArgumentFuncCallToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/back.php.inc b/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/back.php.inc deleted file mode 100644 index 16254cc5023..00000000000 --- a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/back.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ -redirector->back(); - } - - public function actionWithParams() - { - return $this->redirector->back(200); - } -} - -?> diff --git a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/broadcast.php.inc b/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/broadcast.php.inc deleted file mode 100644 index 7eb0ac54a42..00000000000 --- a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/broadcast.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ -broadcastingFactory->event('template.blade'); - } -} - -?> diff --git a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/config.php.inc b/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/config.php.inc deleted file mode 100644 index 43730c7e823..00000000000 --- a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/config.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - $value]); - } -} - -?> ------ -configRepository->get('value'); - } - - public function actionSet($value) - { - $this->configRepository->set(['value' => $value]); - } -} - -?> diff --git a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/route.php.inc b/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/route.php.inc deleted file mode 100644 index e44e6e2f7e5..00000000000 --- a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/route.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ -urlGenerator->route('template.blade'); - } -} - -?> diff --git a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/session.php.inc b/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/session.php.inc deleted file mode 100644 index 2643861968d..00000000000 --- a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/session.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ -sessionManager; - $this->sessionManager->put(['key']); - $this->sessionManager->get('key', 'value'); - } -} - -?> diff --git a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/skip_static_method.php.inc b/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/skip_static_method.php.inc deleted file mode 100644 index 9f1b2ceb9b6..00000000000 --- a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/Fixture/skip_static_method.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - ------ -viewFactory->make('template.blade'); - $viewFactory = $this->viewFactory; - } -} - -?> diff --git a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/config/configured_rule.php deleted file mode 100644 index 8d2df6bc5f2..00000000000 --- a/rules-tests/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector/config/configured_rule.php +++ /dev/null @@ -1,26 +0,0 @@ -services(); - $services->set(ArgumentFuncCallToMethodCallRector::class) - ->call('configure', [[ - ArgumentFuncCallToMethodCallRector::FUNCTIONS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new ArgumentFuncCallToMethodCall('view', 'Illuminate\Contracts\View\Factory', 'make'), - new ArgumentFuncCallToMethodCall('route', 'Illuminate\Routing\UrlGenerator', 'route'), - new ArgumentFuncCallToMethodCall('back', 'Illuminate\Routing\Redirector', 'back', 'back'), - new ArgumentFuncCallToMethodCall('broadcast', 'Illuminate\Contracts\Broadcasting\Factory', 'event'), - ]), - ArgumentFuncCallToMethodCallRector::ARRAY_FUNCTIONS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new ArrayFuncCallToMethodCall('config', 'Illuminate\Contracts\Config\Repository', 'set', 'get'), - new ArrayFuncCallToMethodCall('session', 'Illuminate\Session\SessionManager', 'put', 'get'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToConstFetchRector/FunctionCallToConstantRectorTest.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToConstFetchRector/FunctionCallToConstantRectorTest.php index b3c5d1416c2..4ea7327537b 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToConstFetchRector/FunctionCallToConstantRectorTest.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToConstFetchRector/FunctionCallToConstantRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\FuncCall\FuncCallToConstFetchRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class FunctionCallToConstantRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToConstFetchRector/config/configured_rule.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToConstFetchRector/config/configured_rule.php index e2376431f8c..5eceafe1391 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToConstFetchRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToConstFetchRector/config/configured_rule.php @@ -2,16 +2,13 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Transform\Rector\FuncCall\FuncCallToConstFetchRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(FuncCallToConstFetchRector::class) - ->call('configure', [[ - FuncCallToConstFetchRector::FUNCTIONS_TO_CONSTANTS => [ - 'php_sapi_name' => 'PHP_SAPI', - 'pi' => 'M_PI', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(FuncCallToConstFetchRector::class, [ + 'php_sapi_name' => 'PHP_SAPI', + 'pi' => 'M_PI', + ]); }; diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/Fixture/skip_bare_function.php.inc b/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/Fixture/skip_bare_function.php.inc index 068d9a8f0ff..742aa4cf9da 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/Fixture/skip_bare_function.php.inc +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/Fixture/skip_bare_function.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\Transform\Rector\FuncCall\FuncCallToMethodCallRector\Fixt use function Rector\Tests\Transform\Rector\FuncCall\FuncCallToMethodCallRector\Source\some_view_function; -function anothehFunction() +function anotherFunction() { some_view_function('template'); } diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/FuncCallToMethodCallRectorTest.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/FuncCallToMethodCallRectorTest.php index a36e4502c6d..a4f7cf5bcaf 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/FuncCallToMethodCallRectorTest.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/FuncCallToMethodCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\FuncCall\FuncCallToMethodCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class FuncCallToMethodCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php index d5c6cf571ae..3acbdab1679 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php @@ -2,27 +2,25 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Tests\Transform\Rector\FuncCall\FuncCallToMethodCallRector\Source\SomeTranslator; use Rector\Transform\Rector\FuncCall\FuncCallToMethodCallRector; use Rector\Transform\ValueObject\FuncCallToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersion::PHP_80); - $services->set(FuncCallToMethodCallRector::class) - ->call('configure', [[ - FuncCallToMethodCallRector::FUNC_CALL_TO_CLASS_METHOD_CALL => ValueObjectInliner::inline([ - new FuncCallToMethodCall('view', 'Namespaced\SomeRenderer', 'render'), + $rectorConfig + ->ruleWithConfiguration(FuncCallToMethodCallRector::class, [ + new FuncCallToMethodCall('view', 'Namespaced\SomeRenderer', 'render'), - new FuncCallToMethodCall('translate', SomeTranslator::class, 'translateMethod'), + new FuncCallToMethodCall('translate', SomeTranslator::class, 'translateMethod'), - new FuncCallToMethodCall( - 'Rector\Tests\Transform\Rector\Function_\FuncCallToMethodCallRector\Source\some_view_function', - 'Namespaced\SomeRenderer', - 'render' - ), - ]), - ]]); + new FuncCallToMethodCall( + 'Rector\Tests\Transform\Rector\Function_\FuncCallToMethodCallRector\Source\some_view_function', + 'Namespaced\SomeRenderer', + 'render' + ), + ]); }; diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToNewRector/FuncCallToNewRectorTest.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToNewRector/FuncCallToNewRectorTest.php index 4021033b6a5..26417ef10e8 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToNewRector/FuncCallToNewRectorTest.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToNewRector/FuncCallToNewRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\FuncCall\FuncCallToNewRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class FuncCallToNewRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToNewRector/config/configured_rule.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToNewRector/config/configured_rule.php index b2dfdb9ea27..471893d4d2d 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToNewRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToNewRector/config/configured_rule.php @@ -2,15 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Transform\Rector\FuncCall\FuncCallToNewRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(FuncCallToNewRector::class) - ->call('configure', [[ - FuncCallToNewRector::FUNCTIONS_TO_NEWS => [ - 'collection' => ['Collection'], - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(FuncCallToNewRector::class, [ + 'collection' => 'Collection', + ]); }; diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToStaticCallRector/FuncCallToStaticCallRectorTest.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToStaticCallRector/FuncCallToStaticCallRectorTest.php index 044df0f9c28..6ae8d93f41f 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToStaticCallRector/FuncCallToStaticCallRectorTest.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToStaticCallRector/FuncCallToStaticCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\FuncCall\FuncCallToStaticCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class FuncCallToStaticCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToStaticCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToStaticCallRector/config/configured_rule.php index cfc69174d5f..ac257a7ce71 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToStaticCallRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToStaticCallRector/config/configured_rule.php @@ -2,18 +2,14 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Transform\Rector\FuncCall\FuncCallToStaticCallRector; use Rector\Transform\ValueObject\FuncCallToStaticCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(FuncCallToStaticCallRector::class) - ->call('configure', [[ - FuncCallToStaticCallRector::FUNC_CALLS_TO_STATIC_CALLS => ValueObjectInliner::inline([ - new FuncCallToStaticCall('view', 'SomeStaticClass', 'render'), - new FuncCallToStaticCall('SomeNamespaced\view', 'AnotherStaticClass', 'render'), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(FuncCallToStaticCallRector::class, [ + new FuncCallToStaticCall('view', 'SomeStaticClass', 'render'), + new FuncCallToStaticCall('SomeNamespaced\view', 'AnotherStaticClass', 'render'), + ]); }; diff --git a/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index 0fbd1cc24f3..00000000000 --- a/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ -hasService('someService'); - $container->removeService('someService'); - } -} - -?> diff --git a/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/Source/LocalContainer.php b/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/Source/LocalContainer.php deleted file mode 100644 index a279a75608b..00000000000 --- a/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/Source/LocalContainer.php +++ /dev/null @@ -1,10 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/config/configured_rule.php deleted file mode 100644 index 693bcc6bc47..00000000000 --- a/rules-tests/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector/config/configured_rule.php +++ /dev/null @@ -1,19 +0,0 @@ -services(); - $services->set(UnsetAndIssetToMethodCallRector::class) - ->call('configure', [[ - UnsetAndIssetToMethodCallRector::ISSET_UNSET_TO_METHOD_CALL => ValueObjectInliner::inline([ - new UnsetAndIssetToMethodCall(LocalContainer::class, 'hasService', 'removeService'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/CallableInMethodCallToVariableRectorTest.php b/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/CallableInMethodCallToVariableRectorTest.php deleted file mode 100644 index 1394a17d5f3..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/CallableInMethodCallToVariableRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Fixture/many_stmts.php.inc b/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Fixture/many_stmts.php.inc deleted file mode 100644 index 5e1c6686016..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Fixture/many_stmts.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -save('key', function ($someValue) { - $someValue= $someValue + 1000; - return $someValue + 10; - }); - } -} - -?> ------ -save('key', $result); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Fixture/some_class.php.inc b/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Fixture/some_class.php.inc deleted file mode 100644 index fb9a607ed80..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -save('key', function () { - return 100; - }); - } -} - -?> ------ -save('key', $result); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Source/DummyCache.php b/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Source/DummyCache.php deleted file mode 100644 index 238536765db..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector/Source/DummyCache.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(CallableInMethodCallToVariableRector::class) - ->call('configure', [[ - CallableInMethodCallToVariableRector::CALLABLE_IN_METHOD_CALL_TO_VARIABLE => ValueObjectInliner::inline([ - new CallableInMethodCallToVariable(DummyCache::class, 'save', 1), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/Fixture/fixture.php.inc deleted file mode 100644 index 007abc4eea3..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -setInject(); - } -} - -?> ------ -addTag('inject'); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/MethodCallToAnotherMethodCallWithArgumentsRectorTest.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/MethodCallToAnotherMethodCallWithArgumentsRectorTest.php deleted file mode 100644 index 996e4fbe7f4..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/MethodCallToAnotherMethodCallWithArgumentsRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/Source/NetteServiceDefinition.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/Source/NetteServiceDefinition.php deleted file mode 100644 index 63157044e2f..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/Source/NetteServiceDefinition.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - - $configuration = ValueObjectInliner::inline([ - new MethodCallToAnotherMethodCallWithArguments( - NetteServiceDefinition::class, - 'setInject', - 'addTag', - ['inject'] - ), - ]); - - $services->set(MethodCallToAnotherMethodCallWithArgumentsRector::class) - ->call('configure', [[ - MethodCallToAnotherMethodCallWithArgumentsRector::METHOD_CALL_RENAMES_WITH_ADDED_ARGUMENTS => $configuration, - ]]); -}; diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/Fixture/some_class.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..5f0e47b06ab --- /dev/null +++ b/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/Fixture/some_class.php.inc @@ -0,0 +1,31 @@ +render('some_template'); + } +} + +?> +----- + diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/MethodCallToFuncCallRectorTest.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/MethodCallToFuncCallRectorTest.php new file mode 100644 index 00000000000..305ef5b28c6 --- /dev/null +++ b/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/MethodCallToFuncCallRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/Source/ParentControllerWithRender.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/Source/ParentControllerWithRender.php new file mode 100644 index 00000000000..31659e9d0af --- /dev/null +++ b/rules-tests/Transform/Rector/MethodCall/MethodCallToFuncCallRector/Source/ParentControllerWithRender.php @@ -0,0 +1,10 @@ +ruleWithConfiguration(MethodCallToFuncCallRector::class, [ + new MethodCallToFuncCall( + 'Rector\Tests\Transform\Rector\MethodCall\MethodCallToFuncCallRector\Source\ParentControllerWithRender', + 'render', + 'view' + ), + ]); +}; diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/re_use_existing_property.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/re_use_existing_property.php.inc deleted file mode 100644 index b6958d4d818..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/re_use_existing_property.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -firstDependency->go(); - } -} - -?> ------ -secondDependency->away(); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/skip_different_method.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/skip_different_method.php.inc deleted file mode 100644 index 1e14acce019..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/skip_different_method.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -firstDependency->another(); - } -} diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/some_class.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/some_class.php.inc deleted file mode 100644 index caa843d2725..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -firstDependency->go(); - } -} - -?> ------ -secondDependency->away(); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_dependency.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_dependency.php.inc deleted file mode 100644 index e9d0a54b804..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_dependency.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -firstDependency->go(); - } -} - -?> ------ -secondDependency->away(); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_inject_dependency.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_inject_dependency.php.inc deleted file mode 100644 index 1eedb755e8e..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_inject_dependency.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -firstDependency->go(); - } -} - -?> ------ -secondDependency->away(); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_inject_dependency_same_type_different_name.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_inject_dependency_same_type_different_name.php.inc deleted file mode 100644 index dc4e6891450..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Fixture/with_parent_inject_dependency_same_type_different_name.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -firstDependency->go(); - } -} - -?> ------ -wooohoooo->away(); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/MethodCallToMethodCallRectorTest.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/MethodCallToMethodCallRectorTest.php deleted file mode 100644 index 53489703a93..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/MethodCallToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Source/FirstDependency.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Source/FirstDependency.php deleted file mode 100644 index 1e73887dbce..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToMethodCallRector/Source/FirstDependency.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(MethodCallToMethodCallRector::class) - ->call('configure', [[ - MethodCallToMethodCallRector::METHOD_CALLS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new MethodCallToMethodCall(FirstDependency::class, 'go', SecondDependency::class, 'away'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/Fixture/fixture.php.inc deleted file mode 100644 index 27b238856b3..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,27 +0,0 @@ -getEntityManager(); - } -} - -?> ------ -entityManager; - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/MethodCallToPropertyFetchRectorTest.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/MethodCallToPropertyFetchRectorTest.php deleted file mode 100644 index b38713a78bc..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/MethodCallToPropertyFetchRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/config/configured_rule.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/config/configured_rule.php deleted file mode 100644 index 3b674a1d564..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector/config/configured_rule.php +++ /dev/null @@ -1,16 +0,0 @@ -services(); - $services->set(MethodCallToPropertyFetchRector::class) - ->call('configure', [[ - MethodCallToPropertyFetchRector::METHOD_CALL_TO_PROPERTY_FETCHES => [ - 'getEntityManager' => 'entityManager', - ], - ]]); -}; diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToStaticCallRector/MethodCallToStaticCallRectorTest.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToStaticCallRector/MethodCallToStaticCallRectorTest.php index df6011e658f..6226b10ff9f 100644 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToStaticCallRector/MethodCallToStaticCallRectorTest.php +++ b/rules-tests/Transform/Rector/MethodCall/MethodCallToStaticCallRector/MethodCallToStaticCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\MethodCall\MethodCallToStaticCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class MethodCallToStaticCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/MethodCall/MethodCallToStaticCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/MethodCall/MethodCallToStaticCallRector/config/configured_rule.php index 6af0d670792..7221b0957a5 100644 --- a/rules-tests/Transform/Rector/MethodCall/MethodCallToStaticCallRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/MethodCall/MethodCallToStaticCallRector/config/configured_rule.php @@ -2,20 +2,15 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Tests\Transform\Rector\MethodCall\MethodCallToStaticCallRector\Source\AnotherDependency; use Rector\Transform\Rector\MethodCall\MethodCallToStaticCallRector; use Rector\Transform\ValueObject\MethodCallToStaticCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(MethodCallToStaticCallRector::class) - ->call('configure', [[ - MethodCallToStaticCallRector::METHOD_CALLS_TO_STATIC_CALLS => ValueObjectInliner::inline([ +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(MethodCallToStaticCallRector::class, [ + new MethodCallToStaticCall(AnotherDependency::class, 'process', 'StaticCaller', 'anotherMethod'), - new MethodCallToStaticCall(AnotherDependency::class, 'process', 'StaticCaller', 'anotherMethod'), - - ]), - ]]); + ]); }; diff --git a/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/Fixture/fixture.php.inc deleted file mode 100644 index 2fb203e7771..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -someMethod(); - } -} - -?> ------ -someProperty->someMethod(); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/ReplaceParentCallByPropertyCallRectorTest.php b/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/ReplaceParentCallByPropertyCallRectorTest.php deleted file mode 100644 index dea8092c5f0..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/ReplaceParentCallByPropertyCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/Source/TypeClassToReplaceMethodCallBy.php b/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/Source/TypeClassToReplaceMethodCallBy.php deleted file mode 100644 index 54e6663413b..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector/Source/TypeClassToReplaceMethodCallBy.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(ReplaceParentCallByPropertyCallRector::class) - ->call('configure', [[ - ReplaceParentCallByPropertyCallRector::PARENT_CALLS_TO_PROPERTIES => ValueObjectInliner::inline([ - new ReplaceParentCallByPropertyCall( - TypeClassToReplaceMethodCallBy::class, - 'someMethod', - 'someProperty' - ), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/Fixture/fixture.php.inc deleted file mode 100644 index 264dd690e8e..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,53 +0,0 @@ -firstService = $firstService; - } - - public function run() - { - $anotherService = $this->firstService->getAnotherService(); - $anotherService->run(); - } -} - -?> ------ -firstService = $firstService; - } - - public function run() - { - $anotherService = $this->anotherService; - $anotherService->run(); - } -} - -?> diff --git a/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/ServiceGetterToConstructorInjectionRectorTest.php b/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/ServiceGetterToConstructorInjectionRectorTest.php deleted file mode 100644 index 3f84d987e6b..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/ServiceGetterToConstructorInjectionRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/Source/AnotherService.php b/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/Source/AnotherService.php deleted file mode 100644 index 2dab519aec1..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/Source/AnotherService.php +++ /dev/null @@ -1,12 +0,0 @@ -anotherService = $anotherService; - } - - public function getAnotherService(): AnotherService - { - return $this->anotherService; - } -} diff --git a/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/config/configured_rule.php b/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/config/configured_rule.php deleted file mode 100644 index 2525d9c3a05..00000000000 --- a/rules-tests/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector/config/configured_rule.php +++ /dev/null @@ -1,27 +0,0 @@ -services(); - $services->set(ServiceGetterToConstructorInjectionRector::class) - ->call( - 'configure', - [[ - ServiceGetterToConstructorInjectionRector::METHOD_CALL_TO_SERVICES => ValueObjectInliner::inline([ - new ServiceGetterToConstructorInjection( - FirstService::class, - 'getAnotherService', - AnotherService::class - ), - ]), - ]] - ); -}; diff --git a/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/Fixture/some_class.php.inc b/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/Fixture/some_class.php.inc deleted file mode 100644 index cbbd5dc97b8..00000000000 --- a/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ -usePutenv(); - } -} - -?> diff --git a/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/NewArgToMethodCallRectorTest.php b/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/NewArgToMethodCallRectorTest.php deleted file mode 100644 index 35d6c466949..00000000000 --- a/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/NewArgToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/Source/SomeDotenv.php b/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/Source/SomeDotenv.php deleted file mode 100644 index da93c4acbb2..00000000000 --- a/rules-tests/Transform/Rector/New_/NewArgToMethodCallRector/Source/SomeDotenv.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(NewArgToMethodCallRector::class) - ->call('configure', [[ - NewArgToMethodCallRector::NEW_ARGS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new NewArgToMethodCall(SomeDotenv::class, true, 'usePutenv'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/multi_assign.php.inc b/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/multi_assign.php.inc deleted file mode 100644 index 6d1b676bb99..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/multi_assign.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -validate(100000); - } -} - -?> ------ -dummyValidator->validate(100000); - } -} - -?> diff --git a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/skip_property_call.php.inc b/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/skip_property_call.php.inc deleted file mode 100644 index 9262f27cdce..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/skip_property_call.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -dummyValidator->validate(10000); - } -} diff --git a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/some_class.php.inc b/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/some_class.php.inc deleted file mode 100644 index 4b3ecd4b208..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -validate(100000); - } -} - -?> ------ -dummyValidator->validate(100000); - } -} - -?> diff --git a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/FixturePhp80/some_class_to_promoted_property.php.inc b/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/FixturePhp80/some_class_to_promoted_property.php.inc deleted file mode 100644 index 038e79c25f3..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/FixturePhp80/some_class_to_promoted_property.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -validate(100000); - } -} - -?> ------ -dummyValidator->validate(100000); - } -} - -?> diff --git a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/NewToConstructorInjectionRectorTest.php b/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/NewToConstructorInjectionRectorTest.php deleted file mode 100644 index 5ad3fd3df8e..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/NewToConstructorInjectionRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Php80Test.php b/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Php80Test.php deleted file mode 100644 index 2351ee6a118..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Php80Test.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Source/DummyValidator.php b/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Source/DummyValidator.php deleted file mode 100644 index bdab113e0c8..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToConstructorInjectionRector/Source/DummyValidator.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(NewToConstructorInjectionRector::class) - ->call('configure', [[ - NewToConstructorInjectionRector::TYPES_TO_CONSTRUCTOR_INJECTION => [DummyValidator::class], - ]]); -}; diff --git a/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index 2ecd6cdff80..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ -myClassFactory->create('abcd'); - $class = $this->myClassFactory->create('abcd'); - } -} -?> diff --git a/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Fixture/fixture2.php.inc b/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Fixture/fixture2.php.inc deleted file mode 100644 index ba4614cd794..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -mySomeFactory = $mySomeFactory; - } - public function default() - { - new MyClass('abcd'); - $class = new MyClass('abcd'); - } -} -?> ------ -mySomeFactory = $mySomeFactory; - } - public function default() - { - $this->mySomeFactory->create('abcd'); - $class = $this->mySomeFactory->create('abcd'); - } -} -?> diff --git a/rules-tests/Transform/Rector/New_/NewToMethodCallRector/NewToMethodCallRectorTest.php b/rules-tests/Transform/Rector/New_/NewToMethodCallRector/NewToMethodCallRectorTest.php deleted file mode 100644 index bb6c5727814..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToMethodCallRector/NewToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Source/MyClass.php b/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Source/MyClass.php deleted file mode 100644 index ca82a84ce56..00000000000 --- a/rules-tests/Transform/Rector/New_/NewToMethodCallRector/Source/MyClass.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(NewToMethodCallRector::class) - ->call('configure', [[ - NewToMethodCallRector::NEWS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new NewToMethodCall(MyClass::class, MyClassFactory::class, 'create'), - ]), - ]]); -}; diff --git a/rules-tests/Transform/Rector/New_/NewToStaticCallRector/NewToStaticCallRectorTest.php b/rules-tests/Transform/Rector/New_/NewToStaticCallRector/NewToStaticCallRectorTest.php index a85b345cd44..460d9e2ff17 100644 --- a/rules-tests/Transform/Rector/New_/NewToStaticCallRector/NewToStaticCallRectorTest.php +++ b/rules-tests/Transform/Rector/New_/NewToStaticCallRector/NewToStaticCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\New_\NewToStaticCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class NewToStaticCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/New_/NewToStaticCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/New_/NewToStaticCallRector/config/configured_rule.php index f379261aa56..9281f6b8a44 100644 --- a/rules-tests/Transform/Rector/New_/NewToStaticCallRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/New_/NewToStaticCallRector/config/configured_rule.php @@ -2,21 +2,16 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Tests\Transform\Rector\New_\NewToStaticCallRector\Source\FromNewClass; use Rector\Tests\Transform\Rector\New_\NewToStaticCallRector\Source\IntoStaticClass; use Rector\Transform\Rector\New_\NewToStaticCallRector; use Rector\Transform\ValueObject\NewToStaticCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(NewToStaticCallRector::class) - ->call('configure', [[ - NewToStaticCallRector::TYPE_TO_STATIC_CALLS => ValueObjectInliner::inline([ - - new NewToStaticCall(FromNewClass::class, IntoStaticClass::class, 'run'), - - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration( + NewToStaticCallRector::class, + [new NewToStaticCall(FromNewClass::class, IntoStaticClass::class, 'run')] + ); }; diff --git a/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..cbf3da8589b --- /dev/null +++ b/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Fixture/fixture.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Fixture/skip_non_matching_values.php.inc b/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Fixture/skip_non_matching_values.php.inc new file mode 100644 index 00000000000..eca2dc7b94a --- /dev/null +++ b/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Fixture/skip_non_matching_values.php.inc @@ -0,0 +1,9 @@ + diff --git a/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/ScalarValueToConstFetchRectorTest.php b/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/ScalarValueToConstFetchRectorTest.php new file mode 100644 index 00000000000..a91675385f5 --- /dev/null +++ b/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/ScalarValueToConstFetchRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Source/ClassWithConst.php b/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Source/ClassWithConst.php new file mode 100644 index 00000000000..590eb770379 --- /dev/null +++ b/rules-tests/Transform/Rector/Scalar/ScalarValueToConstFetchRector/Source/ClassWithConst.php @@ -0,0 +1,12 @@ +ruleWithConfiguration( + ScalarValueToConstFetchRector::class, + [ + new ScalarValueToConstFetch( + new Int_(10), + new ClassConstFetch(new FullyQualified(ClassWithConst::class), new Identifier('FOOBAR_INT')) + ), + new ScalarValueToConstFetch( + new Float_(10.1), + new ClassConstFetch(new FullyQualified(ClassWithConst::class), new Identifier('FOOBAR_FLOAT')) + ), + new ScalarValueToConstFetch( + new String_('ABC'), + new ClassConstFetch(new FullyQualified(ClassWithConst::class), new Identifier('FOOBAR_STRING')) + ), + ] + ); +}; diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/Source/SomeOldStaticClass.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/Source/SomeOldStaticClass.php index 15f063d81df..16b4164a18c 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/Source/SomeOldStaticClass.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/Source/SomeOldStaticClass.php @@ -6,5 +6,7 @@ final class SomeOldStaticClass { - + public static function render() + { + } } diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/StaticCallToFuncCallRectorTest.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/StaticCallToFuncCallRectorTest.php index 1cd990adf88..7f2a30cdf77 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/StaticCallToFuncCallRectorTest.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/StaticCallToFuncCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\StaticCall\StaticCallToFuncCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StaticCallToFuncCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/config/configured_rule.php index edd92a9e4a9..8bee2f7255c 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToFuncCallRector/config/configured_rule.php @@ -2,18 +2,15 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Tests\Transform\Rector\StaticCall\StaticCallToFuncCallRector\Source\SomeOldStaticClass; use Rector\Transform\Rector\StaticCall\StaticCallToFuncCallRector; use Rector\Transform\ValueObject\StaticCallToFuncCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StaticCallToFuncCallRector::class) - ->call('configure', [[ - StaticCallToFuncCallRector::STATIC_CALLS_TO_FUNCTIONS => ValueObjectInliner::inline([ - new StaticCallToFuncCall(SomeOldStaticClass::class, 'render', 'view'), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration( + StaticCallToFuncCallRector::class, + [new StaticCallToFuncCall(SomeOldStaticClass::class, 'render', 'view')] + ); }; diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/extends_parent_with_private_construct.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/extends_parent_with_private_construct.php.inc new file mode 100644 index 00000000000..ebc453d9a4c --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/extends_parent_with_private_construct.php.inc @@ -0,0 +1,46 @@ + $this->user_id ?? App::get(MissingValue::class), + ]; + } +} + +?> +----- + $this->user_id ?? $this->application->get(MissingValue::class), + ]; + } +} + +?> diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/fixture.php.inc index 799581f1135..b92c4f6e5e5 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/fixture.php.inc +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/fixture.php.inc @@ -22,12 +22,12 @@ use Nette\Utils\FileSystem; class SomeClass { - public function __construct(private \Symplify\SmartFileSystem\SmartFileSystem $smartFileSystem) + public function __construct(private \Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector\Source\TargetFileSystem $targetFileSystem) { } public function run() { - return $this->smartFileSystem->dumpFile('file', 'content'); + return $this->targetFileSystem->dumpFile('file', 'content'); } } diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/follow_parent_construct.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/follow_parent_construct.php.inc new file mode 100644 index 00000000000..c68300797d4 --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/follow_parent_construct.php.inc @@ -0,0 +1,47 @@ + $this->user_id ?? App::get(MissingValue::class), + ]; + } +} + +?> +----- + $this->user_id ?? $this->application->get(MissingValue::class), + ]; + } +} + +?> diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/follow_parent_empty_construct.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/follow_parent_empty_construct.php.inc new file mode 100644 index 00000000000..af9f208f319 --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/follow_parent_empty_construct.php.inc @@ -0,0 +1,47 @@ + $this->user_id ?? App::get(MissingValue::class), + ]; + } +} + +?> +----- + $this->user_id ?? $this->application->get(MissingValue::class), + ]; + } +} + +?> diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/in_constructor.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/in_constructor.php.inc new file mode 100644 index 00000000000..c47a4d1d480 --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/in_constructor.php.inc @@ -0,0 +1,31 @@ + +----- +dumpFile('file', 'content'); + } +} + +?> diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/in_consturctor.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/in_consturctor.php.inc deleted file mode 100644 index a8e574d6f7f..00000000000 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/in_consturctor.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ -dumpFile('file', 'content'); - } -} - -?> diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/instant_make_in_static_method.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/instant_make_in_static_method.php.inc index 8bfb660d7b4..18d19123c67 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/instant_make_in_static_method.php.inc +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Fixture/instant_make_in_static_method.php.inc @@ -2,12 +2,15 @@ namespace Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector\Fixture; +use Illuminate\Support\Facades\Response; use Nette\Utils\FileSystem; class InstantMakeInStaticMethod { public static function run() { + Response::view('example', ['new_example' => 123])->render(); + return FileSystem::write('file', 'content'); } } @@ -18,13 +21,16 @@ class InstantMakeInStaticMethod namespace Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector\Fixture; +use Illuminate\Support\Facades\Response; use Nette\Utils\FileSystem; class InstantMakeInStaticMethod { public static function run() { - return (new \Symplify\SmartFileSystem\SmartFileSystem())->dumpFile('file', 'content'); + (new \Illuminate\Contracts\Routing\ResponseFactory())->view('example', ['new_example' => 123])->render(); + + return (new \Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector\Source\TargetFileSystem())->dumpFile('file', 'content'); } } diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/FixturePhp74/follow_parent_construct_on_php74.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/FixturePhp74/follow_parent_construct_on_php74.php.inc new file mode 100644 index 00000000000..39bb72454ee --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/FixturePhp74/follow_parent_construct_on_php74.php.inc @@ -0,0 +1,49 @@ + $this->user_id ?? App::get(MissingValue::class), + ]; + } +} + +?> +----- +application = $application; + } + public function toArray( + Request $request, + ): array { + return [ + 'user_id' => $this->user_id ?? $this->application->get(MissingValue::class), + ]; + } +} + +?> diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/FixturePhp74/follow_parent_empty_construct_on_php74.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/FixturePhp74/follow_parent_empty_construct_on_php74.php.inc new file mode 100644 index 00000000000..a3e30933dfb --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/FixturePhp74/follow_parent_empty_construct_on_php74.php.inc @@ -0,0 +1,49 @@ + $this->user_id ?? App::get(MissingValue::class), + ]; + } +} + +?> +----- +application = $application; + } + public function toArray( + Request $request, + ): array { + return [ + 'user_id' => $this->user_id ?? $this->application->get(MissingValue::class), + ]; + } +} + +?> diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/ClassWithFileSystem.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/ClassWithFileSystem.php index 62b38b00f87..7718207b12b 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/ClassWithFileSystem.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/ClassWithFileSystem.php @@ -4,12 +4,10 @@ namespace Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector\Source; -use Symplify\SmartFileSystem\SmartFileSystem; - abstract class ClassWithFileSystem { /** - * @var SmartFileSystem + * @var TargetFileSystem */ public $smartFileSystemProperty; } diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/ClassWithFileSystemMethod.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/ClassWithFileSystemMethod.php index 8090cda0d75..1826323410c 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/ClassWithFileSystemMethod.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/ClassWithFileSystemMethod.php @@ -4,12 +4,10 @@ namespace Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector\Source; -use Symplify\SmartFileSystem\SmartFileSystem; - abstract class ClassWithFileSystemMethod { - public function getSmartFileSystem(): SmartFileSystem + public function getSmartFileSystem(): TargetFileSystem { - return new SmartFileSystem(); + return new TargetFileSystem(); } } diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/JsonResource.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/JsonResource.php new file mode 100644 index 00000000000..6033d87e96a --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/JsonResource.php @@ -0,0 +1,15 @@ +resource = $resource; + } +} diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/SomeResource.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/SomeResource.php new file mode 100644 index 00000000000..a4ea1a30416 --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/Source/SomeResource.php @@ -0,0 +1,12 @@ +resource = new \SimpleXMLElement(''); + } +} diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/StaticCallToMethodCallRectorOnPhp74Test.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/StaticCallToMethodCallRectorOnPhp74Test.php new file mode 100644 index 00000000000..8cb1fd30f64 --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/StaticCallToMethodCallRectorOnPhp74Test.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php74.php'; + } +} diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/StaticCallToMethodCallRectorTest.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/StaticCallToMethodCallRectorTest.php index 34925ff263c..b81a2775c16 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/StaticCallToMethodCallRectorTest.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/StaticCallToMethodCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StaticCallToMethodCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/config/configured_rule.php index 7c20771641a..27840daff3a 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/config/configured_rule.php @@ -2,28 +2,29 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; +use Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector\Source\TargetFileSystem; use Rector\Transform\Rector\StaticCall\StaticCallToMethodCallRector; use Rector\Transform\ValueObject\StaticCallToMethodCall; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StaticCallToMethodCallRector::class) - ->call('configure', [[ - StaticCallToMethodCallRector::STATIC_CALLS_TO_METHOD_CALLS => ValueObjectInliner::inline([ - new StaticCallToMethodCall( - 'Nette\Utils\FileSystem', - 'write', - 'Symplify\SmartFileSystem\SmartFileSystem', - 'dumpFile' - ), - new StaticCallToMethodCall( - 'Illuminate\Support\Facades\Response', - '*', - 'Illuminate\Contracts\Routing\ResponseFactory', - '*' - ), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersionFeature::PROPERTY_PROMOTION); + + $rectorConfig + ->ruleWithConfiguration(StaticCallToMethodCallRector::class, [ + new StaticCallToMethodCall('Nette\Utils\FileSystem', 'write', TargetFileSystem::class, 'dumpFile'), + new StaticCallToMethodCall( + 'Illuminate\Support\Facades\Response', + '*', + 'Illuminate\Contracts\Routing\ResponseFactory', + '*' + ), + new StaticCallToMethodCall( + 'Illuminate\Support\Facades\App', + '*', + 'Illuminate\Foundation\Application', + '*' + ), + ]); }; diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/config/configured_rule_php74.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/config/configured_rule_php74.php new file mode 100644 index 00000000000..14b7207f17a --- /dev/null +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToMethodCallRector/config/configured_rule_php74.php @@ -0,0 +1,22 @@ +phpVersion(PhpVersionFeature::TYPED_PROPERTIES); + + $rectorConfig + ->ruleWithConfiguration(StaticCallToMethodCallRector::class, [ + new StaticCallToMethodCall( + 'Illuminate\Support\Facades\App', + '*', + 'Illuminate\Foundation\Application', + '*' + ), + ]); +}; diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/Fixture/some_class.php.inc b/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/Fixture/some_class.php.inc index d8b444e293f..6096090f591 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/Fixture/some_class.php.inc +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/Fixture/some_class.php.inc @@ -8,7 +8,7 @@ class SomeClass { public function run() { - $dotenv = SomeJsonResponse::create(true); + $dotenv = SomeJsonResponse::create(['foo' => 'bar'], Response::HTTP_OK); } } @@ -24,7 +24,7 @@ class SomeClass { public function run() { - $dotenv = new \Rector\Tests\Transform\Rector\StaticCall\StaticCallToNewRector\Source\SomeJsonResponse(); + $dotenv = new \Rector\Tests\Transform\Rector\StaticCall\StaticCallToNewRector\Source\SomeJsonResponse(['foo' => 'bar'], Response::HTTP_OK); } } diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/StaticCallToNewRectorTest.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/StaticCallToNewRectorTest.php index eb403bf2ded..ca4f4c68286 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/StaticCallToNewRectorTest.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/StaticCallToNewRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Transform\Rector\StaticCall\StaticCallToNewRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class StaticCallToNewRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/config/configured_rule.php b/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/config/configured_rule.php index ed1a1d82a73..18bc72b4754 100644 --- a/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/StaticCall/StaticCallToNewRector/config/configured_rule.php @@ -2,18 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Tests\Transform\Rector\StaticCall\StaticCallToNewRector\Source\SomeJsonResponse; use Rector\Transform\Rector\StaticCall\StaticCallToNewRector; use Rector\Transform\ValueObject\StaticCallToNew; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StaticCallToNewRector::class) - ->call('configure', [[ - StaticCallToNewRector::STATIC_CALLS_TO_NEWS => ValueObjectInliner::inline([ - new StaticCallToNew(SomeJsonResponse::class, 'create'), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(StaticCallToNewRector::class, [new StaticCallToNew(SomeJsonResponse::class, 'create')]); }; diff --git a/rules-tests/Transform/Rector/String_/StringToClassConstantRector/Fixture/skip_not_found_in_enum_case.php.inc b/rules-tests/Transform/Rector/String_/StringToClassConstantRector/Fixture/skip_not_found_in_enum_case.php.inc new file mode 100644 index 00000000000..5101b0fed1a --- /dev/null +++ b/rules-tests/Transform/Rector/String_/StringToClassConstantRector/Fixture/skip_not_found_in_enum_case.php.inc @@ -0,0 +1,8 @@ +doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Transform/Rector/String_/StringToClassConstantRector/config/configured_rule.php b/rules-tests/Transform/Rector/String_/StringToClassConstantRector/config/configured_rule.php index 70eaf6f0d6d..ee63c98a74f 100644 --- a/rules-tests/Transform/Rector/String_/StringToClassConstantRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/String_/StringToClassConstantRector/config/configured_rule.php @@ -2,20 +2,16 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Transform\Rector\String_\StringToClassConstantRector; use Rector\Transform\ValueObject\StringToClassConstant; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(StringToClassConstantRector::class) - ->call('configure', [[ - StringToClassConstantRector::STRINGS_TO_CLASS_CONSTANTS => ValueObjectInliner::inline([ +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(StringToClassConstantRector::class, [ - new StringToClassConstant('compiler.post_dump', 'Yet\AnotherClass', 'CONSTANT'), - new StringToClassConstant('compiler.to_class', 'Yet\AnotherClass', 'class'), + new StringToClassConstant('compiler.post_dump', 'Yet\AnotherClass', 'CONSTANT'), + new StringToClassConstant('compiler.to_class', 'Yet\AnotherClass', 'class'), - ]), - ]]); + ]); }; diff --git a/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/Fixture/fixture.php.inc b/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/Fixture/fixture.php.inc deleted file mode 100644 index ee5f47352aa..00000000000 --- a/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,25 +0,0 @@ -__toString(); -} - -?> ------ -getPath(); - - $stringValue = $configCache->getPath(); -} - -?> diff --git a/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/ToStringToMethodCallRectorTest.php b/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/ToStringToMethodCallRectorTest.php deleted file mode 100644 index 18509f2e4fb..00000000000 --- a/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/ToStringToMethodCallRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/config/configured_rule.php deleted file mode 100644 index 5f6e50dee52..00000000000 --- a/rules-tests/Transform/Rector/String_/ToStringToMethodCallRector/config/configured_rule.php +++ /dev/null @@ -1,17 +0,0 @@ -services(); - $services->set(ToStringToMethodCallRector::class) - ->call('configure', [[ - ToStringToMethodCallRector::METHOD_NAMES_BY_TYPE => [ - ConfigCache::class => 'getPath', - ], - ]]); -}; diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/AddArrowFunctionReturnTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/AddArrowFunctionReturnTypeRectorTest.php new file mode 100644 index 00000000000..153e1423677 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/AddArrowFunctionReturnTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/union_types.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..14daa76d0d2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + rand(0, 1) ? []: null; + } +} + +?> +----- + rand(0, 1) ? []: null; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_array.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_array.php.inc new file mode 100644 index 00000000000..44794b4809b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_array.php.inc @@ -0,0 +1,31 @@ + [1, 2, 3]; + } +} + +?> +----- + [1, 2, 3]; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_by_array_shape_type.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_by_array_shape_type.php.inc new file mode 100644 index 00000000000..c26c1fe0509 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_by_array_shape_type.php.inc @@ -0,0 +1,33 @@ + $values + */ + private function foo(array $values): void + { + $bars = array_map(fn($value) => $value['bar'], $values); + } +} + +?> +----- + $values + */ + private function foo(array $values): void + { + $bars = array_map(fn($value): int => $value['bar'], $values); + } +} + +?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_new.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_new.php.inc new file mode 100644 index 00000000000..a40bfba87fb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_new.php.inc @@ -0,0 +1,31 @@ + new \stdClass(); + } +} + +?> +----- + new \stdClass(); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_docblock.php.inc new file mode 100644 index 00000000000..6e9b35607ba --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_docblock.php.inc @@ -0,0 +1,19 @@ + $this->execute(); + } + + /** + * @return string + */ + private function execute() + { + return 1; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_return_declaration_exists.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_return_declaration_exists.php.inc new file mode 100644 index 00000000000..0eea3d8b087 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_return_declaration_exists.php.inc @@ -0,0 +1,11 @@ + []; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_void_return.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_void_return.php.inc new file mode 100644 index 00000000000..f6ff2d8360c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_void_return.php.inc @@ -0,0 +1,16 @@ + $this->doNothing($a); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_void_return_on_callback_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_void_return_on_callback_docblock.php.inc new file mode 100644 index 00000000000..32e58b32963 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/skip_void_return_on_callback_docblock.php.inc @@ -0,0 +1,26 @@ + $this->executesCallback(fn() => $this->doNothing('a')); + } + + /** + * @template T + * @param callable(): T $callback + * @return T + */ + public function executesCallback(callable $callback): mixed + { + return $callback(); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/union_type_arrow_function.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/union_type_arrow_function.php.inc new file mode 100644 index 00000000000..49d4c6e6204 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/union_type_arrow_function.php.inc @@ -0,0 +1,27 @@ + rand(0, 1) ? true : (rand(0, 1) ? [] : 0); + } +} + +?> +----- + rand(0, 1) ? true : (rand(0, 1) ? [] : 0); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/union_type_with_anonymous_class.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/union_type_with_anonymous_class.php.inc new file mode 100644 index 00000000000..a91cab03c49 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/union_type_with_anonymous_class.php.inc @@ -0,0 +1,27 @@ + rand(0, 1) ? new class {} : true; + } +} + +?> +----- + rand(0, 1) ? new class {} : true; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/config/union_types.php b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/config/union_types.php new file mode 100644 index 00000000000..e410d41b58f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/config/union_types.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersionFeature::UNION_TYPES); + $rectorConfig->rule(AddArrowFunctionReturnTypeRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/BinaryOpNullableToInstanceofRectorTest.php b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/BinaryOpNullableToInstanceofRectorTest.php new file mode 100644 index 00000000000..ebd394eb86a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/BinaryOpNullableToInstanceofRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or.php.inc new file mode 100644 index 00000000000..8fa43bf1987 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or.php.inc @@ -0,0 +1,33 @@ +someMethod()) { + return 'yes'; + } + + return 'no'; +} + +?> +----- +someMethod()) { + return 'yes'; + } + + return 'no'; +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or_negation.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or_negation.php.inc new file mode 100644 index 00000000000..60b41c67d31 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or_negation.php.inc @@ -0,0 +1,33 @@ +someMethod()) { + return 'yes'; + } + + return 'no'; +} + +?> +----- +someMethod()) { + return 'yes'; + } + + return 'no'; +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or_negation_other_direction.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or_negation_other_direction.php.inc new file mode 100644 index 00000000000..cee316ea525 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/binary_or_negation_other_direction.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/both_negation.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/both_negation.php.inc new file mode 100644 index 00000000000..71b74672225 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/both_negation.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/both_truthy.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/both_truthy.php.inc new file mode 100644 index 00000000000..d1648ac1340 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/both_truthy.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/other_direction.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/other_direction.php.inc new file mode 100644 index 00000000000..18b6e740efd --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/other_direction.php.inc @@ -0,0 +1,35 @@ +someMethod(); + + } + + return 'no'; +} + +?> +----- +someMethod(); + + } + + return 'no'; +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_assign.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_assign.php.inc new file mode 100644 index 00000000000..4da84c219e9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_assign.php.inc @@ -0,0 +1,24 @@ +someClass = $this->get()) && $this->someClass->someMethod()) { + return $this->someClass->someMethod(); + } + + return 'no'; + } + + private function get(): ?SomeInstance + { + return rand(0, 1) ? new SomeInstance() : null; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_assign2.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_assign2.php.inc new file mode 100644 index 00000000000..c835f01f4fc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_assign2.php.inc @@ -0,0 +1,24 @@ +someClass = $this->get()) && $this->someClass->someMethod()) { + return $this->someClass->someMethod(); + } + + return 'no'; + } + + private function get(): ?SomeInstance + { + return rand(0, 1) ? new SomeInstance() : null; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_phpdoc.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_phpdoc.php.inc new file mode 100644 index 00000000000..12532d04f0d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/skip_phpdoc.php.inc @@ -0,0 +1,18 @@ +someMethod()) { + return 'yes'; + } + + return 'no'; +} + diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..1a241128af3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/some_class.php.inc @@ -0,0 +1,33 @@ +someMethod()) { + return 'yes'; + } + + return 'no'; +} + +?> +----- +someMethod()) { + return 'yes'; + } + + return 'no'; +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/with_property.php.inc b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/with_property.php.inc new file mode 100644 index 00000000000..42613fe54e7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Fixture/with_property.php.inc @@ -0,0 +1,43 @@ +someClass && $this->someClass->someMethod()) { + return 'yes'; + } + + return 'no'; + } +} + +?> +----- +someClass instanceof \Rector\Tests\TypeDeclaration\Rector\BooleanAnd\BinaryOpNullableToInstanceofRector\Source\SomeInstance && $this->someClass->someMethod()) { + return 'yes'; + } + + return 'no'; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Source/SomeInstance.php b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Source/SomeInstance.php new file mode 100644 index 00000000000..1735630d895 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector/Source/SomeInstance.php @@ -0,0 +1,11 @@ +withRules([BinaryOpNullableToInstanceofRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/AddArrayParamDocTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/AddArrayParamDocTypeRectorTest.php deleted file mode 100644 index 40acd15c365..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/AddArrayParamDocTypeRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/edge_case.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/edge_case.php.inc deleted file mode 100644 index c3bb047dbc6..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/edge_case.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -amenities = $amenities; - } -} - -?> ------ -amenities = $amenities; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/from_getter.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/from_getter.php.inc deleted file mode 100644 index f1bc027b4fc..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/from_getter.php.inc +++ /dev/null @@ -1,50 +0,0 @@ -values = $values; - } - - /** - * @return int[] - */ - public function getValues(): array - { - return $this->values; - } -} - -?> ------ -values = $values; - } - - /** - * @return int[] - */ - public function getValues(): array - { - return $this->values; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/from_property.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/from_property.php.inc deleted file mode 100644 index 054263e550d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/from_property.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -values = $values; - } -} - -?> ------ -values = $values; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_data_provider.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_data_provider.php.inc deleted file mode 100644 index fcf38e2f5f3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_data_provider.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_filled.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_filled.php.inc deleted file mode 100644 index acce0a540cc..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_filled.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -values = $values; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_mixed.php.inc deleted file mode 100644 index 855c0c693bb..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_mixed.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -values = $values; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_template.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_template.php.inc deleted file mode 100644 index f5e76ce7ae9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/keep_template.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -findFirstParentByTypes([String_::class]); - } - - /** - * @template T of Node - * @param class-string[] $nodeTypes - * @return T|null - */ - public function findFirstParentByTypes(array $nodeTypes): ?Node - { - return $nodeTypes[0]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/known_param_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/known_param_array.php.inc deleted file mode 100644 index 35207defa77..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/known_param_array.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/nullable_setter.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/nullable_setter.php.inc deleted file mode 100644 index 52c52bcb89e..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/nullable_setter.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -bar = $bar; - } -} -?> ------ -bar = $bar; - } -} -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/nullable_setter2.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/nullable_setter2.php.inc deleted file mode 100644 index c0cf8a10848..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/nullable_setter2.php.inc +++ /dev/null @@ -1,36 +0,0 @@ -bar = $bar; - } -} -?> ------ -bar = $bar; - } -} -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/param_no_double.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/param_no_double.php.inc deleted file mode 100644 index d27c1a278eb..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/param_no_double.php.inc +++ /dev/null @@ -1,48 +0,0 @@ -meta = $meta; - } -} - -?> ------ -meta = $meta; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_already_param.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_already_param.php.inc deleted file mode 100644 index d54d9f9eb1a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_already_param.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -rectorClass = $rectorClass; - } - - public function getRectorClass(): ?string - { - return $this->rectorClass; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_nullable_typed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_nullable_typed.php.inc deleted file mode 100644 index f3d1b712950..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_nullable_typed.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -bar = $bar; - } -} -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_set_node.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_set_node.php.inc deleted file mode 100644 index 2d0965b203d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_set_node.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -node = $node; - } - - public function getNode(): ?Node - { - return $this->node; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_template.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_template.php.inc deleted file mode 100644 index bdafda22ac1..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/skip_template.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - $args - * @param array $expectedOrderedParams - * @return array - */ - public function run(array $args): array - { - return $args; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/token_php_cs_fixer.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/token_php_cs_fixer.php.inc deleted file mode 100644 index c36b9f3080f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/token_php_cs_fixer.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - $tokens - */ - public function __construct(Tokens $tokens) - { - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/token_php_cs_fixer_doctrine.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/token_php_cs_fixer_doctrine.php.inc deleted file mode 100644 index ccd4fbc6ce7..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Fixture/token_php_cs_fixer_doctrine.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - $tokens - */ - public function __construct(Tokens $tokens) - { - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Source/MeetingRoom.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Source/MeetingRoom.php deleted file mode 100644 index 3e4dc5f5de9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector/Source/MeetingRoom.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(AddArrayParamDocTypeRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/AddArrayReturnDocTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/AddArrayReturnDocTypeRectorTest.php deleted file mode 100644 index bdb877c37c0..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/AddArrayReturnDocTypeRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_error_string.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_error_string.php.inc deleted file mode 100644 index df6a6341922..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_error_string.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - 100]; - } -} - -?> ------ - - */ - public function get() - { - return ['error' => 100]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_from_child.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_from_child.php.inc deleted file mode 100644 index 50802709609..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_from_child.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -getData(); - } -} - -?> ------ -getData(); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_without_return_type_declaration.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_without_return_type_declaration.php.inc deleted file mode 100644 index 18106419916..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/add_without_return_type_declaration.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -values; - } -} - -?> ------ -values; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/call_reflection_resolver.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/call_reflection_resolver.php.inc deleted file mode 100644 index 126927045db..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/call_reflection_resolver.php.inc +++ /dev/null @@ -1,70 +0,0 @@ -resolveFunctionCall($node); - } - - return $this->resolveMethodCall($node); - } - - /** - * @return FunctionReflection|MethodReflection|null - */ - private function resolveFunctionCall(FuncCall $funcCall) - { - } - - private function resolveMethodCall(Node $node): ?MethodReflection - { - } - -} -?> ------ -resolveFunctionCall($node); - } - - return $this->resolveMethodCall($node); - } - - /** - * @return FunctionReflection|MethodReflection|null - */ - private function resolveFunctionCall(FuncCall $funcCall) - { - } - - private function resolveMethodCall(Node $node): ?MethodReflection - { - } - -} -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/child_has_priority.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/child_has_priority.php.inc deleted file mode 100644 index a1182442c59..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/child_has_priority.php.inc +++ /dev/null @@ -1,66 +0,0 @@ - 'string', - 'b' => 1, - 'c' => 1.0 - ] - ]; - } -} - - -abstract class ParentClassWithDefinedReturn -{ - /** - * @return mixed[] - */ - public function getData() - { - return ['...']; - } -} - -?> ------ -> - */ - public function getData() - { - return [ - [ - 'a' => 'string', - 'b' => 1, - 'c' => 1.0 - ] - ]; - } -} - - -abstract class ParentClassWithDefinedReturn -{ - /** - * @return mixed[] - */ - public function getData() - { - return ['...']; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/configuration_with_string_string_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/configuration_with_string_string_array.php.inc deleted file mode 100644 index d13ee3249bc..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/configuration_with_string_string_array.php.inc +++ /dev/null @@ -1,56 +0,0 @@ - [ - DimFetchAssignToMethodCallRector::DIM_FETCH_ASSIGN_TO_METHOD_CALL => [ - new DimFetchAssignToMethodCall( - 'Nette\Application\Routers\RouteList', - 'Nette\Application\Routers\Route', - 'addRoute' - ), - ], - ], - ]; - } -} - -?> ------ -, array> - */ - public function getSome(): array - { - return [ - DimFetchAssignToMethodCallRector::class => [ - DimFetchAssignToMethodCallRector::DIM_FETCH_ASSIGN_TO_METHOD_CALL => [ - new DimFetchAssignToMethodCall( - 'Nette\Application\Routers\RouteList', - 'Nette\Application\Routers\Route', - 'addRoute' - ), - ], - ], - ]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fix_incorrect_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fix_incorrect_array.php.inc deleted file mode 100644 index 832f33163ea..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fix_incorrect_array.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fixture.php.inc deleted file mode 100644 index 586c7a79b34..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -values; - } -} - -?> ------ -values; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fully_qualified_name.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fully_qualified_name.php.inc deleted file mode 100644 index fb8318600eb..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fully_qualified_name.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fully_qualified_name_nested_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fully_qualified_name_nested_array.php.inc deleted file mode 100644 index 5b5356fa245..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/fully_qualified_name_nested_array.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - [new ValidationResult(), 'ha_ja'], - ]; - } -} - -?> ------ -> - */ - public function getValidationErrorMessagesAsStringDataProvider(): array - { - return [ - 'no_errors' => [new ValidationResult(), 'ha_ja'], - ]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/has_offset.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/has_offset.php.inc deleted file mode 100644 index 0aa6ec77d18..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/has_offset.php.inc +++ /dev/null @@ -1,46 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/iterator_no_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/iterator_no_mixed.php.inc deleted file mode 100644 index 1dec101c44a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/iterator_no_mixed.php.inc +++ /dev/null @@ -1,52 +0,0 @@ -yieldFilesFromDirectory(); - } - - /** - * @return Iterator - */ - private function yieldFilesFromDirectory() - { - yield [new SmartFileInfo('...')]; - } -} - -?> ------ - - */ - public function getFiles(): iterable - { - return $this->yieldFilesFromDirectory(); - } - - /** - * @return Iterator - */ - private function yieldFilesFromDirectory() - { - yield [new SmartFileInfo('...')]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/nested_key_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/nested_key_type.php.inc deleted file mode 100644 index e186919cd85..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/nested_key_type.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - 1000 - ]; - } -} - -?> ------ - - */ - public function getValues(): array - { - return [ - 'string' => 1000 - ]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/nested_stdclass_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/nested_stdclass_array.php.inc deleted file mode 100644 index 20f077edb05..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/nested_stdclass_array.php.inc +++ /dev/null @@ -1,63 +0,0 @@ -convertVideos($items); - } - - public function convertVideos($items): array - { - $objects = []; - foreach ($items as $item) { - $objects[] = $this->create($item); - } - - return $objects; - } - - public function create($item): stdClass - { - $object = new stdClass(); - $object->name = $item->name; - return $object; - } -} - -?> ------ -convertVideos($items); - } - - /** - * @return \stdClass[] - */ - public function convertVideos($items): array - { - $objects = []; - foreach ($items as $item) { - $objects[] = $this->create($item); - } - - return $objects; - } - - public function create($item): stdClass - { - $object = new stdClass(); - $object->name = $item->name; - return $object; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/parent_definition.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/parent_definition.php.inc deleted file mode 100644 index 8fa44f20350..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/parent_definition.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/respect_array_return_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/respect_array_return_type.php.inc deleted file mode 100644 index ff81beea024..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/respect_array_return_type.php.inc +++ /dev/null @@ -1,54 +0,0 @@ -items[$message]; - } - - return [ - 'ids' => [], - 'count' => 0, - ]; - } -} - -?> ------ -|array - */ - public function process($message): array - { - if ($message) { - return $this->items[$message]; - } - - return [ - 'ids' => [], - 'count' => 0, - ]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/respect_mixed_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/respect_mixed_type.php.inc deleted file mode 100644 index 545918b6be3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/respect_mixed_type.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -items[$message]; - } -} - -?> ------ -items[$message]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_array_closure.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_array_closure.php.inc deleted file mode 100644 index a82390bb6d9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_array_closure.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - function() { - return $this->description; - }, - ]; - } -} - -?> ------ - - */ - public function fields(): array - { - return [ - 'description' => function() { - return $this->description; - }, - ]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_string_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_string_type.php.inc deleted file mode 100644 index 3b9b53e5f3e..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_string_type.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_tokens.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_tokens.php.inc deleted file mode 100644 index 4e13dc09954..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_tokens.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - - */ - public function createTokens() - { - return new Tokens(); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_uuid.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_uuid.php.inc deleted file mode 100644 index 542657066e7..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_uuid.php.inc +++ /dev/null @@ -1,57 +0,0 @@ -amenityBuildings as $amenityBuilding) { - $buildingIds[] = $amenityBuilding->getId(); - } - - return $buildingIds; - } -} - -?> ------ -amenityBuildings as $amenityBuilding) { - $buildingIds[] = $amenityBuilding->getId(); - } - - return $buildingIds; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_yield_iterator.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_yield_iterator.php.inc deleted file mode 100644 index 0c901bf8566..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/return_yield_iterator.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - - */ - public function someMethod() - { - yield ['test', 'test2']; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/setter_based.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/setter_based.php.inc deleted file mode 100644 index ffbf6eaa23f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/setter_based.php.inc +++ /dev/null @@ -1,54 +0,0 @@ -values = $values; - } - - public function getValues(): array - { - return $this->values; - } -} - -?> ------ -values = $values; - } - - /** - * @return \stdClass[]|null - */ - public function getValues(): array - { - return $this->values; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/simple_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/simple_array.php.inc deleted file mode 100644 index 043896c5679..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/simple_array.php.inc +++ /dev/null @@ -1,46 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_already_array_shape.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_already_array_shape.php.inc deleted file mode 100644 index 750b142a298..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_already_array_shape.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - - */ - public function getFluentCalls(): array - { - $values = []; - $values['name'] = new String_('name'); - - return $values; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_already_array_shape_imported.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_already_array_shape_imported.php.inc deleted file mode 100644 index e5053715690..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_already_array_shape_imported.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function getFluentCalls(): array - { - $values = []; - $values['name'] = new String_('name'); - - return $values; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_array_after_array_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_array_after_array_type.php.inc deleted file mode 100644 index 304b1cb4b13..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_array_after_array_type.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -getOldToNewNamespaces() as $oldNamespace => $newNamespace) { - if (Strings::startsWith($namespace, $oldNamespace)) { - return [$oldNamespace, $newNamespace]; - } - } - - return []; - } - - /** - * @return string[] - */ - private function getOldToNewNamespaces(): array - { - return ['one', 'two']; - } - - /** - * @return mixed[] - */ - public function getAnEmptyArray(): array - { - return []; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_array_shape.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_array_shape.php.inc deleted file mode 100644 index 3b908d8a2d7..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_array_shape.php.inc +++ /dev/null @@ -1,18 +0,0 @@ - 'string', - 'b' => 1, - 'c' => 1.0, - ] - ]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_bare_iterator.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_bare_iterator.php.inc deleted file mode 100644 index c63303bd3b9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_bare_iterator.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - private function yieldFilesFromDirectory(string $string): Iterator - { - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_closure_callable_override.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_closure_callable_override.php.inc deleted file mode 100644 index 933ad4c0fa6..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_closure_callable_override.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - function (array $items): int { - $flattenItems = Arrays::flatten($items); - - return count($flattenItems); - }, - ]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_common_interface.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_common_interface.php.inc deleted file mode 100644 index a76f202b603..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_common_interface.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -nikicParser = $nikicParser; - $this->smartFileSystem = $smartFileSystem; - } - - /** - * @return Stmt[] - */ - public function parseFileInfo(SmartFileInfo $smartFileInfo): array - { - $fileRealPath = $smartFileInfo->getRealPath(); - - if (isset($this->nodesByFile[$fileRealPath])) { - return $this->nodesByFile[$fileRealPath]; - } - - $fileContent = $this->smartFileSystem->readFile($fileRealPath); - - $nodes = $this->nikicParser->parse($fileContent); - if ($nodes === null) { - $this->nodesByFile[$fileRealPath] = []; - } else { - $this->nodesByFile[$fileRealPath] = []; - } - - return $this->nodesByFile[$fileRealPath]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_empty_array_override.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_empty_array_override.php.inc deleted file mode 100644 index f7ddf9c5e14..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_empty_array_override.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -betterNodeFinder = $betterNodeFinder; - } - - /** - * @return PropertyFetch[] - */ - public function resolveAssignsToLocalPropertyFetches(FunctionLike $functionLike): array - { - return $this->betterNodeFinder->find((array) $functionLike->getStmts(), function (Node $node): bool { - return true; - }); - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_inherit_doc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_inherit_doc.php.inc deleted file mode 100644 index 10619695c5c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_inherit_doc.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - 'string', - 'b' => 1, - 'c' => 1.0 - ] - ]; - } -} - - -abstract class ParentClassWithDefinedReturnSecond -{ - /** - * @return mixed[] - */ - public function getData() - { - return ['...']; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_inner_function_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_inner_function_return.php.inc deleted file mode 100644 index 8b9fcf960a6..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_inner_function_return.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -> - */ - public function provideValidConstruction(): array { - $mockEditInfo = $this->getMockBuilder( EditInfo::class ) - ->disableOriginalConstructor() - ->getMock(); - $mockTitle = $this->getMockBuilder( Title::class ) - ->disableOriginalConstructor() - ->getMock(); - - return [ - [ "a", new PageIdentifier( $mockTitle ), 1 , $mockEditInfo, 'foo', null ], - [ 1, new PageIdentifier( $mockTitle, 3 ), 1 , $mockEditInfo, 'foo', '20141212121212' ], - ]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_iterable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_iterable.php.inc deleted file mode 100644 index 6bd9db8ab2d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_iterable.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - - */ - public function someMethod(): iterable - { - yield self::class; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_mixed_iterable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_mixed_iterable.php.inc deleted file mode 100644 index c3d0da84a54..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_mixed_iterable.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - - */ - public function someDataProvider(): iterable - { - yield [42]; - yield [[42]]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_mixed_of_specific_override.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_mixed_of_specific_override.php.inc deleted file mode 100644 index 5c07cdee4d4..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_mixed_of_specific_override.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -getData(); - - return $value; - } - - /** - * @return stdClass[]|DateTime[] - */ - public function provideForVendor2(array $items): array - { - if (rand(0,1)) { - /** @var stdClass[]|DateTime[] $value */ - $value = $this->getData(); - - return $value; - } - - return []; - } - - /** - * @return \stdClass[]|\DateTime[] - */ - private function getData() - { - if (rand(0,1)) { - return [new stdClass()]; - } - - return [new DateTime()]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_non_array_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_non_array_void.php.inc deleted file mode 100644 index 18899761145..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_non_array_void.php.inc +++ /dev/null @@ -1,22 +0,0 @@ -names !== []) { - return $this->names; - } - - foreach ([1, 2, 3] as $value) { - $this->names[] = (string) $value; - } - - return $this->names; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_reprint_union_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_reprint_union_array.php.inc deleted file mode 100644 index 711f0c7550f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_reprint_union_array.php.inc +++ /dev/null @@ -1,42 +0,0 @@ -simpleCallableNodeTraverser = $simpleCallableNodeTraverser; - } - /** - * @return Yield_[]|YieldFrom[] - */ - private function findCurrentScopeYieldNodes(FunctionLike $functionLike): array - { - $yieldNodes = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $functionLike->getStmts(), - function (\PhpParser\Node $node) use (&$yieldNodes): ?int { - if (! $node instanceof Yield_ && ! $node instanceof YieldFrom) { - return null; - } - - $yieldNodes[] = $node; - return null; - } - ); - - return $yieldNodes; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_array_merge.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_array_merge.php.inc deleted file mode 100644 index 762b40e12c5..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_array_merge.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - - */ - private $resolvedConfigFileInfos = []; - - /** - * @param SmartFileInfo[] $setFileInfos - * @return SmartFileInfo[] - */ - public function run(SmartFileInfo $configFileInfo): array - { - $hash = sha1($configFileInfo->getRealPath()); - - if (isset($this->resolvedConfigFileInfos[$hash])) { - return $this->resolvedConfigFileInfos[$hash]; - } - - return []; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_class_string.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_class_string.php.inc deleted file mode 100644 index 86405500cdc..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_class_string.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - */ - public function get(): array { - - return [ - [ - 'a' => 'hi!', - 'b' => 3.14, - 'c' => new stdClass, - ] - ]; - } -} -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_php_doc2.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_php_doc2.php.inc deleted file mode 100644 index 6210268c3fb..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_php_doc2.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -} */ - public function get(): array { - - return [ - [ - 'a' => 'hi!', - 'b' => 3.14, - 'c' => [], - ] - ]; - } -} -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_php_doc3.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_php_doc3.php.inc deleted file mode 100644 index 0cdd66ca2cf..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_php_doc3.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -> */ - public function get(): array { - - return [ - [ - [ - 'a' => 'hi!', - 'b' => 3.14, - 'c' => [], - ], - ] - ]; - } -} -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_types_template.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_types_template.php.inc deleted file mode 100644 index 71a3bcaa72d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_return_types_template.php.inc +++ /dev/null @@ -1,30 +0,0 @@ -nodeFinder = $nodeFinder; - } - - /** - * @return T[] - */ - public function isValidDataProvider($nodes): array - { - return $this->nodeFinder->findInstanceOf($nodes, String_::class); - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_shorten_class_name.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_shorten_class_name.php.inc deleted file mode 100644 index 4f8b2feeb1a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_shorten_class_name.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -cachedStartAndEnds !== []) { - return $this->cachedStartAndEnds; - } - - $this->cachedStartAndEnds[] = $startAndEnd; - - return $this->cachedStartAndEnds; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_some_iterator.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_some_iterator.php.inc deleted file mode 100644 index 9ea8a9104c9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_some_iterator.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -> - */ - public static function someIterator(): Iterator - { - yield [100 => new SmartFileInfo('...')]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_template.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_template.php.inc deleted file mode 100644 index ead4a730ff9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_template.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - $otherModelClass - * @return T[] - */ - public static function findMany(string $otherModelClass): array - { - /** @var T[] $rArray */ - $rArray = []; // get data from somewhere - return $rArray; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_too_many_iterator.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_too_many_iterator.php.inc deleted file mode 100644 index a8731dd8504..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_too_many_iterator.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - 'new_2', - 'old_1' => 'new_1', - ], - ]; - - yield [ - __DIR__ . '/config/one_set_with_own_rename.php', [ - 'PHPUnit_Framework_MockObject_Stub' => 'PHPUnit\Framework\MockObject\Stub', - 'PHPUnit_Framework_MockObject_Stub_Return' => 'PHPUnit\Framework\MockObject\Stub\ReturnStub', - 'PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject', - 'Old' => 'New', - ], - ]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_union_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_union_array.php.inc deleted file mode 100644 index 41f9f13cae3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/skip_union_array.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -readFile(__DIR__ . '/Expected/ExpectedRandomInterface.php') - ) - ]; - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Control/ControlFactory.php'), - new AddedFileWithContent( - '/Source/Control/ControlFactory.php', - $smartFileSystem->readFile(__DIR__ . '/Source/Control/ControlFactory.php') - ), - ]; - } -} - -?> ------ - - */ - public function provideData(): Iterator - { - $smartFileSystem = new SmartFileSystem(); - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Entity/RandomInterface.php'), - new AddedFileWithContent( - '/Source/Contract/RandomInterface.php', - $smartFileSystem->readFile(__DIR__ . '/Expected/ExpectedRandomInterface.php') - ) - ]; - - yield [ - new SmartFileInfo(__DIR__ . '/Source/Control/ControlFactory.php'), - new AddedFileWithContent( - '/Source/Control/ControlFactory.php', - $smartFileSystem->readFile(__DIR__ . '/Source/Control/ControlFactory.php') - ), - ]; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/with_comment.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/with_comment.php.inc deleted file mode 100644 index 9721449be62..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/with_comment.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -values; - } -} - -?> ------ -values; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/yield_strings.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/yield_strings.php.inc deleted file mode 100644 index 6d441a682ec..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Fixture/yield_strings.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - - */ - public function getValues(): iterable - { - yield 'tom'; - yield 'tam'; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Source/BaseModel.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Source/BaseModel.php deleted file mode 100644 index 0dd9ada95f0..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector/Source/BaseModel.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(AddArrayReturnDocTypeRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/AddMethodCallBasedStrictParamTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/AddMethodCallBasedStrictParamTypeRectorTest.php index d69180454a6..0b3f9ddcae2 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/AddMethodCallBasedStrictParamTypeRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/AddMethodCallBasedStrictParamTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AddMethodCallBasedStrictParamTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/final_class_without_extends.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/final_class_without_extends.php.inc new file mode 100644 index 00000000000..ed91bd7a973 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/final_class_without_extends.php.inc @@ -0,0 +1,39 @@ +process($data); + } + + protected function process($data) + { + } +} + +?> +----- +process($data); + } + + protected function process(string $data) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/final_method_without_extends.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/final_method_without_extends.php.inc new file mode 100644 index 00000000000..6041ea2422b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/final_method_without_extends.php.inc @@ -0,0 +1,39 @@ +process($data); + } + + final protected function process($data) + { + } +} + +?> +----- +process($data); + } + + final protected function process(string $data) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape.php.inc new file mode 100644 index 00000000000..6f0aecc1a89 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape.php.inc @@ -0,0 +1,31 @@ +doBar($shape['basename']); + } + + private function doBar($param) { + + } +} +?> +----- +doBar($shape['basename']); + } + + private function doBar(string $param) { + + } +} +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape2.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape2.php.inc new file mode 100644 index 00000000000..ccdccae143a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape2.php.inc @@ -0,0 +1,33 @@ +doBar($arr[10]); + } + + private function doBar($param) { + + } +} +?> +----- +doBar($arr[10]); + } + + private function doBar(string $param) { + + } +} +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape3.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape3.php.inc new file mode 100644 index 00000000000..a729b5d0938 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/native_array_shape3.php.inc @@ -0,0 +1,33 @@ +doBar($arr[10]); + } + + private function doBar($param) { + + } +} +?> +----- +doBar($arr[10]); + } + + private function doBar(array $param) { + + } +} +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/on_static_call.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/on_static_call.php.inc new file mode 100644 index 00000000000..166e7756672 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/on_static_call.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/param_array_offset.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/param_array_offset.php.inc new file mode 100644 index 00000000000..cf4fa59fd7c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/param_array_offset.php.inc @@ -0,0 +1,57 @@ + $data + */ + public function runFirst($data) + { + if (! isset($data['key'])) { + throw new ShouldNotHappenException(); + } + + $this->process($data); + } + + private function process($data) + { + echo $data['key']; + } +} +?> +----- + $data + */ + public function runFirst($data) + { + if (! isset($data['key'])) { + throw new ShouldNotHappenException(); + } + + $this->process($data); + } + + private function process(array $data) + { + echo $data['key']; + } +} +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_alias.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_alias.php.inc new file mode 100644 index 00000000000..29037704935 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_alias.php.inc @@ -0,0 +1,17 @@ +getById($node); + } + + private function getById(PhpParserNode $node) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_arg_unpack.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_arg_unpack.php.inc new file mode 100644 index 00000000000..9c471ec777b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_arg_unpack.php.inc @@ -0,0 +1,18 @@ + $list + */ + public function __construct(array $list) + { + $this->process(...$list); + } + + private function process(int ...$variadic): void + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_array_reverse_index.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_array_reverse_index.php.inc new file mode 100644 index 00000000000..60caba5f5a5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_array_reverse_index.php.inc @@ -0,0 +1,25 @@ + $uri) { + if ($index === 0) { + continue; + } + + $this->someOtherMethod($index, $uri); + } + } + + private function someOtherMethod(int $index, string $uri) + { + return sprintf('%d-%s', $index, $uri); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_contract_doc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_contract_doc.php.inc new file mode 100644 index 00000000000..07325d0d0f4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_contract_doc.php.inc @@ -0,0 +1,34 @@ +processString($node); + return null; + } + + private function processString(String_ $string) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_default.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_default.php.inc new file mode 100644 index 00000000000..955e62c0d29 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_default.php.inc @@ -0,0 +1,22 @@ +process('first'); + } + + public function runEmpty() + { + $this->process(); + } + + private function process($data = false) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_final_class_with_extends.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_final_class_with_extends.php.inc new file mode 100644 index 00000000000..f22cac900c5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_final_class_with_extends.php.inc @@ -0,0 +1,19 @@ +process($data); + } + + protected function process($data) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_final_class_with_implements.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_final_class_with_implements.php.inc new file mode 100644 index 00000000000..4520227251b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_final_class_with_implements.php.inc @@ -0,0 +1,19 @@ +process($data); + } + + protected function process($data) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_has_property_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_has_property_type.php.inc new file mode 100644 index 00000000000..a3ceb5af64c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_has_property_type.php.inc @@ -0,0 +1,26 @@ +type)) { + return; + } + + if ($data->type === 'value') { + $this->runData($data); + } + } + + private function runData($data) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_if_typed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_if_typed.php.inc new file mode 100644 index 00000000000..2d738d6592c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_if_typed.php.inc @@ -0,0 +1,20 @@ +} $filters */ + public function addFilters(array &$params, array $filters): array + { + if ([] !== $filters['date'] && !empty($filters['date']['start'])) { + $this->addFilterDate($params, $filters['date']); + } + return $params; + } + + /** @param array{type: null|string, start: null|string, end: null|string, dateLimit: null|string} $date */ + private function addFilterDate(array &$params, array $date): array { } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_instanceof_enum.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_instanceof_enum.php.inc new file mode 100644 index 00000000000..67a55fdcda1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_instanceof_enum.php.inc @@ -0,0 +1,53 @@ +processTheRest($object); + } + + private function processTheRest($object) + { + return $object; + } +} + +?> +----- +processTheRest($object); + } + + private function processTheRest(object $object) + { + return $object; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_invalid_generics.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_invalid_generics.php.inc new file mode 100644 index 00000000000..8cabb3afa08 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_invalid_generics.php.inc @@ -0,0 +1,41 @@ +entityManager = $this->get('entity_manager'); + $this->initDatabase($this->entityManager); + } + + /** + * @template TObject as object + * + * @param class-string $type + * @return TObject + */ + public function get(string $type): object + { + $container = $this->getContainer(); + return $container->get($type); + } + + private function initDatabase(EntityManagerInterface $entityManager) + { + } + + private function getContainer(): Container + { + return new Container(); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_more_detail_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_more_detail_type.php.inc new file mode 100644 index 00000000000..90b6ebd5626 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_more_detail_type.php.inc @@ -0,0 +1,28 @@ +execute($node); + } + + private function execute(Name $node) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_native_optional_array_shape.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_native_optional_array_shape.php.inc new file mode 100644 index 00000000000..22cad482a56 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_native_optional_array_shape.php.inc @@ -0,0 +1,15 @@ +doBar($shape['dirname']); // dirname is only conditionally returned + } + + private function doBar($param) { + + } +} +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_non_final_class_without_extends.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_non_final_class_without_extends.php.inc new file mode 100644 index 00000000000..13b7600acd0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_non_final_class_without_extends.php.inc @@ -0,0 +1,19 @@ +process($data); + } + + protected function process($data) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_not_natively_typed_property.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_not_natively_typed_property.php.inc new file mode 100644 index 00000000000..893846d1698 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_not_natively_typed_property.php.inc @@ -0,0 +1,22 @@ +runData($this->user); + } + + private function runData($user) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_phpdoc_shape.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_phpdoc_shape.php.inc new file mode 100644 index 00000000000..a723080ae43 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_phpdoc_shape.php.inc @@ -0,0 +1,21 @@ +returnsShape(); + $this->doBar($shape['hello']); + } + + /** + * @return array{'hello': string, 'world': int} + */ + private function returnsShape() { + + } + + private function doBar($param) { + + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_public_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_public_method.php.inc new file mode 100644 index 00000000000..613637e0388 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_public_method.php.inc @@ -0,0 +1,26 @@ +toArray(true); + } + + public function __toString() + { + return implode(', ', $this->toArray()); + } + + final public function toArray($cols = false) + { + if ((! is_bool($cols)) && (! is_array($cols))) { + throw new Exception('Invalid value cols'); + } + } + +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_return_array_doc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_return_array_doc.php.inc new file mode 100644 index 00000000000..32ef7e49c9b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_return_array_doc.php.inc @@ -0,0 +1,25 @@ + + */ + private function run() + { + } + + private function execute($data) + { + } + + public function runData() + { + $values = $this->run(); + $this->execute($values['data']); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_too_wide_union.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_too_wide_union.php.inc new file mode 100644 index 00000000000..fda9aa91607 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_too_wide_union.php.inc @@ -0,0 +1,24 @@ +someExpr($methodCall); + $this->someExpr($staticCall); + $this->someExpr($string); + $this->someExpr($number); + } + + private function someExpr(Expr $expr) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/type_compatible_default.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/type_compatible_default.php.inc new file mode 100644 index 00000000000..7bcbbc36ba0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/type_compatible_default.php.inc @@ -0,0 +1,49 @@ +process('first'); + } + + public function runEmpty() + { + $this->process(); + } + + private function process($data = 'yes') + { + } +} + +?> +----- +process('first'); + } + + public function runEmpty() + { + $this->process(); + } + + private function process(string $data = 'yes') + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/unique_types.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/unique_types.php.inc new file mode 100644 index 00000000000..47ef84af009 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/unique_types.php.inc @@ -0,0 +1,39 @@ +getById(5); + $this->getById(null); + $this->getById(5); + } + + private function getById($id) + { + } +} + +?> +----- +getById(5); + $this->getById(null); + $this->getById(5); + } + + private function getById(?int $id) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/with_empty_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/with_empty_array.php.inc new file mode 100644 index 00000000000..934273ea39e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/with_empty_array.php.inc @@ -0,0 +1,39 @@ +process([]); + } + + private function process($data) + { + } +} + +?> +----- +process([]); + } + + private function process(array $data) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/cover_intersection.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/cover_intersection.php.inc new file mode 100644 index 00000000000..5e80b9f7f8d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/cover_intersection.php.inc @@ -0,0 +1,51 @@ +nextItems($items); + } + + private function nextItems($items): void + { + } +} + +?> +----- +nextItems($items); + } + + private function nextItems(array $items): void + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/intersection_in_union.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/intersection_in_union.php.inc new file mode 100644 index 00000000000..d35a73cd4d2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/intersection_in_union.php.inc @@ -0,0 +1,65 @@ +getRequestParameters(), [ + 'request' => $this->getRequestName(), + ]); + + $this->removeEmptyValues($parameters); + + return []; + } + + private function removeEmptyValues($input): array + { + foreach ($input as &$value) { + if (! is_array($value)) { + continue; + } + + $value = $this->removeEmptyValues($value); + } + + return []; + } +} + +?> +----- +getRequestParameters(), [ + 'request' => $this->getRequestName(), + ]); + + $this->removeEmptyValues($parameters); + + return []; + } + + private function removeEmptyValues(array $input): array + { + foreach ($input as &$value) { + if (! is_array($value)) { + continue; + } + + $value = $this->removeEmptyValues($value); + } + + return []; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/skip_valid_type_from_return_doc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/skip_valid_type_from_return_doc.php.inc new file mode 100644 index 00000000000..cc18e96b6a0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/skip_valid_type_from_return_doc.php.inc @@ -0,0 +1,28 @@ +connection(); + + if ( + $connection instanceof SqlServerConnection + || ! method_exists($connection, 'getSchemaState') + ) { + return; + } + + $this->ensureCleanDatabase($connection); + } + + private function ensureCleanDatabase(ConnectionInterface $connection): void + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/string_intersection.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/string_intersection.php.inc new file mode 100644 index 00000000000..a2a96991e81 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureIntersection/string_intersection.php.inc @@ -0,0 +1,37 @@ +run($value); + } + + private function run($value) + { + } +} + +?> +----- +run($value); + } + + private function run(string $value) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureTreatClassesAsFinal/skip_abstract_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureTreatClassesAsFinal/skip_abstract_class.php.inc new file mode 100644 index 00000000000..1a5842c68d6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureTreatClassesAsFinal/skip_abstract_class.php.inc @@ -0,0 +1,27 @@ +process($data); + } + + protected function process($data) + { + } +} + +class SomeChildClass extends SkipAbstractClass +{ + public function runFirst(int|string $data) + { + $this->process($data); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/array_reverse_index.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/array_reverse_index.php.inc new file mode 100644 index 00000000000..84fe8411d05 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/array_reverse_index.php.inc @@ -0,0 +1,23 @@ + $uri) { + if ($index === 0) { + continue; + } + + $this->someOtherMethod($index, $uri); + } + } + + private function someOtherMethod(int $index, string $uri) + { + return sprintf('%d-%s', $index, $uri); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/narrow_union.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/narrow_union.php.inc new file mode 100644 index 00000000000..75bed626f75 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/narrow_union.php.inc @@ -0,0 +1,51 @@ +someExpr($methodCall); + $this->someExpr($staticCall); + $this->someExpr($string); + } + + private function someExpr($expr) + { + } +} + +?> +----- +someExpr($methodCall); + $this->someExpr($staticCall); + $this->someExpr($string); + } + + private function someExpr(\PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Scalar\String_ $expr) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/narrow_union_multi_params.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/narrow_union_multi_params.php.inc new file mode 100644 index 00000000000..608442eb85c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/narrow_union_multi_params.php.inc @@ -0,0 +1,51 @@ +someExpr($methodCall, $staticCall); + $this->someExpr($staticCall, $staticCall); + $this->someExpr($string, $staticCall); + } + + private function someExpr($expr, $staticCall) + { + } +} + +?> +----- +someExpr($methodCall, $staticCall); + $this->someExpr($staticCall, $staticCall); + $this->someExpr($string, $staticCall); + } + + private function someExpr(\PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Scalar\String_ $expr, \PhpParser\Node\Expr\StaticCall $staticCall) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_named_arg.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_named_arg.php.inc new file mode 100644 index 00000000000..059c754f628 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_named_arg.php.inc @@ -0,0 +1,23 @@ +method(var1: new \DateTime(), var3: ['1']); + $this->method(var1: new \DateTime(), var2: true); + } + + private function method( + ?\DateTime $var1, + ?bool $var2 = false, + array $var3 = [], + ): void + { + return [$var1, $var2, $var2]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_too_wide_union.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_too_wide_union.php.inc new file mode 100644 index 00000000000..91f57955b3c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_too_wide_union.php.inc @@ -0,0 +1,24 @@ +someExpr($methodCall); + $this->someExpr($staticCall); + $this->someExpr($string); + $this->someExpr($number); + } + + private function someExpr(Expr $expr) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_union_if_parent.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_union_if_parent.php.inc new file mode 100644 index 00000000000..fba674db0c7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureUnion/skip_union_if_parent.php.inc @@ -0,0 +1,22 @@ +someExpr($methodCall); + $this->someExpr($staticCall); + $this->someExpr($string); + } + + private function someExpr(Expr $expr) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureWeakDocBlock/unioned_double_calls.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureWeakDocBlock/unioned_double_calls.php.inc deleted file mode 100644 index 2f30466e96a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/FixtureWeakDocBlock/unioned_double_calls.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -getById($value); - } - - private function getById($id) - { - } -} - -?> ------ -getById($value); - } - - private function getById(int $id) - { - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/IntersectionTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/IntersectionTest.php new file mode 100644 index 00000000000..b514a5eeb58 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/IntersectionTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureIntersection'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/intersection_config.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Source/ConnectionInterface.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Source/ConnectionInterface.php new file mode 100644 index 00000000000..2ce18164db5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Source/ConnectionInterface.php @@ -0,0 +1,9 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureUnion'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/union_config.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/WeakDocBlockTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/WeakDocBlockTest.php deleted file mode 100644 index 49adb883c85..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/WeakDocBlockTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureWeakDocBlock'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/weak_doc_block.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/WithTreatClassesAsFinalTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/WithTreatClassesAsFinalTest.php new file mode 100644 index 00000000000..7d12dda4374 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/WithTreatClassesAsFinalTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureTreatClassesAsFinal'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_with_treat_classes_as_final.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/configured_rule.php index 747a9a91887..e9d662f4fd6 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/configured_rule.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/configured_rule.php @@ -2,15 +2,11 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(AddMethodCallBasedStrictParamTypeRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES - 1); + $rectorConfig->rule(AddMethodCallBasedStrictParamTypeRector::class); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/configured_rule_with_treat_classes_as_final.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/configured_rule_with_treat_classes_as_final.php new file mode 100644 index 00000000000..d2ef30bb41f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/configured_rule_with_treat_classes_as_final.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersionFeature::UNION_TYPES); + $rectorConfig->rule(AddMethodCallBasedStrictParamTypeRector::class); + $rectorConfig->treatClassesAsFinal(); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/intersection_config.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/intersection_config.php new file mode 100644 index 00000000000..5f335441e6d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/intersection_config.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersionFeature::INTERSECTION_TYPES); + $rectorConfig->rule(AddMethodCallBasedStrictParamTypeRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/union_config.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/union_config.php new file mode 100644 index 00000000000..17e72de655d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/union_config.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersionFeature::UNION_TYPES); + $rectorConfig->rule(AddMethodCallBasedStrictParamTypeRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/weak_doc_block.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/weak_doc_block.php deleted file mode 100644 index fc2a7a7f3df..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/config/weak_doc_block.php +++ /dev/null @@ -1,19 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(AddMethodCallBasedStrictParamTypeRector::class) - ->call('configure', [[ - AddMethodCallBasedStrictParamTypeRector::TRUST_DOC_BLOCKS => true, - ]]); -}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/AddParamArrayDocblockBasedOnCallableNativeFuncCallRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/AddParamArrayDocblockBasedOnCallableNativeFuncCallRectorTest.php new file mode 100644 index 00000000000..f37bd81659f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/AddParamArrayDocblockBasedOnCallableNativeFuncCallRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/different_usage.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/different_usage.php.inc new file mode 100644 index 00000000000..4e77ccb4457 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/different_usage.php.inc @@ -0,0 +1,54 @@ +value; + }); + + return; + } + + array_walk($items, function (DateTime $item) { + echo $item->format('Y-m-d'); + }); + } +} + +?> +----- +value; + }); + + return; + } + + array_walk($items, function (DateTime $item) { + echo $item->format('Y-m-d'); + }); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..0c73ad99c5d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/fixture.php.inc @@ -0,0 +1,38 @@ +value; + }); + } +} + +?> +----- +value; + }); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..d8ec98bb6dd --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,12 @@ +one = new stdClass; + $items->two = new stdClass; + + array_walk($items, function (stdClass $item) { + echo $item->value; + }); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/skip_typed_doc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/skip_typed_doc.php.inc new file mode 100644 index 00000000000..34e3181c496 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/skip_typed_doc.php.inc @@ -0,0 +1,17 @@ +value; + }); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_filter_and_array_walk.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_filter_and_array_walk.php.inc new file mode 100644 index 00000000000..cdd7a0e2d49 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_filter_and_array_walk.php.inc @@ -0,0 +1,54 @@ + $str === 'a' + ); + return; + } + + array_walk($items, function (stdClass $item) { + echo $item->value; + }); + } +} + +?> +----- + $str === 'a' + ); + return; + } + + array_walk($items, function (stdClass $item) { + echo $item->value; + }); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_map.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_map.php.inc new file mode 100644 index 00000000000..04fb8ee7b37 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_map.php.inc @@ -0,0 +1,38 @@ +value; + }, $items); + } +} + +?> +----- +value; + }, $items); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_named_arg.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_named_arg.php.inc new file mode 100644 index 00000000000..d197660664d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_named_arg.php.inc @@ -0,0 +1,38 @@ +value; + }, array: $items); + } +} + +?> +----- +value; + }, array: $items); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_usort.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_usort.php.inc new file mode 100644 index 00000000000..34e214fa6c6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_usort.php.inc @@ -0,0 +1,38 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/config/configured_rule.php new file mode 100644 index 00000000000..b6fc0bb2836 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(AddParamArrayDocblockBasedOnCallableNativeFuncCallRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/AddParamFromDimFetchKeyUseRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/AddParamFromDimFetchKeyUseRectorTest.php new file mode 100644 index 00000000000..b947ade8f89 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/AddParamFromDimFetchKeyUseRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..d53ab9c48e9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/fixture.php.inc @@ -0,0 +1,37 @@ + 'Firstitem', + 'second' => 'Seconditem', + ]; + + return $items[$key]; + } +} + +?> +----- + 'Firstitem', + 'second' => 'Seconditem', + ]; + + return $items[$key]; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/int_or_string.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/int_or_string.php.inc new file mode 100644 index 00000000000..4fff25673ad --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/int_or_string.php.inc @@ -0,0 +1,39 @@ + 'Firstitem', + 111 => 'Seconditem', + 'second' => 'Seconditem', + ]; + + return $items[$key]; + } +} + +?> +----- + 'Firstitem', + 111 => 'Seconditem', + 'second' => 'Seconditem', + ]; + + return $items[$key]; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_existing_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_existing_type.php.inc new file mode 100644 index 00000000000..6337a78d847 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_existing_type.php.inc @@ -0,0 +1,16 @@ + 'Firstitem', + 'second' => 'Seconditem', + ]; + + return $items[$key]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_has_parent_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_has_parent_method.php.inc new file mode 100644 index 00000000000..440c7b5d479 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_has_parent_method.php.inc @@ -0,0 +1,18 @@ + 'Firstitem', + 'second' => 'Seconditem', + ]; + + return $items[$key]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_mix_clear_and_unclear_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_mix_clear_and_unclear_type.php.inc new file mode 100644 index 00000000000..5abf6729ffe --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_mix_clear_and_unclear_type.php.inc @@ -0,0 +1,20 @@ + 'Firstitem', + 'second' => 'Seconditem', + ]; + + return $items[$key]; + } + + return $unclearType[$key]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_possibly_null.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_possibly_null.php.inc new file mode 100644 index 00000000000..41193f6e54a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_possibly_null.php.inc @@ -0,0 +1,21 @@ + 'Firstitem', + 111 => 'Seconditem', + 'second' => 'Seconditem', + ]; + + return $items[$key]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_unclear_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_unclear_type.php.inc new file mode 100644 index 00000000000..282f08dfe48 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Fixture/skip_unclear_type.php.inc @@ -0,0 +1,11 @@ +|string $key + */ + public function __construct($object, $key) + { + if (\is_array($key) && (string) $object->{$key[1]} !== '') { + } + + if (isset($object->validation_errors[$key])) { + } + } + +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Source/SomeObjectValidation.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Source/SomeObjectValidation.php new file mode 100644 index 00000000000..e713051ac26 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Source/SomeObjectValidation.php @@ -0,0 +1,14 @@ + + */ + public $validation_errors = []; +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Source/SomeParentMethod.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Source/SomeParentMethod.php new file mode 100644 index 00000000000..a611d6df148 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector/Source/SomeParentMethod.php @@ -0,0 +1,12 @@ +rule(AddParamFromDimFetchKeyUseRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/AddParamStringTypeFromSprintfUseRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/AddParamStringTypeFromSprintfUseRectorTest.php new file mode 100644 index 00000000000..ee0cfa78f73 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/AddParamStringTypeFromSprintfUseRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/add_arrow_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/add_arrow_function.php.inc new file mode 100644 index 00000000000..702d3b9509a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/add_arrow_function.php.inc @@ -0,0 +1,27 @@ + sprintf('Hello %s', $name); + } +} + +?> +----- + sprintf('Hello %s', $name); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/constant_variable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/constant_variable.php.inc new file mode 100644 index 00000000000..ee917caad7f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/constant_variable.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/cover_closure.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/cover_closure.php.inc new file mode 100644 index 00000000000..0ef5f6bd900 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/cover_closure.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/other_function_and_sprintf.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/other_function_and_sprintf.php.inc new file mode 100644 index 00000000000..44234c77c1a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/other_function_and_sprintf.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/pass_sprintf.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/pass_sprintf.php.inc new file mode 100644 index 00000000000..246b1e0dc56 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/pass_sprintf.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_conditional_non_string.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_conditional_non_string.php.inc new file mode 100644 index 00000000000..fdf88a20cd8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_conditional_non_string.php.inc @@ -0,0 +1,20 @@ + 'Fatal error', + \E_PARSE => 'Parse error', + \E_USER_WARNING, \E_WARNING, \E_COMPILE_WARNING => 'Warning', + \E_USER_NOTICE, \E_NOTICE => 'Notice', + \E_USER_DEPRECATED, \E_DEPRECATED => 'Deprecated', + default => sprintf('Unknown (%s)', $errno), + }; + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_with_empty_check.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_with_empty_check.php.inc new file mode 100644 index 00000000000..85d1f03673d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector/Fixture/skip_with_empty_check.php.inc @@ -0,0 +1,16 @@ +rule(AddParamStringTypeFromSprintfUseRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/AddParamTypeBasedOnPHPUnitDataProviderRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/AddParamTypeBasedOnPHPUnitDataProviderRectorTest.php new file mode 100644 index 00000000000..27cd8cf3605 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/AddParamTypeBasedOnPHPUnitDataProviderRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers.php.inc new file mode 100644 index 00000000000..d0f324c3c11 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers.php.inc @@ -0,0 +1,26 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute.php.inc new file mode 100644 index 00000000000..e8f88d35623 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute.php.inc @@ -0,0 +1,24 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute_and_phpdoc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute_and_phpdoc.php.inc new file mode 100644 index 00000000000..2e83a4bfd9a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute_and_phpdoc.php.inc @@ -0,0 +1,26 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..b1dae6663c3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture_using_attribute.php.inc new file mode 100644 index 00000000000..4a4e11ee0ad --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture_using_attribute.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars.php.inc new file mode 100644 index 00000000000..5967745222b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars.php.inc @@ -0,0 +1,59 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars_using_attribute.php.inc new file mode 100644 index 00000000000..420afbb1202 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars_using_attribute.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array.php.inc new file mode 100644 index 00000000000..39356d99e02 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array_using_attribute.php.inc new file mode 100644 index 00000000000..93711609955 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array_using_attribute.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_existing.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_existing.php.inc new file mode 100644 index 00000000000..0eb2460802c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_existing.php.inc @@ -0,0 +1,24 @@ + [4, 'arg'] // <-- int + string + ]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_with_nested_method.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_with_nested_method.inc new file mode 100644 index 00000000000..4ce7e7a1f8a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_with_nested_method.inc @@ -0,0 +1,27 @@ +generateData() + ]; + } + + public function generateData(): array + { + return [1234]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/with_inner_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/with_inner_function.php.inc new file mode 100644 index 00000000000..863e85c9d9b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/with_inner_function.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/config/configured_rule.php new file mode 100644 index 00000000000..f6952e1ffb2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(AddParamTypeBasedOnPHPUnitDataProviderRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES - 1); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/AddParamTypeDeclarationRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/AddParamTypeDeclarationRectorTest.php index 39a19cb033b..e7223431ea4 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/AddParamTypeDeclarationRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/AddParamTypeDeclarationRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AddParamTypeDeclarationRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/add_param_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/add_param_mixed.php.inc new file mode 100644 index 00000000000..6edfccd7f45 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/add_param_mixed.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/fixture.php.inc index d8eedefab01..6c3cfca2899 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/fixture.php.inc +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/fixture.php.inc @@ -2,13 +2,21 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector\Fixture; -use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector\Contract\ParentInterfaceWithChangeTypeInterface; +use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector\Source\Contract\ParentInterfaceWithChangeTypeInterface; class DetectedByParentInterface implements ParentInterfaceWithChangeTypeInterface { + public function notChanged($name) + { + } + public function process($name) { } + + public function run($name) + { + } } ?> @@ -17,13 +25,21 @@ class DetectedByParentInterface implements ParentInterfaceWithChangeTypeInterfac namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector\Fixture; -use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector\Contract\ParentInterfaceWithChangeTypeInterface; +use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector\Source\Contract\ParentInterfaceWithChangeTypeInterface; class DetectedByParentInterface implements ParentInterfaceWithChangeTypeInterface { + public function notChanged($name) + { + } + public function process(string $name) { } + + public function run(string $name) + { + } } ?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/skip_enum.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/skip_enum.php.inc new file mode 100644 index 00000000000..c001efdfa1a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector/Fixture/skip_enum.php.inc @@ -0,0 +1,10 @@ +services(); - $services->set(AddParamTypeDeclarationRector::class) - ->call('configure', [[ - AddParamTypeDeclarationRector::PARAMETER_TYPEHINTS => ValueObjectInliner::inline([ - new AddParamTypeDeclaration( - ParentInterfaceWithChangeTypeInterface::class, - 'process', - 0, - new StringType() - ), - new AddParamTypeDeclaration(ParserInterface::class, 'parse', 0, new StringType()), - new AddParamTypeDeclaration( - ClassMetadataFactory::class, - 'setEntityManager', - 0, - new ObjectType('Doctrine\ORM\EntityManagerInterface') - ), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(AddParamTypeDeclarationRector::class, [ + new AddParamTypeDeclaration( + ParentInterfaceWithChangeTypeInterface::class, + 'process', + 0, + new StringType() + ), + new AddParamTypeDeclaration(ParentInterfaceWithChangeTypeInterface::class, 'run', 0, new StringType()), + new AddParamTypeDeclaration(ParserInterface::class, 'parse', 0, new StringType()), + new AddParamTypeDeclaration( + ClassMetadataFactory::class, + 'setEntityManager', + 0, + new ObjectType('Doctrine\ORM\EntityManagerInterface') + ), + new AddParamTypeDeclaration(ParentTypeToMixed::class, 'process', 0, new MixedType(true)), + ]); + + $rectorConfig->phpVersion(PhpVersionFeature::MIXED_TYPE); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/AddParamTypeFromPropertyTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/AddParamTypeFromPropertyTypeRectorTest.php new file mode 100644 index 00000000000..ded3762f27a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/AddParamTypeFromPropertyTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..b5a093b47ac --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/fixture.php.inc @@ -0,0 +1,31 @@ +number = $number; + } +} + +?> +----- +number = $number; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/nullable_property.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/nullable_property.php.inc new file mode 100644 index 00000000000..7537c928815 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/nullable_property.php.inc @@ -0,0 +1,37 @@ +bar = $bar; + } +} + +?> +----- +bar = $bar; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_anonymous_class.php.inc new file mode 100644 index 00000000000..a2df5da6796 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_anonymous_class.php.inc @@ -0,0 +1,43 @@ +number = $number; + } + }; + } +} + +?> +----- +number = $number; + } + }; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_in_conditional.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_in_conditional.php.inc new file mode 100644 index 00000000000..0de20ed4b04 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_in_conditional.php.inc @@ -0,0 +1,15 @@ +array = $arrayOrNull; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_known_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_known_type.php.inc new file mode 100644 index 00000000000..4e6f0995cce --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_known_type.php.inc @@ -0,0 +1,17 @@ +bar = $bar; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_not_natively_typed_property.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_not_natively_typed_property.php.inc new file mode 100644 index 00000000000..73d1431654c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_not_natively_typed_property.php.inc @@ -0,0 +1,18 @@ +user = $user; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_override_from_parent.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_override_from_parent.php.inc new file mode 100644 index 00000000000..cf89aa8b805 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_override_from_parent.php.inc @@ -0,0 +1,13 @@ +number = $number; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_param_override.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_param_override.php.inc new file mode 100644 index 00000000000..6be698a350d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_param_override.php.inc @@ -0,0 +1,20 @@ +bar = $bar; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_variadic_constructor_param_of_mixed_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_variadic_constructor_param_of_mixed_type.php.inc new file mode 100644 index 00000000000..bbb97da27e5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Fixture/skip_variadic_constructor_param_of_mixed_type.php.inc @@ -0,0 +1,18 @@ +elements = $elements; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Source/ParentWithNumber.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Source/ParentWithNumber.php new file mode 100644 index 00000000000..1df782eae72 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector/Source/ParentWithNumber.php @@ -0,0 +1,10 @@ +withRules([AddParamTypeFromPropertyTypeRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/AddReturnArrayDocblockBasedOnArrayMapRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/AddReturnArrayDocblockBasedOnArrayMapRectorTest.php new file mode 100644 index 00000000000..178d257c36d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/AddReturnArrayDocblockBasedOnArrayMapRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/function_array_map_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/function_array_map_return.php.inc new file mode 100644 index 00000000000..b323fea4d6e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/function_array_map_return.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/improve_simple_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/improve_simple_array.php.inc new file mode 100644 index 00000000000..f1056a5025c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/improve_simple_array.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/include_arrow_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/include_arrow_function.php.inc new file mode 100644 index 00000000000..daaa287326e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/include_arrow_function.php.inc @@ -0,0 +1,30 @@ + 1000, $items); + } +} + +?> +----- + 1000, $items); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/multiple_filters.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/multiple_filters.php.inc new file mode 100644 index 00000000000..fd6e008958a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/multiple_filters.php.inc @@ -0,0 +1,46 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/multiple_types.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/multiple_types.php.inc new file mode 100644 index 00000000000..044c3d40c4a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/multiple_types.php.inc @@ -0,0 +1,46 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/override_mixyed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/override_mixyed.php.inc new file mode 100644 index 00000000000..caa3455e01e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/override_mixyed.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/skip_already_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/skip_already_class.php.inc new file mode 100644 index 00000000000..4bea83fb7c9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/skip_already_class.php.inc @@ -0,0 +1,18 @@ + + */ + public function process(array $items) + { + return array_map(function ($item): stdClass { + return $item; + }, $items); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..98d8044330c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,11 @@ +withRules([AddReturnArrayDocblockBasedOnArrayMapRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/AddReturnDocblockForScalarArrayFromAssignsRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/AddReturnDocblockForScalarArrayFromAssignsRectorTest.php new file mode 100644 index 00000000000..09544e4d0b7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/AddReturnDocblockForScalarArrayFromAssignsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/float_assigns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/float_assigns.php.inc new file mode 100644 index 00000000000..5024f967cc7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/float_assigns.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/nested_assign.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/nested_assign.php.inc new file mode 100644 index 00000000000..e36bcf3e13c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/nested_assign.php.inc @@ -0,0 +1,58 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/simple_array_assigns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/simple_array_assigns.php.inc new file mode 100644 index 00000000000..593db6322ba --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/simple_array_assigns.php.inc @@ -0,0 +1,120 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/skip_various_cases.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/skip_various_cases.php.inc new file mode 100644 index 00000000000..da9f8a9344c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector/Fixture/skip_various_cases.php.inc @@ -0,0 +1,85 @@ +withRules([AddReturnDocblockForScalarArrayFromAssignsRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/AddReturnTypeDeclarationBasedOnParentClassMethodRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/AddReturnTypeDeclarationBasedOnParentClassMethodRectorTest.php new file mode 100644 index 00000000000..fef18e07df8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/AddReturnTypeDeclarationBasedOnParentClassMethodRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_class.php.inc new file mode 100644 index 00000000000..376be9004c0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_class.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_class_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_class_mixed.php.inc new file mode 100644 index 00000000000..36597487481 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_class_mixed.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_interface.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_interface.php.inc new file mode 100644 index 00000000000..8386431bb47 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_interface.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_interface_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_interface_mixed.php.inc new file mode 100644 index 00000000000..7e5e37ccfd0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/extended_interface_mixed.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/recursive_extended_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/recursive_extended_class.php.inc new file mode 100644 index 00000000000..662cdadfc24 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/recursive_extended_class.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_destruct.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_destruct.php.inc new file mode 100644 index 00000000000..53440202b9d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_destruct.php.inc @@ -0,0 +1,12 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_extended_phpdoc_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_extended_phpdoc_type.php.inc new file mode 100644 index 00000000000..3944b837591 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_extended_phpdoc_type.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_narrowed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_narrowed.php.inc new file mode 100644 index 00000000000..8f9eda237ad --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Fixture/skip_narrowed.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Source/SomeClassWithPHPDocReturnType.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Source/SomeClassWithPHPDocReturnType.php new file mode 100644 index 00000000000..d5e29a1f965 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector/Source/SomeClassWithPHPDocReturnType.php @@ -0,0 +1,16 @@ +rule(AddReturnTypeDeclarationBasedOnParentClassMethodRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::MIXED_TYPE); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/AddReturnTypeDeclarationRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/AddReturnTypeDeclarationRectorTest.php index 2a11ff74be6..4b1c7e0954e 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/AddReturnTypeDeclarationRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/AddReturnTypeDeclarationRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AddReturnTypeDeclarationRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/null_with_object_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/null_with_object_type.php.inc new file mode 100644 index 00000000000..f8967e7c085 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/null_with_object_type.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_different_over_static.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_different_over_static.php.inc new file mode 100644 index 00000000000..1e4cc56752b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_different_over_static.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_of_static.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_of_static.php.inc new file mode 100644 index 00000000000..95b2a349564 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_of_static.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_the_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_the_mixed.php.inc new file mode 100644 index 00000000000..7ad9e8b699c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/return_the_mixed.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/skip_already_static_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/skip_already_static_return.php.inc new file mode 100644 index 00000000000..f60d3434b5a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector/Fixture/skip_already_static_return.php.inc @@ -0,0 +1,11 @@ +services(); - $services->set(AddReturnTypeDeclarationRector::class) - ->call('configure', [[ - AddReturnTypeDeclarationRector::METHOD_RETURN_TYPES => ValueObjectInliner::inline([ - new AddReturnTypeDeclaration(PHPUnitTestCase::class, 'tearDown', new VoidType()), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $nullableStringType = new UnionType([new NullType(), new StringType()]); + $nullableObjectType = new UnionType([new NullType(), new ObjectType(FileInterface::class)]); + + $rectorConfig + ->ruleWithConfiguration(AddReturnTypeDeclarationRector::class, [ + new AddReturnTypeDeclaration(PHPUnitTestCase::class, 'tearDown', new VoidType()), + new AddReturnTypeDeclaration(ReturnTheMixed::class, 'create', new MixedType(true)), + new AddReturnTypeDeclaration( + ReturnOfStatic::class, + 'create', + new SimpleStaticType(ReturnOfStatic::class) + ), + new AddReturnTypeDeclaration(DataTransformerInterface::class, 'transform', new MixedType()), + new AddReturnTypeDeclaration(FormTypeInterface::class, 'getParent', $nullableStringType), + new AddReturnTypeDeclaration(FolderInterface::class, 'create', $nullableObjectType), + new AddReturnTypeDeclaration( + SkipAlreadyStaticReturn::class, + 'transform', + new SimpleStaticType(SkipAlreadyStaticReturn::class) + ), + new AddReturnTypeDeclaration( + ReturnDifferentOverStatic::class, + 'transform', + new SimpleStaticType(ReturnDifferentOverStatic::class) + ), + ]); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/AddReturnTypeFromTryCatchTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/AddReturnTypeFromTryCatchTypeRectorTest.php new file mode 100644 index 00000000000..ef0766b30d1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/AddReturnTypeFromTryCatchTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/simple_try_catch.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/simple_try_catch.php.inc new file mode 100644 index 00000000000..e146312ebe4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/simple_try_catch.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/skip_doc_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/skip_doc_type.php.inc new file mode 100644 index 00000000000..29f9b436f08 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/skip_doc_type.php.inc @@ -0,0 +1,23 @@ +getIntDoc(); + } + } + + /** + * @return int + */ + private function getIntDoc() + { + return mt_rand(0, 1) ? 'string' : 1; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/skip_if_no_catch.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/skip_if_no_catch.php.inc new file mode 100644 index 00000000000..ae456d0762d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/Fixture/skip_if_no_catch.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..fe482d8d40c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(AddReturnTypeFromTryCatchTypeRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/AddVoidReturnTypeWhereNoReturnRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/AddVoidReturnTypeWhereNoReturnRectorTest.php index 1aace10703f..177cc1adb2c 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/AddVoidReturnTypeWhereNoReturnRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/AddVoidReturnTypeWhereNoReturnRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class AddVoidReturnTypeWhereNoReturnRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/exception.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/exception.php.inc new file mode 100644 index 00000000000..b2587a9d688 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/exception.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/final_protected_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/final_protected_method.php.inc new file mode 100644 index 00000000000..f71c99e20c2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/final_protected_method.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/magic_invoke.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/magic_invoke.php.inc new file mode 100644 index 00000000000..98a1d133b23 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/magic_invoke.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/never_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/never_method.php.inc new file mode 100644 index 00000000000..2f2aa34fb2e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/never_method.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/parent_and_child_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/parent_and_child_void.php.inc new file mode 100644 index 00000000000..a66b5756b2d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/parent_and_child_void.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/return_inside_inner_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/return_inside_inner_function.php.inc new file mode 100644 index 00000000000..4b8064f7e9f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/return_inside_inner_function.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/skip_abstract_empty_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/skip_abstract_empty_class.php.inc new file mode 100644 index 00000000000..9c9166c26b9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/skip_abstract_empty_class.php.inc @@ -0,0 +1,13 @@ +run('foo', function (): array { + return []; + }, strtotime('+6 hours')); + + return $someData[$this->get()]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/some_trait.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/some_trait.php.inc new file mode 100644 index 00000000000..878fbf12b1c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/some_trait.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/throws_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/throws_method.php.inc new file mode 100644 index 00000000000..5b3c3682cb7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/throws_method.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/void_on_private_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/void_on_private_method.php.inc new file mode 100644 index 00000000000..7c4a59580de --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/void_on_private_method.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/yield_inside_inner_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/yield_inside_inner_function.php.inc new file mode 100644 index 00000000000..6b72206ff38 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/yield_inside_inner_function.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/config/configured_rule.php index 4af22258dae..00fec810892 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/config/configured_rule.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/config/configured_rule.php @@ -2,10 +2,8 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(AddVoidReturnTypeWhereNoReturnRector::class); -}; +return RectorConfig::configure() + ->withRules([AddVoidReturnTypeWhereNoReturnRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/BoolReturnTypeFromBooleanConstReturnsRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/BoolReturnTypeFromBooleanConstReturnsRectorTest.php new file mode 100644 index 00000000000..6df0c57084a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/BoolReturnTypeFromBooleanConstReturnsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/direct_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/direct_return.php.inc new file mode 100644 index 00000000000..ab1845cf240 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/direct_return.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/skip_all_nested.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/skip_all_nested.php.inc new file mode 100644 index 00000000000..f27b9f3068a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/skip_all_nested.php.inc @@ -0,0 +1,17 @@ + $second; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/skip_call.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/skip_call.php.inc new file mode 100644 index 00000000000..395a7c918e8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/Fixture/skip_call.php.inc @@ -0,0 +1,11 @@ += $second; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/config/configured_rule.php new file mode 100644 index 00000000000..51b9175f41a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([BoolReturnTypeFromBooleanConstReturnsRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/BoolReturnTypeFromBooleanStrictReturnsRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/BoolReturnTypeFromBooleanStrictReturnsRectorTest.php new file mode 100644 index 00000000000..f37cdcca6a0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/BoolReturnTypeFromBooleanStrictReturnsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/add_to_call.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/add_to_call.php.inc new file mode 100644 index 00000000000..764746c3797 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/add_to_call.php.inc @@ -0,0 +1,27 @@ += $second; + } +} + +?> +----- += $second; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/func_call_found.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/func_call_found.php.inc new file mode 100644 index 00000000000..ca60150b9b7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/func_call_found.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/skip_direct_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/skip_direct_return.php.inc new file mode 100644 index 00000000000..6434a91a05c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/skip_direct_return.php.inc @@ -0,0 +1,16 @@ +bar(); + } + } + + private function bar() + { + if (rand(0, 1)) { + return; + } + + return 'Hello World'; + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/skip_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/skip_void.php.inc new file mode 100644 index 00000000000..c995fcabab0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/skip_void.php.inc @@ -0,0 +1,16 @@ +bar(); + } + + private function bar() + { + echo 'Hello, world!'; + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_empty.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_empty.php.inc new file mode 100644 index 00000000000..39f1c8b3f7f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_empty.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_isset.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_isset.php.inc new file mode 100644 index 00000000000..06c95d24589 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_isset.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_negation.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_negation.php.inc new file mode 100644 index 00000000000..1700c1f8e59 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/Fixture/with_negation.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/config/configured_rule.php new file mode 100644 index 00000000000..ba0ffa299bb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([BoolReturnTypeFromBooleanStrictReturnsRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_method.php.inc new file mode 100644 index 00000000000..44478f276ce --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_method.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_static_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_static_method.php.inc new file mode 100644 index 00000000000..56d58471ac4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_static_method.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/get_and_set.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/get_and_set.php.inc new file mode 100644 index 00000000000..134da3a0697 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/get_and_set.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/isset_unset.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/isset_unset.php.inc new file mode 100644 index 00000000000..15894cb6271 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/isset_unset.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/skip_exists_in_parent.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/skip_exists_in_parent.php.inc new file mode 100644 index 00000000000..890a68ce4ad --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/skip_exists_in_parent.php.inc @@ -0,0 +1,12 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/KnownMagicClassMethodTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/KnownMagicClassMethodTypeRectorTest.php new file mode 100644 index 00000000000..f6a8b8adcdc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/KnownMagicClassMethodTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Source/ParentClassOtherMethod.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Source/ParentClassOtherMethod.php new file mode 100644 index 00000000000..3ad667c19e3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Source/ParentClassOtherMethod.php @@ -0,0 +1,10 @@ +withRules([KnownMagicClassMethodTypeRector::class]) + ->withPhpVersion(PhpVersionFeature::MIXED_TYPE); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/also_update_identifier_return_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/also_update_identifier_return_docblock.php.inc new file mode 100644 index 00000000000..f7bfca44677 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/also_update_identifier_return_docblock.php.inc @@ -0,0 +1,37 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/do_not_change_valid_identifier_return_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/do_not_change_valid_identifier_return_docblock.php.inc new file mode 100644 index 00000000000..59d5507ce0c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/do_not_change_valid_identifier_return_docblock.php.inc @@ -0,0 +1,37 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_final_method_in_non_final_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_final_method_in_non_final_class.php.inc new file mode 100644 index 00000000000..006d7fcf390 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_final_method_in_non_final_class.php.inc @@ -0,0 +1,31 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_abstract_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_abstract_class.php.inc new file mode 100644 index 00000000000..f637b3d771e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_abstract_class.php.inc @@ -0,0 +1,33 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_object.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_object.php.inc new file mode 100644 index 00000000000..b98b55f9572 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_object.php.inc @@ -0,0 +1,33 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_parent_to_child.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_parent_to_child.php.inc new file mode 100644 index 00000000000..c17afb9e332 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_from_parent_to_child.php.inc @@ -0,0 +1,33 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_parent_has_specific_return_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_parent_has_specific_return_type.php.inc new file mode 100644 index 00000000000..53daff4f5b8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_parent_has_specific_return_type.php.inc @@ -0,0 +1,35 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_with_generic_parent.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_with_generic_parent.php.inc new file mode 100644 index 00000000000..a107ec47b35 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/narrow_with_generic_parent.php.inc @@ -0,0 +1,39 @@ + + */ +final class ConcreteGenericFactory extends AbstractGenericFactory +{ + public function build(): object + { + return new ConferenceTalk(); + } +} + +?> +----- + + */ +final class ConcreteGenericFactory extends AbstractGenericFactory +{ + public function build(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk + { + return new ConferenceTalk(); + } +} + +?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_abstract_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_abstract_class.php.inc new file mode 100644 index 00000000000..9ed7d55f8e8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_abstract_class.php.inc @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_final_return_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_final_return_type.php.inc new file mode 100644 index 00000000000..6ae19a07336 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_final_return_type.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_interface_as_on_purpose.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_interface_as_on_purpose.php.inc new file mode 100644 index 00000000000..5f946401491 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_interface_as_on_purpose.php.inc @@ -0,0 +1,14 @@ + + */ + public function run(): \Iterator + { + $arr = [new \stdClass]; + return new \ArrayIterator($arr); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_non_final_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_non_final_class.php.inc new file mode 100644 index 00000000000..5e65d6dc4c4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_non_final_class.php.inc @@ -0,0 +1,13 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Source/AbstractFactory.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Source/AbstractFactory.php new file mode 100644 index 00000000000..ab38daec5a7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Source/AbstractFactory.php @@ -0,0 +1,9 @@ +rule(NarrowObjectReturnTypeRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/Fixture/include_operation.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/Fixture/include_operation.php.inc new file mode 100644 index 00000000000..5d041729e37 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/Fixture/include_operation.php.inc @@ -0,0 +1,23 @@ +x; + } + +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/NumericReturnTypeFromStrictReturnsRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/NumericReturnTypeFromStrictReturnsRectorTest.php new file mode 100644 index 00000000000..92631eb31ef --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/NumericReturnTypeFromStrictReturnsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/config/configured_rule.php new file mode 100644 index 00000000000..7403e5097f4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([NumericReturnTypeFromStrictReturnsRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/direct_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/direct_return.php.inc new file mode 100644 index 00000000000..72234a6904f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/direct_return.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/include_unary_minus.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/include_unary_minus.php.inc new file mode 100644 index 00000000000..bb04bb1f69d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/include_unary_minus.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/skip_float_or_int.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/skip_float_or_int.php.inc new file mode 100644 index 00000000000..259aab67886 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/Fixture/skip_float_or_int.php.inc @@ -0,0 +1,15 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/config/configured_rule.php new file mode 100644 index 00000000000..a2c75f4c64a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([NumericReturnTypeFromStrictScalarReturnsRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_arrow_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_arrow_function.php.inc new file mode 100644 index 00000000000..f5604405706 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_arrow_function.php.inc @@ -0,0 +1,41 @@ + $this->someTypedService->run($value); + } +} + +?> +----- + $this->someTypedService->run($value); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_closure.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_closure.php.inc new file mode 100644 index 00000000000..bd270ca8e1d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_closure.php.inc @@ -0,0 +1,45 @@ +someTypedService->run($value); + }; + } +} + +?> +----- +someTypedService->run($value); + }; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/avoid_nested_call.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/avoid_nested_call.php.inc new file mode 100644 index 00000000000..bfe3e7573e9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/avoid_nested_call.php.inc @@ -0,0 +1,45 @@ +someTypedService->run($value); + }; + } +} + +?> +----- +someTypedService->run($value); + }; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/default_null.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/default_null.php.inc new file mode 100644 index 00000000000..fa1c5789494 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/default_null.php.inc @@ -0,0 +1,41 @@ +someTypedService->withDefaultNull($value); + } +} + +?> +----- +someTypedService->withDefaultNull($value); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/default_null_union.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/default_null_union.php.inc new file mode 100644 index 00000000000..dbe44eff2c0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/default_null_union.php.inc @@ -0,0 +1,41 @@ +someTypedService->withDefaultNullUnion($value); + } +} + +?> +----- +someTypedService->withDefaultNullUnion($value); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_default_numeric_string_param_int.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_default_numeric_string_param_int.php.inc new file mode 100644 index 00000000000..90a7788763a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_default_numeric_string_param_int.php.inc @@ -0,0 +1,15 @@ +execute($value); + } + + private function execute(int $value) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_in_form_builder_callback.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_in_form_builder_callback.php.inc new file mode 100644 index 00000000000..fbc4f87df2c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_in_form_builder_callback.php.inc @@ -0,0 +1,37 @@ +add('someType', DocumentType::class, [ + 'class' => SomeType::class, + 'required' => false, + 'label' => 'Some Type', + 'attr' => [ + 'data-help' => 'some data help', + ], + 'constraints' => [ + new Assert\Callback(function ($someType, ExecutionContextInterface $context): void { + if ($someType === null) { + return; + } + + $this->use($someType); + + // some logic here + }) + ] + ]); + } + + private function use(SomeType $someType): void + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_in_form_builder_callback_instanceof.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_in_form_builder_callback_instanceof.php.inc new file mode 100644 index 00000000000..bc88fc7b309 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_in_form_builder_callback_instanceof.php.inc @@ -0,0 +1,37 @@ +add('someType', DocumentType::class, [ + 'class' => SomeType::class, + 'required' => false, + 'label' => 'Some Type', + 'attr' => [ + 'data-help' => 'some data help', + ], + 'constraints' => [ + new Assert\Callback(function ($someType, ExecutionContextInterface $context): void { + if (! $someType instanceof SomeType) { + return; + } + + $this->use($someType); + + // some logic here + }) + ] + ]); + } + + private function use(SomeType $someType): void + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_mixed_as_not_precise.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_mixed_as_not_precise.php.inc new file mode 100644 index 00000000000..3d37ca9ba5d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_mixed_as_not_precise.php.inc @@ -0,0 +1,17 @@ +processArray($value); + } + + private function processArray(mixed $value) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_on_right_and.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_on_right_and.php.inc new file mode 100644 index 00000000000..be51bf25425 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_on_right_and.php.inc @@ -0,0 +1,28 @@ +code = $code; + } + + public static function tryFromUntrusted($code): ?self + { + $ok = \is_string($code) && self::isCodeOk($code); + if (! $ok) { + return null; + } + + return new self($code); + } + + private static function isCodeOk(string $code): bool + { + return true; + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_param_reassign.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_param_reassign.php.inc new file mode 100644 index 00000000000..007e4f30173 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_param_reassign.php.inc @@ -0,0 +1,27 @@ +resolve($item); + $this->send($item); + } + + /** @param int|stdClass $item */ + private function resolve($item): stdClass { + if ($item instanceof stdClass) { + return $item; + } + return new stdClass(); + } + + private function send(stdClass $item): void { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_ternary.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_ternary.php.inc new file mode 100644 index 00000000000..c496207c29d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_ternary.php.inc @@ -0,0 +1,23 @@ +processArray($value) : $this->processMixed($value); + } + + private function processArray(array $value) + { + + } + + private function processMixed($value) + { + + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_unknown_parent.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_unknown_parent.php.inc new file mode 100644 index 00000000000..41285f968dc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_unknown_parent.php.inc @@ -0,0 +1,19 @@ +someTypedService->run($value); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_variadic_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_variadic_array.php.inc new file mode 100644 index 00000000000..00bc0d597cf --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_variadic_array.php.inc @@ -0,0 +1,17 @@ +processVariadic($items); + } + + private function processVariadic(array $value) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_with_possible_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_with_possible_docblock.php.inc new file mode 100644 index 00000000000..ac3905f02e5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/skip_with_possible_docblock.php.inc @@ -0,0 +1,26 @@ +hashSet($key, $value); + case 'value': + return $this->valueSet($key, $value); + default: + throw new \Exception('ohno'); + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/throw_without_new.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/throw_without_new.php.inc new file mode 100644 index 00000000000..227762e9993 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/throw_without_new.php.inc @@ -0,0 +1,39 @@ +execute($value); + } + + throw $t; + } +} + +?> +----- +execute($value); + } + + throw $t; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/union_on_multiple_calls.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/union_on_multiple_calls.php.inc new file mode 100644 index 00000000000..5a4f07281b8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/union_on_multiple_calls.php.inc @@ -0,0 +1,61 @@ +hashSet($key, $value); + case 'value': + return $this->valueSet($key, $value); + case 'hash2': + return $this->hashSet2($key, $value); + default: + throw new \Exception('ohno'); + } + } +} + +?> +----- +hashSet($key, $value); + case 'value': + return $this->valueSet($key, $value); + case 'hash2': + return $this->hashSet2($key, $value); + default: + throw new \Exception('ohno'); + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/union_on_multiple_calls_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/union_on_multiple_calls_nullable.php.inc new file mode 100644 index 00000000000..942a40f2842 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/union_on_multiple_calls_nullable.php.inc @@ -0,0 +1,61 @@ +hashSet($key, $value); + case 'value': + return $this->valueSet($key, $value); + case 'hash2': + return $this->hashSet2($key, $value); + default: + throw new \Exception('ohno'); + } + } +} + +?> +----- +hashSet($key, $value); + case 'value': + return $this->valueSet($key, $value); + case 'hash2': + return $this->hashSet2($key, $value); + default: + throw new \Exception('ohno'); + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/with_default_constant_integer_type_param_int.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/with_default_constant_integer_type_param_int.php.inc new file mode 100644 index 00000000000..08fdf355636 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/with_default_constant_integer_type_param_int.php.inc @@ -0,0 +1,35 @@ +execute($value); + } + + private function execute(int $value) + { + } +} + +?> +----- +execute($value); + } + + private function execute(int $value) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/ParamTypeByMethodCallTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/ParamTypeByMethodCallTypeRectorTest.php index 235059d2d3f..eb854040241 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/ParamTypeByMethodCallTypeRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/ParamTypeByMethodCallTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ParamTypeByMethodCallTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Source/SomeTypedService.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Source/SomeTypedService.php index 8b774525cda..814dd3059bb 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Source/SomeTypedService.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Source/SomeTypedService.php @@ -13,4 +13,12 @@ public function run(string $name) public static function fun($surname, string $name) { } + + public function withDefaultNull(string $name = null) + { + } + + public function withDefaultNullUnion(bool|string $name = null) + { + } } diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/config/configured_rule.php index f66da1a0714..c5cee68b2b0 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/config/configured_rule.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/config/configured_rule.php @@ -2,10 +2,10 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ParamTypeByMethodCallTypeRector::class); -}; +return RectorConfig::configure() + ->withRules([ParamTypeByMethodCallTypeRector::class]) + ->withPhpVersion(PhpVersion::PHP_80); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Fixture/extends_exception.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Fixture/extends_exception.php.inc new file mode 100644 index 00000000000..acd7625e433 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Fixture/extends_exception.php.inc @@ -0,0 +1,15 @@ +message = $message; + $this->code = $code; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Fixture/skip_just_spread_arg.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Fixture/skip_just_spread_arg.php.inc new file mode 100644 index 00000000000..11f59310e20 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Fixture/skip_just_spread_arg.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/ParamTypeByParentCallTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/ParamTypeByParentCallTypeRectorTest.php index c58d784ba75..88d00b20e07 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/ParamTypeByParentCallTypeRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/ParamTypeByParentCallTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ParamTypeByParentCallTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Source/ParentClassWithArraySpread.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Source/ParentClassWithArraySpread.php new file mode 100644 index 00000000000..547e1e2a693 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector/Source/ParentClassWithArraySpread.php @@ -0,0 +1,12 @@ +rule(ParamTypeByParentCallTypeRector::class); -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ParamTypeByParentCallTypeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_change_parent_anonymous_class_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_change_parent_anonymous_class_method.php.inc new file mode 100644 index 00000000000..9eaecb2071f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_change_parent_anonymous_class_method.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_change_parent_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_change_parent_function.php.inc new file mode 100644 index 00000000000..9fd9bbfc58d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_change_parent_function.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_skip_return_parent_anonymous_class_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_skip_return_parent_anonymous_class_method.php.inc new file mode 100644 index 00000000000..5af0aa89461 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_skip_return_parent_anonymous_class_method.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_skip_yield_parent_anonymous_class_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_skip_yield_parent_anonymous_class_method.php.inc new file mode 100644 index 00000000000..27910fe990f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/do_not_skip_yield_parent_anonymous_class_method.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/from_anonymous.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/from_anonymous.php.inc new file mode 100644 index 00000000000..a074012648c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/from_anonymous.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/has_children_return_never.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/has_children_return_never.php.inc new file mode 100644 index 00000000000..1a99a09cdc1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/has_children_return_never.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/improve_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/improve_void.php.inc index d460725fc29..9483b09daca 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/improve_void.php.inc +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/improve_void.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture; -use Rector\Core\Exception\ShouldNotHappenException; +use Rector\Exception\ShouldNotHappenException; final class ImproveVoid { @@ -18,7 +18,7 @@ final class ImproveVoid namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture; -use Rector\Core\Exception\ShouldNotHappenException; +use Rector\Exception\ShouldNotHappenException; final class ImproveVoid { diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/require_existing_final_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/require_existing_final_void.php.inc new file mode 100644 index 00000000000..f73985ecf0b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/require_existing_final_void.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_already_typed_non_void_or_never.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_already_typed_non_void_or_never.php.inc new file mode 100644 index 00000000000..1ac3e8f3985 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_already_typed_non_void_or_never.php.inc @@ -0,0 +1,11 @@ + 0) { + throw new Exception($messagePrefix.'Unexpected start of output:'.print_r(ob_get_contents(), true)); + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_existing_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_existing_void.php.inc new file mode 100644 index 00000000000..5e751ce34e2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_existing_void.php.inc @@ -0,0 +1,13 @@ +expectException(Exception::class); + + throw new \InvalidArgumentException(); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_throw_as_expr.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_throw_as_expr.php.inc new file mode 100644 index 00000000000..1799c6c84c6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_throw_as_expr.php.inc @@ -0,0 +1,13 @@ +foo = $someClass ?: throw new Exception('current request is null'); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_throw_in_construct.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_throw_in_construct.php.inc new file mode 100644 index 00000000000..d7c914e470d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Fixture/skip_throw_in_construct.php.inc @@ -0,0 +1,13 @@ + ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/exit_some.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/exit_some.php.inc deleted file mode 100644 index f3fe8f82e6f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/exit_some.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/improve_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/improve_void.php.inc deleted file mode 100644 index 45c7a20bb20..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/improve_void.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/parent_protected.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/parent_protected.php.inc deleted file mode 100644 index 3d59fe7cc7f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/FixturePHP72/parent_protected.php.inc +++ /dev/null @@ -1,17 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/PHP72Test.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/PHP72Test.php deleted file mode 100644 index 9a19972cef2..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/PHP72Test.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePHP72'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule_php72.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/ReturnNeverTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/ReturnNeverTypeRectorTest.php index 5a656a8b68f..14212e04183 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/ReturnNeverTypeRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/ReturnNeverTypeRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReturnNeverTypeRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Source/MixedReturn.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Source/MixedReturn.php new file mode 100644 index 00000000000..0550c1ec4b1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/Source/MixedReturn.php @@ -0,0 +1,10 @@ +parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_81); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersionFeature::NEVER_TYPE); - $services = $containerConfigurator->services(); - $services->set(ReturnNeverTypeRector::class); + $rectorConfig->rule(ReturnNeverTypeRector::class); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/config/configured_rule_php72.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/config/configured_rule_php72.php deleted file mode 100644 index adf3a6f817d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector/config/configured_rule_php72.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_72); - - $services = $containerConfigurator->services(); - $services->set(ReturnNeverTypeRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/nullable_false_to_nullable_bool.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/nullable_false_to_nullable_bool.php.inc new file mode 100644 index 00000000000..185a9dc7e49 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/nullable_false_to_nullable_bool.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/return_nullable_int.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/return_nullable_int.php.inc new file mode 100644 index 00000000000..a423e546e64 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/return_nullable_int.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/skip_nullable_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/skip_nullable_mixed.php.inc new file mode 100644 index 00000000000..a91383b640c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/Fixture/skip_nullable_mixed.php.inc @@ -0,0 +1,17 @@ +someMixed; + } + + return null; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/FixturePhp82/standalone_nullable_false.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/FixturePhp82/standalone_nullable_false.php.inc new file mode 100644 index 00000000000..ff2e7635052 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/FixturePhp82/standalone_nullable_false.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/ReturnNullableTypeRectorPhp82Test.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/ReturnNullableTypeRectorPhp82Test.php new file mode 100644 index 00000000000..5f5f3704408 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/ReturnNullableTypeRectorPhp82Test.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/ReturnNullableTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/ReturnNullableTypeRectorTest.php new file mode 100644 index 00000000000..2b66c4d2b60 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/ReturnNullableTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..8c108815356 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/config/configured_rule.php @@ -0,0 +1,12 @@ +rule(ReturnNullableTypeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::NULLABLE_TYPE); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/config/configured_rule_php82.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/config/configured_rule_php82.php new file mode 100644 index 00000000000..a8bdff43c7b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector/config/configured_rule_php82.php @@ -0,0 +1,12 @@ +rule(ReturnNullableTypeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::NULL_FALSE_TRUE_STANDALONE_TYPE); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_filled_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_filled_type.php.inc new file mode 100644 index 00000000000..5bf3b608f06 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_filled_type.php.inc @@ -0,0 +1,16 @@ +createMock('SomeType'); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_possible_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_possible_void.php.inc new file mode 100644 index 00000000000..2ffa3b2e8b4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_possible_void.php.inc @@ -0,0 +1,17 @@ +createMock('SomeType'); + } + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_return_by_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_return_by_docblock.php.inc new file mode 100644 index 00000000000..6c952a1dcc3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/Fixture/skip_return_by_docblock.php.inc @@ -0,0 +1,17 @@ +createMock('SomeType'); + } +} + +?> +----- +createMock('SomeType'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/ReturnTypeFromMockObjectRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/ReturnTypeFromMockObjectRectorTest.php new file mode 100644 index 00000000000..ca11affd54d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/ReturnTypeFromMockObjectRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/config/configured_rule.php new file mode 100644 index 00000000000..16a43e57ea5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReturnTypeFromMockObjectRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/add_return_array_by_cast.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/add_return_array_by_cast.php.inc new file mode 100644 index 00000000000..a18bb8c6564 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/add_return_array_by_cast.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/add_return_object_by_cast.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/add_return_object_by_cast.php.inc new file mode 100644 index 00000000000..ae6800dc3fc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/add_return_object_by_cast.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/skip_multiple_cast.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/skip_multiple_cast.php.inc new file mode 100644 index 00000000000..bb26acba20a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/Fixture/skip_multiple_cast.php.inc @@ -0,0 +1,17 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/config/configured_rule.php new file mode 100644 index 00000000000..756aa78791c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReturnTypeFromReturnCastRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/add_return_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/add_return_array.php.inc new file mode 100644 index 00000000000..ec8ad5004fd --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/add_return_array.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/child_class_no_typed_from_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/child_class_no_typed_from_void.php.inc new file mode 100644 index 00000000000..f6a5ee01a38 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/child_class_no_typed_from_void.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/skip_multi_types_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/skip_multi_types_return.php.inc new file mode 100644 index 00000000000..0f1d3618a77 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/Fixture/skip_multi_types_return.php.inc @@ -0,0 +1,19 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/config/configured_rule.php new file mode 100644 index 00000000000..cb4c95ae684 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReturnTypeFromReturnDirectArrayRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/arrow_function_.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/arrow_function_.php.inc deleted file mode 100644 index 1419d2eec67..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/arrow_function_.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - new SomeResponse(); - } -} - -?> ------ - new SomeResponse(); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/assigned_to_variable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/assigned_to_variable.php.inc new file mode 100644 index 00000000000..a35f3714a25 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/assigned_to_variable.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/explicit_if_new_else_new.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/explicit_if_new_else_new.php.inc new file mode 100644 index 00000000000..ec80c49ae3a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/explicit_if_new_else_new.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/if_new_else_new.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/if_new_else_new.php.inc new file mode 100644 index 00000000000..94d9b9b2015 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/if_new_else_new.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/include_response_outside_controller.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/include_response_outside_controller.php.inc new file mode 100644 index 00000000000..de924a01906 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/include_response_outside_controller.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class.php.inc new file mode 100644 index 00000000000..9c2cc98fb7f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_implements.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_implements.php.inc new file mode 100644 index 00000000000..50d3dd8b364 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_implements.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_multiple_implements.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_multiple_implements.php.inc new file mode 100644 index 00000000000..c4d8b0616fc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_multiple_implements.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_variable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_variable.php.inc new file mode 100644 index 00000000000..d4ccb006a21 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_variable.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/same_variable_return_nested.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/same_variable_return_nested.php.inc new file mode 100644 index 00000000000..4446e752088 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/same_variable_return_nested.php.inc @@ -0,0 +1,51 @@ +setBody('... some body content'); + + return $response; + } + + return $response; + } +} + +?> +----- +setBody('... some body content'); + + return $response; + } + + return $response; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/skip_abstract_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/skip_abstract_method.php.inc new file mode 100644 index 00000000000..204d0ff35e9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/skip_abstract_method.php.inc @@ -0,0 +1,18 @@ +setBody('... some body content'); + + return $response; + } + + return $anotherResponse; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/skip_type_modified_between_assign_and_return_with_if.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/skip_type_modified_between_assign_and_return_with_if.php.inc new file mode 100644 index 00000000000..dc15fdedc07 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/skip_type_modified_between_assign_and_return_with_if.php.inc @@ -0,0 +1,21 @@ +setBody('some message'); + + return $response; + } +} + +?> +----- +setBody('some message'); + + return $response; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/FixturePhp74/return_static.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/FixturePhp74/return_static.php.inc new file mode 100644 index 00000000000..d60d9751148 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/FixturePhp74/return_static.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/FixturePhp74/skip_return_static.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/FixturePhp74/skip_return_static.php.inc deleted file mode 100644 index 840e7402e85..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/FixturePhp74/skip_return_static.php.inc +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Php74Test.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Php74Test.php index ea3ead539b0..fbbccbe432c 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Php74Test.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Php74Test.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class Php74Test extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/ReturnTypeFromReturnNewRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/ReturnTypeFromReturnNewRectorTest.php index 62cbd5e85ba..5f31277ccb2 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/ReturnTypeFromReturnNewRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/ReturnTypeFromReturnNewRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReturnTypeFromReturnNewRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Source/FirstResponse.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Source/FirstResponse.php new file mode 100644 index 00000000000..6fe81d59240 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Source/FirstResponse.php @@ -0,0 +1,8 @@ +body = $body; + } } diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/config/configured_rule.php index 4b5b7b14b10..8b191931111 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/config/configured_rule.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/config/configured_rule.php @@ -2,10 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReturnTypeFromReturnNewRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(ReturnTypeFromReturnNewRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::STATIC_RETURN_TYPE); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/config/php74.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/config/php74.php index 1fb0d241912..8a2eadeedb3 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/config/php74.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/config/php74.php @@ -2,15 +2,12 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReturnTypeFromReturnNewRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(ReturnTypeFromReturnNewRector::class); - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::STATIC_RETURN_TYPE - 1); + $rectorConfig->phpVersion(PhpVersionFeature::STATIC_RETURN_TYPE - 1); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/Fixture/skip_interface_mixin.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/Fixture/skip_interface_mixin.php.inc new file mode 100644 index 00000000000..eb40d20cee3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/Fixture/skip_interface_mixin.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/Fixture/with_constant.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/Fixture/with_constant.php.inc new file mode 100644 index 00000000000..d1046da2d4f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/Fixture/with_constant.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/ReturnTypeFromStrictConstantReturnRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/ReturnTypeFromStrictConstantReturnRectorTest.php new file mode 100644 index 00000000000..82cfd9d5157 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/ReturnTypeFromStrictConstantReturnRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/config/configured_rule.php new file mode 100644 index 00000000000..82747dad709 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReturnTypeFromStrictConstantReturnRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/inner_classmethod.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/inner_classmethod.php.inc new file mode 100644 index 00000000000..6fe1e2f5610 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/inner_classmethod.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/multi_return_this.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/multi_return_this.php.inc new file mode 100644 index 00000000000..909d4774bd7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/multi_return_this.php.inc @@ -0,0 +1,41 @@ +foo = 'foo'; + return $this; + } + + return $this; + } +} + +?> +----- +foo = 'foo'; + return $this; + } + + return $this; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/non_final_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/non_final_class.php.inc new file mode 100644 index 00000000000..ae80420b1df --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/non_final_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/return_self.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/return_self.php.inc new file mode 100644 index 00000000000..c7bd4073378 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/return_self.php.inc @@ -0,0 +1,37 @@ +foo = 'foo'; + + return $obj; + } +} + +?> +----- +foo = 'foo'; + + return $obj; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/return_static.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/return_static.php.inc new file mode 100644 index 00000000000..cac7d2f3e51 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/return_static.php.inc @@ -0,0 +1,37 @@ +foo = 'foo'; + + return $obj; + } +} + +?> +----- +foo = 'foo'; + + return $obj; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/single_return_this.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/single_return_this.php.inc new file mode 100644 index 00000000000..37d0a8fc493 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/single_return_this.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/skip_new_other_object.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/skip_new_other_object.php.inc new file mode 100644 index 00000000000..6ffb01c472f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Fixture/skip_new_other_object.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/FixturePhp74/return_static_on_php74.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/FixturePhp74/return_static_on_php74.php.inc new file mode 100644 index 00000000000..dfc53110724 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/FixturePhp74/return_static_on_php74.php.inc @@ -0,0 +1,37 @@ +foo = 'foo'; + + return $obj; + } +} + +?> +----- +foo = 'foo'; + + return $obj; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Php74SelfReturnTypeTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Php74SelfReturnTypeTest.php new file mode 100644 index 00000000000..3101c4cb59c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/Php74SelfReturnTypeTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/php74_configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/ReturnTypeFromStrictFluentReturnRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/ReturnTypeFromStrictFluentReturnRectorTest.php new file mode 100644 index 00000000000..c33cb184551 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/ReturnTypeFromStrictFluentReturnRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/config/configured_rule.php new file mode 100644 index 00000000000..8914a47f0fb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/config/configured_rule.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersion::PHP_80); + $rectorConfig->rule(ReturnTypeFromStrictFluentReturnRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/config/php74_configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/config/php74_configured_rule.php new file mode 100644 index 00000000000..b65ea3a6e4d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector/config/php74_configured_rule.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersion::PHP_74); + $rectorConfig->rule(ReturnTypeFromStrictFluentReturnRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_boolean_or_union_false.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_boolean_or_union_false.php.inc new file mode 100644 index 00000000000..28dc5e9c034 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_boolean_or_union_false.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union.php.inc new file mode 100644 index 00000000000..733081c13fa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union_false.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union_false.php.inc new file mode 100644 index 00000000000..d55de046aa8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union_false.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union_false_or_boolean.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union_false_or_boolean.php.inc new file mode 100644 index 00000000000..abfda2962ba --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/native_method_call_returning_union_false_or_boolean.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/sin_float.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/sin_float.php.inc new file mode 100644 index 00000000000..000ad16c628 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/sin_float.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/skip_custom_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/skip_custom_function.php.inc new file mode 100644 index 00000000000..a62152093c6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/skip_custom_function.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/some_native_method_call.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/some_native_method_call.php.inc new file mode 100644 index 00000000000..348abb39078 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/Fixture/some_native_method_call.php.inc @@ -0,0 +1,27 @@ +isDir(); + } +} + +?> +----- +isDir(); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/ReturnTypeFromStrictNativeCallRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/ReturnTypeFromStrictNativeCallRectorTest.php new file mode 100644 index 00000000000..38338b9dc52 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/ReturnTypeFromStrictNativeCallRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/config/configured_rule.php new file mode 100644 index 00000000000..1ab94536212 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector/config/configured_rule.php @@ -0,0 +1,11 @@ +withRules([ReturnTypeFromStrictNativeCallRector::class]) + ->withPhpVersion(PhpVersionFeature::NULL_FALSE_TRUE_STANDALONE_TYPE); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/append_in_loop.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/append_in_loop.php.inc new file mode 100644 index 00000000000..1c3228fed87 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/append_in_loop.php.inc @@ -0,0 +1,50 @@ +groupid.'--'.$offer->tarifid] = $offer; + $groupedOffers[$offer->groupid.'--'.$offer->tarifid]->groupedOffers = $groupOffers; + } + + return $groupedOffers; + } +} + +?> +----- +groupid.'--'.$offer->tarifid] = $offer; + $groupedOffers[$offer->groupid.'--'.$offer->tarifid]->groupedOffers = $groupOffers; + } + + return $groupedOffers; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/array_shape_simple_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/array_shape_simple_array.php.inc new file mode 100644 index 00000000000..2f76c72aff5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/array_shape_simple_array.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/deep_nested_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/deep_nested_array.php.inc new file mode 100644 index 00000000000..13e60a7da7f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/deep_nested_array.php.inc @@ -0,0 +1,54 @@ +getRows(); + foreach ($rows as $row) { + $arr[$row["a"]][$row["b"]] = ["c" => $row["c"]]; + } + return $arr; + } + + private function getRows(): array { + return [ + ["a" => 1, "b" => 2, "c" => 3, "d" => 4], + ]; + } +} + +?> +----- +[] + */ + public function test(): array { + $arr = []; + $rows = $this->getRows(); + foreach ($rows as $row) { + $arr[$row["a"]][$row["b"]] = ["c" => $row["c"]]; + } + return $arr; + } + + private function getRows(): array { + return [ + ["a" => 1, "b" => 2, "c" => 3, "d" => 4], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/do_not_change_more_detailed_return_doc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/do_not_change_more_detailed_return_doc.php.inc new file mode 100644 index 00000000000..97d60952daa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/do_not_change_more_detailed_return_doc.php.inc @@ -0,0 +1,67 @@ + $ParameterArray + * + * @return array + */ + public function getFilterFromParameterArray2($ParameterArray) { + + $filter = [ + 'where' => '', + 'having' => '', + 'from' => '', + ]; + + $where_filter_sql = '1=1'; + + $where_filter_sql .= ' ... '; // sql stuff -> one line is working with reactor + $where_filter_sql .= ' ... '; // sql stuff -> the next lines will break the phpdoc?? + + $filter['where'] = $where_filter_sql; + + return $filter; + } +} + +?> +----- + $ParameterArray + * + * @return array + */ + public function getFilterFromParameterArray2($ParameterArray): array { + + $filter = [ + 'where' => '', + 'having' => '', + 'from' => '', + ]; + + $where_filter_sql = '1=1'; + + $where_filter_sql .= ' ... '; // sql stuff -> one line is working with reactor + $where_filter_sql .= ' ... '; // sql stuff -> the next lines will break the phpdoc?? + + $filter['where'] = $where_filter_sql; + + return $filter; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/do_not_change_more_detailed_return_doc2.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/do_not_change_more_detailed_return_doc2.php.inc new file mode 100644 index 00000000000..53fb0203f7f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/do_not_change_more_detailed_return_doc2.php.inc @@ -0,0 +1,45 @@ + + */ + public function getCsvHeaderArray() { + $HeaderArray = []; + $HeaderArray[] = Resource::_('Mwst'); + + return $HeaderArray; + } +} + +?> +----- + + */ + public function getCsvHeaderArray(): array { + $HeaderArray = []; + $HeaderArray[] = Resource::_('Mwst'); + + return $HeaderArray; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/fallback_return_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/fallback_return_array.php.inc new file mode 100644 index 00000000000..0f09067b69b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/fallback_return_array.php.inc @@ -0,0 +1,58 @@ +groupid.'--'.$offer->tarifid] = $offer; + $groupedOffers[$offer->groupid.'--'.$offer->tarifid]->groupedOffers = $groupOffers; + } + + return $groupedOffers; + } +} + +?> +----- +groupid.'--'.$offer->tarifid] = $offer; + $groupedOffers[$offer->groupid.'--'.$offer->tarifid]->groupedOffers = $groupOffers; + } + + return $groupedOffers; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/init_assign_method_call.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/init_assign_method_call.php.inc new file mode 100644 index 00000000000..07f04f71e99 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/init_assign_method_call.php.inc @@ -0,0 +1,50 @@ +fetchAllForBuyer(); + + foreach ($orders as $order) { + + } + + return $orders; + } +} + +?> +----- +fetchAllForBuyer(); + + foreach ($orders as $order) { + + } + + return $orders; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/new_array_replace.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/new_array_replace.php.inc new file mode 100644 index 00000000000..83f9f1df9d1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/new_array_replace.php.inc @@ -0,0 +1,56 @@ +getFilePaths('/' . $identifier . '_' . $size . '.jpg/'); + } else { + $imagePaths = $this->getFilePaths('/' . $identifier . '.jpg/'); + } + + return $imagePaths; + } + + private function getFilePaths(): array + { + return []; + } +} + +?> +----- +getFilePaths('/' . $identifier . '_' . $size . '.jpg/'); + } else { + $imagePaths = $this->getFilePaths('/' . $identifier . '.jpg/'); + } + + return $imagePaths; + } + + private function getFilePaths(): array + { + return []; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/return_override_with_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/return_override_with_array.php.inc new file mode 100644 index 00000000000..0f88a35ae3e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/return_override_with_array.php.inc @@ -0,0 +1,38 @@ + +----- + + */ + public function run(): array + { + $values = []; + + $values = [1000]; + + return $values; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/return_specific_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/return_specific_type.php.inc new file mode 100644 index 00000000000..a799afa0180 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/return_specific_type.php.inc @@ -0,0 +1,44 @@ + +----- + + */ + public function run(): array + { + $values = []; + $values[] = new AppleJuice(); + + return $values; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/returns-list.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/returns-list.php.inc new file mode 100644 index 00000000000..1fb655da0aa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/returns-list.php.inc @@ -0,0 +1,32 @@ + +----- + + */ + function doFoo(int $i): array { + $a = []; + $a[] = $i; + return $a; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_array_by_doc_different_assign_returned.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_array_by_doc_different_assign_returned.php.inc new file mode 100644 index 00000000000..61cde69fc7e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_array_by_doc_different_assign_returned.php.inc @@ -0,0 +1,24 @@ + 'value', + ]; + + if (is_array($data)) { + foreach ($data as $propertyName => $propertyData) { + $data[$propertyName] = 'value'; + } + } + + return $data; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_init_assign_method_call_from_docblock_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_init_assign_method_call_from_docblock_return.php.inc new file mode 100644 index 00000000000..2d08b67f07f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_init_assign_method_call_from_docblock_return.php.inc @@ -0,0 +1,21 @@ +fetchAllForBuyer(); + + foreach ($orders as $order) { + + } + + return $orders; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_modified_type_between_assign_and_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_modified_type_between_assign_and_return.php.inc new file mode 100644 index 00000000000..d0dfb246e19 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_modified_type_between_assign_and_return.php.inc @@ -0,0 +1,20 @@ +settings($values); + + return $values; + } + + private function settings(&$values) + { + $values = 1; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_override.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_override.php.inc new file mode 100644 index 00000000000..ad7e3d6fadc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_override.php.inc @@ -0,0 +1,15 @@ + $compareValue) { + + $return[$compareKey] = [ + $compareValue, + $pricesItem->$compareKey === null + ? 'NULL' + : (float) $pricesItem->$compareKey, + ]; + + $isSame = bccomp( + (string) ($pricesItem->$compareKey), + (string) ($compareValue), + 2 + ); + + if (!empty($isSame) && !$couldUpdate) { + $couldUpdate = true; + } + } + + if ($couldUpdate) { + return $return; + } + + return false; + } + +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_two_returns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_two_returns.php.inc new file mode 100644 index 00000000000..9f776f702ff --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Fixture/skip_two_returns.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/ReturnTypeFromStrictNewArrayRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/ReturnTypeFromStrictNewArrayRectorTest.php new file mode 100644 index 00000000000..7173f8d3f1f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/ReturnTypeFromStrictNewArrayRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Source/AppleJuice.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Source/AppleJuice.php new file mode 100644 index 00000000000..1dcbfb991ae --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector/Source/AppleJuice.php @@ -0,0 +1,9 @@ +withRules([ReturnTypeFromStrictNewArrayRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example.php.inc new file mode 100644 index 00000000000..d7d67f5aceb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_nullable.php.inc new file mode 100644 index 00000000000..3ed30a5a1bd --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_nullable.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_scalar.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_scalar.php.inc new file mode 100644 index 00000000000..a4decf18ae8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_scalar.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_with_call.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_with_call.php.inc new file mode 100644 index 00000000000..c3d21b9e0ab --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/example_with_call.php.inc @@ -0,0 +1,31 @@ +doBar($param); + + return $param; + } + + public function doBar($ref) {} +} + +?> +----- +doBar($param); + + return $param; + } + + public function doBar($ref) {} +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/from_parent_has_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/from_parent_has_return.php.inc new file mode 100644 index 00000000000..d90a3b163b6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/from_parent_has_return.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/parent_overridden.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/parent_overridden.php.inc new file mode 100644 index 00000000000..8e65a87da64 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/parent_overridden.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/skip_already_return_typed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/skip_already_return_typed.php.inc new file mode 100644 index 00000000000..7bbad73f15f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/skip_already_return_typed.php.inc @@ -0,0 +1,10 @@ +doBar($param); + + return $param; + } + + public function doBar(&$ref) {} +} + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/skip_conditional_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/skip_conditional_return.php.inc new file mode 100644 index 00000000000..5b2d15c36c9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Fixture/skip_conditional_return.php.inc @@ -0,0 +1,14 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Source/ParentHasReturn.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Source/ParentHasReturn.php new file mode 100644 index 00000000000..837eea44a31 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector/Source/ParentHasReturn.php @@ -0,0 +1,10 @@ +rule(ReturnTypeFromStrictParamRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_74); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/arrow_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/arrow_function.php.inc deleted file mode 100644 index 81854934be5..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/arrow_function.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - App::init(); - -}; - -?> ------ - App::init(); - -}; - -?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/do_not_return_parent_inner_functionlike_only_has_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/do_not_return_parent_inner_functionlike_only_has_return.php.inc index a72c711a46d..ac333427e9f 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/do_not_return_parent_inner_functionlike_only_has_return.php.inc +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/do_not_return_parent_inner_functionlike_only_has_return.php.inc @@ -12,10 +12,6 @@ class App function () { - function () { - return App::init(); - }; - function foo() { return App::init(); }; @@ -45,10 +41,6 @@ class App function () { - function (): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector\Fixture\App { - return App::init(); - }; - function foo(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector\Fixture\App { return App::init(); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/inner_functionlike_and_parent_has_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/inner_functionlike_and_parent_has_return.php.inc deleted file mode 100644 index 7da3705f502..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/inner_functionlike_and_parent_has_return.php.inc +++ /dev/null @@ -1,69 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/invoke_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/invoke_method.php.inc new file mode 100644 index 00000000000..a2169a0af16 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/invoke_method.php.inc @@ -0,0 +1,31 @@ +getName(); + } +} + +?> +----- +getName(); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_in_test_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_in_test_method.php.inc new file mode 100644 index 00000000000..1c129ee9898 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_in_test_method.php.inc @@ -0,0 +1,41 @@ +getValue(); + } + + private function getValue(): int + { + return 123; + } +} + +?> +----- +getValue(); + } + + private function getValue(): int + { + return 123; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_self_different_namespace.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_self_different_namespace.php.inc deleted file mode 100644 index f802743332c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_self_different_namespace.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_self_different_namespace_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_self_different_namespace_nullable.php.inc deleted file mode 100644 index dc6007bef05..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_self_different_namespace_nullable.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_static_different_object.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_static_different_object.php.inc new file mode 100644 index 00000000000..3c30ce714b1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_static_different_object.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_static_from_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_static_from_function.php.inc new file mode 100644 index 00000000000..e8d19361699 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/return_static_from_function.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_array_returns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_array_returns.php.inc new file mode 100644 index 00000000000..1c976d8a181 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_array_returns.php.inc @@ -0,0 +1,45 @@ +getArray(); + } + + public function getArray(): array + { + + } +} + +?> +----- +getArray(); + } + + public function getArray(): array + { + + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_constant_int_returns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_constant_int_returns.php.inc new file mode 100644 index 00000000000..6060b1e1fa7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_constant_int_returns.php.inc @@ -0,0 +1,49 @@ +getInt(); + } + + public function getInt(): int + { + + } +} + +?> +----- +getInt(); + } + + public function getInt(): int + { + + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_int_returns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_int_returns.php.inc new file mode 100644 index 00000000000..59ae8647cfa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_int_returns.php.inc @@ -0,0 +1,45 @@ +getInt(); + } + + public function getInt(): int + { + + } +} + +?> +----- +getInt(); + } + + public function getInt(): int + { + + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_string_returns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_string_returns.php.inc new file mode 100644 index 00000000000..2b8aa0a5156 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/same_typed_string_returns.php.inc @@ -0,0 +1,45 @@ +getString(); + } + + public function getString(): string + { + + } +} + +?> +----- +getString(); + } + + public function getString(): string + { + + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_external_caller_return_doc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_external_caller_return_doc.php.inc new file mode 100644 index 00000000000..ef49d91ffb3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_external_caller_return_doc.php.inc @@ -0,0 +1,13 @@ +getData($x); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_false_report_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_false_report_type.php.inc new file mode 100644 index 00000000000..a883b562865 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_false_report_type.php.inc @@ -0,0 +1,19 @@ +fakedTypes(); + } + + /** + * @return bool + */ + private function fakedTypes() + { + return 100; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_mix_typed_returns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_mix_typed_returns.php.inc new file mode 100644 index 00000000000..e97e41d8808 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_mix_typed_returns.php.inc @@ -0,0 +1,22 @@ +differentType(); + } + + public function differentType(): float + { + + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_once_type_and_native_call_first.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_once_type_and_native_call_first.php.inc new file mode 100644 index 00000000000..ad4ca4d3f4b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_once_type_and_native_call_first.php.inc @@ -0,0 +1,22 @@ +getString(); + } + + private function getString() + { + return '...'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_once_type_and_native_call_second.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_once_type_and_native_call_second.php.inc new file mode 100644 index 00000000000..8e0ffea2fe7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_once_type_and_native_call_second.php.inc @@ -0,0 +1,22 @@ +getString(); + } + + return array_map(function ($i) { + return $i; + }, []); + } + + private function getString() + { + return '...'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_return_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_return_docblock.php.inc new file mode 100644 index 00000000000..720ad7ec987 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_return_docblock.php.inc @@ -0,0 +1,18 @@ +oddLevel($node); + } + + protected function oddLevel(SomeNode $node) + { + if ($node->next) { + return $this->evenLevel($node->next); + } + return $node->value; + } + + protected function evenLevel(SomeNode $node) + { + if ($node->next) { + return $this->oddLevel($node->next); + } + return $node->value; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_union_type_on_php74.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_union_type_on_php74.php.inc new file mode 100644 index 00000000000..c743ebf08e6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_union_type_on_php74.php.inc @@ -0,0 +1,23 @@ +getValue(); + } + + return $this->getNextValue(); + } + + private function getValue(): int|string + { + } + + private function getNextValue(): float|string + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_unknown_constant_int_returns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_unknown_constant_int_returns.php.inc new file mode 100644 index 00000000000..5c2dcbda1e1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_unknown_constant_int_returns.php.inc @@ -0,0 +1,22 @@ +getInt(); + } + + public function getInt(): int + { + + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_void_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_void_type.php.inc new file mode 100644 index 00000000000..32ac0ad9928 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_void_type.php.inc @@ -0,0 +1,15 @@ +execute(); + } + + private function execute(): void + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_wrong_typed_constant.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_wrong_typed_constant.php.inc new file mode 100644 index 00000000000..1308b097f7e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/skip_wrong_typed_constant.php.inc @@ -0,0 +1,24 @@ +getInt(); + } + + public function getInt(): int + { + + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/use_return_self.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/use_return_self.php.inc deleted file mode 100644 index 500558b57b7..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/use_return_self.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/use_return_self_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/use_return_self_nullable.php.inc deleted file mode 100644 index 1b80abc79ac..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/use_return_self_nullable.php.inc +++ /dev/null @@ -1,55 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/wrong_typed_constant_returns.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/wrong_typed_constant_returns.php.inc new file mode 100644 index 00000000000..7f24ca2fd7d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Fixture/wrong_typed_constant_returns.php.inc @@ -0,0 +1,55 @@ +getInt(); + } + + public function getInt(): int + { + + } +} + +?> +----- +getInt(); + } + + public function getInt(): int + { + + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/abs_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/abs_return.php.inc new file mode 100644 index 00000000000..e807beda580 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/abs_return.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/abs_return_int.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/abs_return_int.php.inc new file mode 100644 index 00000000000..75d1c27b550 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/abs_return_int.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/datetime_format_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/datetime_format_return.php.inc new file mode 100644 index 00000000000..97d4274bdf2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/datetime_format_return.php.inc @@ -0,0 +1,31 @@ +format('Y-m-d'); + } +} + +?> +----- +format('Y-m-d'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/merge_union_types.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/merge_union_types.php.inc new file mode 100644 index 00000000000..c214114062d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/merge_union_types.php.inc @@ -0,0 +1,51 @@ +getValue(); + } + + return $this->getNextValue(); + } + + private function getValue(): int|string + { + } + + private function getNextValue(): float|string + { + } +} + +?> +----- +getValue(); + } + + return $this->getNextValue(); + } + + private function getValue(): int|string + { + } + + private function getNextValue(): float|string + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/pathinfo_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/pathinfo_return.php.inc new file mode 100644 index 00000000000..3475e981ad5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/pathinfo_return.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/skip_native_mixed_and_other_type_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/skip_native_mixed_and_other_type_return.php.inc new file mode 100644 index 00000000000..69bb3570083 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/skip_native_mixed_and_other_type_return.php.inc @@ -0,0 +1,28 @@ +get(); + } + + return $this->got(); + } + + private function get(): mixed + { + return null; + } + + private function got(): int + { + return 1; + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/skip_with_mixin.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/skip_with_mixin.php.inc new file mode 100644 index 00000000000..7c333604da0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/FixturePhp80/skip_with_mixin.php.inc @@ -0,0 +1,27 @@ +get(); + } + + private function get(): mixed + { + return null; + } +} + +?> +----- +get(); + } + + private function get(): mixed + { + return null; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Php80Test.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Php80Test.php index 8bd34f01534..91192c6868e 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Php80Test.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Php80Test.php @@ -5,26 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class Php80Test extends AbstractRectorTestCase { - /** - * @requires PHP 8.0 - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp80'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/ReturnTypeFromStrictTypedCallRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/ReturnTypeFromStrictTypedCallRectorTest.php index 7cdacfa3537..decd80b1a53 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/ReturnTypeFromStrictTypedCallRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/ReturnTypeFromStrictTypedCallRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReturnTypeFromStrictTypedCallRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Source/Repository.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Source/Repository.php new file mode 100644 index 00000000000..b4ff914f329 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Source/Repository.php @@ -0,0 +1,16 @@ +services(); - $services->set(ReturnTypeFromStrictTypedCallRector::class); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(ReturnTypeFromStrictTypedCallRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_74); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/config/php80_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/config/php80_rule.php index 6a61ec44210..cf1c3edcd02 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/config/php80_rule.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/config/php80_rule.php @@ -2,15 +2,11 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersion; +use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersion; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReturnTypeFromStrictTypedCallRector::class); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_80); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(ReturnTypeFromStrictTypedCallRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_80); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/initialized_in_getter.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/initialized_in_getter.php.inc new file mode 100644 index 00000000000..f534528d2ea --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/initialized_in_getter.php.inc @@ -0,0 +1,39 @@ +demo === null) { + $this->demo = new stdClass(); + } + return $this->demo; + } +} + +?> +----- +demo === null) { + $this->demo = new stdClass(); + } + return $this->demo; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/inner_classmethod.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/inner_classmethod.php.inc new file mode 100644 index 00000000000..ce2e8d4bda3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/inner_classmethod.php.inc @@ -0,0 +1,39 @@ +age; + } + }; + } +} + +?> +----- +age; + } + }; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/return_static_property.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/return_static_property.php.inc new file mode 100644 index 00000000000..cb3b50c8588 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/Fixture/return_static_property.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/ReturnTypeFromStrictTypedPropertyRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/ReturnTypeFromStrictTypedPropertyRectorTest.php index 7c275dc46b2..058827b9adf 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/ReturnTypeFromStrictTypedPropertyRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/ReturnTypeFromStrictTypedPropertyRectorTest.php @@ -5,26 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedPropertyRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ReturnTypeFromStrictTypedPropertyRectorTest extends AbstractRectorTestCase { - /** - * @requires PHP 8.0 - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/config/configured_rule.php index 2f27395e053..51aab0a5818 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/config/configured_rule.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector/config/configured_rule.php @@ -2,10 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedPropertyRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ReturnTypeFromStrictTypedPropertyRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES); + + $rectorConfig->rule(ReturnTypeFromStrictTypedPropertyRector::class); }; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Fixture/skip_different_object.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Fixture/skip_different_object.php.inc new file mode 100644 index 00000000000..f06a2adac59 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Fixture/skip_different_object.php.inc @@ -0,0 +1,15 @@ +serializer->deserialize($data, \stdClass::class, 'json'); + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..c8b73a664d3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Fixture/some_class.php.inc @@ -0,0 +1,31 @@ +serializer->deserialize($data, \stdClass::class, 'json'); + } +} + +?> +----- +serializer->deserialize($data, \stdClass::class, 'json'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/ReturnTypeFromSymfonySerializerRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/ReturnTypeFromSymfonySerializerRectorTest.php new file mode 100644 index 00000000000..2ce6022a568 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/ReturnTypeFromSymfonySerializerRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Source/DifferentType.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Source/DifferentType.php new file mode 100644 index 00000000000..ff893005920 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector/Source/DifferentType.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersionFeature::HAS_RETURN_TYPE); + $rectorConfig->rule(ReturnTypeFromSymfonySerializerRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/anonymous_class_implements_interface_and_other_object.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/anonymous_class_implements_interface_and_other_object.php.inc new file mode 100644 index 00000000000..ab5304ded3e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/anonymous_class_implements_interface_and_other_object.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/change_nullable_union_different_in_parent.php copy.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/change_nullable_union_different_in_parent.php copy.inc new file mode 100644 index 00000000000..8f3f70b0b42 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/change_nullable_union_different_in_parent.php copy.inc @@ -0,0 +1,59 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/extended_class.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/extended_class.php.inc new file mode 100644 index 00000000000..fe9c1792bb1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/extended_class.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/false_bool_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/false_bool_docblock.php.inc new file mode 100644 index 00000000000..ad6236862bb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/false_bool_docblock.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/multi_scalar.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/multi_scalar.php.inc new file mode 100644 index 00000000000..1c874c58bf9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/multi_scalar.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_anonymous_class_and_other_object.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_anonymous_class_and_other_object.php.inc new file mode 100644 index 00000000000..1ddfb1996f5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_anonymous_class_and_other_object.php.inc @@ -0,0 +1,15 @@ +execute(); + } + + /** + * @return null|stdClass + */ + private function execute() + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_from_docblock_param.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_from_docblock_param.php.inc new file mode 100644 index 00000000000..b3ccdc84067 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_from_docblock_param.php.inc @@ -0,0 +1,16 @@ +foo = 1; + return $stdClass; + } + + if (rand(0, 1)) { + $stdClass->bar = 1; + return $stdClass; + } + + return $stdClass; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_possible_void.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_possible_void.php.inc new file mode 100644 index 00000000000..2e1342a4e75 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_possible_void.php.inc @@ -0,0 +1,23 @@ +foo = 1; + return $stdClass; + } + + if (rand(0, 1)) { + $stdClass->bar = 1; + return $stdClass; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_union_with_yield.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_union_with_yield.php.inc new file mode 100644 index 00000000000..eabea7c5b5e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/skip_union_with_yield.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_array.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_array.php.inc new file mode 100644 index 00000000000..7f97220cb06 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_array.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_callable_return.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_callable_return.php.inc new file mode 100644 index 00000000000..561f3026450 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_callable_return.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_multi.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_multi.php.inc new file mode 100644 index 00000000000..c37715dd5e5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Fixture/union_multi.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixturePhp81/false_in_union.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixturePhp81/false_in_union.php.inc new file mode 100644 index 00000000000..19372ea1e1f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixturePhp81/false_in_union.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixturePhp81/skip_nullable_handled_by_another_rule.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixturePhp81/skip_nullable_handled_by_another_rule.php.inc new file mode 100644 index 00000000000..6b883951106 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixturePhp81/skip_nullable_handled_by_another_rule.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureTrueInUnion/true_in_union.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureTrueInUnion/true_in_union.php.inc new file mode 100644 index 00000000000..db2839b5ad0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureTrueInUnion/true_in_union.php.inc @@ -0,0 +1,41 @@ += php 8.2, ref https://3v4l.org/UJqXT + */ +final class TrueInUnion +{ + public function run($value) + { + if ($value) { + return true; + } + + return substr('warning', 1); + } +} + +?> +----- += php 8.2, ref https://3v4l.org/UJqXT + */ +final class TrueInUnion +{ + public function run($value): true|string + { + if ($value) { + return true; + } + + return substr('warning', 1); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureTrueInUnion/true_in_union_strtoupper.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureTrueInUnion/true_in_union_strtoupper.php.inc new file mode 100644 index 00000000000..eb25a83bc0e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureTrueInUnion/true_in_union_strtoupper.php.inc @@ -0,0 +1,41 @@ += php 8.2, ref https://3v4l.org/UJqXT + */ +final class TrueInUnionStrToUpper +{ + public function run($value) + { + if ($value) { + return true; + } + + return strtoupper('warning'); + } +} + +?> +----- += php 8.2, ref https://3v4l.org/UJqXT + */ +final class TrueInUnionStrToUpper +{ + public function run($value): true|string + { + if ($value) { + return true; + } + + return strtoupper('warning'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureUnionIntersection/union_intersection.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureUnionIntersection/union_intersection.php.inc new file mode 100644 index 00000000000..ee58e3da974 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/FixtureUnionIntersection/union_intersection.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/ReturnUnionTypeRectorPhp81Test.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/ReturnUnionTypeRectorPhp81Test.php new file mode 100644 index 00000000000..4d849133d7a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/ReturnUnionTypeRectorPhp81Test.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp81'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php81.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/ReturnUnionTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/ReturnUnionTypeRectorTest.php new file mode 100644 index 00000000000..aae506d5d7b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/ReturnUnionTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Source/SomeAnonymousInterface.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Source/SomeAnonymousInterface.php new file mode 100644 index 00000000000..89f40a08638 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/Source/SomeAnonymousInterface.php @@ -0,0 +1,9 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureTrueInUnion'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_true_in_union.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/UnionIntersectionTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/UnionIntersectionTest.php new file mode 100644 index 00000000000..0d0c2edbcfe --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/UnionIntersectionTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureUnionIntersection'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_union_intersection.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..d1132dea29c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule.php @@ -0,0 +1,12 @@ +rule(ReturnUnionTypeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_php81.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_php81.php new file mode 100644 index 00000000000..c6d755206f1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_php81.php @@ -0,0 +1,12 @@ +rule(ReturnUnionTypeRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_81); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_true_in_union.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_true_in_union.php new file mode 100644 index 00000000000..3870cf3c697 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_true_in_union.php @@ -0,0 +1,12 @@ +rule(ReturnUnionTypeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::NULL_FALSE_TRUE_STANDALONE_TYPE); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_union_intersection.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_union_intersection.php new file mode 100644 index 00000000000..576f52eb528 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector/config/configured_rule_union_intersection.php @@ -0,0 +1,12 @@ +rule(ReturnUnionTypeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::UNION_INTERSECTION_TYPES); +}; diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/both_integer_and_string_index.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/both_integer_and_string_index.php.inc new file mode 100644 index 00000000000..968c273d442 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/both_integer_and_string_index.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/cover_isset_with_dim_fetch.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/cover_isset_with_dim_fetch.php.inc new file mode 100644 index 00000000000..917d1b0e17e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/cover_isset_with_dim_fetch.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/direct-coalesce.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/direct-coalesce.php.inc new file mode 100644 index 00000000000..e2c3d7b3b08 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/direct-coalesce.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/empty_index.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/empty_index.php.inc new file mode 100644 index 00000000000..95a6ca143ac --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/empty_index.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/include_isset_index.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/include_isset_index.php.inc new file mode 100644 index 00000000000..2efe88605bb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/include_isset_index.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_array_access.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_array_access.php.inc new file mode 100644 index 00000000000..aeeeeee0957 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_array_access.php.inc @@ -0,0 +1,24 @@ + $item + */ + public function resolve($item) + { + return $item['name']; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_array_possible_string.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_array_possible_string.php.inc new file mode 100644 index 00000000000..28e7fbe6f37 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_array_possible_string.php.inc @@ -0,0 +1,17 @@ + $param) { + echo $params[$key]; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_coalesce2.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_coalesce2.php.inc new file mode 100644 index 00000000000..69fdf5cb08a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_coalesce2.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_empty_check.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_empty_check.php.inc new file mode 100644 index 00000000000..1815d0f012c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_empty_check.php.inc @@ -0,0 +1,15 @@ +doSomething(); + + return $item['name']; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_possible_string.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_possible_string.php.inc new file mode 100644 index 00000000000..e663583e9f2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_possible_string.php.inc @@ -0,0 +1,13 @@ +convert($maybe_not_array); + echo $maybe_not_array['test']; + } + + private function convert($maybe_not_array) + { + return ['test' => 'test']; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_same_name_property_append.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_same_name_property_append.php.inc new file mode 100644 index 00000000000..4c53d8ddb49 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_same_name_property_append.php.inc @@ -0,0 +1,11 @@ +success[] = $success; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_typed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_typed.php.inc new file mode 100644 index 00000000000..43192922cdb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_typed.php.inc @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_unknown_parent.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_unknown_parent.php.inc new file mode 100644 index 00000000000..6e5f772ade7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/skip_unknown_parent.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/with_closure.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/with_closure.php.inc new file mode 100644 index 00000000000..22ccaad40c4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/with_closure.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/with_default_array_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/with_default_array_type.php.inc new file mode 100644 index 00000000000..beb306ea1af --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Fixture/with_default_array_type.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Source/SomeInterface.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Source/SomeInterface.php new file mode 100644 index 00000000000..26cf282692e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/Source/SomeInterface.php @@ -0,0 +1,10 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/config/configured_rule.php new file mode 100644 index 00000000000..5e62b85cfaf --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StrictArrayParamDimFetchRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/do_not_change_different_type_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/do_not_change_different_type_default_value.php.inc new file mode 100644 index 00000000000..413ffc1bd40 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/do_not_change_different_type_default_value.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/nullable_param_from_null_compare.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/nullable_param_from_null_compare.php.inc new file mode 100644 index 00000000000..e625cb0ea0c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/nullable_param_from_null_compare.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/skip_another_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/skip_another_type.php.inc new file mode 100644 index 00000000000..1f17a905643 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/skip_another_type.php.inc @@ -0,0 +1,11 @@ += 1_073_741_824) { + $bytes = number_format($bytes / 1_073_741_824, 1).' GB'; + } elseif ($bytes >= 1_048_576) { + $bytes = number_format($bytes / 1_048_576, 1).' MB'; + } elseif ($bytes >= 1024) { + $bytes = number_format($bytes / 1024, 1).' KB'; + } elseif ($bytes > 1) { + $bytes .= ' bytes'; + } elseif (1 === $bytes) { + $bytes .= ' byte'; + } else { + $bytes = '0 bytes'; + } + + return $bytes; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/skip_override_trait.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/skip_override_trait.php.inc new file mode 100644 index 00000000000..681bd56f3b0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/skip_override_trait.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/with_inner_closure.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/with_inner_closure.php.inc new file mode 100644 index 00000000000..6e15c876672 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/with_inner_closure.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/with_inner_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/with_inner_function.php.inc new file mode 100644 index 00000000000..202d01ed284 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Fixture/with_inner_function.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Source/SomeTrait.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Source/SomeTrait.php new file mode 100644 index 00000000000..639893260f6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/Source/SomeTrait.php @@ -0,0 +1,10 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/config/configured_rule.php new file mode 100644 index 00000000000..27e602c5f8b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StrictStringParamConcatRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/skip_return_concat.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/skip_return_concat.php.inc new file mode 100644 index 00000000000..60409c4421f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/skip_return_concat.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/string_from_encapsed.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/string_from_encapsed.php.inc new file mode 100644 index 00000000000..ed69507cea0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/string_from_encapsed.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/string_herenowdoc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/string_herenowdoc.php.inc new file mode 100644 index 00000000000..9bff9198e94 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/Fixture/string_herenowdoc.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/StringReturnTypeFromStrictScalarReturnsRectorTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/StringReturnTypeFromStrictScalarReturnsRectorTest.php new file mode 100644 index 00000000000..daf3cf0b201 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/StringReturnTypeFromStrictScalarReturnsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/config/configured_rule.php new file mode 100644 index 00000000000..38cc9e8e8aa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StringReturnTypeFromStrictScalarReturnsRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/Fixture/return_concat.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/Fixture/return_concat.php.inc new file mode 100644 index 00000000000..29614890db8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/Fixture/return_concat.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/Fixture/skip_booleans.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/Fixture/skip_booleans.php.inc new file mode 100644 index 00000000000..56d25313c80 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/Fixture/skip_booleans.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/config/configured_rule.php new file mode 100644 index 00000000000..336cd234cbc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StringReturnTypeFromStrictStringReturnsRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/AddTestsVoidReturnTypeWhereNoReturnRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/AddTestsVoidReturnTypeWhereNoReturnRectorTest.php new file mode 100644 index 00000000000..ad10994c448 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/AddTestsVoidReturnTypeWhereNoReturnRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/Fixture/skip_data_provider.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/Fixture/skip_data_provider.php.inc new file mode 100644 index 00000000000..6a83044dd0b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/Fixture/skip_data_provider.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/config/configured_rule.php new file mode 100644 index 00000000000..c1f794f5731 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(AddTestsVoidReturnTypeWhereNoReturnRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/ChildDoctrineRepositoryClassTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/ChildDoctrineRepositoryClassTypeRectorTest.php new file mode 100644 index 00000000000..2b4d213025c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/ChildDoctrineRepositoryClassTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/array_find_by.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/array_find_by.php.inc new file mode 100644 index 00000000000..7a5fdcd3b6c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/array_find_by.php.inc @@ -0,0 +1,46 @@ + + */ +final class ArrayFindBy extends EntityRepository +{ + public function findSome($userId) + { + return $this->findBy([ + 'userId' => $userId, + ]); + } +} + +?> +----- + + */ +final class ArrayFindBy extends EntityRepository +{ + /** + * @return SomeObject[] + */ + public function findSome($userId): array + { + return $this->findBy([ + 'userId' => $userId, + ]); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/get_one_or_null.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/get_one_or_null.php.inc new file mode 100644 index 00000000000..338e608b650 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/get_one_or_null.php.inc @@ -0,0 +1,41 @@ + + */ +final class GetOneOrNull extends EntityRepository +{ + public function findSome() + { + return $this->createQueryBuilder() + ->getOneOrNullResult(); + } +} + +?> +----- + + */ +final class GetOneOrNull extends EntityRepository +{ + public function findSome(): ?SomeObject + { + return $this->createQueryBuilder() + ->getOneOrNullResult(); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/skip_generics.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/skip_generics.php.inc new file mode 100644 index 00000000000..04ef3b6e122 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/skip_generics.php.inc @@ -0,0 +1,27 @@ + + */ +final class ExtendedEntityRepository extends EntityRepository +{ + /** + * Get an object or throws an exception + * + * @param array $conditions + * + * @return T + */ + public function findOneByOrThrow(array $conditions) { + $entity = $this->findOneBy($conditions); + if ($entity === null) { + throw new \RuntimeException(); + } + return $entity; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..adb750d6f33 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Fixture/some_class.php.inc @@ -0,0 +1,43 @@ + + */ +final class ProductRepository extends EntityRepository +{ + public function findSome($userId) + { + return $this->findOneBy([ + 'userId' => $userId, + ]); + } +} + +?> +----- + + */ +final class ProductRepository extends EntityRepository +{ + public function findSome($userId): ?SomeObject + { + return $this->findOneBy([ + 'userId' => $userId, + ]); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Source/SomeObject.php b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Source/SomeObject.php new file mode 100644 index 00000000000..7b5945e36bc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector/Source/SomeObject.php @@ -0,0 +1,7 @@ +rule(ChildDoctrineRepositoryClassTypeRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/do_not_remove_doc_with_description.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/do_not_remove_doc_with_description.php.inc new file mode 100644 index 00000000000..c8f0c095477 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/do_not_remove_doc_with_description.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/skip_different_type.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/skip_different_type.php.inc new file mode 100644 index 00000000000..b1be45a5a71 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/skip_different_type.php.inc @@ -0,0 +1,8 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/type_from_use.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/type_from_use.php.inc new file mode 100644 index 00000000000..10f131b3de8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/Fixture/type_from_use.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/MergeDateTimePropertyTypeDeclarationRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/MergeDateTimePropertyTypeDeclarationRectorTest.php new file mode 100644 index 00000000000..9733e8590d8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/MergeDateTimePropertyTypeDeclarationRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/config/configured_rule.php new file mode 100644 index 00000000000..d1c2edb328e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector/config/configured_rule.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersionFeature::TYPED_PROPERTIES); + $rectorConfig->rule(MergeDateTimePropertyTypeDeclarationRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/date_time_with_format.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/date_time_with_format.php.inc new file mode 100644 index 00000000000..182b31072b6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/date_time_with_format.php.inc @@ -0,0 +1,23 @@ +")] + private $name; +} + +?> +----- +")] + private ?\DateTime $name = null; +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/exact_class.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/exact_class.php.inc new file mode 100644 index 00000000000..1b3453807a1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/exact_class.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/handle_public_as_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/handle_public_as_nullable.php.inc new file mode 100644 index 00000000000..6d05626f069 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/handle_public_as_nullable.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_class_not_found.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_class_not_found.php.inc new file mode 100644 index 00000000000..37d08b15dc9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_class_not_found.php.inc @@ -0,0 +1,11 @@ +')] + private $name; +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_scalar.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_scalar.php.inc new file mode 100644 index 00000000000..391a8e59641 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_scalar.php.inc @@ -0,0 +1,14 @@ +doTestFile($filePath); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Source/SomeClassInSerializer.php b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Source/SomeClassInSerializer.php new file mode 100644 index 00000000000..c903e8c7d3a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector/Source/SomeClassInSerializer.php @@ -0,0 +1,8 @@ +phpVersion(PhpVersionFeature::ATTRIBUTES); + $rectorConfig->rule(ObjectTypedPropertyFromJMSSerializerAttributeTypeRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/combine_union.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/combine_union.php.inc new file mode 100644 index 00000000000..9711e99bc13 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/combine_union.php.inc @@ -0,0 +1,41 @@ +test; + } + + public function setTest(?string $test): void + { + $this->test = $test; + } +} + +?> +----- +test; + } + + public function setTest(?string $test): void + { + $this->test = $test; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/keep_existing_float_default.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/keep_existing_float_default.php.inc new file mode 100644 index 00000000000..7f97898bb2c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/keep_existing_float_default.php.inc @@ -0,0 +1,41 @@ +test; + } + + public function setTest(float $test): void + { + $this->test = $test; + } +} + +?> +----- +test; + } + + public function setTest(float $test): void + { + $this->test = $test; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/nullable_getter_setter.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/nullable_getter_setter.php.inc new file mode 100644 index 00000000000..f4ee236e7d1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/nullable_getter_setter.php.inc @@ -0,0 +1,41 @@ +name = $name; + } + + public function getName(): ?string + { + return $this->name; + } +} + +?> +----- +name = $name; + } + + public function getName(): ?string + { + return $this->name; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/remove_default_null.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/remove_default_null.php.inc new file mode 100644 index 00000000000..01a593c8493 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/remove_default_null.php.inc @@ -0,0 +1,41 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} + +?> +----- +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_different_getter_property_name.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_different_getter_property_name.php.inc new file mode 100644 index 00000000000..0de9fb2ee03 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_different_getter_property_name.php.inc @@ -0,0 +1,20 @@ +name = $name; + } + + public function getDifferentName(): string + { + return $this->differentName; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_doc_type.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_doc_type.php.inc new file mode 100644 index 00000000000..5b38acfddae --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_doc_type.php.inc @@ -0,0 +1,22 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_public_property.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_public_property.php.inc new file mode 100644 index 00000000000..d3762b5d0ab --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_public_property.php.inc @@ -0,0 +1,18 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_used_by_trait.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_used_by_trait.php.inc new file mode 100644 index 00000000000..7404307ad17 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/skip_used_by_trait.php.inc @@ -0,0 +1,22 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..1b7670c1c60 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/some_class.php.inc @@ -0,0 +1,41 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} + +?> +----- +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/use_float_type_with_defaults.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/use_float_type_with_defaults.php.inc new file mode 100644 index 00000000000..e277b20e457 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/use_float_type_with_defaults.php.inc @@ -0,0 +1,41 @@ +test; + } + + public function setTest(float $test): void + { + $this->test = $test; + } +} + +?> +----- +test; + } + + public function setTest(float $test): void + { + $this->test = $test; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/use_lnumber_type_with_defaults.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/use_lnumber_type_with_defaults.php.inc new file mode 100644 index 00000000000..b12586ef328 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Fixture/use_lnumber_type_with_defaults.php.inc @@ -0,0 +1,41 @@ +test; + } + + public function setTest(int $test): void + { + $this->test = $test; + } +} + +?> +----- +test; + } + + public function setTest(int $test): void + { + $this->test = $test; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/PropertyTypeFromStrictSetterGetterRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/PropertyTypeFromStrictSetterGetterRectorTest.php new file mode 100644 index 00000000000..7fea6ea01e2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/PropertyTypeFromStrictSetterGetterRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Source/SomeTrait.php b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Source/SomeTrait.php new file mode 100644 index 00000000000..e0745c14537 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/Source/SomeTrait.php @@ -0,0 +1,13 @@ +name = new stdClass; + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/config/configured_rule.php new file mode 100644 index 00000000000..f9a34d12de9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([PropertyTypeFromStrictSetterGetterRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/skip_already_known.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/skip_already_known.php.inc new file mode 100644 index 00000000000..d838c90fc8e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/skip_already_known.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/ReturnIteratorInDataProviderRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/ReturnIteratorInDataProviderRectorTest.php new file mode 100644 index 00000000000..484cf523761 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/ReturnIteratorInDataProviderRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/config/configured_rule.php new file mode 100644 index 00000000000..81ec5e6cac8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ReturnIteratorInDataProviderRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/float_ternary.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/float_ternary.php.inc new file mode 100644 index 00000000000..fb0ba3f6485 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/float_ternary.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/function.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/function.php.inc new file mode 100644 index 00000000000..039645cfd53 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/function.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/get_truthy_check.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/get_truthy_check.php.inc new file mode 100644 index 00000000000..62d2912fab2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/get_truthy_check.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/in_inner_function.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/in_inner_function.php.inc new file mode 100644 index 00000000000..4d1f94a9c19 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/in_inner_function.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/local_constants.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/local_constants.php.inc new file mode 100644 index 00000000000..9c1fba82299 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/local_constants.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/return_from_ternary.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/return_from_ternary.php.inc new file mode 100644 index 00000000000..00eabc4d877 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/return_from_ternary.php.inc @@ -0,0 +1,39 @@ +find_one(); + + return $rent ?: null; + } + +} +?> +----- +find_one(); + + return $rent ?: null; + } + +} +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_dynamic_scalar.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_dynamic_scalar.php.inc new file mode 100644 index 00000000000..2faf7cc8daf --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_dynamic_scalar.php.inc @@ -0,0 +1,11 @@ +get(); + } + return $x ?: null; + } + + /** + * @return SkipPhpdocs + */ + public function get() {} +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_union_void.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_union_void.php.inc new file mode 100644 index 00000000000..6594b5224ce --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_union_void.php.inc @@ -0,0 +1,20 @@ +find_one(); + + return $rent ?: null; + } + } + +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_wrong_return.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_wrong_return.php.inc new file mode 100644 index 00000000000..034cc06c370 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/skip_wrong_return.php.inc @@ -0,0 +1,24 @@ +get(); + } + + if (rand(0,1)) { + return $x ?: null; + } + + // missing return + } + + public function get(): SkipWrongReturn {} +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..dba1d41ea97 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/some_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/ternary_short_var_call.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/ternary_short_var_call.php.inc new file mode 100644 index 00000000000..1a7440694c4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/Fixture/ternary_short_var_call.php.inc @@ -0,0 +1,31 @@ +get() ?: null; + } + + public function get(): CallInShortTernaryVar|false {} +} + +?> +----- +get() ?: null; + } + + public function get(): CallInShortTernaryVar|false {} +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/ReturnTypeFromStrictTernaryRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/ReturnTypeFromStrictTernaryRectorTest.php new file mode 100644 index 00000000000..7b11b13cbb4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/ReturnTypeFromStrictTernaryRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/config/configured_rule.php new file mode 100644 index 00000000000..6f1bf462898 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector/config/configured_rule.php @@ -0,0 +1,12 @@ +phpVersion(PhpVersionFeature::NULLABLE_TYPE); + $rectorConfig->rule(ReturnTypeFromStrictTernaryRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/exact_type_assigned.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/exact_type_assigned.php.inc new file mode 100644 index 00000000000..fd1f89eb794 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/exact_type_assigned.php.inc @@ -0,0 +1,33 @@ +name = $name; + } +} + +?> +----- +name = $name; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/float_var_string.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/float_var_string.php.inc new file mode 100644 index 00000000000..89ec15f6390 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/float_var_string.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/non_typed_assigned.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/non_typed_assigned.php.inc new file mode 100644 index 00000000000..b5718cf96df --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/non_typed_assigned.php.inc @@ -0,0 +1,33 @@ +name = $name; + } +} + +?> +----- +name = $name; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/non_typed_assigned_by_constructor.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/non_typed_assigned_by_constructor.php.inc new file mode 100644 index 00000000000..b69d5f55a1f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/non_typed_assigned_by_constructor.php.inc @@ -0,0 +1,33 @@ +name = $name; + } +} + +?> +----- +name = $name; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/nullable_int_with_constructor_set.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/nullable_int_with_constructor_set.php.inc new file mode 100644 index 00000000000..92bc12c8c29 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/nullable_int_with_constructor_set.php.inc @@ -0,0 +1,33 @@ +number = $number; + } +} + +?> +----- +number = $number; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_object.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_object.php.inc new file mode 100644 index 00000000000..828861b1549 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_object.php.inc @@ -0,0 +1,12 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/string_var_float.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/string_var_float.php.inc new file mode 100644 index 00000000000..82202dcd380 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/string_var_float.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/ScalarTypedPropertyFromJMSSerializerAttributeTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/ScalarTypedPropertyFromJMSSerializerAttributeTypeRectorTest.php new file mode 100644 index 00000000000..74decd09ed8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/ScalarTypedPropertyFromJMSSerializerAttributeTypeRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Source/SomeClassInSerializer.php b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Source/SomeClassInSerializer.php new file mode 100644 index 00000000000..9398214f2fa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector/Source/SomeClassInSerializer.php @@ -0,0 +1,8 @@ +phpVersion(PhpVersionFeature::ATTRIBUTES); + $rectorConfig->rule(ScalarTypedPropertyFromJMSSerializerAttributeTypeRector::class); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/nullable_mock_object.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/nullable_mock_object.php.inc new file mode 100644 index 00000000000..bacc324ea24 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/nullable_mock_object.php.inc @@ -0,0 +1,47 @@ +someMock = $this->createMock(SomeMockedClass::class); + } + + protected function tearDown(): void + { + $this->someMock = null; + } +} + +?> +----- +someMock = $this->createMock(SomeMockedClass::class); + } + + protected function tearDown(): void + { + $this->someMock = null; + } +} + +?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/override_existing_wrong_type.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/override_existing_wrong_type.php.inc new file mode 100644 index 00000000000..01dd6117321 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/override_existing_wrong_type.php.inc @@ -0,0 +1,37 @@ +someMock = $this->createMock(SomeMockedClass::class); + } +} + +?> +----- +someMock = $this->createMock(SomeMockedClass::class); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/setup_method_exists_but_not_assign.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/setup_method_exists_but_not_assign.php.inc new file mode 100644 index 00000000000..ffa74b4f681 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/setup_method_exists_but_not_assign.php.inc @@ -0,0 +1,45 @@ +someMock = $this->createMock(SomeMockedClass::class); + } +} + +?> +----- +someMock = $this->createMock(SomeMockedClass::class); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_already_mockobject_type.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_already_mockobject_type.php.inc new file mode 100644 index 00000000000..3108f9b7b1e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_already_mockobject_type.php.inc @@ -0,0 +1,16 @@ +someMock = $this->createMock(SomeMockedClass::class); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_intersection_type.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_intersection_type.php.inc new file mode 100644 index 00000000000..986a5c7f7fa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_intersection_type.php.inc @@ -0,0 +1,17 @@ +someMock = $this->createMock(SomeMockedClass::class); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_non_mock_object_type.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_non_mock_object_type.php.inc new file mode 100644 index 00000000000..34f2133c7ac --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/skip_non_mock_object_type.php.inc @@ -0,0 +1,15 @@ +someProperty = 'some property value'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/some_fixture.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/some_fixture.php.inc new file mode 100644 index 00000000000..a3476eb9a85 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/some_fixture.php.inc @@ -0,0 +1,37 @@ +someMock = $this->createMock(SomeMockedClass::class); + } +} + +?> +----- +someMock = $this->createMock(SomeMockedClass::class); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/with_finalized_type.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/with_finalized_type.php.inc new file mode 100644 index 00000000000..824d550d953 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/with_finalized_type.php.inc @@ -0,0 +1,39 @@ +someMock = $this->createMock(SomeFinalizedMockedClass::class); + } +} + +?> +----- +someMock = $this->createMock(SomeFinalizedMockedClass::class); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/with_non_setup_method.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/with_non_setup_method.php.inc new file mode 100644 index 00000000000..4574cd5c313 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Fixture/with_non_setup_method.php.inc @@ -0,0 +1,37 @@ +someMock = $this->createMock(SomeMockedClass::class); + } +} + +?> +----- +someMock = $this->createMock(SomeMockedClass::class); + } +} + +?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Source/SomeFinalizedMockedClass.php b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Source/SomeFinalizedMockedClass.php new file mode 100644 index 00000000000..4cfd17aca6b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/Source/SomeFinalizedMockedClass.php @@ -0,0 +1,9 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/rule_config.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/config/rule_config.php b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/config/rule_config.php new file mode 100644 index 00000000000..e66249aab4c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector/config/rule_config.php @@ -0,0 +1,11 @@ +withRules([TypedPropertyFromCreateMockAssignRector::class]) + ->withPhpVersion(PhpVersionFeature::TYPED_PROPERTIES); diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/keep_generic_type_doc.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/keep_generic_type_doc.php.inc new file mode 100644 index 00000000000..ac1056a79f6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/keep_generic_type_doc.php.inc @@ -0,0 +1,43 @@ + + */ + private $someType; + + protected function setUp(): void + { + $this->someType = $this->create('string'); + } +} + +?> +----- + + */ + private \Iterator $someType; + + protected function setUp(): void + { + $this->someType = $this->create('string'); + } +} + +?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/magic_setup_test_assign.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/magic_setup_test_assign.php.inc new file mode 100644 index 00000000000..b168c2536e0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/magic_setup_test_assign.php.inc @@ -0,0 +1,40 @@ +someType = $this->create('string'); + } +} + +?> +----- +someType = $this->create('string'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/skip_no_docblock.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/skip_no_docblock.php.inc new file mode 100644 index 00000000000..7e9a8fc62e5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/skip_no_docblock.php.inc @@ -0,0 +1,16 @@ +someType = $this->create('string'); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/skip_public_property.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/skip_public_property.php.inc new file mode 100644 index 00000000000..744ed5fd27d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Fixture/skip_public_property.php.inc @@ -0,0 +1,19 @@ +someType = $this->create('string'); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Source/SomeDocblockType.php b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Source/SomeDocblockType.php new file mode 100644 index 00000000000..411046e54a9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/Source/SomeDocblockType.php @@ -0,0 +1,9 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/rule_config.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/config/rule_config.php b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/config/rule_config.php new file mode 100644 index 00000000000..88c338e2c6a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector/config/rule_config.php @@ -0,0 +1,11 @@ +withRules([TypedPropertyFromDocblockSetUpDefinedRector::class]) + ->withPhpVersion(PhpVersionFeature::TYPED_PROPERTIES); diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/Fixture/skip_non_null_default.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/Fixture/skip_non_null_default.php.inc new file mode 100644 index 00000000000..d216b1e5418 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/Fixture/skip_non_null_default.php.inc @@ -0,0 +1,14 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/Source/OptionalBehatProperty.php b/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/Source/OptionalBehatProperty.php new file mode 100644 index 00000000000..14278dbedaa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/Source/OptionalBehatProperty.php @@ -0,0 +1,8 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/config/configured_rule.php new file mode 100644 index 00000000000..3fbb2c71599 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRectorTest/config/configured_rule.php @@ -0,0 +1,10 @@ +rules([TypedStaticPropertyInBehatContextRector::class]); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/AddClosureNeverReturnTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/AddClosureNeverReturnTypeRectorTest.php new file mode 100644 index 00000000000..394cf5251f5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/AddClosureNeverReturnTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..dd927965287 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/Fixture/skip_has_return.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/Fixture/skip_has_return.php.inc new file mode 100644 index 00000000000..0b720a86ef0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector/Fixture/skip_has_return.php.inc @@ -0,0 +1,19 @@ +rule(AddClosureNeverReturnTypeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::NEVER_TYPE); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/AddClosureReturnTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/AddClosureReturnTypeRectorTest.php deleted file mode 100644 index ebcdc2e9f52..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/AddClosureReturnTypeRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/before_union_types.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/callable_false_positive.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/callable_false_positive.php.inc deleted file mode 100644 index 93cf5d3863b..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/callable_false_positive.php.inc +++ /dev/null @@ -1,59 +0,0 @@ - $callable) { - // 1. convert instant assign to callable - if (!is_callable($callable)) { - $populatedCallables[$key] = function () use ($callable) { - return $callable; - }; - continue; - } - } - - return $populatedCallables; - } -} - -?> ------ - $callable) { - // 1. convert instant assign to callable - if (!is_callable($callable)) { - $populatedCallables[$key] = function () use ($callable): string { - return $callable; - }; - continue; - } - } - - return $populatedCallables; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/fixture.php.inc deleted file mode 100644 index 44621a1876e..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/return_type_object.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/return_type_object.php.inc deleted file mode 100644 index a01776d0d16..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/return_type_object.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -name; - }); - } -} - -?> ------ -name; - }); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/subtype_of_object.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/subtype_of_object.php.inc deleted file mode 100644 index 3022c515e9d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/Fixture/subtype_of_object.php.inc +++ /dev/null @@ -1,69 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable([$stmt], function (Node $node) - { - if (!$node instanceof String_) { - return $node; - } - - $match = Strings::match($node->value, '#(\\$|\\\\)(?\d+)#'); - if (!$match) { - return $node; - } - - $matchesVariable = new Variable('matches'); - - return new ArrayDimFetch($matchesVariable, new LNumber((int)$match['number'])); - }); - } -} - -?> ------ -simpleCallableNodeTraverser->traverseNodesWithCallable([$stmt], function (Node $node): \PhpParser\Node - { - if (!$node instanceof String_) { - return $node; - } - - $match = Strings::match($node->value, '#(\\$|\\\\)(?\d+)#'); - if (!$match) { - return $node; - } - - $matchesVariable = new Variable('matches'); - - return new ArrayDimFetch($matchesVariable, new LNumber((int)$match['number'])); - }); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/config/before_union_types.php b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/config/before_union_types.php deleted file mode 100644 index 893297c4033..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector/config/before_union_types.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(AddClosureReturnTypeRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector/AddClosureVoidReturnTypeWhereNoReturnRectorTest.php b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector/AddClosureVoidReturnTypeWhereNoReturnRectorTest.php new file mode 100644 index 00000000000..0ff0ab3e2b2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector/AddClosureVoidReturnTypeWhereNoReturnRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector/Fixture/return_inside_inner_function.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector/Fixture/return_inside_inner_function.php.inc new file mode 100644 index 00000000000..6b98d44c850 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector/Fixture/return_inside_inner_function.php.inc @@ -0,0 +1,18 @@ +withRules([AddClosureVoidReturnTypeWhereNoReturnRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/ClosureReturnTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/ClosureReturnTypeRectorTest.php new file mode 100644 index 00000000000..3509412c5da --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/ClosureReturnTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/bool_type_check.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/bool_type_check.php.inc new file mode 100644 index 00000000000..9c5fa8c3aff --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/bool_type_check.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/cast_string_return.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/cast_string_return.php.inc new file mode 100644 index 00000000000..b355d1f578d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/cast_string_return.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/from_new_object.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/from_new_object.php.inc new file mode 100644 index 00000000000..1fcd0a3d375 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/from_new_object.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/inline_closure.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/inline_closure.php.inc new file mode 100644 index 00000000000..2e5a65b9561 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/inline_closure.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/nullable_intersection.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/nullable_intersection.php.inc new file mode 100644 index 00000000000..97de38dc71d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/nullable_intersection.php.inc @@ -0,0 +1,61 @@ +mock = $this->createMock(\Exception::class); + } + + public function testConfigureOptions(): void + { + $matcher = static::exactly(6); + $this->mock + ->expects($matcher) + ->method('__toString') + ->willReturnCallback(function (){ + return $this->mock; + }) + ; + } +} + +?> +----- +mock = $this->createMock(\Exception::class); + } + + public function testConfigureOptions(): void + { + $matcher = static::exactly(6); + $this->mock + ->expects($matcher) + ->method('__toString') + ->willReturnCallback(function (): (\Exception&\PHPUnit\Framework\MockObject\MockObject)|null{ + return $this->mock; + }) + ; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/numeric_result.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/numeric_result.php.inc new file mode 100644 index 00000000000..dda568588b2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/numeric_result.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/param_return_type.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/param_return_type.php.inc new file mode 100644 index 00000000000..cb9b337f9f8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/param_return_type.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_date_time_format.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_date_time_format.php.inc new file mode 100644 index 00000000000..62a793619de --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_date_time_format.php.inc @@ -0,0 +1,33 @@ +format('Y-m-d'); + }; + } +} + +?> +----- +format('Y-m-d'); + }; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_direct_array.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_direct_array.php.inc new file mode 100644 index 00000000000..4d6b21bd376 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_direct_array.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_self.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_self.php.inc new file mode 100644 index 00000000000..d0683795ec3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/return_self.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/skip_mixed_in_union.php.inc b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/skip_mixed_in_union.php.inc new file mode 100644 index 00000000000..1019757298f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/Fixture/skip_mixed_in_union.php.inc @@ -0,0 +1,17 @@ +format('Y-m-d'); + }; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..2e5891cdf49 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector/config/configured_rule.php @@ -0,0 +1,12 @@ +rule(ClosureReturnTypeRector::class); + $rectorConfig->phpVersion(PhpVersion::PHP_82); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/EmptyOnNullableObjectToInstanceOfRectorTest.php b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/EmptyOnNullableObjectToInstanceOfRectorTest.php new file mode 100644 index 00000000000..b5f796a6407 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/EmptyOnNullableObjectToInstanceOfRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/empty_on_exact_type.php.inc b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/empty_on_exact_type.php.inc new file mode 100644 index 00000000000..117329059b3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/empty_on_exact_type.php.inc @@ -0,0 +1,55 @@ +getExactType(); + if (empty($exactType)) { + return; + } + } + + private function getExactType(): ?AnotherType + { + if (mt_rand(0, 1)) { + return new AnotherType(); + } + + return null; + } +} + +?> +----- +getExactType(); + if (!$exactType instanceof \Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\VariableTypeResolver\Source\AnotherType) { + return; + } + } + + private function getExactType(): ?AnotherType + { + if (mt_rand(0, 1)) { + return new AnotherType(); + } + + return null; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/negated_empty.php.inc b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/negated_empty.php.inc new file mode 100644 index 00000000000..103e54aba25 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/negated_empty.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/skip_on_array_dim_fetch.php.inc b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/skip_on_array_dim_fetch.php.inc new file mode 100644 index 00000000000..2894098e249 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/skip_on_array_dim_fetch.php.inc @@ -0,0 +1,23 @@ +data[$key])) { + return $this->data[$key]; + } + + $this->data[$key] = new AnotherType(); + return $this->data[$key]; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/skip_phpdoc.php.inc b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/skip_phpdoc.php.inc new file mode 100644 index 00000000000..7c7e7ea5d18 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/skip_phpdoc.php.inc @@ -0,0 +1,20 @@ +id) && !$id->equals($this->id)) { + throw new \InvalidArgumentException('The ID is already set.'); + } + + $this->id = $id; + + return $this; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..47201c8657f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Fixture/some_class.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Source/AnotherObject.php b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Source/AnotherObject.php new file mode 100644 index 00000000000..a144d0fbcd5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector/Source/AnotherObject.php @@ -0,0 +1,11 @@ +withRules([EmptyOnNullableObjectToInstanceOfRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/AddArrayFunctionClosureParamTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/AddArrayFunctionClosureParamTypeRectorTest.php new file mode 100644 index 00000000000..a1da32cafd6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/AddArrayFunctionClosureParamTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_list.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_list.php.inc new file mode 100644 index 00000000000..0a5fd7765ba --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_list.php.inc @@ -0,0 +1,33 @@ + $items + */ + public function run(array $items) + { + $result = array_filter($items, fn ($item) => $item * 2); + } +} + +?> +----- + $items + */ + public function run(array $items) + { + $result = array_filter($items, fn (int $item) => $item * 2); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_type.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_type.php.inc new file mode 100644 index 00000000000..bd0703b44f2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_type.php.inc @@ -0,0 +1,33 @@ + $item * 2); + } +} + +?> +----- + $item * 2); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/include_array_map.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/include_array_map.php.inc new file mode 100644 index 00000000000..33cebffe87c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/include_array_map.php.inc @@ -0,0 +1,33 @@ + $item * 2, $items); + } +} + +?> +----- + $item * 2, $items); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/skip_direct_callable_without_params.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/skip_direct_callable_without_params.php.inc new file mode 100644 index 00000000000..57bb8e3bcc7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/skip_direct_callable_without_params.php.inc @@ -0,0 +1,11 @@ + random_int(1, 100), range(0, 23)); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/skip_unclear_param.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/skip_unclear_param.php.inc new file mode 100644 index 00000000000..a382cf1337d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/skip_unclear_param.php.inc @@ -0,0 +1,14 @@ + $item * 2); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/some_function.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/some_function.php.inc new file mode 100644 index 00000000000..286cd63bd86 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/some_function.php.inc @@ -0,0 +1,29 @@ + $item * 2); + } +} + +?> +----- + $item * 2); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/with_named_arg.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/with_named_arg.php.inc new file mode 100644 index 00000000000..9c95706707b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/with_named_arg.php.inc @@ -0,0 +1,29 @@ + $item * 2, array: $array); + } +} + +?> +----- + $item * 2, array: $array); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..2f210e0509c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddArrayFunctionClosureParamTypeRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/AddArrowFunctionParamArrayWhereDimFetchRectorTest.php b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/AddArrowFunctionParamArrayWhereDimFetchRectorTest.php new file mode 100644 index 00000000000..d2fb05f11aa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/AddArrowFunctionParamArrayWhereDimFetchRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/handle_usort.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/handle_usort.php.inc new file mode 100644 index 00000000000..8366d44243e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/handle_usort.php.inc @@ -0,0 +1,33 @@ + $a['key'] <=> $b['key'] + ); + } +} + +?> +----- + $a['key'] <=> $b['key'] + ); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/nested_dim_fetches.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/nested_dim_fetches.php.inc new file mode 100644 index 00000000000..4515c461e53 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/nested_dim_fetches.php.inc @@ -0,0 +1,31 @@ + 'John']]; + + $result = array_map(fn ($item) => $item['name']['nested'], $array); + } +} + +?> +----- + 'John']]; + + $result = array_map(fn (array $item) => $item['name']['nested'], $array); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_array_access_type.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_array_access_type.php.inc new file mode 100644 index 00000000000..2cfddb62ace --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_array_access_type.php.inc @@ -0,0 +1,17 @@ +> $items + */ + public function run(array $items) + { + usort( + $items, + fn ($a, $b): int => $a['key'] <=> $b['key'] + ); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_binary_op.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_binary_op.php.inc new file mode 100644 index 00000000000..f7635867883 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_binary_op.php.inc @@ -0,0 +1,15 @@ + 'John']]; + + $result = array_map(fn ($item) => ! is_array($item) || $item['name'] === 'some name', $array); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_different_name.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_different_name.php.inc new file mode 100644 index 00000000000..bc069b0d49b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_different_name.php.inc @@ -0,0 +1,13 @@ + 'John']]; + + $result = array_map(fn ($item) => $hemen['name'], $array); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_filled_type.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_filled_type.php.inc new file mode 100644 index 00000000000..1d7250f4ab2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_filled_type.php.inc @@ -0,0 +1,13 @@ + 'John']]; + + $result = array_map(fn (iterable $item) => $item['name'], $array); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_integer_index_on_string.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_integer_index_on_string.php.inc new file mode 100644 index 00000000000..1846fac2c0d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_integer_index_on_string.php.inc @@ -0,0 +1,19 @@ + strtoupper((string) $name[0]), + explode('.', $username), + ), + ); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_ternary.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_ternary.php.inc new file mode 100644 index 00000000000..17c25630990 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_ternary.php.inc @@ -0,0 +1,15 @@ + 'John']]; + + $result = array_map(fn ($item) => is_array($item) ? $item['name'] : null, $array); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_with_closure_inside.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_with_closure_inside.php.inc new file mode 100644 index 00000000000..095936ae1a5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/skip_with_closure_inside.php.inc @@ -0,0 +1,28 @@ +name = 'John'; + + $array = [['name' => 'John'], $std]; + + $result = array_map(fn ($item) => + function () use ($item) { + if ($item instanceof stdClass) { + return $item->name; + } + + return $item['name']; + } + , $array); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/some_function.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/some_function.php.inc new file mode 100644 index 00000000000..15447864b0d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/Fixture/some_function.php.inc @@ -0,0 +1,31 @@ + 'John']]; + + $result = array_map(fn ($item) => $item['name'], $array); + } +} + +?> +----- + 'John']]; + + $result = array_map(fn (array $item) => $item['name'], $array); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/config/configured_rule.php new file mode 100644 index 00000000000..533744f1125 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddArrowFunctionParamArrayWhereDimFetchRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/AddClosureParamTypeForArrayMapRectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/AddClosureParamTypeForArrayMapRectorTest.php new file mode 100644 index 00000000000..6aca71dfd25 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/AddClosureParamTypeForArrayMapRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..3b78c8e2daa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/fixture.php.inc @@ -0,0 +1,131 @@ + $array + */ + public function run(array $array) + { + return array_map(function ($value, $key) { + return $value . $key; + }, $array); + } + + /** + * @param array $array + * @param array $arrayTwo + */ + public function runTwo(array $array, array $arrayTwo) + { + return array_map(function ($value, $key) { + return get_class($value) . $key; + }, $array, $arrayTwo); + } + + /** + * @param array $array + * @param array $arrayTwo + */ + public function runThree(array $array, array $arrayTwo) + { + return array_map(function ($value, $key) { + return get_class($value) . $key; + }, $array, $arrayTwo); + } + + /** + * @param array $array + * @param array $arrayTwo tested for the missing key + */ + public function runFour(array $array, array $arrayTwo) + { + return array_map(function ($value, $key) { + return get_class($value) . $key; + }, $array, $arrayTwo); + } + + /** + * @param array $array + * @param list $arrayTwo tested for the missing key + */ + public function runFive(array $array, array $arrayTwo) + { + return array_map(function ($value, $key) { + return get_class($value) . $key; + }, $array, $arrayTwo); + } +} + +?> +----- + $array + */ + public function run(array $array) + { + return array_map(function (string $value, int $key) { + return $value . $key; + }, $array); + } + + /** + * @param array $array + * @param array $arrayTwo + */ + public function runTwo(array $array, array $arrayTwo) + { + return array_map(function (\Rector\Tests\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeForArrayMapRector\Source\Foo|string $value, int $key) { + return get_class($value) . $key; + }, $array, $arrayTwo); + } + + /** + * @param array $array + * @param array $arrayTwo + */ + public function runThree(array $array, array $arrayTwo) + { + return array_map(function (\Rector\Tests\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeForArrayMapRector\Source\Bar|\Rector\Tests\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeForArrayMapRector\Source\Foo|string $value, int $key) { + return get_class($value) . $key; + }, $array, $arrayTwo); + } + + /** + * @param array $array + * @param array $arrayTwo tested for the missing key + */ + public function runFour(array $array, array $arrayTwo) + { + return array_map(function (string $value, int|string $key) { + return get_class($value) . $key; + }, $array, $arrayTwo); + } + + /** + * @param array $array + * @param list $arrayTwo tested for the missing key + */ + public function runFive(array $array, array $arrayTwo) + { + return array_map(function (string $value, int $key) { + return get_class($value) . $key; + }, $array, $arrayTwo); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_already_param_typed.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_already_param_typed.php.inc new file mode 100644 index 00000000000..3c257e2e9c7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_already_param_typed.php.inc @@ -0,0 +1,18 @@ + $array + */ + public function run(array $array) + { + return array_map(function (string $value, int|string $key) { + return $value . $key; + }, $array); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_mixed_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_mixed_type.php.inc new file mode 100644 index 00000000000..36c379e61c6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_mixed_type.php.inc @@ -0,0 +1,18 @@ + $array + */ + public function run(array $array) + { + return array_map(function ($value) { + return $value; + }, $array); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_non_array_map_functions.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_non_array_map_functions.php.inc new file mode 100644 index 00000000000..e70b2c68455 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_non_array_map_functions.php.inc @@ -0,0 +1,23 @@ + $array + */ + public function run(array $array) + { + return \Rector\Tests\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeForArrayMapRector\Fixture\array_map(function ($value, $key) { + return $value . $key; + }, $array); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_tuple.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_tuple.php.inc new file mode 100644 index 00000000000..90b9ef38e22 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Fixture/skip_tuple.php.inc @@ -0,0 +1,16 @@ +, 1: list} $tuple + */ + public function run(array $tuple) + { + array_map(function ($first, $second) { + return $first . $second; + }, ...$tuple); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Source/Bar.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Source/Bar.php new file mode 100644 index 00000000000..31a9362a320 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector/Source/Bar.php @@ -0,0 +1,9 @@ +withRules([AddClosureParamTypeForArrayMapRector::class]) + ->withPhpVersion(PhpVersionFeature::UNION_TYPES); diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/AddClosureParamTypeForArrayReduceRectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/AddClosureParamTypeForArrayReduceRectorTest.php new file mode 100644 index 00000000000..f0812fabc41 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/AddClosureParamTypeForArrayReduceRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/combine_union.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/combine_union.php.inc new file mode 100644 index 00000000000..fde1b843130 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/combine_union.php.inc @@ -0,0 +1,41 @@ + $arr + */ + public function run(array $arr) + { + $sumCents = \array_reduce($arr, function ($carry, PaymentPeriod $paymentPeriod) : float|int { + return $carry + $paymentPeriod->getSum(); + }, 0); + } +} + +?> +----- + $arr + */ + public function run(array $arr) + { + $sumCents = \array_reduce($arr, function (int|float $carry, PaymentPeriod $paymentPeriod) : float|int { + return $carry + $paymentPeriod->getSum(); + }, 0); + } +} + +?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..e542e797af1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/fixture.php.inc @@ -0,0 +1,57 @@ + $array + */ + public function run(array $array) + { + return array_reduce($array, function ($carry, $value) { + return $carry . $value; + }, ''); + } + + /** + * @param list $array + */ + public function runTwo(array $array) + { + return array_reduce($array, function ($carry, $value) { + return $carry . $value; + }, 100); + } +} + +?> +----- + $array + */ + public function run(array $array) + { + return array_reduce($array, function (string $carry, string $value) { + return $carry . $value; + }, ''); + } + + /** + * @param list $array + */ + public function runTwo(array $array) + { + return array_reduce($array, function (int|string $carry, int|string $value) { + return $carry . $value; + }, 100); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_already_typed_param.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_already_typed_param.php.inc new file mode 100644 index 00000000000..af26bb0facc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_already_typed_param.php.inc @@ -0,0 +1,16 @@ + $array + */ + public function run(array $array) + { + return array_reduce($array, function (string $carry, ?string $value) { + return $carry . $value; + }, ''); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_mixed_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_mixed_type.php.inc new file mode 100644 index 00000000000..8fdf1ffc469 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_mixed_type.php.inc @@ -0,0 +1,28 @@ + $array + */ + public function run(array $array) + { + return array_reduce($array, function ($carry, $value) { + return $value->foo($carry); + }, ''); + } + + /** + * @param array $array + */ + public function runTwo(array $array, mixed $initial) + { + return array_reduce($array, function ($carry, $value) { + return ''; + }, $initial); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_non_array_reduce_functions.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_non_array_reduce_functions.php.inc new file mode 100644 index 00000000000..0e838bcdba5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_non_array_reduce_functions.php.inc @@ -0,0 +1,23 @@ + $array + */ + public function run(array $array) + { + return \Rector\Tests\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeForArrayMapRector\Fixture\array_reduce($array, function ($value, $key) { + return $value . $key; + }); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_union_intersection.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_union_intersection.php.inc new file mode 100644 index 00000000000..ac1da5eada2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Fixture/skip_union_intersection.php.inc @@ -0,0 +1,22 @@ +contains($item)) { + $carry->add($item); + } + + return $carry; + }, new ArrayCollection()); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Source/PaymentPeriod.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Source/PaymentPeriod.php new file mode 100644 index 00000000000..1b8083bb3ab --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector/Source/PaymentPeriod.php @@ -0,0 +1,7 @@ +rules([AddClosureParamTypeForArrayReduceRector::class]); + + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES); +}; diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/AddClosureParamTypeFromIterableMethodCallRectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/AddClosureParamTypeFromIterableMethodCallRectorTest.php new file mode 100644 index 00000000000..401a550c5fa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/AddClosureParamTypeFromIterableMethodCallRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..f550366a65c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Fixture/fixture.php.inc @@ -0,0 +1,61 @@ + $collection + */ + public function run(Collection $collection) + { + return $collection->map(function ($item, $key) { + return $item . $key; + }); + } + + /** + * @param Collection $collection + */ + public function runFoo(Collection $collection) + { + return $collection->map(callback: function ($item, $key) { + return $item . $key; + }); + } +} + +?> +----- + $collection + */ + public function run(Collection $collection) + { + return $collection->map(function (string $item, int $key) { + return $item . $key; + }); + } + + /** + * @param Collection $collection + */ + public function runFoo(Collection $collection) + { + return $collection->map(callback: function (string $item, int $key) { + return $item . $key; + }); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Fixture/skip_missing_iterator_types.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Fixture/skip_missing_iterator_types.php.inc new file mode 100644 index 00000000000..8c5e9440170 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Fixture/skip_missing_iterator_types.php.inc @@ -0,0 +1,20 @@ +map(function ($item, $key) { + return $item . $key; + }); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Source/Collection.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Source/Collection.php new file mode 100644 index 00000000000..96e9cb13e04 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/Source/Collection.php @@ -0,0 +1,47 @@ + + */ + public function map(callable|string $callback) + { + return $this; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/config/configured_rule.php new file mode 100644 index 00000000000..0929b75b79c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector/config/configured_rule.php @@ -0,0 +1,14 @@ +rules([AddClosureParamTypeFromIterableMethodCallRector::class]); + + $rectorConfig->phpVersion(PhpVersionFeature::MIXED_TYPE); +}; diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/AddParamTypeSplFixedArrayRectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/AddParamTypeSplFixedArrayRectorTest.php new file mode 100644 index 00000000000..ef29b890652 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/AddParamTypeSplFixedArrayRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/Fixture/some_fixture.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/Fixture/some_fixture.php.inc new file mode 100644 index 00000000000..f0c823a883b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/Fixture/some_fixture.php.inc @@ -0,0 +1,32 @@ + +----- + $tokens + */ + public function someFunction(Tokens $tokens) + { + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/config/configured_rule.php new file mode 100644 index 00000000000..150e972057a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddParamTypeSplFixedArrayRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/AddReturnTypeDeclarationFromYieldsRectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/AddReturnTypeDeclarationFromYieldsRectorTest.php new file mode 100644 index 00000000000..d7806a50d6e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/AddReturnTypeDeclarationFromYieldsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/empty_yield.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/empty_yield.php.inc new file mode 100644 index 00000000000..ac21d97b0e3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/empty_yield.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_construct.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_construct.php.inc new file mode 100644 index 00000000000..c8a11f277c4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_construct.php.inc @@ -0,0 +1,11 @@ + + */ + public function run(string $part): \Generator + { + $result = ['a']; + parse_str($part, $result); + + yield from $result; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_filled_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_filled_type.php.inc new file mode 100644 index 00000000000..8374bf15b61 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_filled_type.php.inc @@ -0,0 +1,16 @@ +getData(); + } + + private function getData(): array + { + return []; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_yield_optional.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_yield_optional.php.inc new file mode 100644 index 00000000000..d1aad97dc44 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Fixture/skip_yield_optional.php.inc @@ -0,0 +1,10 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Source/some_file.txt b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Source/some_file.txt new file mode 100644 index 00000000000..f70d6b13982 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/Source/some_file.txt @@ -0,0 +1 @@ +some contents diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/config/configured_rule.php new file mode 100644 index 00000000000..f9d4d5041ee --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddReturnTypeDeclarationFromYieldsRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/SkipVariadic.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/SkipVariadic.php.inc deleted file mode 100644 index e81e5ca46d4..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/SkipVariadic.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/complex_array.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/complex_array.php.inc deleted file mode 100644 index 435e4acbbfc..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/complex_array.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/dataprovider_array.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/dataprovider_array.php.inc deleted file mode 100644 index 9de9e9227dd..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/dataprovider_array.php.inc +++ /dev/null @@ -1,51 +0,0 @@ -id = $id; - } -} - -?> ------ -id = $id; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/local_and_external_scope.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/local_and_external_scope.php.inc deleted file mode 100644 index 11c75b6ebce..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/local_and_external_scope.php.inc +++ /dev/null @@ -1,46 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/local_scope_with_parent_class.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/local_scope_with_parent_class.php.inc deleted file mode 100644 index 51c98425a70..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/local_scope_with_parent_class.php.inc +++ /dev/null @@ -1,56 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/nullable_false.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/nullable_false.php.inc deleted file mode 100644 index f40b9a5d9ae..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/nullable_false.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/skip_false_and_array.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/skip_false_and_array.php.inc deleted file mode 100644 index 4dc039affe9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/skip_false_and_array.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -getData(); - - return $data; - } - - /** - * @dataProvider dataProvider - */ - public function test($a, $b, $c) - { - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/skip_interface_extends.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/skip_interface_extends.php.inc deleted file mode 100644 index 2ad952b3877..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/skip_interface_extends.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/undesired.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/undesired.php.inc deleted file mode 100644 index 31533f83d14..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/undesired.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/union_false_true.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/union_false_true.php.inc deleted file mode 100644 index 01dac2e0e3a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Fixture/union_false_true.php.inc +++ /dev/null @@ -1,28 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixturePropertyType/infer_from_property_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixturePropertyType/infer_from_property_type.php.inc deleted file mode 100644 index ad0b3985f84..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixturePropertyType/infer_from_property_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -id = $id; - } -} - -?> ------ -id = $id; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixtureUnionType/undesired.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixtureUnionType/undesired.php.inc deleted file mode 100644 index 705b8a65ffe..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixtureUnionType/undesired.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixtureUnionType/undesired_int.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixtureUnionType/undesired_int.php.inc deleted file mode 100644 index 0b9d61e7423..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/FixtureUnionType/undesired_int.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/ParamTypeDeclarationRectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/ParamTypeDeclarationRectorTest.php deleted file mode 100644 index 196a8dddc4e..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/ParamTypeDeclarationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/before_union_types.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/PropertyTypeParamTypeDeclarationRectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/PropertyTypeParamTypeDeclarationRectorTest.php deleted file mode 100644 index c0e74c48a1c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/PropertyTypeParamTypeDeclarationRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePropertyType'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/typed_properties.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Source/ParentInterface.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Source/ParentInterface.php deleted file mode 100644 index ff4fd2b9ccb..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/Source/ParentInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureUnionType'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/before_union_types.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/before_union_types.php deleted file mode 100644 index 71ba4bf5672..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/before_union_types.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); - - $services = $containerConfigurator->services(); - $services->set(ParamTypeDeclarationRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/configured_rule.php deleted file mode 100644 index 318b4d6e7f0..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(ParamTypeDeclarationRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/typed_properties.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/typed_properties.php deleted file mode 100644 index 178920ff4b1..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector/config/typed_properties.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::TYPED_PROPERTIES); - - $services = $containerConfigurator->services(); - $services->set(ParamTypeDeclarationRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/constructor_property_assign_over_getter.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/constructor_property_assign_over_getter.php.inc deleted file mode 100644 index 417a214ef5d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/constructor_property_assign_over_getter.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -nullableValue = $nullableValue; - } - - public function getNullableValue(): array - { - return $this->nullableValue; - } -} - -?> ------ -nullableValue = $nullableValue; - } - - public function getNullableValue(): ?array - { - return $this->nullableValue; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/prefix_fqn.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/prefix_fqn.php.inc deleted file mode 100644 index f14c078b941..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/prefix_fqn.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/skip_override_of_the_same_class.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/skip_override_of_the_same_class.php.inc deleted file mode 100644 index 7dd7b571710..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/Correction/skip_override_of_the_same_class.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/code_over_doc_priority.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/code_over_doc_priority.php.inc deleted file mode 100644 index c4ec728ae0c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/code_over_doc_priority.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/complex_array.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/complex_array.php.inc deleted file mode 100644 index 86adfa97e57..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/complex_array.php.inc +++ /dev/null @@ -1,56 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/double_return.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/double_return.php.inc deleted file mode 100644 index a535533146a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/double_return.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -expectsJson()) { - return $this->getName(); - } - - return ''; - } - - private function getName(): string - { - return 'name'; - } -} - -?> ------ -expectsJson()) { - return $this->getName(); - } - - return ''; - } - - private function getName(): string - { - return 'name'; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/external_void_interface.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/external_void_interface.php.inc deleted file mode 100644 index ccd246c7392..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/external_void_interface.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/false.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/false.php.inc deleted file mode 100644 index 98fa4ab733e..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/false.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/false_true_union_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/false_true_union_type.php.inc deleted file mode 100644 index 4b55ef1db8d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/false_true_union_type.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/generator.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/generator.php.inc deleted file mode 100644 index cb610374f58..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/generator.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_false.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_false.php.inc deleted file mode 100644 index 397d9e478ce..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_false.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float.php.inc deleted file mode 100644 index 16f7cd5aaae..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_in_variable.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_in_variable.php.inc deleted file mode 100644 index c67314852db..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_in_variable.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_param_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_param_type.php.inc deleted file mode 100644 index 85b7b4cfe6a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_param_type.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_param_type_with_condition.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_param_type_with_condition.php.inc deleted file mode 100644 index 19e7d026c9a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_float_param_type_with_condition.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static.php.inc deleted file mode 100644 index acbe2273a1d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static.php.inc +++ /dev/null @@ -1,77 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_method.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_method.php.inc deleted file mode 100644 index b7ecdd3e296..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_method.php.inc +++ /dev/null @@ -1,89 +0,0 @@ -anotherClass = $anotherClass; - } - - public function getMoreWhatever($value) - { - return $this->anotherClass->getAnotherMethod($value); - } - - public function getMoreWhateverDoc($value) - { - return $this->anotherClass->getAnotherMethodDoc($value); - } -} - -class AnotherClass -{ - public function getAnotherMethod($value): int - { - return $value; - } - - /** - * @return false|true - */ - public function getAnotherMethodDoc($value) - { - return $value; - } -} - -?> ------ -anotherClass = $anotherClass; - } - - public function getMoreWhatever($value): int - { - return $this->anotherClass->getAnotherMethod($value); - } - - public function getMoreWhateverDoc($value): bool - { - return $this->anotherClass->getAnotherMethodDoc($value); - } -} - -class AnotherClass -{ - public function getAnotherMethod($value): int - { - return $value; - } - - /** - * @return false|true - */ - public function getAnotherMethodDoc($value): bool - { - return $value; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_nullable.php.inc deleted file mode 100644 index cb53db05223..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_nullable.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_object.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_object.php.inc deleted file mode 100644 index e6b98bf9c80..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_object.php.inc +++ /dev/null @@ -1,69 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_object_parent.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_object_parent.php.inc deleted file mode 100644 index bbda6e783cb..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_object_parent.php.inc +++ /dev/null @@ -1,51 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_void.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_void.php.inc deleted file mode 100644 index 234e2ee2c77..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/known_static_void.php.inc +++ /dev/null @@ -1,25 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/parent_override.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/parent_override.php.inc deleted file mode 100644 index 2246381f5a1..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/parent_override.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/private_property_reflection.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/private_property_reflection.php.inc deleted file mode 100644 index b3130adefcc..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/private_property_reflection.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -getTokens(); - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/remove_docblock_when_no_added_value.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/remove_docblock_when_no_added_value.php.inc deleted file mode 100644 index 14476ff200e..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/remove_docblock_when_no_added_value.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/respect_vendor_return_type2.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/respect_vendor_return_type2.php.inc deleted file mode 100644 index 2f85c8fa58b..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/respect_vendor_return_type2.php.inc +++ /dev/null @@ -1,35 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_already_set_return_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_already_set_return_type.php.inc deleted file mode 100644 index 585c6195aa9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_already_set_return_type.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -connection->executeQuery('SELECT * FROM *'); - - while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - yield EventsEntity::forCollection(); - } - }; - } - - private function again(): callable - { - return function (int $offset, int $itemCountPerPage): iterable { - return 25; - }; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_array_and_false.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_array_and_false.php.inc deleted file mode 100644 index 85a8741c50d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_array_and_false.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -repository = $repository; - } - - public function __invoke(object $query): \stdClass - { - $objects = $this->repository->listBy([$query->id]); - - if (empty($objects)) { - throw new \InvalidArgumentException($query->id); - } - - if (1 !== count($objects)) { - throw new \UnexpectedValueException(); - } - - return $objects[0]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_call.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_call.php.inc deleted file mode 100644 index 5bf1e725153..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_call.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -call; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_class_string_generic_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_class_string_generic_type.php.inc deleted file mode 100644 index eb72a5789c8..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_class_string_generic_type.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - - */ - abstract protected function getClassStringForT(): string; -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_closure.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_closure.php.inc deleted file mode 100644 index 131e008c5fe..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_closure.php.inc +++ /dev/null @@ -1,24 +0,0 @@ -functionLike; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_container_get.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_container_get.php.inc deleted file mode 100644 index 57ff62461e3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_container_get.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -setConfigs([__DIR__ . '/config/php-config-printer-config.php']); - $phpConfigPrinterKernel->boot(); - $container = $phpConfigPrinterKernel->getContainer(); - - return $container->get(SmartPhpConfigPrinter::class); - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_correct_iterable.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_correct_iterable.php.inc deleted file mode 100644 index e2b429ef2eb..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_correct_iterable.php.inc +++ /dev/null @@ -1,22 +0,0 @@ - $tokens - */ - public function getNextMeaningfulToken(Tokens $tokens, int $index): ?Token - { - $nextMeaninfulTokenPosition = $tokens->getNextMeaningfulToken($index); - if ($nextMeaninfulTokenPosition === null) { - return null; - } - - return $tokens[$index]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_empty_return_array.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_empty_return_array.php.inc deleted file mode 100644 index c2d7645bbac..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_empty_return_array.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -getArray())) { - $values['name'] = $this->getArray(); - } - - return $values; - } - - public function getArray(): array - { - return []; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_explicit_generator_from.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_explicit_generator_from.php.inc deleted file mode 100644 index 952b9eb6bc5..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_explicit_generator_from.php.inc +++ /dev/null @@ -1,19 +0,0 @@ - - */ - public function run(string $part): Generator - { - $result = ['a']; - parse_str($part, $result); - - yield from $result; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_from_external_void_trait.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_from_external_void_trait.php.inc deleted file mode 100644 index 901f5c635ef..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_from_external_void_trait.php.inc +++ /dev/null @@ -1,14 +0,0 @@ - $some_Type - * @return T - */ - public function getValue(ContainerInterface $container, string $some_Type): ?object - { - return $container->get($some_Type); - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_iterable_array_iterator_co_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_iterable_array_iterator_co_type.php.inc deleted file mode 100644 index bda066b40ef..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_iterable_array_iterator_co_type.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -[]|Array_[]> - */ - public function provideDataForArray(): Iterator - { - $array = new Array_(); - $array->items[] = new ArrayItem(new LNumber(1)); - yield [[1], $array]; - - $array = new Array_(); - $array->items[] = new ArrayItem(new LNumber(1), new String_('a')); - yield [[ - 'a' => 1, - ], $array]; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_magic_caller.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_magic_caller.php.inc deleted file mode 100644 index 06688d28446..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_magic_caller.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -callPrivateMethod(new StringInput(''), 'tokenize', []); - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_nested_switch_with_default.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_nested_switch_with_default.php.inc deleted file mode 100644 index eb2017661ed..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_nested_switch_with_default.php.inc +++ /dev/null @@ -1,22 +0,0 @@ -value = 'hey'; - - return $string; - } -} - -interface ReturnTypeInterface -{ - /** - * @return String_|LNumber|null - */ - public function getNode(): ?Node; -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_parent_node_visitor_contract.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_parent_node_visitor_contract.php.inc deleted file mode 100644 index 02a9f2884b6..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_parent_node_visitor_contract.php.inc +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_respect_children_return_type_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_respect_children_return_type_mixed.php.inc deleted file mode 100644 index 1231ca155e3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_respect_children_return_type_mixed.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -expectsJson()) { - return route2("test"); - } - } -} - -/** - * @param string $name - */ -function route($name, $parameters = [], $absolute = true): string -{ - return $name; -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_self.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_self.php.inc deleted file mode 100644 index c8c0b52f1f1..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_self.php.inc +++ /dev/null @@ -1,11 +0,0 @@ -relativePathname = $pathname; - - return $this; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_switch_returns.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_switch_returns.php.inc deleted file mode 100644 index c1ad716075f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_switch_returns.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -expr; - } - - return $stmt; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_union_mixed_return.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_union_mixed_return.php.inc deleted file mode 100644 index cb8b142db97..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_union_mixed_return.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/this.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/this.php.inc deleted file mode 100644 index c6176dfe597..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/this.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/void_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/void_type.php.inc deleted file mode 100644 index 11fce7b4cd4..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/void_type.php.inc +++ /dev/null @@ -1,44 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/do_not_duplicate_array_return.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/do_not_duplicate_array_return.php.inc deleted file mode 100644 index 3477d8fc1d1..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/do_not_duplicate_array_return.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/skip_static_self_override.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/skip_static_self_override.php.inc deleted file mode 100644 index f8efd8e5419..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/skip_static_self_override.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -relativePathname = $pathname; - - return $this; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/static_.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/static_.php.inc deleted file mode 100644 index c93931d3e96..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/FixtureForPhp80/static_.php.inc +++ /dev/null @@ -1,61 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Php80RectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Php80RectorTest.php deleted file mode 100644 index 0e7559161a7..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Php80RectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureForPhp80'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_80.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/ReturnTypeDeclarationRectorTest.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/ReturnTypeDeclarationRectorTest.php deleted file mode 100644 index 4efadd0ca60..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/ReturnTypeDeclarationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/before_union_types.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/Bar.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/Bar.php deleted file mode 100644 index bfeb70d461c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/Bar.php +++ /dev/null @@ -1,10 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); - $parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, __DIR__ . '/../../../../../../phpstan-for-rector.neon'); - - $services = $containerConfigurator->services(); - $services->set(ReturnTypeDeclarationRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/config/php_80.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/config/php_80.php deleted file mode 100644 index b5ada1e910f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/config/php_80.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::STATIC_RETURN_TYPE); - - $services = $containerConfigurator->services(); - $services->set(ReturnTypeDeclarationRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/config/scalar_types.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/config/scalar_types.php deleted file mode 100644 index 081e6faeb9f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/config/scalar_types.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::SCALAR_TYPES); - - $services = $containerConfigurator->services(); - $services->set(ReturnTypeDeclarationRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector/AddFunctionVoidReturnTypeWhereNoReturnRectorTest.php b/rules-tests/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector/AddFunctionVoidReturnTypeWhereNoReturnRectorTest.php new file mode 100644 index 00000000000..42bd38ec198 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector/AddFunctionVoidReturnTypeWhereNoReturnRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/some_function.php.inc b/rules-tests/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector/Fixture/some_function.php.inc similarity index 100% rename from rules-tests/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector/Fixture/some_function.php.inc rename to rules-tests/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector/Fixture/some_function.php.inc diff --git a/rules-tests/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector/config/configured_rule.php new file mode 100644 index 00000000000..f76ef31b1e3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddFunctionVoidReturnTypeWhereNoReturnRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/aliased_class.php.inc b/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/aliased_class.php.inc deleted file mode 100644 index cf968d6ace5..00000000000 --- a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/aliased_class.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -setValue(null); - $nowString::setValue(null); - } -} - -?> ------ -setValue(''); - $nowString::setValue(''); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/fixture.php.inc b/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/fixture.php.inc deleted file mode 100644 index 9f2cd3c7a4e..00000000000 --- a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -setValue(null); - } -} - -?> ------ -setValue(''); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/former_bool.php.inc b/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/former_bool.php.inc deleted file mode 100644 index 906640bd830..00000000000 --- a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/former_bool.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -setValue(null); - } -} - -?> ------ -setValue(false); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/on_static_call.php.inc b/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/on_static_call.php.inc deleted file mode 100644 index 1d013914205..00000000000 --- a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/on_static_call.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/skip_nette_form_class_call.php.inc b/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/skip_nette_form_class_call.php.inc deleted file mode 100644 index 996c0e258bf..00000000000 --- a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/skip_nette_form_class_call.php.inc +++ /dev/null @@ -1,20 +0,0 @@ -addText('reading', 'Reading') - ->setRequired('...') - ->getControlPrototype() - ->nonExistingMagic('rating'); - - return $form; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/with_parent_method.php.inc b/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/with_parent_method.php.inc deleted file mode 100644 index 22c760480bd..00000000000 --- a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Fixture/with_parent_method.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -setValue(null); - } -} - -?> ------ -setValue(0); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/FormerNullableArgumentToScalarTypedRectorTest.php b/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/FormerNullableArgumentToScalarTypedRectorTest.php deleted file mode 100644 index a8571fb3ea1..00000000000 --- a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/FormerNullableArgumentToScalarTypedRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Source/NowBool.php b/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Source/NowBool.php deleted file mode 100644 index 8de23b712a9..00000000000 --- a/rules-tests/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector/Source/NowBool.php +++ /dev/null @@ -1,12 +0,0 @@ -services(); - $services->set(FormerNullableArgumentToScalarTypedRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/changed_after_assign_type.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/changed_after_assign_type.php.inc deleted file mode 100644 index 4aac1df5f29..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/changed_after_assign_type.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -kind = $kind; - - if (!is_array($kind)) { - $kind = [$kind]; - } - } -} - -?> ------ -kind = $kind; - - if (!is_array($kind)) { - $kind = [$kind]; - } - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/even_constructor.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/even_constructor.php.inc deleted file mode 100644 index 53615c122e8..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/even_constructor.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -age = $age; - } -} - -?> ------ -age = $age; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/external_type.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/external_type.php.inc deleted file mode 100644 index 05057f6c40d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/external_type.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -age = $age; - } -} - -final class ExternalClass -{ - public int $age; -} - -?> ------ -age = $age; - } -} - -final class ExternalClass -{ - public int $age; -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/multiple_params.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/multiple_params.php.inc deleted file mode 100644 index 756a52b1747..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/multiple_params.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -age = $age; - $this->nick = $name; - } -} - -?> ------ -age = $age; - $this->nick = $name; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_already_type.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_already_type.php.inc deleted file mode 100644 index a5bb0b29759..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_already_type.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -age = $age; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_changed_type.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_changed_type.php.inc deleted file mode 100644 index 469c44c18b6..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_changed_type.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -kind = $kind; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_doc_type.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_doc_type.php.inc deleted file mode 100644 index 3a57a0e17fe..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_doc_type.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -age = $age; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_nullable_type.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_nullable_type.php.inc deleted file mode 100644 index 34810e689db..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_nullable_type.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -age = $age; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_union_type.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_union_type.php.inc deleted file mode 100644 index cab47d8db8d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/skip_union_type.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -age = $age; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/some_class.php.inc deleted file mode 100644 index 4f2c73d7fc5..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -age = $age; - } -} - -?> ------ -age = $age; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/vendor_external_type.php.inc b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/vendor_external_type.php.inc deleted file mode 100644 index aea6e92da2d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Fixture/vendor_external_type.php.inc +++ /dev/null @@ -1,31 +0,0 @@ -name = $age; - } -} - -?> ------ -name = $age; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/ParamTypeFromStrictTypedPropertyRectorTest.php b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/ParamTypeFromStrictTypedPropertyRectorTest.php deleted file mode 100644 index 23ac3a96be2..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/ParamTypeFromStrictTypedPropertyRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Source/OutOfControlExternalClass.php b/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Source/OutOfControlExternalClass.php deleted file mode 100644 index 7393dd6d214..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector/Source/OutOfControlExternalClass.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(ParamTypeFromStrictTypedPropertyRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/AddPropertyTypeDeclarationRectorTest.php b/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/AddPropertyTypeDeclarationRectorTest.php new file mode 100644 index 00000000000..4d9e16b7fa2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/AddPropertyTypeDeclarationRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Fixture/include_trait_property.php.inc b/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Fixture/include_trait_property.php.inc new file mode 100644 index 00000000000..255d846b7dc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Fixture/include_trait_property.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Fixture/skip_already_added_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Fixture/skip_already_added_type.php.inc new file mode 100644 index 00000000000..29f70a91d6f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Fixture/skip_already_added_type.php.inc @@ -0,0 +1,10 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Source/ParentClassWithProperty.php b/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Source/ParentClassWithProperty.php new file mode 100644 index 00000000000..d6aacf92cc8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector/Source/ParentClassWithProperty.php @@ -0,0 +1,10 @@ +ruleWithConfiguration(AddPropertyTypeDeclarationRector::class, [ + new AddPropertyTypeDeclaration(ParentClassWithProperty::class, 'name', new StringType()), + new AddPropertyTypeDeclaration(SomeTraitWithProperty::class, 'value', new IntegerType()), + ]); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/AutoImportTest.php b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/AutoImportTest.php deleted file mode 100644 index b5ab4c36931..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/AutoImportTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureAutoImport'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/auto_import.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/CompleteVarDocTypePropertyRectorTest.php b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/CompleteVarDocTypePropertyRectorTest.php deleted file mode 100644 index 3f04333768a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/CompleteVarDocTypePropertyRectorTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/assign_conflict.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/assign_conflict.php.inc deleted file mode 100644 index 191b53dfa1d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/assign_conflict.php.inc +++ /dev/null @@ -1,54 +0,0 @@ -eventDispatcher = $eventDispatcher; - } - - public function run(Robocop $stdClass) - { - $this->eventDispatcher = $stdClass; - } -} - -?> ------ -eventDispatcher = $eventDispatcher; - } - - public function run(Robocop $stdClass) - { - $this->eventDispatcher = $stdClass; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/default_value.php.inc deleted file mode 100644 index 873027a7c9a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/default_value.php.inc +++ /dev/null @@ -1,45 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/default_value_array_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/default_value_array_mixed.php.inc deleted file mode 100644 index 955850a653e..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/default_value_array_mixed.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/improve_type_and_keep_comment.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/improve_type_and_keep_comment.php.inc deleted file mode 100644 index c56e5676901..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/improve_type_and_keep_comment.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -ids[] = 'hello'; - if (random_int(1, 100)) { - $this->ids = 'hey'; - } - } -} - -?> ------ -ids[] = 'hello'; - if (random_int(1, 100)) { - $this->ids = 'hey'; - } - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/object_cast_to_stdclass.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/object_cast_to_stdclass.php.inc deleted file mode 100644 index e3925a6376b..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/object_cast_to_stdclass.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/private_var_null_unused.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/private_var_null_unused.php.inc deleted file mode 100644 index 0c05a49d65a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/private_var_null_unused.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/property_assign.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/property_assign.php.inc deleted file mode 100644 index 9874005a801..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/property_assign.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -eventDispatcher = $eventDispatcher; - } -} - -?> ------ -eventDispatcher = $eventDispatcher; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/public_var_null_used.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/public_var_null_used.php.inc deleted file mode 100644 index 41085e587c4..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/public_var_null_used.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -config = new stdClass; - } -} - -?> ------ -config = new stdClass; - } -} - -?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/recursive_multiple_class_string_array.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/recursive_multiple_class_string_array.php.inc deleted file mode 100644 index 38dd3cccd51..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/recursive_multiple_class_string_array.php.inc +++ /dev/null @@ -1,60 +0,0 @@ - [ - InvokableA::class, - InvokableB::class, - InvokableC::class, - ], - 'factories' => [ - FactoryA::class, - FactoryB::class, - FactoryC::class, - ], - ]; -} - -?> ------ - - */ - public $services = [ - 'invokables' => [ - InvokableA::class, - InvokableB::class, - InvokableC::class, - ], - 'factories' => [ - FactoryA::class, - FactoryB::class, - FactoryC::class, - ], - ]; -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_already_used.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_already_used.php.inc deleted file mode 100644 index cb2772acf3a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_already_used.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -signalSlotDispatcher = $signalSlotDispatcher; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_more_specific.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_more_specific.php.inc deleted file mode 100644 index 2b2e272bc03..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_more_specific.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -getLabel()] = $service; - } - - /** - * @return SomeService[] - */ - public static function getRegisteredSomeServices() - { - return self::$registry; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_multiple_class_string_array.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_multiple_class_string_array.php.inc deleted file mode 100644 index 37eecc078cf..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_multiple_class_string_array.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_public_var_null_unused.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_public_var_null_unused.php.inc deleted file mode 100644 index 340476bc267..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/skip_public_var_null_unused.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -getClosureThis()) { - $code = \Closure::bind($code, $this); - } - } - $this->code = $code; - return $this; - } -} - -?> ------ -getClosureThis()) { - $code = \Closure::bind($code, $this); - } - } - $this->code = $code; - return $this; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_command_defined_in_constructor.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_command_defined_in_constructor.php.inc deleted file mode 100644 index c0a4469207b..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_command_defined_in_constructor.php.inc +++ /dev/null @@ -1,62 +0,0 @@ -definition = new InputDefinition(); - } - - /** - * @param array|InputDefinition $definition An array of argument and option instances or a definition instance - */ - public function setDefinition($definition) - { - if ($definition instanceof InputDefinition) { - $this->definition = $definition; - } else { - $this->definition->setDefinition($definition); - } - } -} - -?> ------ -definition = new InputDefinition(); - } - - /** - * @param array|InputDefinition $definition An array of argument and option instances or a definition instance - */ - public function setDefinition($definition) - { - if ($definition instanceof InputDefinition) { - $this->definition = $definition; - } else { - $this->definition->setDefinition($definition); - } - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_helper_set.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_helper_set.php.inc deleted file mode 100644 index 455e53e3174..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_helper_set.php.inc +++ /dev/null @@ -1,76 +0,0 @@ -application = $application; - if ($application) { - $this->setHelperSet($application->getHelperSet()); - } else { - $this->helperSet = null; - } - } - - public function setHelperSet(HelperSet $helperSet) - { - $this->helperSet = $helperSet; - } - - /** - * @return HelperSet - */ - public function getHelperSet() - { - return $this->helperSet; - } -} - -?> ------ -application = $application; - if ($application) { - $this->setHelperSet($application->getHelperSet()); - } else { - $this->helperSet = null; - } - } - - public function setHelperSet(HelperSet $helperSet) - { - $this->helperSet = $helperSet; - } - - /** - * @return HelperSet - */ - public function getHelperSet() - { - return $this->helperSet; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/typed_array.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/typed_array.php.inc deleted file mode 100644 index eeb893793f7..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/typed_array.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -items = $items; - } -} - -?> ------ -items = $items; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/typed_array_nested.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/typed_array_nested.php.inc deleted file mode 100644 index 85d0d0d663a..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/typed_array_nested.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -itemsNested = $itemsNested; - } -} - -?> ------ -itemsNested = $itemsNested; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/with_numeric_check.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/with_numeric_check.php.inc deleted file mode 100644 index 76917dc8ae7..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/with_numeric_check.php.inc +++ /dev/null @@ -1,47 +0,0 @@ -value = $value; - return; - } - - $this->value = 100; - } -} - -?> ------ -value = $value; - return; - } - - $this->value = 100; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/FixtureAutoImport/keep_simple_class.php.inc b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/FixtureAutoImport/keep_simple_class.php.inc deleted file mode 100644 index 6a7a06e0bbe..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/FixtureAutoImport/keep_simple_class.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -obj = new stdClass(); - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Source/EventDispatcher.php b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Source/EventDispatcher.php deleted file mode 100644 index 89a90ed8177..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/Source/EventDispatcher.php +++ /dev/null @@ -1,8 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(CompleteVarDocTypePropertyRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/config/configured_rule.php deleted file mode 100644 index 198e93cd9a3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(CompleteVarDocTypePropertyRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_array_param_with_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_array_param_with_nullable.php.inc deleted file mode 100644 index d2385676d66..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_array_param_with_nullable.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -data = $data; - } -} - -?> ------ -data = $data; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_assign.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_assign.php.inc deleted file mode 100644 index a190dbb8ae3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_assign.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -result = 5 + $value; - $this->name = 'Tomas'; - } -} - -?> ------ -result = 5 + $value; - $this->name = 'Tomas'; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param.php.inc deleted file mode 100644 index 976f7fcd114..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param.php.inc +++ /dev/null @@ -1,51 +0,0 @@ -stringValue = $value; - $this->docBlockService = $docBlockService; - } -} - -?> ------ -stringValue = $value; - $this->docBlockService = $docBlockService; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param_with_aliased_param.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param_with_aliased_param.php.inc deleted file mode 100644 index 5d273a70cc7..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param_with_aliased_param.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -aliasedParam = $aliasedParam; - } -} - -?> ------ -aliasedParam = $aliasedParam; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param_with_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param_with_nullable.php.inc deleted file mode 100644 index 58bbf5bbf79..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/constructor_param_with_nullable.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -nullableString = $nullableString; - } -} - -?> ------ -nullableString = $nullableString; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/default_value.php.inc deleted file mode 100644 index 3c7aaae5617..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/default_value.php.inc +++ /dev/null @@ -1,29 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_column.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_column.php.inc deleted file mode 100644 index 8cd09336463..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_column.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_target_entity_same_namespace.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_target_entity_same_namespace.php.inc deleted file mode 100644 index 12eba262258..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_target_entity_same_namespace.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_to_many.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_to_many.php.inc deleted file mode 100644 index 1b720bf017c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_to_many.php.inc +++ /dev/null @@ -1,43 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_to_one.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_to_one.php.inc deleted file mode 100644 index 35d12e374e8..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/doctrine_relation_to_one.php.inc +++ /dev/null @@ -1,58 +0,0 @@ - ------ - diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/getter_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/getter_type.php.inc deleted file mode 100644 index cea29505232..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/getter_type.php.inc +++ /dev/null @@ -1,128 +0,0 @@ -email; - } - - public function setEmail(string $email): void - { - $this->email = $email; - } - - public function hasLanguage(): bool - { - return $this->language !== null; - } - - public function getLanguage() - { - return $this->language; - } - - public function setLanguage(string $language): void - { - $this->language = $language; - } - - public function getPassword(): string - { - return $this->password; - } - - public function setPassword(string $password): void - { - $this->password = $password; - } -} - -?> ------ -email; - } - - public function setEmail(string $email): void - { - $this->email = $email; - } - - public function hasLanguage(): bool - { - return $this->language !== null; - } - - public function getLanguage() - { - return $this->language; - } - - public function setLanguage(string $language): void - { - $this->language = $language; - } - - public function getPassword(): string - { - return $this->password; - } - - public function setPassword(string $password): void - { - $this->password = $password; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/getter_type_from_var_doc.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/getter_type_from_var_doc.php.inc deleted file mode 100644 index 86f689e8ecf..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/getter_type_from_var_doc.php.inc +++ /dev/null @@ -1,44 +0,0 @@ -surname; - } -} - -?> ------ -surname; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/native_simple_classname.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/native_simple_classname.php.inc deleted file mode 100644 index db27568390b..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/native_simple_classname.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - 'a simple class name']; -} - -?> ------ -, string> - */ - private $data = ['stdClass' => 'a simple class name']; -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/non_native_simple_classname.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/non_native_simple_classname.php.inc deleted file mode 100644 index 9f2c783f716..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/non_native_simple_classname.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - 'Rocket Framework']; -} - -?> ------ - - */ - private $data = ['Rocket' => 'Rocket Framework']; -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/phpunit_setup.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/phpunit_setup.php.inc deleted file mode 100644 index 248ee1fe9e3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/phpunit_setup.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -value = 16236; - } -} - -?> ------ -value = 16236; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/private_var_null_unused.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/private_var_null_unused.php.inc deleted file mode 100644 index 308e80a53af..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/private_var_null_unused.php.inc +++ /dev/null @@ -1,24 +0,0 @@ - ------ - \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/public_var_null_used.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/public_var_null_used.php.inc deleted file mode 100644 index 3ef9053e45f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/public_var_null_used.php.inc +++ /dev/null @@ -1,38 +0,0 @@ -config = new stdClass; - } -} - -?> ------ -config = new stdClass; - } -} - -?> \ No newline at end of file diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/setter_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/setter_type.php.inc deleted file mode 100644 index 1c8adbda842..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/setter_type.php.inc +++ /dev/null @@ -1,59 +0,0 @@ -email = $email; - } - - /** - * @param string $name - */ - public function setName($name) - { - $this->name = $name; - } -} - -?> ------ -email = $email; - } - - /** - * @param string $name - */ - public function setName($name) - { - $this->name = $name; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/single_nullable_return.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/single_nullable_return.php.inc deleted file mode 100644 index 0a34c823ae3..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/single_nullable_return.php.inc +++ /dev/null @@ -1,48 +0,0 @@ -file; - } - - public function setFile(File $file): void - { - $this->file = $file; - } -} - -?> ------ -file; - } - - public function setFile(File $file): void - { - $this->file = $file; - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_anonymous_class.php.inc deleted file mode 100644 index 187ce5b868c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_anonymous_class.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -bar = $bar; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_multi_vars.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_multi_vars.php.inc deleted file mode 100644 index 2d9a69052ac..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_multi_vars.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -someFQCN = $someFQCN; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_phpstan_generics_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_phpstan_generics_type.php.inc deleted file mode 100644 index ca1811760a0..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_phpstan_generics_type.php.inc +++ /dev/null @@ -1,16 +0,0 @@ - - */ - private $twoOfMe; - - public function __construct() - { - $this->twoOfMe = new \ArrayIterator([new self(), new self()]); - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_phpstan_var_annotated.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_phpstan_var_annotated.php.inc deleted file mode 100644 index 5269d75629d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_phpstan_var_annotated.php.inc +++ /dev/null @@ -1,22 +0,0 @@ - - */ - private static $availableValuesPhpstan = [ - self::VALUE_ON => '💡 veröffentlicht', - self::VALUE_OFF => '❌ nicht veröffentlicht', - ]; - - /** - * @psalm-var array - */ - private static $availableValuesPsalm = [ - self::VALUE_ON => '💡 veröffentlicht', - self::VALUE_OFF => '❌ nicht veröffentlicht', - ]; -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_public_var_null_unused.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_public_var_null_unused.php.inc deleted file mode 100644 index f05e6315a05..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Fixture/skip_public_var_null_unused.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -cacheFile = $this->coreCache(); - $this->cacheFiles[] = $this->coreCache(); - } -} - -?> ------ -cacheFile = $this->coreCache(); - $this->cacheFiles[] = $this->coreCache(); - } -} - -?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/FixturePhp74/skip_typed_property.php.inc b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/FixturePhp74/skip_typed_property.php.inc deleted file mode 100644 index c4ef6a24109..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/FixturePhp74/skip_typed_property.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePhp74'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/typed_property.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/PropertyTypeDeclarationRectorTest.php b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/PropertyTypeDeclarationRectorTest.php deleted file mode 100644 index e405dab307f..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/PropertyTypeDeclarationRectorTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Source/ObjectToBeAliased.php b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Source/ObjectToBeAliased.php deleted file mode 100644 index 1d615ab7e5c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/Source/ObjectToBeAliased.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(PropertyTypeDeclarationRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/config/typed_property.php b/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/config/typed_property.php deleted file mode 100644 index 704f5751a8d..00000000000 --- a/rules-tests/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector/config/typed_property.php +++ /dev/null @@ -1,16 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::TYPED_PROPERTIES); - - $services = $containerConfigurator->services(); - $services->set(PropertyTypeDeclarationRector::class); -}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/Doctrine/skip_collection.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/Doctrine/skip_collection.php.inc new file mode 100644 index 00000000000..85189284e3e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/Doctrine/skip_collection.php.inc @@ -0,0 +1,13 @@ +x = new class extends \DateTime {}; + } +} + +?> +----- +x = new class extends \DateTime {}; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/anonymous_extends_existing_class_in_union.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/anonymous_extends_existing_class_in_union.php.inc new file mode 100644 index 00000000000..245f9aad009 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/anonymous_extends_existing_class_in_union.php.inc @@ -0,0 +1,39 @@ +x = new \DateTime('now'); + } else { + $this->x = new class extends \DateTime {}; + } + } +} + +?> +----- +x = new \DateTime('now'); + } else { + $this->x = new class extends \DateTime {}; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/array_type_filled_default_null.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/array_type_filled_default_null.php.inc new file mode 100644 index 00000000000..009038cafd3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/array_type_filled_default_null.php.inc @@ -0,0 +1,34 @@ +property = $property; + } +} + +?> +----- +property = $property; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/array_with_index_assigned.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/array_with_index_assigned.php.inc new file mode 100644 index 00000000000..0b79dfcdced --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/array_with_index_assigned.php.inc @@ -0,0 +1,57 @@ + + */ + private $exitcodeLabels; + + private int $currentExitCode = 1; + + /** + * @param array $exitcodeLabels + */ + public function __construct(array $exitcodeLabels) + { + $this->exitcodeLabels = $exitcodeLabels; + } + + public function setMessageForCode(int $exitCode, string $message): void + { + $this->exitcodeLabels[$exitCode] = $message; + } +} + +?> +----- + + */ + private array $exitcodeLabels; + + private int $currentExitCode = 1; + + /** + * @param array $exitcodeLabels + */ + public function __construct(array $exitcodeLabels) + { + $this->exitcodeLabels = $exitcodeLabels; + } + + public function setMessageForCode(int $exitCode, string $message): void + { + $this->exitcodeLabels[$exitCode] = $message; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_object_aliased.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_object_aliased.php.inc new file mode 100644 index 00000000000..2b1778a7671 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_object_aliased.php.inc @@ -0,0 +1,35 @@ +int = new PHPParser\Scalar\Int_(1); + } +} + +?> +----- +int = new PHPParser\Scalar\Int_(1); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_object_subnamespace.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_object_subnamespace.php.inc new file mode 100644 index 00000000000..6ef6bfb5ce6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_object_subnamespace.php.inc @@ -0,0 +1,35 @@ +int = new Node\Scalar\Int_(1); + } +} + +?> +----- +int = new Node\Scalar\Int_(1); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_this_become_self.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_this_become_self.php.inc new file mode 100644 index 00000000000..96b71cf50ff --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/assign_this_become_self.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/closure_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/closure_type.php.inc new file mode 100644 index 00000000000..f53463ab1cb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/closure_type.php.inc @@ -0,0 +1,35 @@ +handlers = $handlers; + } +} + +?> +----- +handlers = $handlers; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/default_type_constantbool_assigned_booltype.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/default_type_constantbool_assigned_booltype.php.inc new file mode 100644 index 00000000000..63dcdadce25 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/default_type_constantbool_assigned_booltype.php.inc @@ -0,0 +1,31 @@ +property = false; + } +} + +?> +----- +property = false; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/default_type_constantbool_assigned_booltype2.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/default_type_constantbool_assigned_booltype2.php.inc new file mode 100644 index 00000000000..0633596e595 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/default_type_constantbool_assigned_booltype2.php.inc @@ -0,0 +1,31 @@ +property = $property; + } +} + +?> +----- +property = $property; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/do_not_remove_multiple_docblock_comment_2.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/do_not_remove_multiple_docblock_comment_2.php.inc new file mode 100644 index 00000000000..77fef9e057e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/do_not_remove_multiple_docblock_comment_2.php.inc @@ -0,0 +1,49 @@ +property = new \DateTime(); + } +} + +?> +----- +property = new \DateTime(); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/generic_object_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/generic_object_type.php.inc new file mode 100644 index 00000000000..d1c64c8307d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/generic_object_type.php.inc @@ -0,0 +1,49 @@ +command = $command; + } +} + +?> +----- +command = $command; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/if_else_assign.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/if_else_assign.php.inc new file mode 100644 index 00000000000..000d769fa6d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/if_else_assign.php.inc @@ -0,0 +1,39 @@ +apiUrl = 'https://example.com/'; + } else { + $this->apiUrl = 'https://another.example.com/'; + } + } +} + +?> +----- +apiUrl = 'https://example.com/'; + } else { + $this->apiUrl = 'https://another.example.com/'; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/if_else_assign_complex.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/if_else_assign_complex.php.inc new file mode 100644 index 00000000000..d1cf51f3baa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/if_else_assign_complex.php.inc @@ -0,0 +1,43 @@ +apiUrl = 'https://example.com/'; + } + } else { + $this->apiUrl = 'https://another.example.com/'; + } + } +} + +?> +----- +apiUrl = 'https://example.com/'; + } + } else { + $this->apiUrl = 'https://another.example.com/'; + } + } +} + +?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/include_node_visitor.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/include_node_visitor.php.inc similarity index 81% rename from rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/include_node_visitor.php.inc rename to rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/include_node_visitor.php.inc index e6b1d4bf001..6f72a3e73bc 100644 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/include_node_visitor.php.inc +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/include_node_visitor.php.inc @@ -1,6 +1,6 @@ previousStmt = $node; } } + +?> ----- previousStmt = $node; } } + +?> diff --git a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/iterable_property_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/iterable_property_type.php.inc similarity index 75% rename from rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/iterable_property_type.php.inc rename to rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/iterable_property_type.php.inc index e0d8a926d11..36e203efe75 100644 --- a/rules-tests/Php74/Rector/Property/TypedPropertyRector/Fixture/iterable_property_type.php.inc +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/iterable_property_type.php.inc @@ -1,6 +1,6 @@ property = $dataObject->nativelyTypedProperty; + } +} + +?> +----- +property = $dataObject->nativelyTypedProperty; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/non_nullable_mock_object.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/non_nullable_mock_object.php.inc new file mode 100644 index 00000000000..9849d1548c7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/non_nullable_mock_object.php.inc @@ -0,0 +1,35 @@ +someValue = $this->createMock('SomeClass'); + } +} + +?> +----- +someValue = $this->createMock('SomeClass'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/non_nullable_mock_object_with_var.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/non_nullable_mock_object_with_var.php.inc new file mode 100644 index 00000000000..74b0e9483f6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/non_nullable_mock_object_with_var.php.inc @@ -0,0 +1,41 @@ +someValue = $this->createMock('DateTime'); + } +} + +?> +----- +someValue = $this->createMock('DateTime'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/not_in_tets_typed_property_in_tests.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/not_in_tets_typed_property_in_tests.php.inc new file mode 100644 index 00000000000..77a20389106 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/not_in_tets_typed_property_in_tests.php.inc @@ -0,0 +1,34 @@ +value = 1000; + } +} + +?> +----- +value = 1000; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_class_string.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_class_string.php.inc new file mode 100644 index 00000000000..cc8ee22f83c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_class_string.php.inc @@ -0,0 +1,37 @@ +property = 'stdClass'; + } +} + +?> +----- +property = 'stdClass'; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_if_checked.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_if_checked.php.inc new file mode 100644 index 00000000000..df3a324a292 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_if_checked.php.inc @@ -0,0 +1,52 @@ +connection == null) { + $this->initConnection(); + } + } + + private function initConnection(): void + { + $this->connection = new SomeConnection(); + } +} + +?> +----- +connection == null) { + $this->initConnection(); + } + } + + private function initConnection(): void + { + $this->connection = new SomeConnection(); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_mock_object.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_mock_object.php.inc new file mode 100644 index 00000000000..5060489a57e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/nullable_mock_object.php.inc @@ -0,0 +1,49 @@ +property = $this->createMock(DateTime::class); + $this->property->expects(self::once())->method('format'); + $this->property->format('Y'); + } +} + +?> +----- +property = $this->createMock(DateTime::class); + $this->property->expects(self::once())->method('format'); + $this->property->format('Y'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/preserve_multiple_docs_no_change.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/preserve_multiple_docs_no_change.php.inc new file mode 100644 index 00000000000..11d9b95ea9f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/preserve_multiple_docs_no_change.php.inc @@ -0,0 +1,49 @@ +property = 'DateTime'; + } +} + +?> +----- +property = 'DateTime'; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/property_with_default_null.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/property_with_default_null.php.inc new file mode 100644 index 00000000000..9b8a9b1b204 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/property_with_default_null.php.inc @@ -0,0 +1,57 @@ +cacheFile = $this->coreCache(); + $this->cacheFiles[] = $this->coreCache(); + } +} + +?> +----- +cacheFile = $this->coreCache(); + $this->cacheFiles[] = $this->coreCache(); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_default_type_different.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_default_type_different.php.inc new file mode 100644 index 00000000000..67416f1accb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_default_type_different.php.inc @@ -0,0 +1,13 @@ +property = 100; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_default_type_mixed.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_default_type_mixed.php.inc new file mode 100644 index 00000000000..8fd4ede2e23 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_default_type_mixed.php.inc @@ -0,0 +1,13 @@ +property = $prop; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_in_trait.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_in_trait.php.inc new file mode 100644 index 00000000000..a6077e514e7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_in_trait.php.inc @@ -0,0 +1,19 @@ +handlers = $handlers; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_mixed_native_with_docblock_union.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_mixed_native_with_docblock_union.php.inc new file mode 100644 index 00000000000..94078268582 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_mixed_native_with_docblock_union.php.inc @@ -0,0 +1,24 @@ +property = new stdClass; + } + + /** + * @param stdClass|DateTime $property + */ + public function run2($property) + { + $this->property = $property; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_not_natively_typed_property_from_data_object.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_not_natively_typed_property_from_data_object.php.inc new file mode 100644 index 00000000000..dbd45a15a77 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_not_natively_typed_property_from_data_object.php.inc @@ -0,0 +1,15 @@ +property = $dataObject->notNativelyTypedProperty; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_nullable_callable.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_nullable_callable.php.inc new file mode 100644 index 00000000000..3afbf8f41dc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_nullable_callable.php.inc @@ -0,0 +1,18 @@ +prop = $prop; + } + + public function reset(): void + { + $this->prop = null; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_set_type_by_param_doc.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_set_type_by_param_doc.php.inc new file mode 100644 index 00000000000..8fa7ee622f5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_set_type_by_param_doc.php.inc @@ -0,0 +1,16 @@ +property = $property; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_typed_from_default_value_null_assign.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_typed_from_default_value_null_assign.php.inc new file mode 100644 index 00000000000..ff90ba37a53 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/skip_typed_from_default_value_null_assign.php.inc @@ -0,0 +1,8 @@ +x = $x; + } else { + $this->x = null; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..aed87aca1fa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/some_class.php.inc @@ -0,0 +1,31 @@ +name = 'string'; + } +} + +?> +----- +name = 'string'; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/strict_setter_types.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/strict_setter_types.php.inc new file mode 100644 index 00000000000..515df28aa44 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/strict_setter_types.php.inc @@ -0,0 +1,35 @@ +email = $email; + } +} + +?> +----- +email = $email; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/typed_from_default_value_assign.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/typed_from_default_value_assign.php.inc new file mode 100644 index 00000000000..9c649967749 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/typed_from_default_value_assign.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/with_comment.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/with_comment.php.inc new file mode 100644 index 00000000000..a3c9e66f3e7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Fixture/with_comment.php.inc @@ -0,0 +1,36 @@ +property = new \DateTime(); + } +} + +?> +----- +property = new \DateTime(); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/anonymous_extends_existing_class.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/anonymous_extends_existing_class.php.inc new file mode 100644 index 00000000000..6bc65847edb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/anonymous_extends_existing_class.php.inc @@ -0,0 +1,39 @@ +x = new \DateTime('now'); + } else { + $this->x = new class extends \DateTime {}; + } + } +} + +?> +----- +x = new \DateTime('now'); + } else { + $this->x = new class extends \DateTime {}; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/anonymous_extends_existing_class_docblock_exists.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/anonymous_extends_existing_class_docblock_exists.php.inc new file mode 100644 index 00000000000..d6c5ee62dc9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/anonymous_extends_existing_class_docblock_exists.php.inc @@ -0,0 +1,44 @@ +x = new \DateTime('now'); + } else { + $this->x = new class() extends \DateTime { + }; + } + } +} + +?> +----- +x = new \DateTime('now'); + } else { + $this->x = new class() extends \DateTime { + }; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/assign_this_become_self.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/assign_this_become_self.php.inc new file mode 100644 index 00000000000..3eb2aed3cbe --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/assign_this_become_self.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/false_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/false_default_value.php.inc new file mode 100644 index 00000000000..7c02f8e11b7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/false_default_value.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/intersection_type_with_setup.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/intersection_type_with_setup.php.inc new file mode 100644 index 00000000000..9ca9a56351b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/intersection_type_with_setup.php.inc @@ -0,0 +1,57 @@ +property = $this->createMock(DateTime::class); + } + + public function testExample(): void + { + $this->property->expects(self::once())->method('format'); + $this->property->format('Y'); + } +} + +?> +----- +property = $this->createMock(DateTime::class); + } + + public function testExample(): void + { + $this->property->expects(self::once())->method('format'); + $this->property->format('Y'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/nullable_intersection_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/nullable_intersection_type.php.inc new file mode 100644 index 00000000000..e8403d3f2cf --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/nullable_intersection_type.php.inc @@ -0,0 +1,49 @@ +property = $this->createMock(DateTime::class); + $this->property->expects(self::once())->method('format'); + $this->property->format('Y'); + } +} + +?> +----- +property = $this->createMock(DateTime::class); + $this->property->expects(self::once())->method('format'); + $this->property->format('Y'); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/parse_url_with_second_arg.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/parse_url_with_second_arg.php.inc new file mode 100644 index 00000000000..468c6b6ac52 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/parse_url_with_second_arg.php.inc @@ -0,0 +1,31 @@ +host = parse_url($url, \PHP_URL_HOST); + } +} + +?> +----- +host = parse_url($url, \PHP_URL_HOST); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/parse_url_with_second_arg2.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/parse_url_with_second_arg2.php.inc new file mode 100644 index 00000000000..9332969a085 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/parse_url_with_second_arg2.php.inc @@ -0,0 +1,31 @@ +host = parse_url($url, \PHP_URL_HOST); + } +} + +?> +----- +host = parse_url($url, \PHP_URL_HOST); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_callable.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_callable.php.inc new file mode 100644 index 00000000000..71dd84c27fe --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_callable.php.inc @@ -0,0 +1,17 @@ +property = $prop; + } else { + $this->property = false; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_protected_property.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_protected_property.php.inc new file mode 100644 index 00000000000..eb659ad51b7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_protected_property.php.inc @@ -0,0 +1,17 @@ +stringOrInteger = 'hey'; + } else { + $this->stringOrInteger = 1000; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/true_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/true_default_value.php.inc new file mode 100644 index 00000000000..ee2f7c3b0f0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/true_default_value.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/union_closure.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/union_closure.php.inc new file mode 100644 index 00000000000..1a7f41156bb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/union_closure.php.inc @@ -0,0 +1,41 @@ +property = function (): void { + }; + } else { + $this->property = false; + } + } +} + +?> +----- +property = function (): void { + }; + } else { + $this->property = false; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/has_method_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/has_method_type.php.inc new file mode 100644 index 00000000000..869de76f1de --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/has_method_type.php.inc @@ -0,0 +1,39 @@ +obj = $obj; + } +} + +?> +----- +obj = $obj; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/if_else.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/if_else.php.inc new file mode 100644 index 00000000000..8dfcd526bdf --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/if_else.php.inc @@ -0,0 +1,39 @@ +stringOrInteger = 'hey'; + } else { + $this->stringOrInteger = 1000; + } + } +} + +?> +----- +stringOrInteger = 'hey'; + } else { + $this->stringOrInteger = 1000; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/public_property_doc_assign.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/public_property_doc_assign.php.inc new file mode 100644 index 00000000000..c806be1f0f1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureInlinePublic/public_property_doc_assign.php.inc @@ -0,0 +1,35 @@ +config = new stdClass; + } +} + +?> +----- +config = new stdClass; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixturePhp82/false_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixturePhp82/false_default_value.php.inc new file mode 100644 index 00000000000..331289ef2b1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixturePhp82/false_default_value.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixturePhp82/skip_null_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixturePhp82/skip_null_default_value.php.inc new file mode 100644 index 00000000000..79ebb1f8711 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixturePhp82/skip_null_default_value.php.inc @@ -0,0 +1,8 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/InlinePublicTest.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/InlinePublicTest.php new file mode 100644 index 00000000000..601177907dd --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/InlinePublicTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureInlinePublic'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/inline_public_configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Php82Test.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Php82Test.php new file mode 100644 index 00000000000..cd704d1375a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Php82Test.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixturePhp82'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_php82.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Source/HandlerInterface.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Source/HandlerInterface.php new file mode 100644 index 00000000000..7b9e6ff3e2f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/Source/HandlerInterface.php @@ -0,0 +1,8 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureComplexTypes'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/complex_types.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/TypedPropertyFromAssignsRectorTest.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/TypedPropertyFromAssignsRectorTest.php new file mode 100644 index 00000000000..d9d4662c5ed --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/TypedPropertyFromAssignsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/complex_types.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/complex_types.php new file mode 100644 index 00000000000..2effe7d959d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/complex_types.php @@ -0,0 +1,13 @@ +rule(TypedPropertyFromAssignsRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::INTERSECTION_TYPES); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/configured_rule.php new file mode 100644 index 00000000000..6d027600a75 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(TypedPropertyFromAssignsRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::TYPED_PROPERTIES); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/configured_rule_php82.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/configured_rule_php82.php new file mode 100644 index 00000000000..7506f408a0b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/configured_rule_php82.php @@ -0,0 +1,13 @@ +rule(TypedPropertyFromAssignsRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::NULL_FALSE_TRUE_STANDALONE_TYPE); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/inline_public_configured_rule.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/inline_public_configured_rule.php new file mode 100644 index 00000000000..31b561c6950 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/config/inline_public_configured_rule.php @@ -0,0 +1,15 @@ +ruleWithConfiguration(TypedPropertyFromAssignsRector::class, [ + TypedPropertyFromAssignsRector::INLINE_PUBLIC => true, + ]); + + $rectorConfig->phpVersion(PhpVersionFeature::INTERSECTION_TYPES); +}; diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/conditionally_changed_property_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/conditionally_changed_property_default_value.php.inc new file mode 100644 index 00000000000..67a779631b2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/conditionally_changed_property_default_value.php.inc @@ -0,0 +1,38 @@ +getRequestUri(), '/super-admin')) { + $this->rootView = 'super_admin.index'; + } + } +} + +?> +----- +getRequestUri(), '/super-admin')) { + $this->rootView = 'super_admin.index'; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/default_array_merge.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/default_array_merge.php.inc new file mode 100644 index 00000000000..2e0f774937c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/default_array_merge.php.inc @@ -0,0 +1,35 @@ + 'value' + ]; + + public function __construct(array $options) + { + $this->options = array_merge($this->options, $options); + } +} + +?> +----- + 'value' + ]; + + public function __construct(array $options) + { + $this->options = array_merge($this->options, $options); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/default_null_in_param.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/default_null_in_param.php.inc new file mode 100644 index 00000000000..308833fad7b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/default_null_in_param.php.inc @@ -0,0 +1,31 @@ +nullableString = $nullableString; + } +} + +?> +----- +nullableString = $nullableString; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/do_not_add_nullable_from_constructor.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/do_not_add_nullable_from_constructor.php.inc new file mode 100644 index 00000000000..5a7be02f58c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/do_not_add_nullable_from_constructor.php.inc @@ -0,0 +1,39 @@ +result = $result; + } + + public function getResult(): ?array + { + return $this->result; + } +} + +?> +----- +result = $result; + } + + public function getResult(): ?array + { + return $this->result; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/do_not_add_value_filled_by_construct.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/do_not_add_value_filled_by_construct.php.inc new file mode 100644 index 00000000000..1bfafb2e39a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/do_not_add_value_filled_by_construct.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/has_false_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/has_false_default_value.php.inc new file mode 100644 index 00000000000..da3f35cb680 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/has_false_default_value.php.inc @@ -0,0 +1,38 @@ +stdClass = $stdClass; + } +} + +?> +----- +stdClass = $stdClass; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/has_null_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/has_null_default_value.php.inc new file mode 100644 index 00000000000..1eb50f567bf --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/has_null_default_value.php.inc @@ -0,0 +1,38 @@ +stdClass = $stdClass; + } +} + +?> +----- +stdClass = $stdClass; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/include_intersection_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/include_intersection_type.php.inc new file mode 100644 index 00000000000..a31ce30c42a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/include_intersection_type.php.inc @@ -0,0 +1,37 @@ +firstAndSecond = $firstAndSecond; + } +} + +?> +----- +firstAndSecond = $firstAndSecond; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/include_trait_as_well.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/include_trait_as_well.php.inc new file mode 100644 index 00000000000..10491249128 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/include_trait_as_well.php.inc @@ -0,0 +1,44 @@ +entityManager = $entityManager; + } +} + +?> +----- +entityManager = $entityManager; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/multi_methods_with_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/multi_methods_with_default_value.php.inc new file mode 100644 index 00000000000..a557a1153fc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/multi_methods_with_default_value.php.inc @@ -0,0 +1,44 @@ +rootView = 'super_admin.index'; + } + + public function __construct() + { + $this->rootView = 'super_admin.index'; + } +} + +?> +----- +rootView = 'super_admin.index'; + } + + public function __construct() + { + $this->rootView = 'super_admin.index'; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/natively_typed_property_from_data_object.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/natively_typed_property_from_data_object.php.inc new file mode 100644 index 00000000000..2d35db5028d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/natively_typed_property_from_data_object.php.inc @@ -0,0 +1,35 @@ +property = $dataObject->nativelyTypedProperty; + } +} + +?> +----- +property = $dataObject->nativelyTypedProperty; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/nullable_has_default_value.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/nullable_has_default_value.php.inc new file mode 100644 index 00000000000..6f6005709c5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/nullable_has_default_value.php.inc @@ -0,0 +1,38 @@ +stdClass = $stdClass; + } +} + +?> +----- +stdClass = $stdClass; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/public_property_doc.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/public_property_doc.php.inc new file mode 100644 index 00000000000..3493d0f5ad0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/public_property_doc.php.inc @@ -0,0 +1,38 @@ +config = new stdClass; + } +} + +?> +----- +config = new stdClass; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_by_ref_assign_on_trait.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_by_ref_assign_on_trait.php.inc new file mode 100644 index 00000000000..a807eae706b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_by_ref_assign_on_trait.php.inc @@ -0,0 +1,17 @@ +str = $str; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_default_false_in_param.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_default_false_in_param.php.inc new file mode 100644 index 00000000000..d3d3cf2a9cb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_default_false_in_param.php.inc @@ -0,0 +1,15 @@ +someString = $someString; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_different_type_assigned_different_method.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_different_type_assigned_different_method.php.inc new file mode 100644 index 00000000000..0d524e5bbeb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_different_type_assigned_different_method.php.inc @@ -0,0 +1,18 @@ +name = $name; + } + + public function reset(int $name) + { + $this->name = $name; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_different_type_assigned_different_method2.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_different_type_assigned_different_method2.php.inc new file mode 100644 index 00000000000..4bb64e6252e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_different_type_assigned_different_method2.php.inc @@ -0,0 +1,18 @@ +name = 'test'; + } + + public function reset(int $name) + { + $this->name = $name; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_doctrine_collection.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_doctrine_collection.php.inc new file mode 100644 index 00000000000..5e06f6b4ad2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_doctrine_collection.php.inc @@ -0,0 +1,15 @@ +items = new ArrayCollection(); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_exists_in_parent_class.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_exists_in_parent_class.php.inc new file mode 100644 index 00000000000..42a8041aa0a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_exists_in_parent_class.php.inc @@ -0,0 +1,14 @@ +field = $field; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_if_trait_has_same_property.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_if_trait_has_same_property.php.inc new file mode 100644 index 00000000000..43efa2f3ae9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_if_trait_has_same_property.php.inc @@ -0,0 +1,17 @@ +skipMe = $nullableString; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_inner_class.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_inner_class.php.inc new file mode 100644 index 00000000000..06c1b5d62f1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_inner_class.php.inc @@ -0,0 +1,21 @@ +name = $name; + } + }; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_inner_class2.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_inner_class2.php.inc new file mode 100644 index 00000000000..da2af213034 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_inner_class2.php.inc @@ -0,0 +1,21 @@ +name = 'value'; + } + }; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_literal_incompatible_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_literal_incompatible_type.php.inc new file mode 100644 index 00000000000..47e1fc22cc5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_literal_incompatible_type.php.inc @@ -0,0 +1,16 @@ +due_date = true; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_literal_string_type.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_literal_string_type.php.inc new file mode 100644 index 00000000000..a9ce039f7fc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_literal_string_type.php.inc @@ -0,0 +1,16 @@ +due_date = ''; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_mixed_condition_string.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_mixed_condition_string.php.inc new file mode 100644 index 00000000000..669f0334bba --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_mixed_condition_string.php.inc @@ -0,0 +1,15 @@ +name = $name; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_not_natively_typed_property_from_data_object.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_not_natively_typed_property_from_data_object.php.inc new file mode 100644 index 00000000000..593d7f427ff --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_not_natively_typed_property_from_data_object.php.inc @@ -0,0 +1,15 @@ +property = $dataObject->notNativelyTypedProperty; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_unset_by_trait.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_unset_by_trait.php.inc new file mode 100644 index 00000000000..043cc1385cc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_unset_by_trait.php.inc @@ -0,0 +1,23 @@ +entityManager = $entityManager; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/variadic_constructor_param.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/variadic_constructor_param.php.inc index 39874e37b83..6c800f2a96a 100644 --- a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/variadic_constructor_param.php.inc +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/variadic_constructor_param.php.inc @@ -24,7 +24,7 @@ final class VariadicConstructorParam /** * @var DateTime[] */ - private array $dates = []; + private array $dates; public function __construct(DateTime ...$dates) { $this->dates = $dates; diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/ByRefTrait.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/ByRefTrait.php new file mode 100644 index 00000000000..499a86ddef1 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/ByRefTrait.php @@ -0,0 +1,13 @@ +str; + $str = null; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/FirstType.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/FirstType.php new file mode 100644 index 00000000000..70a99e8bd5c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/FirstType.php @@ -0,0 +1,10 @@ +entityManager->flush(); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/TraitWithUnsetProperty.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/TraitWithUnsetProperty.php new file mode 100644 index 00000000000..1b28b6ab5d3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/TraitWithUnsetProperty.php @@ -0,0 +1,11 @@ +entityManager); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/TypedPropertyFromStrictConstructorRectorTest.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/TypedPropertyFromStrictConstructorRectorTest.php index 21f182a9d49..1daf8ff5bcc 100644 --- a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/TypedPropertyFromStrictConstructorRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/TypedPropertyFromStrictConstructorRectorTest.php @@ -5,28 +5,20 @@ namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromStrictConstructorRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; -/** - * @requires PHP 7.4 - */ final class TypedPropertyFromStrictConstructorRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/config/configured_rule.php index f8599661547..e0a006272cf 100644 --- a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/config/configured_rule.php +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/config/configured_rule.php @@ -2,10 +2,12 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromStrictConstructorRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\ValueObject\PhpVersionFeature; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(TypedPropertyFromStrictConstructorRector::class); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(TypedPropertyFromStrictConstructorRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::INTERSECTION_TYPES); }; diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/include_mock.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/include_mock.php.inc new file mode 100644 index 00000000000..97e04e29fd2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/include_mock.php.inc @@ -0,0 +1,39 @@ +value = $this->createMock(\stdClass::class); + } +} + +?> +----- +value = $this->createMock(\stdClass::class); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/include_mock_doc.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/include_mock_doc.php.inc new file mode 100644 index 00000000000..70ad936484c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/include_mock_doc.php.inc @@ -0,0 +1,41 @@ +value = $this->createMock(\DateTime::class); + } +} + +?> +----- +value = $this->createMock(\DateTime::class); + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/skip_no_test_case.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/skip_no_test_case.php.inc new file mode 100644 index 00000000000..e63cb9235c3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/skip_no_test_case.php.inc @@ -0,0 +1,13 @@ +value = 1000; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..ed35e94b178 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/Fixture/some_class.php.inc @@ -0,0 +1,35 @@ +value = 1000; + } +} + +?> +----- +value = 1000; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/TypedPropertyFromStrictSetUpRectorTest.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/TypedPropertyFromStrictSetUpRectorTest.php new file mode 100644 index 00000000000..8c5d994ae9e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/TypedPropertyFromStrictSetUpRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/config/configured_rule.php new file mode 100644 index 00000000000..79bb52a8223 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([TypedPropertyFromStrictSetUpRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/DeclareStrictTypesRectorTest.php b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/DeclareStrictTypesRectorTest.php new file mode 100644 index 00000000000..2cbfb54a7da --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/DeclareStrictTypesRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/multiple_declare.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/multiple_declare.php.inc new file mode 100644 index 00000000000..b89891feee0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/multiple_declare.php.inc @@ -0,0 +1,43 @@ +"; +} + +register_tick_function('example'); + +declare(ticks=1) { + $cars = ["Ford", "Volvo", "BMW"]; + foreach($cars as $car) { + echo "$car
"; + } +} + +?> +----- +"; +} + +register_tick_function('example'); + +declare(ticks=1) { + $cars = ["Ford", "Volvo", "BMW"]; + foreach($cars as $car) { + echo "$car
"; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/multiple_declare2.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/multiple_declare2.php.inc new file mode 100644 index 00000000000..be70181af63 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/multiple_declare2.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/no_namespace_with_plus_operation.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/no_namespace_with_plus_operation.php.inc new file mode 100644 index 00000000000..5c048cc3cef --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/no_namespace_with_plus_operation.php.inc @@ -0,0 +1,31 @@ +prop2 + $second; + } +} + +?> +----- +prop2 + $second; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/skip_already_with_strict_types.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/skip_already_with_strict_types.php.inc new file mode 100644 index 00000000000..7383a11e7d3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector/Fixture/skip_already_with_strict_types.php.inc @@ -0,0 +1,9 @@ + +----- +withSkip([ + DeclareStrictTypesRector::class => [ + // .php.inc changed .php during running test + realpath(__DIR__ . '/../Fixture') . '/skipped_by_path.php', + ], + ]) + ->withRules([DeclareStrictTypesRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/arrow_function_return.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/arrow_function_return.php.inc new file mode 100644 index 00000000000..82e19482c53 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/arrow_function_return.php.inc @@ -0,0 +1,23 @@ + 42; + +$arrowWithParam = fn (int $x): int => $x + 1; + +$arrowString = fn (): string => 'hello'; + +?> +----- + 42; + +$arrowWithParam = fn (int $x): int => $x + 1; + +$arrowString = fn (): string => 'hello'; diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/closure_return.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/closure_return.php.inc new file mode 100644 index 00000000000..8a29c38fab5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/closure_return.php.inc @@ -0,0 +1,35 @@ + +----- + +----- + +----- + +----- + +----- +add(1, 2); +} + +?> +----- +add(1, 2); +} diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/named_arguments.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/named_arguments.php.inc new file mode 100644 index 00000000000..b4657652b67 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/named_arguments.php.inc @@ -0,0 +1,29 @@ + +----- + +----- + +----- +count = 123; +} + +?> +----- +count = 123; +} diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/return_object.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/return_object.php.inc new file mode 100644 index 00000000000..e46058dbfec --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/return_object.php.inc @@ -0,0 +1,25 @@ + +----- + 'not an int'; diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/skip_attribute_wrong_type.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/skip_attribute_wrong_type.php.inc new file mode 100644 index 00000000000..e0c90008470 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/skip_attribute_wrong_type.php.inc @@ -0,0 +1,10 @@ +count = '123'; +} diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/skip_static_property_wrong_type.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/skip_static_property_wrong_type.php.inc new file mode 100644 index 00000000000..ea1d33d2366 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Fixture/skip_static_property_wrong_type.php.inc @@ -0,0 +1,10 @@ + +----- + +----- + +----- + +----- + +----- + +----- + +----- +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Source/AnotherClass.php b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Source/AnotherClass.php new file mode 100644 index 00000000000..1b24c81cc21 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Source/AnotherClass.php @@ -0,0 +1,12 @@ +name; + } + + public function process(self $other): void + { + } + + public static function format(string $input): string + { + return trim($input); + } +} diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Source/functions.php b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Source/functions.php new file mode 100644 index 00000000000..2a05a34cc2c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector/Source/functions.php @@ -0,0 +1,34 @@ +withRules([SafeDeclareStrictTypesRector::class]); diff --git a/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/do_while_some_class.php.inc b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/do_while_some_class.php.inc new file mode 100644 index 00000000000..9799803271a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/do_while_some_class.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/silent_condition.php.inc b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/silent_condition.php.inc new file mode 100644 index 00000000000..329ad8d5f60 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/silent_condition.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/skip_assign.php.inc b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/skip_assign.php.inc new file mode 100644 index 00000000000..89df6b085c7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/skip_assign.php.inc @@ -0,0 +1,20 @@ +get()) { + $someClass->run(); + } + } + + private function get(): ?CheckedClass + { + return rand(0, 1) ? new CheckedClass() : null; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/skip_phpdoc.php.inc b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/skip_phpdoc.php.inc new file mode 100644 index 00000000000..ec3f25d8e26 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/skip_phpdoc.php.inc @@ -0,0 +1,18 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..fae3562f504 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Fixture/some_class.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Source/CheckedClass.php b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Source/CheckedClass.php new file mode 100644 index 00000000000..afe3f2542f0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/Source/CheckedClass.php @@ -0,0 +1,10 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/config/configured_rule.php new file mode 100644 index 00000000000..e33a8d1d639 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([WhileNullableToInstanceofRector::class]); diff --git a/rules-tests/TypeDeclaration/TypeNormalizerTest.php b/rules-tests/TypeDeclaration/TypeNormalizerTest.php deleted file mode 100644 index 27b6c549ea5..00000000000 --- a/rules-tests/TypeDeclaration/TypeNormalizerTest.php +++ /dev/null @@ -1,56 +0,0 @@ -boot(); - $this->typeNormalizer = $this->getService(TypeNormalizer::class); - } - - protected function tearDown(): void - { - unset($this->typeNormalizer); - } - - /** - * @dataProvider provideDataNormalizeArrayOfUnionToUnionArray() - */ - public function testNormalizeArrayOfUnionToUnionArray(ArrayType $arrayType, string $expectedDocString): void - { - $unionType = $this->typeNormalizer->normalizeArrayOfUnionToUnionArray($arrayType); - $this->assertInstanceOf(UnionType::class, $unionType); - } - - /** - * @return Iterator - */ - public function provideDataNormalizeArrayOfUnionToUnionArray(): Iterator - { - $unionType = new UnionType([new StringType(), new IntegerType()]); - $arrayType = new ArrayType(new MixedType(), $unionType); - yield [$arrayType, 'int[]|string[]']; - - $arrayType = new ArrayType(new MixedType(), $unionType); - $moreNestedArrayType = new ArrayType(new MixedType(), $arrayType); - yield [$moreNestedArrayType, 'int[][]|string[][]']; - - $evenMoreNestedArrayType = new ArrayType(new MixedType(), $moreNestedArrayType); - yield [$evenMoreNestedArrayType, 'int[][][]|string[][][]']; - } -} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/AddParamArrayDocblockBasedOnArrayMapRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/AddParamArrayDocblockBasedOnArrayMapRectorTest.php new file mode 100644 index 00000000000..14244050c09 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/AddParamArrayDocblockBasedOnArrayMapRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_bare_mixed.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_bare_mixed.php.inc new file mode 100644 index 00000000000..c88c3b6b82f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_bare_mixed.php.inc @@ -0,0 +1,33 @@ + trim($item), $items); + } +} + +?> +----- + trim($item), $items); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_dummy_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_dummy_array.php.inc new file mode 100644 index 00000000000..425af84cf8e --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_dummy_array.php.inc @@ -0,0 +1,33 @@ + trim($item), $items); + } +} + +?> +----- + trim($item), $items); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_mixed_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_mixed_type.php.inc new file mode 100644 index 00000000000..461858d30de --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/override_mixed_type.php.inc @@ -0,0 +1,33 @@ + trim($item), $items); + } +} + +?> +----- + trim($item), $items); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/skip_better_existing_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/skip_better_existing_type.php.inc new file mode 100644 index 00000000000..765fd64628f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/skip_better_existing_type.php.inc @@ -0,0 +1,14 @@ + $items + */ + public function run(array $items): void + { + array_map(fn (string $item) => trim($item), $items); + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..14627faf497 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/Fixture/some_class.php.inc @@ -0,0 +1,30 @@ + trim($item), $items); + } +} + +?> +----- + trim($item), $items); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/config/configured_rule.php new file mode 100644 index 00000000000..8be29c33185 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddParamArrayDocblockBasedOnArrayMapRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/AddParamArrayDocblockFromAssignsParamToParamReferenceRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/AddParamArrayDocblockFromAssignsParamToParamReferenceRectorTest.php new file mode 100644 index 00000000000..d43772a6e24 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/AddParamArrayDocblockFromAssignsParamToParamReferenceRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/Fixture/override_dummy_array_param.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/Fixture/override_dummy_array_param.php.inc new file mode 100644 index 00000000000..9c26cd1216f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/Fixture/override_dummy_array_param.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/Fixture/skip_dim_fetch_assign_deep.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/Fixture/skip_dim_fetch_assign_deep.php.inc new file mode 100644 index 00000000000..11c8bba2802 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/Fixture/skip_dim_fetch_assign_deep.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/config/configured_rule.php new file mode 100644 index 00000000000..b3beda53d79 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddParamArrayDocblockFromAssignsParamToParamReferenceRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/AddParamArrayDocblockFromDataProviderRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/AddParamArrayDocblockFromDataProviderRectorTest.php new file mode 100644 index 00000000000..b9bcc91f16f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/AddParamArrayDocblockFromDataProviderRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/complex_case.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/complex_case.php.inc new file mode 100644 index 00000000000..b02fd83874b --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/complex_case.php.inc @@ -0,0 +1,111 @@ + [ + 'anotherKey' => 123, + ], + ], + [ + 'expectedKey' => 456, + ], + 456, + ]; + yield [ + [], + [ + 'expectedKey' => 456, + ], + 456, + ]; + yield [ + [ + 'key' => [ + 'anotherKey' => 123, + ], + ], + [], + 123, + ]; + yield [ + [], + [], + 5, + ]; + } + +} + +?> +----- +> $first + * @param array $second + */ + #[DataProvider('getData')] + public function testSomething(array $first, array $second, int $expected): void + { + } + + public static function getData(): \Iterator + { + yield [ + [ + 'key' => [ + 'anotherKey' => 123, + ], + ], + [ + 'expectedKey' => 456, + ], + 456, + ]; + yield [ + [], + [ + 'expectedKey' => 456, + ], + 456, + ]; + yield [ + [ + 'key' => [ + 'anotherKey' => 123, + ], + ], + [], + 123, + ]; + yield [ + [], + [], + 5, + ]; + } + +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/cover_dummy_iterable_param.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/cover_dummy_iterable_param.php.inc new file mode 100644 index 00000000000..f3058ce4729 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/cover_dummy_iterable_param.php.inc @@ -0,0 +1,53 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/cover_iterable.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/cover_iterable.php.inc new file mode 100644 index 00000000000..99ff2522670 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/cover_iterable.php.inc @@ -0,0 +1,50 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/multiple_options.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/multiple_options.php.inc new file mode 100644 index 00000000000..38ae3020ca5 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/multiple_options.php.inc @@ -0,0 +1,50 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/override_dummy_array_param.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/override_dummy_array_param.php.inc new file mode 100644 index 00000000000..fe34b5646a1 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/override_dummy_array_param.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/skip_mixed.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/skip_mixed.php.inc new file mode 100644 index 00000000000..b1cf1f647ab --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/skip_mixed.php.inc @@ -0,0 +1,22 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/config/configured_rule.php new file mode 100644 index 00000000000..1bfdc172f37 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddParamArrayDocblockFromDataProviderRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/AddParamArrayDocblockFromDimFetchAccessRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/AddParamArrayDocblockFromDimFetchAccessRectorTest.php new file mode 100644 index 00000000000..01d4d9222f7 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/AddParamArrayDocblockFromDimFetchAccessRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/int_keys.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/int_keys.php.inc new file mode 100644 index 00000000000..f3102f2f8e9 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/int_keys.php.inc @@ -0,0 +1,34 @@ + +----- + $data + */ + public function process(array $data): void + { + $item = $data[55]; + + $anotherItem = $data[132]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/override_dummy_array_param.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/override_dummy_array_param.php.inc new file mode 100644 index 00000000000..c8d115ce3e4 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/override_dummy_array_param.php.inc @@ -0,0 +1,37 @@ + +----- + $data + */ + public function process(array $data): void + { + $item = $data['key']; + + $anotherItem = $data['another_key']; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/param_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/param_array.php.inc new file mode 100644 index 00000000000..7f41dfb0a42 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/param_array.php.inc @@ -0,0 +1,34 @@ + +----- + $data + */ + public function process(array $data): void + { + $item = $data['key']; + + $anotherItem = $data['another_key']; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/skip_contract_enforced.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/skip_contract_enforced.php.inc new file mode 100644 index 00000000000..e6ecc20efde --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector/Fixture/skip_contract_enforced.php.inc @@ -0,0 +1,12 @@ +withRules([AddParamArrayDocblockFromDimFetchAccessRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/AddReturnDocblockForArrayDimAssignedObjectRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/AddReturnDocblockForArrayDimAssignedObjectRectorTest.php new file mode 100644 index 00000000000..e8a62bef8a6 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/AddReturnDocblockForArrayDimAssignedObjectRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/assign_dim_fetch.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/assign_dim_fetch.php.inc new file mode 100644 index 00000000000..8e2a3b9727d --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/assign_dim_fetch.php.inc @@ -0,0 +1,44 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/include_method_dim_fetch.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/include_method_dim_fetch.php.inc new file mode 100644 index 00000000000..b7920906451 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/include_method_dim_fetch.php.inc @@ -0,0 +1,54 @@ +createSomeItem(); + } + + return $items; + } + + private function createSomeItem(): SomeItem + { + return new SomeItem(); + } +} + +?> +----- +createSomeItem(); + } + + return $items; + } + + private function createSomeItem(): SomeItem + { + return new SomeItem(); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/override_dummy_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/override_dummy_array.php.inc new file mode 100644 index 00000000000..e4f03c77039 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/override_dummy_array.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/skip_default_assign.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/skip_default_assign.php.inc new file mode 100644 index 00000000000..8ac6894c774 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/skip_default_assign.php.inc @@ -0,0 +1,18 @@ +createSomeItem(); + } + + return $items; + } + + /** + * @return FakeItem + */ + private function createSomeItem() + { + return new SomeItem(); + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/skip_property_fetch_in_case_of_another_assign.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/skip_property_fetch_in_case_of_another_assign.php.inc new file mode 100644 index 00000000000..4acdc03f75f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Fixture/skip_property_fetch_in_case_of_another_assign.php.inc @@ -0,0 +1,18 @@ +items = []; + foreach ($keys as $key) { + $this->items[] = new SomeItem($key); + } + + return $this->items; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Source/SomeItem.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Source/SomeItem.php new file mode 100644 index 00000000000..bbdd333ddbd --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector/Source/SomeItem.php @@ -0,0 +1,7 @@ +withRules([AddReturnDocblockForArrayDimAssignedObjectRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/AddReturnDocblockForCommonObjectDenominatorRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/AddReturnDocblockForCommonObjectDenominatorRectorTest.php new file mode 100644 index 00000000000..d26637482f4 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/AddReturnDocblockForCommonObjectDenominatorRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/override_dummy_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/override_dummy_array.php.inc new file mode 100644 index 00000000000..656e45cba58 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/override_dummy_array.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/return_of_objects.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/return_of_objects.php.inc new file mode 100644 index 00000000000..b5f4e7c69a7 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/return_of_objects.php.inc @@ -0,0 +1,42 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/skip_already_has_return_doc.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/skip_already_has_return_doc.php.inc new file mode 100644 index 00000000000..d3828bf6fba --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/skip_already_has_return_doc.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/with_string_key.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/with_string_key.php.inc new file mode 100644 index 00000000000..fc6e7ecd09d --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Fixture/with_string_key.php.inc @@ -0,0 +1,42 @@ + new FirstExtension(), + 'b' => new SecondExtension() + ]; + } +} + +?> +----- + + */ + public function getExtensions(): array + { + return [ + 'a' => new FirstExtension(), + 'b' => new SecondExtension() + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Source/AnotherExtensionWithDifferentInterface.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Source/AnotherExtensionWithDifferentInterface.php new file mode 100644 index 00000000000..8c31b52bd5a --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector/Source/AnotherExtensionWithDifferentInterface.php @@ -0,0 +1,9 @@ +withRules([AddReturnDocblockForCommonObjectDenominatorRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/AddReturnDocblockForDimFetchArrayFromAssignsRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/AddReturnDocblockForDimFetchArrayFromAssignsRectorTest.php new file mode 100644 index 00000000000..e45ffa6d107 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/AddReturnDocblockForDimFetchArrayFromAssignsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/conditional_assign.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/conditional_assign.php.inc new file mode 100644 index 00000000000..1a031ce2a9f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/conditional_assign.php.inc @@ -0,0 +1,42 @@ + +----- + + */ + public function toArray(): array + { + $items = []; + + if (mt_rand(0, 1)) { + $items['key'] = 100; + } + + return $items; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/multiple_conditional_assign.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/multiple_conditional_assign.php.inc new file mode 100644 index 00000000000..c5929d0b80f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/multiple_conditional_assign.php.inc @@ -0,0 +1,50 @@ + +----- + + */ + public function toArray(): array + { + $items = []; + + if (mt_rand(0, 1)) { + $items['key'] = 'some_string'; + } + + if (mt_rand(0, 1)) { + $items['another_key'] = 500; + } + + return $items; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/some_item.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/some_item.php.inc new file mode 100644 index 00000000000..3edcb1b1f73 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/some_item.php.inc @@ -0,0 +1,36 @@ + +----- + + */ + public function toArray(): array + { + $items = []; + $items['key'] = 100; + + return $items; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/config/configured_rule.php new file mode 100644 index 00000000000..b54cbe0ed37 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddReturnDocblockForDimFetchArrayFromAssignsRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/AddReturnDocblockForJsonArrayRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/AddReturnDocblockForJsonArrayRectorTest.php new file mode 100644 index 00000000000..b4f2a909e35 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/AddReturnDocblockForJsonArrayRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/json_utils.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/json_utils.php.inc new file mode 100644 index 00000000000..705920b65dc --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/json_utils.php.inc @@ -0,0 +1,38 @@ + +----- + + */ + public function provide(string $contents): array + { + return Json::decode($contents, true); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/json_utils_with_named_arg.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/json_utils_with_named_arg.php.inc new file mode 100644 index 00000000000..bf95e165f87 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/json_utils_with_named_arg.php.inc @@ -0,0 +1,38 @@ + +----- + + */ + public function provide(string $contents): array + { + return Json::decode(forceArrays: true, json: $contents); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/override_dummy_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/override_dummy_array.php.inc new file mode 100644 index 00000000000..f97f8ff2d72 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/override_dummy_array.php.inc @@ -0,0 +1,37 @@ + +----- + + */ + public function provide(string $contents): array + { + return json_decode($contents, true); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_already_filled.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_already_filled.php.inc new file mode 100644 index 00000000000..0f33e855bc5 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_already_filled.php.inc @@ -0,0 +1,16 @@ +> + */ + public function provide(string $contents): array + { + return json_decode($contents, true); + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000000..c1b34bc4342 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_missing_arg.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_missing_arg.php.inc new file mode 100644 index 00000000000..960d3db80bc --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/skip_missing_arg.php.inc @@ -0,0 +1,13 @@ + +----- + + */ + public function provide(string $contents): array + { + return json_decode($contents, true); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/with_named_arg.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/with_named_arg.php.inc new file mode 100644 index 00000000000..eaa236e3283 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/Fixture/with_named_arg.php.inc @@ -0,0 +1,34 @@ + +----- + + */ + public function provide(string $contents): array + { + return json_decode(associative: true, json: $contents); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/config/configured_rule.php new file mode 100644 index 00000000000..296575c8aba --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddReturnDocblockForJsonArrayRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/AddReturnDocblockFromMethodCallDocblockRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/AddReturnDocblockFromMethodCallDocblockRectorTest.php new file mode 100644 index 00000000000..c57fcf989d0 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/AddReturnDocblockFromMethodCallDocblockRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/handle_static_call.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/handle_static_call.php.inc new file mode 100644 index 00000000000..9e079d809b4 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/handle_static_call.php.inc @@ -0,0 +1,38 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_fetch_first_column.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_fetch_first_column.php.inc new file mode 100644 index 00000000000..c5c35022334 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_fetch_first_column.php.inc @@ -0,0 +1,22 @@ +connection = $connection; + } + + public function getAll(): array + { + return $this->connection->fetchFirstColumn(); + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_declaration.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_declaration.php.inc new file mode 100644 index 00000000000..87710eeb5be --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_declaration.php.inc @@ -0,0 +1,22 @@ +someRepository = $someRepository; + } + + public function getAll() + { + return $this->someRepository->findAll(); + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_on_call.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_on_call.php.inc new file mode 100644 index 00000000000..f3523603717 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_on_call.php.inc @@ -0,0 +1,22 @@ +someRepository = $someRepository; + } + + public function getAll(): array + { + return $this->someRepository->findAllWithoutArray(); + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..d2322cc4fa3 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/some_class.php.inc @@ -0,0 +1,52 @@ +someRepository = $someRepository; + } + + public function getAll(): array + { + return $this->someRepository->findAll(); + } +} + +?> +----- +someRepository = $someRepository; + } + + /** + * @return \Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeEntity[] + */ + public function getAll(): array + { + return $this->someRepository->findAll(); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Source/SomeEntity.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Source/SomeEntity.php new file mode 100644 index 00000000000..175c7266b1f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Source/SomeEntity.php @@ -0,0 +1,8 @@ +withRules([AddReturnDocblockFromMethodCallDocblockRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/DocblockGetterReturnArrayFromPropertyDocblockVarRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/DocblockGetterReturnArrayFromPropertyDocblockVarRectorTest.php new file mode 100644 index 00000000000..84bc7fbeb43 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/DocblockGetterReturnArrayFromPropertyDocblockVarRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/add_prefix_backslash.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/add_prefix_backslash.php.inc new file mode 100644 index 00000000000..9df08812ee7 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/add_prefix_backslash.php.inc @@ -0,0 +1,54 @@ +>> */ + protected array $duplicateNames, + ) { + parent::__construct( + 'Some solutions have duplicate names.', + ); + } + + public function getDuplicateNames(): array + { + return $this->duplicateNames; + } +} + +?> +----- +>> */ + protected array $duplicateNames, + ) { + parent::__construct( + 'Some solutions have duplicate names.', + ); + } + + /** + * @return array>> + */ + public function getDuplicateNames(): array + { + return $this->duplicateNames; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/override_dummy_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/override_dummy_array.php.inc new file mode 100644 index 00000000000..9e95c3cb443 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/override_dummy_array.php.inc @@ -0,0 +1,43 @@ +names; + } +} + +?> +----- +names; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/respect_int_string_key.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/respect_int_string_key.php.inc new file mode 100644 index 00000000000..f647df2a06c --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/respect_int_string_key.php.inc @@ -0,0 +1,43 @@ + + */ + private array $names = []; + + /** + * @return array + */ + public function getNames(): array + { + return $this->names; + } +} + +?> +----- + + */ + private array $names = []; + + /** + * @return array + */ + public function getNames(): array + { + return $this->names; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_missing_return_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_missing_return_type.php.inc new file mode 100644 index 00000000000..12328162b48 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_missing_return_type.php.inc @@ -0,0 +1,16 @@ +names; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_mixed_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_mixed_array.php.inc new file mode 100644 index 00000000000..4ac330b77e3 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_mixed_array.php.inc @@ -0,0 +1,13 @@ +names; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_nullable_mixed_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_nullable_mixed_array.php.inc new file mode 100644 index 00000000000..f09a68b5d48 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/skip_nullable_mixed_array.php.inc @@ -0,0 +1,13 @@ +names; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/some_property_with_array_docblock.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/some_property_with_array_docblock.php.inc new file mode 100644 index 00000000000..8fdccb7bd3a --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/some_property_with_array_docblock.php.inc @@ -0,0 +1,40 @@ +names; + } +} + +?> +----- +names; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/config/configured_rule.php new file mode 100644 index 00000000000..b852f0fdb07 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([DocblockGetterReturnArrayFromPropertyDocblockVarRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/DocblockReturnArrayFromDirectArrayInstanceRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/DocblockReturnArrayFromDirectArrayInstanceRectorTest.php new file mode 100644 index 00000000000..fba532f8546 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/DocblockReturnArrayFromDirectArrayInstanceRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/avoid_extreme_nesting.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/avoid_extreme_nesting.php.inc new file mode 100644 index 00000000000..e44bfce0de7 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/avoid_extreme_nesting.php.inc @@ -0,0 +1,54 @@ + [ + 'data' => [ + 'data' => [ + 'data' => [ + 'data' => [ + 'name' => 'John', + ], + ], + ], + ], + ], + ]; + } +} + +?> +----- +>>> + */ + public function run(): array + { + return [ + 'data' => [ + 'data' => [ + 'data' => [ + 'data' => [ + 'data' => [ + 'name' => 'John', + ], + ], + ], + ], + ], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/complex_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/complex_array.php.inc new file mode 100644 index 00000000000..e294c99e80d --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/complex_array.php.inc @@ -0,0 +1,64 @@ + false, + 'context' => [ + 'id' => 1, + 'data' => [ + 'foo' => 'bar', + 'baz' => 'qux', + ], + ], + 'values' => [ + 'name' => '111', + 'age' => 1, + ], + 'values_other' => [ + 'name' => '111', + 'age' => 1, + ], + ]; + } +} + +?> +----- +|int>|array|bool> + */ + public function run(): array + { + return [ + 'other' => false, + 'context' => [ + 'id' => 1, + 'data' => [ + 'foo' => 'bar', + 'baz' => 'qux', + ], + ], + 'values' => [ + 'name' => '111', + 'age' => 1, + ], + 'values_other' => [ + 'name' => '111', + 'age' => 1, + ], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/not_too_complex.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/not_too_complex.php.inc new file mode 100644 index 00000000000..20f86cb5131 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/not_too_complex.php.inc @@ -0,0 +1,44 @@ + 123, + 'key2' => '456.7', + 'key3' => 89.0, + 'key4' => -42, + 'key5' => '12%', + 'key6' => -9999, + ]; + } +} + +?> +----- + + */ + public function run(): array + { + return [ + 'key1' => 123, + 'key2' => '456.7', + 'key3' => 89.0, + 'key4' => -42, + 'key5' => '12%', + 'key6' => -9999, + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/simple_nesting.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/simple_nesting.php.inc new file mode 100644 index 00000000000..0e2f21da8de --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/simple_nesting.php.inc @@ -0,0 +1,48 @@ + 'string-1', + 'key2' => [ + 'key2a' => null, + 'key2b' => [ + 'key2b1' => 'ABC', + ], + ], + 'key3' => 'string-2', + 'key4' => null, + 'key5' => 'string-3', + ]; + } +} +----- +|null>|string|null> + */ + public static function returnArray(): array + { + return [ + 'key1' => 'string-1', + 'key2' => [ + 'key2a' => null, + 'key2b' => [ + 'key2b1' => 'ABC', + ], + ], + 'key3' => 'string-2', + 'key4' => null, + 'key5' => 'string-3', + ]; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/two_nested_levels.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/two_nested_levels.php.inc new file mode 100644 index 00000000000..4e4ef24eb83 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/two_nested_levels.php.inc @@ -0,0 +1,48 @@ + [ + 'id' => 1, + 'name' => '111', + ], + 'another_key' => [ + 'id' => 1, + 'name' => '111', + ], + ]; + } +} + +?> +----- +> + */ + public function run(): array + { + return [ + 'key' => [ + 'id' => 1, + 'name' => '111', + ], + 'another_key' => [ + 'id' => 1, + 'name' => '111', + ], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/union_nest.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/union_nest.php.inc new file mode 100644 index 00000000000..681dde218a0 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/union_nest.php.inc @@ -0,0 +1,198 @@ + 1111, + 'field2' => 22, + 'field3' => [ + 'field1' => 'value1', + ], + 'field4' => 9, + 'field5' => '50/50', + 'field6' => true, + 'field7' => 'value2', + 'field8' => [ + 0 => [ + 'field1' => [ + 'field1' => 123.4, + 'field2' => 200.0, + 'field3' => 200.0, + 'field4' => 10.0, + 'field5' => 9.0, + 'field6' => 210.0, + 'field7' => 209.0, + 'field8' => 3.0, + 'field9' => 0.0, + 'field10' => 123.4, + ], + 'field2' => [ + 0 => [ + 'field1' => 3.0, + ], + ], + 'field3' => [ + 0 => [ + 'field1' => 0.0, + ], + ], + 'field4' => false, + 'field5' => false, + 'field6' => [ + 0 => [ + 'field1' => 'value3', + ], + ], + 'field7' => [ + 'field1' => 'value4', + ], + 'field8' => [], + 'field9' => [ + 0 => [ + 'field1' => 123.4, + 'field2' => 200.0, + 'field3' => 200.0, + 'field4' => 10.0, + 'field5' => 9.0, + 'field6' => 210.0, + 'field7' => 209.0, + 'field8' => 3.0, + 'field9' => 0.0, + 'field10' => 123.4, + ], + ], + 'field10' => [], + 'field11' => [ + 'field1' => '2000-01-01T00:00:00+0000', + 'field2' => '2000-01-01T00:00:00+0000', + 'field3' => '2000-01-01T00:00:00+0000', + 'field4' => '2000-01-01T00:00:00+0000', + 'field5' => '2000-01-01T00:00:00+0000', + 'field6' => '2000-01-01T00:00:00+0000', + 'field7' => '2000-01-01T00:00:00+0000', + 'field8' => '2000-01-01T00:00:00+0000', + ], + 'field12' => 'value5', + 'field13' => [], + ], + ], + 'field9' => [ + 'value6', + 'value7', + ], + 'field10' => [ + 'value8', + 'value9', + ], + 'field11' => true, + 'field12' => 'value10', + ]; + } +} + +?> +----- + + */ + public function getData(): array + { + return [ + 'field1' => 1111, + 'field2' => 22, + 'field3' => [ + 'field1' => 'value1', + ], + 'field4' => 9, + 'field5' => '50/50', + 'field6' => true, + 'field7' => 'value2', + 'field8' => [ + 0 => [ + 'field1' => [ + 'field1' => 123.4, + 'field2' => 200.0, + 'field3' => 200.0, + 'field4' => 10.0, + 'field5' => 9.0, + 'field6' => 210.0, + 'field7' => 209.0, + 'field8' => 3.0, + 'field9' => 0.0, + 'field10' => 123.4, + ], + 'field2' => [ + 0 => [ + 'field1' => 3.0, + ], + ], + 'field3' => [ + 0 => [ + 'field1' => 0.0, + ], + ], + 'field4' => false, + 'field5' => false, + 'field6' => [ + 0 => [ + 'field1' => 'value3', + ], + ], + 'field7' => [ + 'field1' => 'value4', + ], + 'field8' => [], + 'field9' => [ + 0 => [ + 'field1' => 123.4, + 'field2' => 200.0, + 'field3' => 200.0, + 'field4' => 10.0, + 'field5' => 9.0, + 'field6' => 210.0, + 'field7' => 209.0, + 'field8' => 3.0, + 'field9' => 0.0, + 'field10' => 123.4, + ], + ], + 'field10' => [], + 'field11' => [ + 'field1' => '2000-01-01T00:00:00+0000', + 'field2' => '2000-01-01T00:00:00+0000', + 'field3' => '2000-01-01T00:00:00+0000', + 'field4' => '2000-01-01T00:00:00+0000', + 'field5' => '2000-01-01T00:00:00+0000', + 'field6' => '2000-01-01T00:00:00+0000', + 'field7' => '2000-01-01T00:00:00+0000', + 'field8' => '2000-01-01T00:00:00+0000', + ], + 'field12' => 'value5', + 'field13' => [], + ], + ], + 'field9' => [ + 'value6', + 'value7', + ], + 'field10' => [ + 'value8', + 'value9', + ], + 'field11' => true, + 'field12' => 'value10', + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-implicit-key.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-implicit-key.php.inc new file mode 100644 index 00000000000..d8194433e41 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-implicit-key.php.inc @@ -0,0 +1,48 @@ + 100, + 'key2' => '-25.5%', + ], + [ + 'key3' => 'Yes', + 'key4' => 200, + ], + ]; + } +} + +?> +----- +> + */ + private static function getExpectedAllOwners(): array + { + return [ + [ + 'key1' => 100, + 'key2' => '-25.5%', + ], + [ + 'key3' => 'Yes', + 'key4' => 200, + ], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-object-return.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-object-return.php.inc new file mode 100644 index 00000000000..d632b292705 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-object-return.php.inc @@ -0,0 +1,40 @@ + 100, + 'key2' => $this->item + ]; + } +} + +?> +----- + + */ + public function toArray(): array + { + return [ + 'key1' => 100, + 'key2' => $this->item + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/false_and_true.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/false_and_true.php.inc new file mode 100644 index 00000000000..8db764d7d68 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/false_and_true.php.inc @@ -0,0 +1,34 @@ + [true], + 'second' => [false], + 'third' => [true], + 'fourth' => [false], + ]; +} + +?> +----- + + */ +function FalseAndTrue() +{ + return [ + 'first' => [true], + 'second' => [false], + 'third' => [true], + 'fourth' => [false], + ]; +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/function_return.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/function_return.php.inc new file mode 100644 index 00000000000..a18a5a568fe --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/function_return.php.inc @@ -0,0 +1,28 @@ + 'value', + ]; +} + +?> +----- + + */ +function functionReturn() +{ + return [ + 'key' => 'value', + ]; +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/include_missing_return_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/include_missing_return_array.php.inc new file mode 100644 index 00000000000..078f42a648f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/include_missing_return_array.php.inc @@ -0,0 +1,34 @@ + 'value', + ]; + } +} + +?> +----- + + */ + public function getNames() + { + return [ + 'key' => 'value', + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/override_bare_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/override_bare_array.php.inc new file mode 100644 index 00000000000..4f150b2b07c --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/override_bare_array.php.inc @@ -0,0 +1,33 @@ + [true], + 'second' => [false], + ]; +} + +?> +----- + + */ +function overrideBareArray() +{ + return [ + 'first' => [true], + 'second' => [false], + ]; +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/repeated_item_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/repeated_item_type.php.inc new file mode 100644 index 00000000000..9bc257ac9f9 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/repeated_item_type.php.inc @@ -0,0 +1,66 @@ + $this->partitionName, + 'replica_number' => $this->replicaNumber, + 'timeout' => $this->timeout, + ]; + + $params = array_filter($params); + + return [ + 'collection_name' => $this->collectionName, + 'params' => $params, + ]; + } +} + +?> +----- +> + */ + public function toRequestPayload(): array + { + $params = [ + 'partition_name' => $this->partitionName, + 'replica_number' => $this->replicaNumber, + 'timeout' => $this->timeout, + ]; + + $params = array_filter($params); + + return [ + 'collection_name' => $this->collectionName, + 'params' => $params, + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/return_empty.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/return_empty.php.inc new file mode 100644 index 00000000000..4beb2996db5 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/return_empty.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/single-nested-level.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/single-nested-level.php.inc new file mode 100644 index 00000000000..2fa60947429 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/single-nested-level.php.inc @@ -0,0 +1,38 @@ + [ + 'subkey' => $value + ], + ]; + } +} + +?> +----- +> + */ + public function getNames($value): array + { + return [ + 'key' => [ + 'subkey' => $value + ], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_already_set_docblock.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_already_set_docblock.php.inc new file mode 100644 index 00000000000..cd9fc5cd09a --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_already_set_docblock.php.inc @@ -0,0 +1,16 @@ + + */ + public function getNames(): array + { + return [ + 'key' => 'value', + ]; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_empty_array_mixed.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_empty_array_mixed.php.inc new file mode 100644 index 00000000000..80ef5c874ed --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_empty_array_mixed.php.inc @@ -0,0 +1,14 @@ + 'value', + ]; + + + return $variable; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_multiple_node_types.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_multiple_node_types.php.inc new file mode 100644 index 00000000000..d951ba74925 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/skip_multiple_node_types.php.inc @@ -0,0 +1,25 @@ + 100, + 'key2' => '-25.5%', + ], + ]; + } +} + +?> +----- +> + */ + private static function getValues(): array + { + $list = [1, 2, 3]; + + return [ + [ + 'key1' => 100, + 'key2' => '-25.5%', + ], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/some_property_with_array_docblock.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/some_property_with_array_docblock.php.inc new file mode 100644 index 00000000000..3ca0274f9bf --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/some_property_with_array_docblock.php.inc @@ -0,0 +1,34 @@ + 'value', + ]; + } +} + +?> +----- + + */ + public function getNames(): array + { + return [ + 'key' => 'value', + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/with_duplicated_nested_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/with_duplicated_nested_array.php.inc new file mode 100644 index 00000000000..63242d692e1 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/with_duplicated_nested_array.php.inc @@ -0,0 +1,54 @@ + [false, 1, $testingUrls], + '$cacheQueryString=true' => [true, 5, $testingUrls], + '$cacheQueryString=array' => [['important_parameter'], 3, $testingUrls], + ]; + } +} + +?> +----- + + */ + public static function run(): iterable + { + $testingUrls = [ + 'test', + 'test?important_parameter=1', + ]; + + return [ + '$cacheQueryString=false' => [false, 1, $testingUrls], + '$cacheQueryString=true' => [true, 5, $testingUrls], + '$cacheQueryString=array' => [['important_parameter'], 3, $testingUrls], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Sourde/SomeObjectInDirectArray.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Sourde/SomeObjectInDirectArray.php new file mode 100644 index 00000000000..3ae7c85169c --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Sourde/SomeObjectInDirectArray.php @@ -0,0 +1,9 @@ +withRules([DocblockReturnArrayFromDirectArrayInstanceRector::class]); diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/AddReturnArrayDocblockFromDataProviderParamRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/AddReturnArrayDocblockFromDataProviderParamRectorTest.php new file mode 100644 index 00000000000..9d5579887b2 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/AddReturnArrayDocblockFromDataProviderParamRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/Fixture/return_from_strict_param.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/Fixture/return_from_strict_param.php.inc new file mode 100644 index 00000000000..c31b7cac233 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/Fixture/return_from_strict_param.php.inc @@ -0,0 +1,46 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/Fixture/skip_iterator.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/Fixture/skip_iterator.php.inc new file mode 100644 index 00000000000..89708d48f27 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector/Fixture/skip_iterator.php.inc @@ -0,0 +1,19 @@ +rule(AddReturnArrayDocblockFromDataProviderParamRector::class); +}; diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/AddReturnDocblockDataProviderRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/AddReturnDocblockDataProviderRectorTest.php new file mode 100644 index 00000000000..51ee9981864 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/AddReturnDocblockDataProviderRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/already_return_typed_generator.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/already_return_typed_generator.php.inc new file mode 100644 index 00000000000..647710a4407 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/already_return_typed_generator.php.inc @@ -0,0 +1,48 @@ + +----- +> + */ + public static function provideData(): \Generator + { + yield ['data1', 'data2']; + yield ['item4', 'item5']; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/data_provider_method.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/data_provider_method.php.inc new file mode 100644 index 00000000000..a30db6c0761 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/data_provider_method.php.inc @@ -0,0 +1,52 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/keep_string_type_simple.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/keep_string_type_simple.php.inc new file mode 100644 index 00000000000..934ba63e8ec --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/keep_string_type_simple.php.inc @@ -0,0 +1,54 @@ + +----- +> + */ + public static function provideData(): \Iterator + { + $anotherTypeObjectType = new ObjectType(AnotherType::class); + yield [__DIR__ . '/Fixture/new_class.php.inc', 1, $anotherTypeObjectType]; + yield [__DIR__ . '/Fixture/argument_typehint.php.inc', 1, $anotherTypeObjectType]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/multi_objects.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/multi_objects.php.inc new file mode 100644 index 00000000000..8143dbd0569 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/multi_objects.php.inc @@ -0,0 +1,52 @@ + +----- +>> + */ + public static function provideData(): iterable + { + yield [[new DateTime(), 'format']]; + yield [[new DateTimeImmutable(), 'format']]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/nested_array_is_mixed.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/nested_array_is_mixed.php.inc new file mode 100644 index 00000000000..d2a686dd973 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/nested_array_is_mixed.php.inc @@ -0,0 +1,48 @@ + +----- +>> + */ + public static function provideData(): \Generator + { + yield [[]]; + yield [[1, 2, 3, 4]]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_array_return.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_array_return.php.inc new file mode 100644 index 00000000000..3dbab25317e --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_array_return.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_mixed.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_mixed.php.inc new file mode 100644 index 00000000000..d01d18109d6 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_mixed.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_mixed_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_mixed_array.php.inc new file mode 100644 index 00000000000..f40c5d067d2 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/override_dummy_mixed_array.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/skip_already_set_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/skip_already_set_type.php.inc new file mode 100644 index 00000000000..b20ce59e523 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/skip_already_set_type.php.inc @@ -0,0 +1,37 @@ +> + */ + public static function provideData(): \Generator + { + yield ['data1', 'data2']; + yield ['item4', 'item5']; + } + + #[DataProvider('provideDataNext')] + public function testSomethingElse() + { + } + + /** + * @return \Iterator> + */ + public static function provideDataNext(): \Iterator + { + yield ['data1', 'data2']; + yield ['item4', 'item5']; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/union_same_array_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/union_same_array_type.php.inc new file mode 100644 index 00000000000..68a3c43e204 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/union_same_array_type.php.inc @@ -0,0 +1,60 @@ + +----- +> + */ + public static function provideData(): \Iterator + { + yield ['...', __DIR__ . '/Source/expected/expected.txt']; + yield ["-old\n+new", __DIR__ . '/Source/expected/expected_old_new.txt']; + + yield [ + FileSystem::read('...'), + __DIR__ . '/Fixture/expected_with_full_diff_by_phpunit.diff', + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/with_integers.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/with_integers.php.inc new file mode 100644 index 00000000000..59e2e5f1553 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/with_integers.php.inc @@ -0,0 +1,52 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc new file mode 100644 index 00000000000..63e5009eb56 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc @@ -0,0 +1,48 @@ + +----- +> + */ + public static function provideData() + { + yield ['data1', 'data2']; + yield ['item4', 'item5']; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider_iterable.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider_iterable.php.inc new file mode 100644 index 00000000000..159e6398e97 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider_iterable.php.inc @@ -0,0 +1,66 @@ + [1, 2], + 'two' => ['three'], + ], + ]; + yield [ + [ + 'four' => 'five', + ], + ]; + } +} + +?> +----- +>, mixed>> + */ + public static function provideData() + { + yield [ + [ + 'one' => [1, 2], + 'two' => ['three'], + ], + ]; + yield [ + [ + 'four' => 'five', + ], + ]; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/config/configured_rule.php new file mode 100644 index 00000000000..60e43dbe171 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(AddReturnDocblockDataProviderRector::class); +}; diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/AddVarArrayDocblockFromDimFetchAssignRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/AddVarArrayDocblockFromDimFetchAssignRectorTest.php new file mode 100644 index 00000000000..43915ced242 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/AddVarArrayDocblockFromDimFetchAssignRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/multiple_assigns.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/multiple_assigns.php.inc new file mode 100644 index 00000000000..5f7efb2e448 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/multiple_assigns.php.inc @@ -0,0 +1,44 @@ +items[] = 'item1'; + } + + public function run() + { + $this->items[] = 'item2'; + } +} + +?> +----- +items[] = 'item1'; + } + + public function run() + { + $this->items[] = 'item2'; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/skp_already_known.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/skp_already_known.php.inc new file mode 100644 index 00000000000..a776be789f3 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/skp_already_known.php.inc @@ -0,0 +1,16 @@ + + */ + private array $items = []; + + public function go() + { + $this->items[] = 'item1'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..858aa98f3ff --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/Fixture/some_class.php.inc @@ -0,0 +1,34 @@ +items[] = 'item1'; + } +} + +?> +----- +items[] = 'item1'; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/config/configured_rule.php new file mode 100644 index 00000000000..28ef793994b --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(AddVarArrayDocblockFromDimFetchAssignRector::class); +}; diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/ClassMethodArrayDocblockParamFromLocalCallsRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/ClassMethodArrayDocblockParamFromLocalCallsRectorTest.php new file mode 100644 index 00000000000..ca978805c6b --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/ClassMethodArrayDocblockParamFromLocalCallsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/NamedArgs/flipped_args.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/NamedArgs/flipped_args.php.inc new file mode 100644 index 00000000000..d4f8d672eea --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/NamedArgs/flipped_args.php.inc @@ -0,0 +1,39 @@ +run(names: ['John', 'Doe'], items: [123, 456]); + } + + private function run(array $items, array $names) + { + } +} + +?> +----- +run(names: ['John', 'Doe'], items: [123, 456]); + } + + /** + * @param int[] $items + * @param string[] $names + */ + private function run(array $items, array $names) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/NamedArgs/named_arg.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/NamedArgs/named_arg.php.inc new file mode 100644 index 00000000000..28214e8d5b3 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/NamedArgs/named_arg.php.inc @@ -0,0 +1,38 @@ +run(items: ['item1', 'item2']); + } + + private function run(array $items) + { + } +} + +?> +----- +run(items: ['item1', 'item2']); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/cover_mixed_and_known.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/cover_mixed_and_known.php.inc new file mode 100644 index 00000000000..01833c54588 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/cover_mixed_and_known.php.inc @@ -0,0 +1,42 @@ +run(['item1', 'item2']); + + $this->run([$mixed, $mixed]); + } + + private function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + + $this->run([$mixed, $mixed]); + } + + /** + * @param string[]|mixed[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/empty_array_no_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/empty_array_no_type.php.inc new file mode 100644 index 00000000000..61f9c0acc29 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/empty_array_no_type.php.inc @@ -0,0 +1,40 @@ +run(['item1', 'item2']); + $this->run([]); + } + + private function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + $this->run([]); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/from_explode.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/from_explode.php.inc new file mode 100644 index 00000000000..cd75ac6e747 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/from_explode.php.inc @@ -0,0 +1,38 @@ +run(explode(':', '1:1')); + } + + private function run(array $items) + { + } +} + +?> +----- +run(explode(':', '1:1')); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_non_final_protected.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_non_final_protected.php.inc new file mode 100644 index 00000000000..97c433b0dea --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_non_final_protected.php.inc @@ -0,0 +1,38 @@ +run(['item1', 'item2']); + } + + protected function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + } + + /** + * @param string[] $items + */ + protected function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_non_final_protected_with_parent.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_non_final_protected_with_parent.php.inc new file mode 100644 index 00000000000..8b93297caec --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_non_final_protected_with_parent.php.inc @@ -0,0 +1,42 @@ +run(['item1', 'item2']); + } + + protected function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + } + + /** + * @param string[] $items + */ + protected function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_protected_method_in_final.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_protected_method_in_final.php.inc new file mode 100644 index 00000000000..8c3477ee311 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_protected_method_in_final.php.inc @@ -0,0 +1,42 @@ +run([2512, 3423]); + + $this->run([324, 534]); + } + + protected function run(array $items) + { + } +} + +?> +----- +run([2512, 3423]); + + $this->run([324, 534]); + } + + /** + * @param int[] $items + */ + protected function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_public.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_public.php.inc new file mode 100644 index 00000000000..9976d430394 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/handle_public.php.inc @@ -0,0 +1,44 @@ +run([2512, 3423]); + + $this->run([324, 534]); + } + + public function run(array $items) + { + } +} + +?> + +----- +run([2512, 3423]); + + $this->run([324, 534]); + } + + /** + * @param int[] $items + */ + public function run(array $items) + { + } +} + +?> + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/iterable_param_from_another_method.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/iterable_param_from_another_method.php.inc new file mode 100644 index 00000000000..e1e82377225 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/iterable_param_from_another_method.php.inc @@ -0,0 +1,44 @@ +run($items); + } + + private function run(array $items) + { + } +} + +?> +----- +run($items); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls.php.inc new file mode 100644 index 00000000000..841d8799bf1 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls.php.inc @@ -0,0 +1,42 @@ +run(['item1', 'item2']); + + $this->run(['item1', 'item2']); + } + + private function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + + $this->run(['item1', 'item2']); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls_with_various_types.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls_with_various_types.php.inc new file mode 100644 index 00000000000..7515deb7f05 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls_with_various_types.php.inc @@ -0,0 +1,40 @@ +run(['item1', 'item2']); + $this->run([123, 456]); + } + + private function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + $this->run([123, 456]); + } + + /** + * @param string[]|int[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/not_zero_position.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/not_zero_position.php.inc new file mode 100644 index 00000000000..b246c65ec18 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/not_zero_position.php.inc @@ -0,0 +1,38 @@ +run([], ['item1', 'item2']); + } + + private function run(array $data, array $items) + { + } +} + +?> +----- +run([], ['item1', 'item2']); + } + + /** + * @param string[] $items + */ + private function run(array $data, array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/objects_overrule_empty_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/objects_overrule_empty_array.php.inc new file mode 100644 index 00000000000..bc0fdeeb75e --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/objects_overrule_empty_array.php.inc @@ -0,0 +1,80 @@ +run($this->getItemsWithArray()); + $this->run($this->getItems()); + + $this->run($emptyArray); + } + + public function run(array $result) + { + } + + /** + * @return SomeReturnedObject[]|array + */ + private function getItems() + { + return []; + } + + /** + * @return SomeReturnedObject[] + */ + private function getItemsWithArray() + { + return []; + } +} + +?> +----- +run($this->getItemsWithArray()); + $this->run($this->getItems()); + + $this->run($emptyArray); + } + + /** + * @param \Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\SomeReturnedObject[] $result + */ + public function run(array $result) + { + } + + /** + * @return SomeReturnedObject[]|array + */ + private function getItems() + { + return []; + } + + /** + * @return SomeReturnedObject[] + */ + private function getItemsWithArray() + { + return []; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/override_dummy_array_param.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/override_dummy_array_param.php.inc new file mode 100644 index 00000000000..1ba998f2cbf --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/override_dummy_array_param.php.inc @@ -0,0 +1,41 @@ +run(['item1', 'item2']); + } + + /** + * @param array $items + */ + private function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/remove_nullable.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/remove_nullable.php.inc new file mode 100644 index 00000000000..0fb53569339 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/remove_nullable.php.inc @@ -0,0 +1,54 @@ +run($this->getNames()); + } + + private function run(array $items) + { + } + + /** + * @return string[]|null + */ + private function getNames(): ?array + { + return ['Jim', 'Rohn']; + } +} + +?> +----- +run($this->getNames()); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } + + /** + * @return string[]|null + */ + private function getNames(): ?array + { + return ['Jim', 'Rohn']; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_empty_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_empty_array.php.inc new file mode 100644 index 00000000000..9dae733d84b --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_empty_array.php.inc @@ -0,0 +1,15 @@ +run([]); + } + + private function run(array $items) + { + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_empty_iterable_value_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_empty_iterable_value_type.php.inc new file mode 100644 index 00000000000..bdfd6bc198b --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_empty_iterable_value_type.php.inc @@ -0,0 +1,18 @@ +someCall($value); + } + + private function someCall(array $value) + { + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed.php.inc new file mode 100644 index 00000000000..fcb42fc552e --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed.php.inc @@ -0,0 +1,15 @@ +run($mixed); + } + + private function run(array $items) + { + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed_array_from_getter.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed_array_from_getter.php.inc new file mode 100644 index 00000000000..c0b64f17bb0 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed_array_from_getter.php.inc @@ -0,0 +1,19 @@ +run($this->getter()); + } + + private function run(array $p) + { + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed_from_empty_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed_from_empty_array.php.inc new file mode 100644 index 00000000000..9be7dcde250 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_mixed_from_empty_array.php.inc @@ -0,0 +1,16 @@ +run($data); + } + + private function run(array $data) + { + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_non_array_param.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_non_array_param.php.inc new file mode 100644 index 00000000000..faf245c5ccc --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_non_array_param.php.inc @@ -0,0 +1,15 @@ +run(['item1', 'item2']); + } + + private function run($items) + { + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_override_dummy_array_mixed_on_public.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_override_dummy_array_mixed_on_public.php.inc new file mode 100644 index 00000000000..f24521e0c00 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_override_dummy_array_mixed_on_public.php.inc @@ -0,0 +1,22 @@ +run([2512, 3423]); + + $this->run([324, 534]); + } + + /** + * @param mixed[] $items + */ + public function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_param_array_override.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_param_array_override.php.inc new file mode 100644 index 00000000000..2748a6dc9c6 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_param_array_override.php.inc @@ -0,0 +1,18 @@ +run(['item1', 'item2']); + } + + /** + * @param int[]|string[] $items + */ + private function run(array $items) + { + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_union_mixed.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_union_mixed.php.inc new file mode 100644 index 00000000000..75783105856 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_union_mixed.php.inc @@ -0,0 +1,33 @@ +compareArrays($orig, $new); + } + + private function compareArrays(array $array1, array $array2): void + { + foreach ($array1 as $key => $value1) { + $value2 = $array2[$key]; + + if (is_array($value1)) { + $this->compareArrays($array1[$key], $array2[$key]); + return; + } + + if ($value1 !== $value2) { + $this->diff[$key] = [ + 'existing' => $value1, + 'new' => $value2, + ]; + } + } + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_union_mixed_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_union_mixed_array.php.inc new file mode 100644 index 00000000000..4218fc66e20 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_union_mixed_array.php.inc @@ -0,0 +1,15 @@ +exec($_SERVER['argv'] ?? []); + } + + private function exec(array $argv) + { + } +} \ No newline at end of file diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_with_first_class_callable.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_with_first_class_callable.php.inc new file mode 100644 index 00000000000..7262b98e869 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_with_first_class_callable.php.inc @@ -0,0 +1,17 @@ +run(['item1', 'item2']); + + $this->run(...)([1, 2]); + } + + private function run(array $items) + { + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..41d2eeed4bc --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/some_class.php.inc @@ -0,0 +1,38 @@ +run(['item1', 'item2']); + } + + private function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/union_objects.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/union_objects.php.inc new file mode 100644 index 00000000000..b2f8dd0b917 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/union_objects.php.inc @@ -0,0 +1,60 @@ +run($this->getUnion()); + } + + private function run(array $items) + { + } + + /** + * @return SomeReturnedObject[]|AnotherReturnedObject[] + */ + private function getUnion(): array + { + return []; + } +} + +?> +----- +run($this->getUnion()); + } + + /** + * @param \Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\AnotherReturnedObject[]|\Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\SomeReturnedObject[] $items + */ + private function run(array $items) + { + } + + /** + * @return SomeReturnedObject[]|AnotherReturnedObject[] + */ + private function getUnion(): array + { + return []; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/unique_types.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/unique_types.php.inc new file mode 100644 index 00000000000..8db2d62a7db --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/unique_types.php.inc @@ -0,0 +1,42 @@ +run(['item1', 'item2']); + $this->run([1, 2]); + $this->run(['item3', 'item4']); + } + + private function run(array $items) + { + } +} + +?> +----- +run(['item1', 'item2']); + $this->run([1, 2]); + $this->run(['item3', 'item4']); + } + + /** + * @param string[]|int[] $items + */ + private function run(array $items) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Source/AnotherReturnedObject.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Source/AnotherReturnedObject.php new file mode 100644 index 00000000000..2ada9dccb16 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Source/AnotherReturnedObject.php @@ -0,0 +1,8 @@ +rule(ClassMethodArrayDocblockParamFromLocalCallsRector::class); +}; diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/DocblockVarArrayFromGetterReturnRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/DocblockVarArrayFromGetterReturnRectorTest.php new file mode 100644 index 00000000000..561e9534cf5 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/DocblockVarArrayFromGetterReturnRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/override_dummy_array_var.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/override_dummy_array_var.php.inc new file mode 100644 index 00000000000..43ef36fd959 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/override_dummy_array_var.php.inc @@ -0,0 +1,43 @@ +names; + } +} + +?> +----- +names; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/skip_missing_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/skip_missing_array.php.inc new file mode 100644 index 00000000000..ee526455ecd --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/skip_missing_array.php.inc @@ -0,0 +1,16 @@ +names; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/skip_missing_return_on_getter.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/skip_missing_return_on_getter.php.inc new file mode 100644 index 00000000000..21008eba801 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/skip_missing_return_on_getter.php.inc @@ -0,0 +1,13 @@ +names; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..9f642d49ea0 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/Fixture/some_class.php.inc @@ -0,0 +1,40 @@ +names; + } +} + +?> +----- +names; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/config/configured_rule.php new file mode 100644 index 00000000000..42e44ef285a --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(DocblockVarArrayFromGetterReturnRector::class); +}; diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/DocblockVarArrayFromPropertyDefaultsRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/DocblockVarArrayFromPropertyDefaultsRectorTest.php new file mode 100644 index 00000000000..f4f8f116aa2 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/DocblockVarArrayFromPropertyDefaultsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc new file mode 100644 index 00000000000..6eb037a11d9 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc @@ -0,0 +1,36 @@ + +----- +|class-string<\DateTime>> + */ + protected array $dependencies = [ + stdClass::class, + DateTime::class, + ]; +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/int_key_inside.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/int_key_inside.php.inc new file mode 100644 index 00000000000..5c16a52841d --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/int_key_inside.php.inc @@ -0,0 +1,38 @@ + [ + 'b' => [ + ['c' => 'e', 'f' => 1], + ['d' => 2], + ], + ], + ]; +} + +?> +----- +>>> + */ + private array $array = [ + 'a' => [ + 'b' => [ + ['c' => 'e', 'f' => 1], + ['d' => 2], + ], + ], + ]; +} + +?> \ No newline at end of file diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/override_dummy_array_var.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/override_dummy_array_var.php.inc new file mode 100644 index 00000000000..d104b54673a --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/override_dummy_array_var.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_default_empty.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_default_empty.php.inc new file mode 100644 index 00000000000..fdd457e4e51 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_default_empty.php.inc @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_multiple_properties.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_multiple_properties.php.inc new file mode 100644 index 00000000000..94b45d2b2b9 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_multiple_properties.php.inc @@ -0,0 +1,8 @@ + +----- + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/config/configured_rule.php new file mode 100644 index 00000000000..e7761d2ecc9 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(DocblockVarArrayFromPropertyDefaultsRector::class); +}; diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/DocblockVarFromParamDocblockInConstructorRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/DocblockVarFromParamDocblockInConstructorRectorTest.php new file mode 100644 index 00000000000..85f501cba87 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/DocblockVarFromParamDocblockInConstructorRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/override_dummy_array_var.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/override_dummy_array_var.php.inc new file mode 100644 index 00000000000..d460b2d41b8 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/override_dummy_array_var.php.inc @@ -0,0 +1,43 @@ +names = $names; + } +} + +?> +----- +names = $names; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/skip_if_array_type_missing.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/skip_if_array_type_missing.php.inc new file mode 100644 index 00000000000..037e1a0f85e --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/skip_if_array_type_missing.php.inc @@ -0,0 +1,16 @@ +names = $names; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..d83aae40a4d --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/Fixture/some_class.php.inc @@ -0,0 +1,40 @@ +names = $names; + } +} + +?> +----- +names = $names; + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/config/configured_rule.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/config/configured_rule.php new file mode 100644 index 00000000000..65f0f0af931 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(DocblockVarFromParamDocblockInConstructorRector::class); +}; diff --git a/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/different_set.php.inc b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/different_set.php.inc new file mode 100644 index 00000000000..14dba59ab3a --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/different_set.php.inc @@ -0,0 +1,33 @@ +name = 100; + + return $this; + } +} + +?> +----- +name = 100; + } +} + +?> diff --git a/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/fixture.php.inc b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..3e32a9f42da --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/fixture.php.inc @@ -0,0 +1,33 @@ +nameValue = $name; + + return $this; + } +} + +?> +----- +nameValue = $name; + } +} + +?> diff --git a/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/skip_different_return.php.inc b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/skip_different_return.php.inc new file mode 100644 index 00000000000..ab9f475a845 --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/skip_different_return.php.inc @@ -0,0 +1,14 @@ +name = $name; + return $name; + } +} diff --git a/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/skip_no_param.php.inc b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/skip_no_param.php.inc new file mode 100644 index 00000000000..282656ae823 --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/Fixture/skip_no_param.php.inc @@ -0,0 +1,15 @@ +name = 'name'; + + return $this; + } +} diff --git a/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/RemoveReturnThisFromSetterClassMethodRectorTest.php b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/RemoveReturnThisFromSetterClassMethodRectorTest.php new file mode 100644 index 00000000000..8942233ac14 --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/RemoveReturnThisFromSetterClassMethodRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/config/configured_rule.php b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/config/configured_rule.php new file mode 100644 index 00000000000..4d26a96f805 --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveReturnThisFromSetterClassMethodRector::class]); diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/fixture.php.inc b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..46e12a13933 --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/fixture.php.inc @@ -0,0 +1,36 @@ +setName('John') + ->setSurname('Doe'); + } +} + +?> +----- +setName('John'); + $someSetterClass->setSurname('Doe'); + return $someSetterClass; + } +} + +?> diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_getter_setter.php.inc b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_getter_setter.php.inc new file mode 100644 index 00000000000..8ee1f312eed --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_getter_setter.php.inc @@ -0,0 +1,18 @@ +getter() + ->setName('John') + ->setSurname('Doe'); + } +} diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_mocks.php.inc b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_mocks.php.inc new file mode 100644 index 00000000000..7832cdff7a3 --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_mocks.php.inc @@ -0,0 +1,16 @@ +createMock(SomeSetterClass::class) + ->expects($this->once()) + ->method('some'); + } +} diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_mocks_on_variable.php.inc b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_mocks_on_variable.php.inc new file mode 100644 index 00000000000..991c9fc08f2 --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_mocks_on_variable.php.inc @@ -0,0 +1,17 @@ +createMock(SomeSetterClass::class); + $someVariable->expects($this->once()) + ->method('some') + ->with(1); + } +} diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_return_different_object.php.inc b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_return_different_object.php.inc new file mode 100644 index 00000000000..e35c779ec74 --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_return_different_object.php.inc @@ -0,0 +1,15 @@ +setName('John') + ->setSurname('Doe'); + } +} diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_sole_setter.php.inc b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_sole_setter.php.inc new file mode 100644 index 00000000000..9a8d204d64e --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Fixture/skip_sole_setter.php.inc @@ -0,0 +1,14 @@ +setName('John'); + } +} diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/FluentSettersToStandaloneCallMethodRectorTest.php b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/FluentSettersToStandaloneCallMethodRectorTest.php new file mode 100644 index 00000000000..0a20edee79e --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/FluentSettersToStandaloneCallMethodRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Source/GetterSetterClass.php b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Source/GetterSetterClass.php new file mode 100644 index 00000000000..0ca827146dc --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Source/GetterSetterClass.php @@ -0,0 +1,11 @@ +name = $name; + return $this; + } + + public function setSurname(?string $surname): self + { + $this->surname = $surname; + return $this; + } +} diff --git a/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Source/SurnameCaller.php b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Source/SurnameCaller.php new file mode 100644 index 00000000000..426cf94f53b --- /dev/null +++ b/rules-tests/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector/Source/SurnameCaller.php @@ -0,0 +1,11 @@ +withRules([FluentSettersToStandaloneCallMethodRector::class]); diff --git a/rules-tests/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector/ChangeConstantVisibilityRectorTest.php b/rules-tests/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector/ChangeConstantVisibilityRectorTest.php index c76421a1e29..07572abc846 100644 --- a/rules-tests/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector/ChangeConstantVisibilityRectorTest.php +++ b/rules-tests/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector/ChangeConstantVisibilityRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ChangeConstantVisibilityRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector/config/configured_rule.php b/rules-tests/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector/config/configured_rule.php index 299c33b4dc6..77eb30a271d 100644 --- a/rules-tests/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector/config/configured_rule.php +++ b/rules-tests/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector/config/configured_rule.php @@ -2,30 +2,22 @@ declare(strict_types=1); -use Rector\Core\ValueObject\Visibility; +use Rector\Config\RectorConfig; use Rector\Tests\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector\Source\ParentObject; +use Rector\ValueObject\Visibility; use Rector\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector; use Rector\Visibility\ValueObject\ChangeConstantVisibility; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ChangeConstantVisibilityRector::class) - ->call('configure', [[ - ChangeConstantVisibilityRector::CLASS_CONSTANT_VISIBILITY_CHANGES => ValueObjectInliner::inline([ - new ChangeConstantVisibility(ParentObject::class, 'TO_BE_PUBLIC_CONSTANT', Visibility::PUBLIC), - new ChangeConstantVisibility( - ParentObject::class, - 'TO_BE_PROTECTED_CONSTANT', - Visibility::PROTECTED - ), - new ChangeConstantVisibility(ParentObject::class, 'TO_BE_PRIVATE_CONSTANT', Visibility::PRIVATE), - new ChangeConstantVisibility( - 'Rector\Tests\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector\Fixture\Fixture2', - 'TO_BE_PRIVATE_CONSTANT', - Visibility::PRIVATE - ), - ]), - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(ChangeConstantVisibilityRector::class, [ + new ChangeConstantVisibility(ParentObject::class, 'TO_BE_PUBLIC_CONSTANT', Visibility::PUBLIC), + new ChangeConstantVisibility(ParentObject::class, 'TO_BE_PROTECTED_CONSTANT', Visibility::PROTECTED), + new ChangeConstantVisibility(ParentObject::class, 'TO_BE_PRIVATE_CONSTANT', Visibility::PRIVATE), + new ChangeConstantVisibility( + 'Rector\Tests\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector\Fixture\Fixture2', + 'TO_BE_PRIVATE_CONSTANT', + Visibility::PRIVATE + ), + ]); }; diff --git a/rules-tests/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector/ChangeMethodVisibilityRectorTest.php b/rules-tests/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector/ChangeMethodVisibilityRectorTest.php index 97c4cfa84c0..8eb0cd90401 100644 --- a/rules-tests/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector/ChangeMethodVisibilityRectorTest.php +++ b/rules-tests/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector/ChangeMethodVisibilityRectorTest.php @@ -5,25 +5,20 @@ namespace Rector\Tests\Visibility\Rector\ClassMethod\ChangeMethodVisibilityRector; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class ChangeMethodVisibilityRectorTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/rules-tests/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector/Fixture/fixture.php.inc b/rules-tests/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector/Fixture/fixture.php.inc index 77fcbe2e4a3..34a8ba19510 100644 --- a/rules-tests/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector/Fixture/fixture.php.inc +++ b/rules-tests/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector/Fixture/fixture.php.inc @@ -1,5 +1,7 @@ services(); - $services->set(ChangeMethodVisibilityRector::class) - ->call('configure', [[ - ChangeMethodVisibilityRector::METHOD_VISIBILITIES => ValueObjectInliner::inline([ +return static function (RectorConfig $rectorConfig): void { + $rectorConfig + ->ruleWithConfiguration(ChangeMethodVisibilityRector::class, [ - new ChangeMethodVisibility(ParentObject::class, 'toBePublicMethod', Visibility::PUBLIC), - new ChangeMethodVisibility(ParentObject::class, 'toBeProtectedMethod', Visibility::PROTECTED), - new ChangeMethodVisibility(ParentObject::class, 'toBePrivateMethod', Visibility::PRIVATE), - new ChangeMethodVisibility(ParentObject::class, 'toBePublicStaticMethod', Visibility::PUBLIC), + new ChangeMethodVisibility(ParentObject::class, 'toBePublicMethod', Visibility::PUBLIC), + new ChangeMethodVisibility(ParentObject::class, 'toBeProtectedMethod', Visibility::PROTECTED), + new ChangeMethodVisibility(ParentObject::class, 'toBePrivateMethod', Visibility::PRIVATE), + new ChangeMethodVisibility(ParentObject::class, 'toBePublicStaticMethod', Visibility::PUBLIC), - ]), - ]]); + ]); }; diff --git a/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/ExplicitPublicClassMethodRectorTest.php b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/ExplicitPublicClassMethodRectorTest.php new file mode 100644 index 00000000000..1a814e97d9a --- /dev/null +++ b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/ExplicitPublicClassMethodRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture.php.inc b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..923a033c38c --- /dev/null +++ b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture.php.inc @@ -0,0 +1,67 @@ + +----- + diff --git a/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture_abstract.php.inc b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture_abstract.php.inc new file mode 100644 index 00000000000..6288dc2a0b8 --- /dev/null +++ b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture_abstract.php.inc @@ -0,0 +1,65 @@ + +----- + diff --git a/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture_final.php.inc b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture_final.php.inc new file mode 100644 index 00000000000..fae01488673 --- /dev/null +++ b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/Fixture/fixture_final.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/config/configured_rule.php b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/config/configured_rule.php new file mode 100644 index 00000000000..a00b4da3235 --- /dev/null +++ b/rules-tests/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ExplicitPublicClassMethodRector::class]); diff --git a/rules/Arguments/ArgumentDefaultValueReplacer.php b/rules/Arguments/ArgumentDefaultValueReplacer.php index 037ae7532c8..d40d444db4b 100644 --- a/rules/Arguments/ArgumentDefaultValueReplacer.php +++ b/rules/Arguments/ArgumentDefaultValueReplacer.php @@ -5,102 +5,233 @@ namespace Rector\Arguments; use PhpParser\BuilderHelpers; -use PhpParser\Node; use PhpParser\Node\Arg; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; +use PhpParser\Node\Name; use PhpParser\Node\Stmt\ClassMethod; use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface; -use Rector\Core\PhpParser\Node\NodeFactory; -use Rector\Core\PhpParser\Node\Value\ValueResolver; +use Rector\Arguments\ValueObject\ReplaceArgumentDefaultValue; +use Rector\NodeAnalyzer\ArgsAnalyzer; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\AstResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; -final class ArgumentDefaultValueReplacer +final readonly class ArgumentDefaultValueReplacer { public function __construct( private NodeFactory $nodeFactory, - private ValueResolver $valueResolver + private ValueResolver $valueResolver, + private ArgsAnalyzer $argsAnalyzer, + private AstResolver $astResolver, + private NodeTypeResolver $nodeTypeResolver ) { } + /** + * @template TCall as (MethodCall|StaticCall|ClassMethod|FuncCall|New_) + * + * @param TCall $node + * @return TCall|null + */ public function processReplaces( - MethodCall | StaticCall | ClassMethod | FuncCall $node, + MethodCall | StaticCall | ClassMethod | FuncCall | New_ $node, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue - ): ?Node { + ): MethodCall | StaticCall | ClassMethod | FuncCall | New_ |null { if ($node instanceof ClassMethod) { if (! isset($node->params[$replaceArgumentDefaultValue->getPosition()])) { return null; } - } elseif (isset($node->args[$replaceArgumentDefaultValue->getPosition()])) { - $this->processArgs($node, $replaceArgumentDefaultValue); + + return $this->processParams($node, $replaceArgumentDefaultValue); + } + + return $this->processArgs($node, $replaceArgumentDefaultValue); + } + + private function isDefaultValueMatched(?Expr $expr, mixed $value): bool + { + // allow any values before, also allow param without default value + if ($value === ReplaceArgumentDefaultValue::ANY_VALUE_BEFORE) { + return true; + } + + if (! $expr instanceof Expr) { + return false; + } + + if ($this->valueResolver->isValue($expr, $value)) { + return true; + } + + // ValueResolver::isValue returns false when default value is `null` + return $value === null && $this->valueResolver->isNull($expr); + } + + private function processParams( + ClassMethod $classMethod, + ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue + ): ?ClassMethod { + $position = $replaceArgumentDefaultValue->getPosition(); + + if (! $this->isDefaultValueMatched( + $classMethod->params[$position]->default, + $replaceArgumentDefaultValue->getValueBefore() + )) { + return null; } - return $node; + $classMethod->params[$position]->default = $this->normalizeValue($replaceArgumentDefaultValue->getValueAfter()); + return $classMethod; } + /** + * @template TCall as (MethodCall|StaticCall|FuncCall|New_) + * + * @param TCall $expr + * @return TCall|null + */ private function processArgs( - MethodCall | StaticCall | FuncCall $expr, + MethodCall | StaticCall | FuncCall | New_ $expr, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue - ): void { + ): ?Expr { + if ($expr->isFirstClassCallable()) { + return null; + } + $position = $replaceArgumentDefaultValue->getPosition(); + $arguments = $expr->getArgs(); + $firstNamedArgPosition = $this->argsAnalyzer->resolveFirstNamedArgPosition($arguments); + // if the call has named argyments and we want to replace an array of values + // we cannot replace it as we cannot really match this array of values to + // the existing arguments, it would be too complex + if ($firstNamedArgPosition !== null && is_array($replaceArgumentDefaultValue->getValueBefore())) { + return null; + } - $argValue = $this->valueResolver->getValue($expr->args[$position]->value); + // if the call has named arguments and the argument that we want to replace is not + // before any named argument, we need to check if it is in the list of named arguments + // if it is, we use the position of the named argyment as the position to replace + // if it is not, we cannot replace it + if ($firstNamedArgPosition !== null && $position >= $firstNamedArgPosition) { + $call = $this->astResolver->resolveClassMethodOrFunctionFromCall($expr); + if ($call === null) { + return null; + } + $paramName = null; + $variable = $call->params[$position]->var; + if ($variable instanceof Variable) { + $paramName = $variable->name; + } + + $newPosition = -1; + if (is_string($paramName)) { + $newPosition = $this->argsAnalyzer->resolveArgPosition($arguments, $paramName, $newPosition); + } + + if ($newPosition === -1) { + return null; + } + + $position = $newPosition; + } + + if (! isset($arguments[$position])) { + return null; + } + + $particularArg = $arguments[$position] ?? null; + if (! $particularArg instanceof Arg) { + return null; + } + + $argValue = $this->valueResolver->getValue($particularArg->value); if (is_scalar( $replaceArgumentDefaultValue->getValueBefore() ) && $argValue === $replaceArgumentDefaultValue->getValueBefore()) { - $expr->args[$position] = $this->normalizeValueToArgument($replaceArgumentDefaultValue->getValueAfter()); - } elseif (is_array($replaceArgumentDefaultValue->getValueBefore())) { - $newArgs = $this->processArrayReplacement($expr->args, $replaceArgumentDefaultValue); + $normalizedValueAfter = $this->normalizeValue($replaceArgumentDefaultValue->getValueAfter()); + if ($particularArg->value instanceof ClassConstFetch + && $particularArg->value->class instanceof Name + && $particularArg->value->class->isSpecialClassName() + && $normalizedValueAfter instanceof ClassConstFetch + && is_string($replaceArgumentDefaultValue->getValueAfter()) + && str_contains($replaceArgumentDefaultValue->getValueAfter(), '::')) { + [$targetClass, $targetConstant] = explode('::', $replaceArgumentDefaultValue->getValueAfter()); + $type = $this->nodeTypeResolver->getType($particularArg->value->class); + if ($type instanceof FullyQualifiedObjectType + && $type->getClassName() === $targetClass + && $particularArg->value->name instanceof Identifier + && $particularArg->value->name->toString() === $targetConstant) { + return null; + } + } + + $particularArg->value = $normalizedValueAfter; + return $expr; + } - if ($newArgs) { + if (is_array($replaceArgumentDefaultValue->getValueBefore())) { + $newArgs = $this->processArrayReplacement($expr->getArgs(), $replaceArgumentDefaultValue); + if (is_array($newArgs)) { $expr->args = $newArgs; + return $expr; } } + + return null; } - /** - * @param mixed $value - */ - private function normalizeValueToArgument($value): Arg + private function normalizeValueToArgument(mixed $value): Arg + { + return new Arg($this->normalizeValue($value)); + } + + private function normalizeValue(mixed $value): ClassConstFetch|Expr { // class constants → turn string to composite if (is_string($value) && \str_contains($value, '::')) { [$class, $constant] = explode('::', $value); - $classConstFetch = $this->nodeFactory->createClassConstFetch($class, $constant); - - return new Arg($classConstFetch); + return $this->nodeFactory->createClassConstFetch($class, $constant); } - return new Arg(BuilderHelpers::normalizeValue($value)); + return BuilderHelpers::normalizeValue($value); } /** - * @param Arg[] $argumentNodes - * @return Arg[]|null + * @param array $args + * @return array|null */ private function processArrayReplacement( - array $argumentNodes, + array $args, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue ): ?array { - $argumentValues = $this->resolveArgumentValuesToBeforeRecipe($argumentNodes, $replaceArgumentDefaultValue); + $argumentValues = $this->resolveArgumentValuesToBeforeRecipe($args, $replaceArgumentDefaultValue); if ($argumentValues !== $replaceArgumentDefaultValue->getValueBefore()) { return null; } if (is_string($replaceArgumentDefaultValue->getValueAfter())) { - $argumentNodes[$replaceArgumentDefaultValue->getPosition()] = $this->normalizeValueToArgument( + $args[$replaceArgumentDefaultValue->getPosition()] = $this->normalizeValueToArgument( $replaceArgumentDefaultValue->getValueAfter() ); // clear following arguments $argumentCountToClear = count($replaceArgumentDefaultValue->getValueBefore()); for ($i = $replaceArgumentDefaultValue->getPosition() + 1; $i <= $replaceArgumentDefaultValue->getPosition() + $argumentCountToClear; ++$i) { - unset($argumentNodes[$i]); + unset($args[$i]); } } - return $argumentNodes; + return $args; } /** @@ -113,8 +244,11 @@ private function resolveArgumentValuesToBeforeRecipe( ): array { $argumentValues = []; - /** @var mixed[] $valueBefore */ $valueBefore = $replaceArgumentDefaultValue->getValueBefore(); + if (! is_array($valueBefore)) { + return []; + } + $beforeArgumentCount = count($valueBefore); for ($i = 0; $i < $beforeArgumentCount; ++$i) { diff --git a/rules/Arguments/Contract/ReplaceArgumentDefaultValueInterface.php b/rules/Arguments/Contract/ReplaceArgumentDefaultValueInterface.php index 18680632a42..14f76fbe1a9 100644 --- a/rules/Arguments/Contract/ReplaceArgumentDefaultValueInterface.php +++ b/rules/Arguments/Contract/ReplaceArgumentDefaultValueInterface.php @@ -8,13 +8,7 @@ interface ReplaceArgumentDefaultValueInterface { public function getPosition(): int; - /** - * @return mixed - */ - public function getValueBefore(); + public function getValueBefore(): mixed; - /** - * @return mixed - */ - public function getValueAfter(); + public function getValueAfter(): mixed; } diff --git a/rules/Arguments/NodeAnalyzer/ArgumentAddingScope.php b/rules/Arguments/NodeAnalyzer/ArgumentAddingScope.php index be64ebbd82a..a24edef7aa5 100644 --- a/rules/Arguments/NodeAnalyzer/ArgumentAddingScope.php +++ b/rules/Arguments/NodeAnalyzer/ArgumentAddingScope.php @@ -8,32 +8,36 @@ use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name; use Rector\Arguments\ValueObject\ArgumentAdder; +use Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue; +use Rector\Enum\ObjectReference; use Rector\NodeNameResolver\NodeNameResolver; -final class ArgumentAddingScope +final readonly class ArgumentAddingScope { /** - * @var string + * @api */ - public const SCOPE_PARENT_CALL = 'parent_call'; + public const string SCOPE_PARENT_CALL = 'parent_call'; /** - * @var string + * @api */ - public const SCOPE_METHOD_CALL = 'method_call'; + public const string SCOPE_METHOD_CALL = 'method_call'; /** - * @var string + * @api */ - public const SCOPE_CLASS_METHOD = 'class_method'; + public const string SCOPE_CLASS_METHOD = 'class_method'; public function __construct( private NodeNameResolver $nodeNameResolver ) { } - public function isInCorrectScope(MethodCall | StaticCall $expr, ArgumentAdder $argumentAdder): bool - { + public function isInCorrectScope( + MethodCall | StaticCall $expr, + ArgumentAdder|ArgumentAdderWithoutDefaultValue $argumentAdder + ): bool { if ($argumentAdder->getScope() === null) { return true; } @@ -44,7 +48,7 @@ public function isInCorrectScope(MethodCall | StaticCall $expr, ArgumentAdder $a return false; } - if ($this->nodeNameResolver->isName($expr->class, 'parent')) { + if ($this->nodeNameResolver->isName($expr->class, ObjectReference::PARENT)) { return $scope === self::SCOPE_PARENT_CALL; } diff --git a/rules/Arguments/NodeAnalyzer/ChangedArgumentsDetector.php b/rules/Arguments/NodeAnalyzer/ChangedArgumentsDetector.php new file mode 100644 index 00000000000..851757c4155 --- /dev/null +++ b/rules/Arguments/NodeAnalyzer/ChangedArgumentsDetector.php @@ -0,0 +1,47 @@ +default instanceof Expr) { + return false; + } + + return ! $this->valueResolver->isValue($param->default, $value); + } + + public function isTypeChanged(Param $param, ?Type $newType): bool + { + if (! $param->type instanceof Node) { + return false; + } + + if (! $newType instanceof Type) { + return true; + } + + $currentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + + return ! $this->typeComparator->areTypesEqual($currentParamType, $newType); + } +} diff --git a/rules/Arguments/Rector/ClassMethod/ArgumentAdderRector.php b/rules/Arguments/Rector/ClassMethod/ArgumentAdderRector.php index 6550af97f72..1c18f35cf74 100644 --- a/rules/Arguments/Rector/ClassMethod/ArgumentAdderRector.php +++ b/rules/Arguments/Rector/ClassMethod/ArgumentAdderRector.php @@ -7,22 +7,29 @@ use PhpParser\BuilderHelpers; use PhpParser\Node; use PhpParser\Node\Arg; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; use PhpParser\Node\Name; -use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\Type\ObjectType; +use PHPStan\Type\Type; use Rector\Arguments\NodeAnalyzer\ArgumentAddingScope; +use Rector\Arguments\NodeAnalyzer\ChangedArgumentsDetector; use Rector\Arguments\ValueObject\ArgumentAdder; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Enum\ObjectReference; +use Rector\Exception\ShouldNotHappenException; +use Rector\NodeAnalyzer\ArgsAnalyzer; +use Rector\PhpParser\AstResolver; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\StaticTypeMapper\StaticTypeMapper; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; @@ -33,46 +40,31 @@ final class ArgumentAdderRector extends AbstractRector implements ConfigurableRectorInterface { /** - * @var string - */ - public const ADDED_ARGUMENTS = 'added_arguments'; - - /** - * @var ArgumentAdder[] + * @var ArgumentAdder[]|ArgumentAdderWithoutDefaultValue[] */ private array $addedArguments = []; + private bool $hasChanged = false; + public function __construct( - private ArgumentAddingScope $argumentAddingScope + private readonly ArgumentAddingScope $argumentAddingScope, + private readonly ChangedArgumentsDetector $changedArgumentsDetector, + private readonly AstResolver $astResolver, + private readonly StaticTypeMapper $staticTypeMapper, + private readonly ArgsAnalyzer $argsAnalyzer ) { } public function getRuleDefinition(): RuleDefinition { - $exampleConfiguration = [ - self::ADDED_ARGUMENTS => [ - new ArgumentAdder('SomeExampleClass', 'someMethod', 0, 'someArgument', true, 'SomeType'), - ], - ]; - return new RuleDefinition( - 'This Rector adds new default arguments in calls of defined methods and class types.', + 'Add new default arguments in calls of defined methods and class types', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' $someObject = new SomeExampleClass; $someObject->someMethod(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someObject = new SomeExampleClass; -$someObject->someMethod(true); -CODE_SAMPLE - , - $exampleConfiguration - ), - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' + class MyCustomClass extends SomeExampleClass { public function someMethod() @@ -82,6 +74,9 @@ public function someMethod() CODE_SAMPLE , <<<'CODE_SAMPLE' +$someObject = new SomeExampleClass; +$someObject->someMethod(true); + class MyCustomClass extends SomeExampleClass { public function someMethod($value = true) @@ -90,7 +85,16 @@ public function someMethod($value = true) } CODE_SAMPLE , - $exampleConfiguration + [ + new ArgumentAdder( + 'SomeExampleClass', + 'someMethod', + 0, + 'someArgument', + true, + new ObjectType('SomeType') + ), + ] ), ] ); @@ -101,91 +105,132 @@ public function someMethod($value = true) */ public function getNodeTypes(): array { - return [MethodCall::class, StaticCall::class, ClassMethod::class]; + return [MethodCall::class, StaticCall::class, Class_::class]; } /** - * @param MethodCall|StaticCall|ClassMethod $node + * @param MethodCall|StaticCall|Class_ $node */ - public function refactor(Node $node): MethodCall | StaticCall | ClassMethod + public function refactor(Node $node): MethodCall | StaticCall | Class_ | null { - foreach ($this->addedArguments as $addedArgument) { - if (! $this->isObjectTypeMatch($node, $addedArgument->getObjectType())) { - continue; - } + $this->hasChanged = false; - if (! $this->isName($node->name, $addedArgument->getMethod())) { - continue; + if ($node instanceof MethodCall || $node instanceof StaticCall) { + $this->refactorCall($node); + } else { + foreach ($node->getMethods() as $classMethod) { + $this->refactorClassMethod($node, $classMethod); } + } - $this->processPositionWithDefaultValues($node, $addedArgument); + if ($this->hasChanged) { + return $node; } - return $node; + return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $addedArguments = $configuration[self::ADDED_ARGUMENTS] ?? []; - Assert::allIsInstanceOf($addedArguments, ArgumentAdder::class); - $this->addedArguments = $addedArguments; + Assert::allIsAnyOf($configuration, [ArgumentAdder::class, ArgumentAdderWithoutDefaultValue::class]); + $this->addedArguments = $configuration; } - private function isObjectTypeMatch(MethodCall | StaticCall | ClassMethod $node, ObjectType $objectType): bool + private function isObjectTypeMatch(MethodCall | StaticCall $call, ObjectType $objectType): bool { - if ($node instanceof MethodCall) { - return $this->isObjectType($node->var, $objectType); - } - - if ($node instanceof StaticCall) { - return $this->isObjectType($node->class, $objectType); + if ($call instanceof MethodCall) { + return $this->isObjectType($call->var, $objectType); } - // ClassMethod - /** @var Class_|null $classLike */ - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - - // anonymous class - if (! $classLike instanceof Class_) { - return false; - } - - return $this->isObjectType($classLike, $objectType); + return $this->isObjectType($call->class, $objectType); } private function processPositionWithDefaultValues( ClassMethod | MethodCall | StaticCall $node, - ArgumentAdder $argumentAdder + ArgumentAdder|ArgumentAdderWithoutDefaultValue $argumentAdder ): void { if ($this->shouldSkipParameter($node, $argumentAdder)) { return; } - $defaultValue = $argumentAdder->getArgumentDefaultValue(); $argumentType = $argumentAdder->getArgumentType(); $position = $argumentAdder->getPosition(); if ($node instanceof ClassMethod) { - $this->addClassMethodParam($node, $argumentAdder, $defaultValue, $argumentType, $position); - } elseif ($node instanceof StaticCall) { + $this->addClassMethodParam($node, $argumentAdder, $argumentType, $position); + return; + } + + if ($node instanceof StaticCall) { $this->processStaticCall($node, $position, $argumentAdder); + return; + } + + $this->processMethodCall($node, $argumentAdder, $position); + } + + private function processMethodCall( + MethodCall $methodCall, + ArgumentAdder|ArgumentAdderWithoutDefaultValue $argumentAdder, + int $position + ): void { + if ($argumentAdder instanceof ArgumentAdderWithoutDefaultValue) { + return; + } + + $defaultValue = $argumentAdder->getArgumentDefaultValue(); + $arg = new Arg(BuilderHelpers::normalizeValue($defaultValue)); + + // if there are named argyments, we just add it at the end as a new named argument + if ($this->argsAnalyzer->hasNamedArg($methodCall->getArgs())) { + $argumentName = $argumentAdder->getArgumentName(); + if ($argumentName === null) { + throw new ShouldNotHappenException(); + } + + $arg->name = new Identifier($argumentName); } else { - $arg = new Arg(BuilderHelpers::normalizeValue($defaultValue)); - if (isset($node->args[$position])) { - return; + $this->fillGapBetweenWithDefaultValue($methodCall, $position); + } + + $methodCall->args[] = $arg; + + $this->hasChanged = true; + } + + private function fillGapBetweenWithDefaultValue(MethodCall | StaticCall $node, int $position): void + { + $lastPosition = count($node->getArgs()) - 1; + if ($position <= $lastPosition) { + return; + } + + if ($position - $lastPosition === 1) { + return; + } + + $classMethod = $this->astResolver->resolveClassMethodFromCall($node); + if (! $classMethod instanceof ClassMethod) { + return; + } + + for ($index = $lastPosition + 1; $index < $position; ++$index) { + $param = $classMethod->params[$index]; + if (! $param->default instanceof Expr) { + throw new ShouldNotHappenException('Previous position does not have default value'); } - $node->args[$position] = $arg; + $node->args[$index] = new Arg($this->nodeFactory->createReprintedNode($param->default)); } } private function shouldSkipParameter( ClassMethod | MethodCall | StaticCall $node, - ArgumentAdder $argumentAdder + ArgumentAdder|ArgumentAdderWithoutDefaultValue $argumentAdder ): bool { $position = $argumentAdder->getPosition(); $argumentName = $argumentAdder->getArgumentName(); @@ -200,29 +245,73 @@ private function shouldSkipParameter( return false; } - return $this->isName($node->params[$position], $argumentName); + $param = $node->params[$position]; + // argument added and name has been changed + if (! $this->isName($param, $argumentName)) { + return true; + } + + // argument added and default has been changed + if ($this->isDefaultValueChanged($argumentAdder, $node, $position)) { + return true; + } + + // argument added and type has been changed + return $this->changedArgumentsDetector->isTypeChanged($param, $argumentAdder->getArgumentType()); + } + + $arguments = $node->getArgs(); + $firstNamedArgumentPosition = $this->argsAnalyzer->resolveFirstNamedArgPosition($arguments); + // If named arguments exist + if ($firstNamedArgumentPosition !== null) { + // Check if the parameter we're trying to add is before the first named argument + if ($position < $firstNamedArgumentPosition) { + return true; //if that is the case, the parameter already exists, skip + } + + // Check if the parameter we're trying to add is already present as a named argument + if ($this->argsAnalyzer->resolveArgPosition($arguments, $argumentName, -1) !== -1) { + return true; // if it exists as a named argument, skip + } + } elseif (isset($node->args[$position])) { + return true; + } + + // Check if default value is the same + $classMethod = $this->astResolver->resolveClassMethodFromCall($node); + if (! $classMethod instanceof ClassMethod) { + // is correct scope? + return ! $this->argumentAddingScope->isInCorrectScope($node, $argumentAdder); } - // already added? - if (! isset($node->args[$position])) { + if (! isset($classMethod->params[$position])) { // is correct scope? return ! $this->argumentAddingScope->isInCorrectScope($node, $argumentAdder); } - if (! $this->isName($node->args[$position], $argumentName)) { + + if ($this->isDefaultValueChanged($argumentAdder, $classMethod, $position)) { // is correct scope? return ! $this->argumentAddingScope->isInCorrectScope($node, $argumentAdder); } + return true; } - /** - * @param mixed $defaultValue - */ + private function isDefaultValueChanged( + ArgumentAdder|ArgumentAdderWithoutDefaultValue $argumentAdder, + ClassMethod $classMethod, + int $position + ): bool { + return $argumentAdder instanceof ArgumentAdder && $this->changedArgumentsDetector->isDefaultValueChanged( + $classMethod->params[$position], + $argumentAdder->getArgumentDefaultValue() + ); + } + private function addClassMethodParam( ClassMethod $classMethod, - ArgumentAdder $argumentAdder, - $defaultValue, - ?string $type, + ArgumentAdder|ArgumentAdderWithoutDefaultValue $argumentAdder, + ?Type $type, int $position ): void { $argumentName = $argumentAdder->getArgumentName(); @@ -230,16 +319,31 @@ private function addClassMethodParam( throw new ShouldNotHappenException(); } - $param = new Param(new Variable($argumentName), BuilderHelpers::normalizeValue($defaultValue)); - if ($type) { - $param->type = ctype_upper($type[0]) ? new FullyQualified($type) : new Identifier($type); + if ($argumentAdder instanceof ArgumentAdder) { + $param = new Param(new Variable($argumentName), BuilderHelpers::normalizeValue( + $argumentAdder->getArgumentDefaultValue() + )); + } else { + $param = new Param(new Variable($argumentName)); + } + + if ($type instanceof Type) { + $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM); } $classMethod->params[$position] = $param; + $this->hasChanged = true; } - private function processStaticCall(StaticCall $staticCall, int $position, ArgumentAdder $argumentAdder): void - { + private function processStaticCall( + StaticCall $staticCall, + int $position, + ArgumentAdder|ArgumentAdderWithoutDefaultValue $argumentAdder + ): void { + if ($argumentAdder instanceof ArgumentAdderWithoutDefaultValue) { + return; + } + $argumentName = $argumentAdder->getArgumentName(); if ($argumentName === null) { throw new ShouldNotHappenException(); @@ -249,10 +353,58 @@ private function processStaticCall(StaticCall $staticCall, int $position, Argume return; } - if (! $this->isName($staticCall->class, 'parent')) { + if (! $this->isName($staticCall->class, ObjectReference::PARENT)) { + return; + } + + // if there are named arguments, we just add it at the end as a new named argument + $arg = new Arg(new Variable($argumentName)); + if ($this->argsAnalyzer->hasNamedArg($staticCall->getArgs())) { + $arg->name = new Identifier($argumentName); + } else { + $this->fillGapBetweenWithDefaultValue($staticCall, $position); + } + + $staticCall->args[] = $arg; + $this->hasChanged = true; + } + + private function refactorCall(StaticCall|MethodCall $call): void + { + if ($call->isFirstClassCallable()) { + return; + } + + $callName = $this->getName($call->name); + if ($callName === null) { return; } - $staticCall->args[$position] = new Arg(new Variable($argumentName)); + foreach ($this->addedArguments as $addedArgument) { + if (! $this->nodeNameResolver->isStringName($callName, $addedArgument->getMethod())) { + continue; + } + + if (! $this->isObjectTypeMatch($call, $addedArgument->getObjectType())) { + continue; + } + + $this->processPositionWithDefaultValues($call, $addedArgument); + } + } + + private function refactorClassMethod(Class_ $class, ClassMethod $classMethod): void + { + foreach ($this->addedArguments as $addedArgument) { + if (! $this->isName($classMethod, $addedArgument->getMethod())) { + continue; + } + + if (! $this->isObjectType($class, $addedArgument->getObjectType())) { + continue; + } + + $this->processPositionWithDefaultValues($classMethod, $addedArgument); + } } } diff --git a/rules/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector.php b/rules/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector.php index 8f3d5fd043e..89f841e8c26 100644 --- a/rules/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector.php +++ b/rules/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector.php @@ -6,40 +6,38 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\ClassMethod; use Rector\Arguments\ArgumentDefaultValueReplacer; use Rector\Arguments\ValueObject\ReplaceArgumentDefaultValue; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; /** + * @api used in rector-symfony * @see \Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\ReplaceArgumentDefaultValueRectorTest */ final class ReplaceArgumentDefaultValueRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const REPLACED_ARGUMENTS = 'replaced_arguments'; - /** * @var ReplaceArgumentDefaultValue[] */ - private array $replacedArguments = []; + private array $replaceArgumentDefaultValues = []; public function __construct( - private ArgumentDefaultValueReplacer $argumentDefaultValueReplacer + private readonly ArgumentDefaultValueReplacer $argumentDefaultValueReplacer ) { } public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Replaces defined map of arguments in defined methods and their calls.', + 'Replace defined map of arguments in defined methods and their calls', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' @@ -53,15 +51,13 @@ public function getRuleDefinition(): RuleDefinition CODE_SAMPLE , [ - self::REPLACED_ARGUMENTS => [ - new ReplaceArgumentDefaultValue( - 'SomeClass', - 'someMethod', - 0, - 'SomeClass::OLD_CONSTANT', - false - ), - ], + new ReplaceArgumentDefaultValue( + 'SomeClass', + 'someMethod', + 0, + 'SomeClass::OLD_CONSTANT', + false + ), ] ), ] @@ -73,39 +69,85 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [MethodCall::class, StaticCall::class, ClassMethod::class]; + return [MethodCall::class, StaticCall::class, ClassMethod::class, New_::class]; } /** - * @param MethodCall|StaticCall|ClassMethod $node + * @param MethodCall|StaticCall|ClassMethod|New_ $node */ - public function refactor(Node $node): MethodCall | StaticCall | ClassMethod + public function refactor(Node $node): MethodCall | StaticCall | ClassMethod | New_ | null { - foreach ($this->replacedArguments as $replacedArgument) { - if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType( - $node, - $replacedArgument->getObjectType() - )) { + if ($node instanceof New_) { + return $this->refactorNew($node); + } + + $nodeName = $this->getName($node->name); + if ($nodeName === null) { + return null; + } + + $hasChanged = false; + $currentNode = $node; + + foreach ($this->replaceArgumentDefaultValues as $replaceArgumentDefaultValue) { + if (! $this->nodeNameResolver->isStringName($nodeName, $replaceArgumentDefaultValue->getMethod())) { continue; } - if (! $this->isName($node->name, $replacedArgument->getMethod())) { + if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType( + $currentNode, + $replaceArgumentDefaultValue->getObjectType() + )) { continue; } - $this->argumentDefaultValueReplacer->processReplaces($node, $replacedArgument); + $replacedNode = $this->argumentDefaultValueReplacer->processReplaces( + $currentNode, + $replaceArgumentDefaultValue + ); + if ($replacedNode !== null && $replacedNode !== $currentNode) { + $currentNode = $replacedNode; + $hasChanged = true; + } } - return $node; + return $hasChanged ? $currentNode : null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $replacedArguments = $configuration[self::REPLACED_ARGUMENTS] ?? []; - Assert::allIsInstanceOf($replacedArguments, ReplaceArgumentDefaultValue::class); - $this->replacedArguments = $replacedArguments; + Assert::allIsAOf($configuration, ReplaceArgumentDefaultValue::class); + + $this->replaceArgumentDefaultValues = $configuration; + } + + private function refactorNew(New_ $new): ?New_ + { + $hasChanged = false; + $currentNode = $new; + + foreach ($this->replaceArgumentDefaultValues as $replaceArgumentDefaultValue) { + if ($replaceArgumentDefaultValue->getMethod() !== MethodName::CONSTRUCT) { + continue; + } + + if (! $this->isObjectType($currentNode, $replaceArgumentDefaultValue->getObjectType())) { + continue; + } + + $replacedNode = $this->argumentDefaultValueReplacer->processReplaces( + $currentNode, + $replaceArgumentDefaultValue + ); + if ($replacedNode !== null && $replacedNode !== $currentNode) { + $currentNode = $replacedNode; + $hasChanged = true; + } + } + + return $hasChanged ? $currentNode : null; } } diff --git a/rules/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector.php b/rules/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector.php index 2ac7a45700d..5dc2d56687f 100644 --- a/rules/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector.php +++ b/rules/Arguments/Rector/FuncCall/FunctionArgumentDefaultValueReplacerRector.php @@ -8,38 +8,30 @@ use PhpParser\Node\Expr\FuncCall; use Rector\Arguments\ArgumentDefaultValueReplacer; use Rector\Arguments\ValueObject\ReplaceFuncCallArgumentDefaultValue; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; /** - * @changelog https://php.watch/versions/8.1/version_compare-operator-restrictions - * @changelog https://github.com/rectorphp/rector/issues/6271 - * * @see \Rector\Tests\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector\FunctionArgumentDefaultValueReplacerRectorTest */ final class FunctionArgumentDefaultValueReplacerRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const REPLACED_ARGUMENTS = 'replaced_arguments'; - /** * @var ReplaceFuncCallArgumentDefaultValue[] */ - private mixed $replacedArguments = []; + private array $replacedArguments = []; public function __construct( - private ArgumentDefaultValueReplacer $argumentDefaultValueReplacer + private readonly ArgumentDefaultValueReplacer $argumentDefaultValueReplacer ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Streamline the operator arguments of version_compare function', [ + return new RuleDefinition('Streamline the operator arguments of `version_compare` function', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' version_compare(PHP_VERSION, '5.6', 'gte'); @@ -49,12 +41,8 @@ public function getRuleDefinition(): RuleDefinition <<<'CODE_SAMPLE' version_compare(PHP_VERSION, '5.6', 'ge'); CODE_SAMPLE - , - [ - self::REPLACED_ARGUMENTS => [ - new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge',), - ], - ] + , + [new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge')] ), ]); } @@ -70,26 +58,33 @@ public function getNodeTypes(): array /** * @param FuncCall $node */ - public function refactor(Node $node): FuncCall + public function refactor(Node $node): FuncCall|null { + $hasChanged = false; foreach ($this->replacedArguments as $replacedArgument) { if (! $this->isName($node->name, $replacedArgument->getFunction())) { continue; } - $this->argumentDefaultValueReplacer->processReplaces($node, $replacedArgument); + $changedNode = $this->argumentDefaultValueReplacer->processReplaces($node, $replacedArgument); + if ($changedNode instanceof Node) { + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; } - return $node; + return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $replacedArguments = $configuration[self::REPLACED_ARGUMENTS] ?? []; - Assert::allIsInstanceOf($replacedArguments, ReplaceFuncCallArgumentDefaultValue::class); - $this->replacedArguments = $replacedArguments; + Assert::allIsAOf($configuration, ReplaceFuncCallArgumentDefaultValue::class); + $this->replacedArguments = $configuration; } } diff --git a/rules/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector.php b/rules/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector.php deleted file mode 100644 index 9b1b21c4872..00000000000 --- a/rules/Arguments/Rector/FuncCall/SwapFuncCallArgumentsRector.php +++ /dev/null @@ -1,135 +0,0 @@ - [new SwapFuncCallArguments('some_function', [1, 0])], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?FuncCall - { - $isJustSwapped = $node->getAttribute(self::JUST_SWAPPED); - if ($isJustSwapped) { - return null; - } - - foreach ($this->functionArgumentSwaps as $functionArgumentSwap) { - if (! $this->isName($node, $functionArgumentSwap->getFunction())) { - continue; - } - - $newArguments = $this->resolveNewArguments($functionArgumentSwap, $node); - if ($newArguments === []) { - return null; - } - - foreach ($newArguments as $newPosition => $argument) { - $node->args[$newPosition] = $argument; - } - - $node->setAttribute(self::JUST_SWAPPED, true); - - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $functionArgumentSwaps = $configuration[self::FUNCTION_ARGUMENT_SWAPS] ?? []; - Assert::allIsInstanceOf($functionArgumentSwaps, SwapFuncCallArguments::class); - $this->functionArgumentSwaps = $functionArgumentSwaps; - } - - /** - * @return array - */ - private function resolveNewArguments(SwapFuncCallArguments $swapFuncCallArguments, FuncCall $funcCall): array - { - $newArguments = []; - foreach ($swapFuncCallArguments->getOrder() as $oldPosition => $newPosition) { - if (! isset($funcCall->args[$oldPosition])) { - continue; - } - if (! isset($funcCall->args[$newPosition])) { - continue; - } - $newArguments[$newPosition] = $funcCall->args[$oldPosition]; - } - - return $newArguments; - } -} diff --git a/rules/Arguments/Rector/MethodCall/RemoveMethodCallParamRector.php b/rules/Arguments/Rector/MethodCall/RemoveMethodCallParamRector.php new file mode 100644 index 00000000000..e70daba48d2 --- /dev/null +++ b/rules/Arguments/Rector/MethodCall/RemoveMethodCallParamRector.php @@ -0,0 +1,158 @@ +process(1, 2); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + public function run(Caller $caller) + { + $caller->process(1); + } +} +CODE_SAMPLE + , + [new RemoveMethodCallParam('Caller', 'process', 1)] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class]; + } + + /** + * @param MethodCall|StaticCall $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + if ($node->isFirstClassCallable()) { + return null; + } + + foreach ($this->removeMethodCallParams as $removeMethodCallParam) { + if (! $this->isName($node->name, $removeMethodCallParam->getMethodName())) { + continue; + } + + if (! $this->isCallerObjectType($node, $removeMethodCallParam)) { + continue; + } + + $args = $node->getArgs(); + $position = $removeMethodCallParam->getParamPosition(); + $firstNamedArgPosition = $this->argsAnalyzer->resolveFirstNamedArgPosition($args); + + // if the call has named arguments and the argument that we want to remove is not + // before any named argument, we need to check if it is in the list of named arguments + // if it is, we use the position of the named argument as the position to remove + // if it is not, we cannot remove it + if ($firstNamedArgPosition !== null && $position >= $firstNamedArgPosition) { + $call = $this->astResolver->resolveClassMethodOrFunctionFromCall($node); + if ($call === null) { + return null; + } + + $paramName = null; + $variable = $call->params[$position]->var; + if ($variable instanceof Variable) { + $paramName = $variable->name; + } + + $newPosition = -1; + if (is_string($paramName)) { + $newPosition = $this->argsAnalyzer->resolveArgPosition($args, $paramName, $newPosition); + } + + if ($newPosition === -1) { + return null; + } + + $position = $newPosition; + } + + if (! isset($args[$position])) { + continue; + } + + unset($node->args[$position]); + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsInstanceOf($configuration, RemoveMethodCallParam::class); + $this->removeMethodCallParams = $configuration; + } + + private function isCallerObjectType(StaticCall|MethodCall $call, RemoveMethodCallParam $removeMethodCallParam): bool + { + if ($call instanceof MethodCall) { + return $this->isObjectType($call->var, $removeMethodCallParam->getObjectType()); + } + + return $this->isObjectType($call->class, $removeMethodCallParam->getObjectType()); + } +} diff --git a/rules/Arguments/ValueObject/ArgumentAdder.php b/rules/Arguments/ValueObject/ArgumentAdder.php index 6de73c40ab3..bd15a59b6dc 100644 --- a/rules/Arguments/ValueObject/ArgumentAdder.php +++ b/rules/Arguments/ValueObject/ArgumentAdder.php @@ -5,6 +5,8 @@ namespace Rector\Arguments\ValueObject; use PHPStan\Type\ObjectType; +use PHPStan\Type\Type; +use Rector\Validation\RectorAssert; final class ArgumentAdder { @@ -12,14 +14,15 @@ final class ArgumentAdder * @param mixed|null $argumentDefaultValue */ public function __construct( - private string $class, - private string $method, - private int $position, - private ?string $argumentName = null, + private readonly string $class, + private readonly string $method, + private readonly int $position, + private readonly ?string $argumentName = null, private $argumentDefaultValue = null, - private ?string $argumentType = null, - private ?string $scope = null + private readonly Type | null $argumentType = null, + private readonly ?string $scope = null ) { + RectorAssert::className($class); } public function getObjectType(): ObjectType @@ -50,7 +53,7 @@ public function getArgumentDefaultValue() return $this->argumentDefaultValue; } - public function getArgumentType(): ?string + public function getArgumentType(): ?Type { return $this->argumentType; } diff --git a/rules/Arguments/ValueObject/ArgumentAdderWithoutDefaultValue.php b/rules/Arguments/ValueObject/ArgumentAdderWithoutDefaultValue.php new file mode 100644 index 00000000000..4140b950621 --- /dev/null +++ b/rules/Arguments/ValueObject/ArgumentAdderWithoutDefaultValue.php @@ -0,0 +1,53 @@ +class); + } + + public function getMethod(): string + { + return $this->method; + } + + public function getPosition(): int + { + return $this->position; + } + + public function getArgumentName(): ?string + { + return $this->argumentName; + } + + public function getArgumentType(): ?Type + { + return $this->argumentType; + } + + public function getScope(): ?string + { + return $this->scope; + } +} diff --git a/rules/Arguments/ValueObject/RemoveMethodCallParam.php b/rules/Arguments/ValueObject/RemoveMethodCallParam.php new file mode 100644 index 00000000000..53c7e016111 --- /dev/null +++ b/rules/Arguments/ValueObject/RemoveMethodCallParam.php @@ -0,0 +1,35 @@ +class); + } + + public function getMethodName(): string + { + return $this->methodName; + } + + public function getParamPosition(): int + { + return $this->paramPosition; + } +} diff --git a/rules/Arguments/ValueObject/ReplaceArgumentDefaultValue.php b/rules/Arguments/ValueObject/ReplaceArgumentDefaultValue.php index 9682510faf9..233669c03e7 100644 --- a/rules/Arguments/ValueObject/ReplaceArgumentDefaultValue.php +++ b/rules/Arguments/ValueObject/ReplaceArgumentDefaultValue.php @@ -6,20 +6,23 @@ use PHPStan\Type\ObjectType; use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface; +use Rector\Validation\RectorAssert; -final class ReplaceArgumentDefaultValue implements ReplaceArgumentDefaultValueInterface +final readonly class ReplaceArgumentDefaultValue implements ReplaceArgumentDefaultValueInterface { + public const string ANY_VALUE_BEFORE = '*ANY_VALUE_BEFORE*'; + /** - * @param mixed $valueBefore - * @param mixed $valueAfter + * @param int<0, max> $position */ public function __construct( private string $class, private string $method, private int $position, - private $valueBefore, - private $valueAfter + private mixed $valueBefore, + private mixed $valueAfter ) { + RectorAssert::className($class); } public function getObjectType(): ObjectType @@ -37,18 +40,12 @@ public function getPosition(): int return $this->position; } - /** - * @return mixed - */ - public function getValueBefore() + public function getValueBefore(): mixed { return $this->valueBefore; } - /** - * @return mixed - */ - public function getValueAfter() + public function getValueAfter(): mixed { return $this->valueAfter; } diff --git a/rules/Arguments/ValueObject/ReplaceFuncCallArgumentDefaultValue.php b/rules/Arguments/ValueObject/ReplaceFuncCallArgumentDefaultValue.php index 4546c5fb993..bd16788d7ff 100644 --- a/rules/Arguments/ValueObject/ReplaceFuncCallArgumentDefaultValue.php +++ b/rules/Arguments/ValueObject/ReplaceFuncCallArgumentDefaultValue.php @@ -6,17 +6,13 @@ use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface; -final class ReplaceFuncCallArgumentDefaultValue implements ReplaceArgumentDefaultValueInterface +final readonly class ReplaceFuncCallArgumentDefaultValue implements ReplaceArgumentDefaultValueInterface { - /** - * @param mixed $valueBefore - * @param mixed $valueAfter - */ public function __construct( private string $function, private int $position, - private $valueBefore, - private $valueAfter + private mixed $valueBefore, + private mixed $valueAfter ) { } @@ -30,18 +26,12 @@ public function getPosition(): int return $this->position; } - /** - * @return mixed - */ - public function getValueBefore() + public function getValueBefore(): mixed { return $this->valueBefore; } - /** - * @return mixed - */ - public function getValueAfter() + public function getValueAfter(): mixed { return $this->valueAfter; } diff --git a/rules/Arguments/ValueObject/SwapFuncCallArguments.php b/rules/Arguments/ValueObject/SwapFuncCallArguments.php deleted file mode 100644 index 20fb37db8bc..00000000000 --- a/rules/Arguments/ValueObject/SwapFuncCallArguments.php +++ /dev/null @@ -1,30 +0,0 @@ - $order - */ - public function __construct( - private string $function, - private array $order - ) { - } - - public function getFunction(): string - { - return $this->function; - } - - /** - * @return array - */ - public function getOrder(): array - { - return $this->order; - } -} diff --git a/rules/Assert/Enum/AssertClassName.php b/rules/Assert/Enum/AssertClassName.php new file mode 100644 index 00000000000..5efd9b28779 --- /dev/null +++ b/rules/Assert/Enum/AssertClassName.php @@ -0,0 +1,12 @@ +stmts === null) { + return []; + } + + $existingAssertCallHashes = []; + $standard = new Standard(); + + foreach ($classMethod->stmts as $currentStmt) { + if (! $currentStmt instanceof Expression) { + continue; + } + + if (! $currentStmt->expr instanceof StaticCall) { + continue; + } + + $staticCall = $currentStmt->expr; + if (! $staticCall->class instanceof Name) { + continue; + } + + if (! in_array( + $staticCall->class->toString(), + [AssertClassName::WEBMOZART, AssertClassName::BEBERLEI], + true + )) { + continue; + } + + $existingAssertCallHashes[] = $standard->prettyPrintExpr($staticCall); + } + + return $existingAssertCallHashes; + } +} diff --git a/rules/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector.php b/rules/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector.php new file mode 100644 index 00000000000..34d769278b4 --- /dev/null +++ b/rules/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector.php @@ -0,0 +1,247 @@ +isInClass()) { + return null; + } + + if ($node->stmts === null || $node->isAbstract()) { + return null; + } + + $methodPhpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $methodPhpDocInfo instanceof PhpDocInfo) { + return null; + } + + $paramTagValueNodes = $methodPhpDocInfo->getParamTagValueNodes(); + if ($paramTagValueNodes === []) { + return null; + } + + $assertStaticCallStmts = []; + + foreach ($node->getParams() as $param) { + if (! $param->type instanceof Identifier) { + continue; + } + + // handle arrays only + if (! $this->isName($param->type, 'array')) { + continue; + } + + if (! $param->var instanceof Variable) { + continue; + } + + $paramName = $param->var->name; + if (! is_string($paramName)) { + continue; + } + + $paramDocType = $methodPhpDocInfo->getParamType($paramName); + if (! $paramDocType instanceof ArrayType) { + continue; + } + + $valueAssertMethod = $this->matchTypeToAssertMethod($paramDocType->getItemType()); + + if (is_string($valueAssertMethod)) { + $assertStaticCallStmts[] = $this->createAssertExpression($param->var, $valueAssertMethod); + } + + $keyAssertMethod = $this->matchTypeToAssertMethod($paramDocType->getKeyType()); + + if (is_string($keyAssertMethod)) { + $arrayKeys = new FuncCall(new Name('array_keys'), [new Arg($param->var)]); + $assertStaticCallStmts[] = $this->createAssertExpression($arrayKeys, $keyAssertMethod); + } + } + + // filter existing assert to avoid duplication + if ($assertStaticCallStmts === []) { + return null; + } + + $existingAssertCallHashes = $this->existingAssertStaticCallResolver->resolve($node); + + $assertStaticCallStmts = $this->filterOutExistingStaticCall($assertStaticCallStmts, $existingAssertCallHashes); + + if ($assertStaticCallStmts === []) { + return null; + } + + $node->stmts = array_merge($assertStaticCallStmts, $node->stmts); + + return $node; + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + if ($configuration === []) { + // default + return; + } + + Assert::count($configuration, 1); + Assert::inArray($configuration[0], [AssertClassName::BEBERLEI, AssertClassName::WEBMOZART]); + + $this->assertClass = $configuration[0]; + } + + private function createAssertExpression(Expr $expr, string $methodName): Expression + { + $assertFullyQualified = new FullyQualified($this->assertClass); + + $staticCall = new StaticCall($assertFullyQualified, $methodName, [new Arg($expr)]); + + return new Expression($staticCall); + } + + /** + * @param Expression[] $assertStaticCallStmts + * @param string[] $existingAssertCallHashes + * @return Expression[] + */ + private function filterOutExistingStaticCall(array $assertStaticCallStmts, array $existingAssertCallHashes): array + { + $standard = new Standard(); + + return array_filter($assertStaticCallStmts, function (Expression $assertStaticCallExpression) use ( + $standard, + $existingAssertCallHashes + ): bool { + $currentStaticCallHash = $standard->prettyPrintExpr($assertStaticCallExpression->expr); + + return ! in_array($currentStaticCallHash, $existingAssertCallHashes, true); + }); + } + + private function matchTypeToAssertMethod(Type $type): ?string + { + if ($type instanceof IntegerType) { + return 'allInteger'; + } + + if ($type instanceof StringType) { + return 'allString'; + } + + if ($type instanceof FloatType) { + return 'allFloat'; + } + + if ($type instanceof BooleanType) { + return 'allBoolean'; + } + + return null; + } +} diff --git a/rules/Autodiscovery/Analyzer/ValueObjectClassAnalyzer.php b/rules/Autodiscovery/Analyzer/ValueObjectClassAnalyzer.php deleted file mode 100644 index 45c6d215883..00000000000 --- a/rules/Autodiscovery/Analyzer/ValueObjectClassAnalyzer.php +++ /dev/null @@ -1,104 +0,0 @@ - - */ - private array $valueObjectStatusByClassName = []; - - public function __construct( - private NodeNameResolver $nodeNameResolver, - private NodeTypeResolver $nodeTypeResolver, - private PhpDocInfoFactory $phpDocInfoFactory, - private AstResolver $astResolver, - private ClassAnalyzer $classAnalyzer - ) { - } - - public function isValueObjectClass(Class_ $class): bool - { - if ($this->classAnalyzer->isAnonymousClass($class)) { - return false; - } - - /** @var string $className */ - $className = $this->nodeNameResolver->getName($class); - - if (isset($this->valueObjectStatusByClassName[$className])) { - return $this->valueObjectStatusByClassName[$className]; - } - - $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); - - if (! $constructClassMethod instanceof ClassMethod) { - return $this->hasExlusivelySerializeProperties($class, $className); - } - - // resolve constructor types - foreach ($constructClassMethod->params as $param) { - $paramType = $this->nodeTypeResolver->resolve($param); - if (! $paramType instanceof ObjectType) { - continue; - } - - // awesome! - // is it services or value object? - $paramTypeClass = $this->astResolver->resolveClassFromName($paramType->getClassName()); - if (! $paramTypeClass instanceof Class_) { - // not sure :/ - continue; - } - - if (! $this->isValueObjectClass($paramTypeClass)) { - return false; - } - } - - // if we didn't prove it's not a value object so far → fallback to true - $this->valueObjectStatusByClassName[$className] = true; - - return true; - } - - private function hasExlusivelySerializeProperties(Class_ $class, string $className): bool - { - // A. has all properties with serialize? - if ($this->hasAllPropertiesWithSerialize($class)) { - $this->valueObjectStatusByClassName[$className] = true; - return true; - } - - // probably not a value object - $this->valueObjectStatusByClassName[$className] = false; - return false; - } - - private function hasAllPropertiesWithSerialize(Class_ $class): bool - { - foreach ($class->getProperties() as $property) { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - if ($phpDocInfo->hasByAnnotationClass('JMS\Serializer\Annotation\Type')) { - continue; - } - - return false; - } - - return true; - } -} diff --git a/rules/Autodiscovery/Configuration/CategoryNamespaceProvider.php b/rules/Autodiscovery/Configuration/CategoryNamespaceProvider.php deleted file mode 100644 index 4e1f017c741..00000000000 --- a/rules/Autodiscovery/Configuration/CategoryNamespaceProvider.php +++ /dev/null @@ -1,38 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->doctrineDocBlockResolver->isDoctrineEntityClass($node)) { - return null; - } - - // is entity in expected directory? - $smartFileInfo = $this->file->getSmartFileInfo(); - if (Strings::match($smartFileInfo->getRealPath(), self::ENTITY_PATH_REGEX)) { - return null; - } - - $addedFileWithNodes = $this->addedFileWithNodesFactory->createWithDesiredGroup( - $smartFileInfo, - $this->file, - 'Entity' - ); - - if (! $addedFileWithNodes instanceof AddedFileWithNodes) { - return null; - } - - $this->removedAndAddedFilesCollector->removeFile($smartFileInfo); - $this->removedAndAddedFilesCollector->addAddedFile($addedFileWithNodes); - - return null; - } -} diff --git a/rules/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector.php b/rules/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector.php deleted file mode 100644 index 9836e266811..00000000000 --- a/rules/Autodiscovery/Rector/Class_/MoveServicesBySuffixToDirectoryRector.php +++ /dev/null @@ -1,164 +0,0 @@ - ['Repository'], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - $this->processGroupNamesBySuffix($this->file->getSmartFileInfo(), $this->file, $this->groupNamesBySuffix); - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $groupNamesBySuffix = $configuration[self::GROUP_NAMES_BY_SUFFIX] ?? []; - Assert::allString($groupNamesBySuffix); - - $this->groupNamesBySuffix = $groupNamesBySuffix; - } - - /** - * A. Match classes by suffix and move them to group namespace - * - * E.g. "App\Controller\SomeException" - * ↓ - * "App\Exception\SomeException" - * - * @param string[] $groupNamesBySuffix - */ - private function processGroupNamesBySuffix( - SmartFileInfo $smartFileInfo, - File $file, - array $groupNamesBySuffix - ): void { - foreach ($groupNamesBySuffix as $groupNames) { - // has class suffix - $suffixPattern = '\w+' . $groupNames . '(Test)?\.php$'; - if (! Strings::match($smartFileInfo->getRealPath(), '#' . $suffixPattern . '#')) { - continue; - } - - if ($this->isLocatedInExpectedLocation($groupNames, $suffixPattern, $smartFileInfo)) { - continue; - } - - // file is already in the group - if (Strings::match($smartFileInfo->getPath(), '#' . $groupNames . '$#')) { - continue; - } - - $this->moveFileToGroupName($smartFileInfo, $this->file, $groupNames); - return; - } - } - - private function isLocatedInExpectedLocation( - string $groupName, - string $suffixPattern, - SmartFileInfo $smartFileInfo - ): bool { - $expectedLocationFilePattern = $this->expectedFileLocationResolver->resolve($groupName, $suffixPattern); - - return (bool) Strings::match($smartFileInfo->getRealPath(), $expectedLocationFilePattern); - } - - private function moveFileToGroupName(SmartFileInfo $fileInfo, File $file, string $desiredGroupName): void - { - $addedFileWithNodes = $this->addedFileWithNodesFactory->createWithDesiredGroup( - $fileInfo, - $file, - $desiredGroupName - ); - - if (! $addedFileWithNodes instanceof AddedFileWithNodes) { - return; - } - - $this->removedAndAddedFilesCollector->removeFile($fileInfo); - $this->removedAndAddedFilesCollector->addAddedFile($addedFileWithNodes); - } -} diff --git a/rules/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector.php b/rules/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector.php deleted file mode 100644 index 570d80be580..00000000000 --- a/rules/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector.php +++ /dev/null @@ -1,220 +0,0 @@ -[] - */ - private const COMMON_SERVICE_SUFFIXES = [ - 'Repository', 'Command', 'Mapper', 'Controller', 'Presenter', 'Factory', 'Test', 'TestCase', 'Service', - ]; - - private bool $enableValueObjectGuessing = true; - - /** - * @var class-string[] - */ - private array $types = []; - - /** - * @var string[] - */ - private array $suffixes = []; - - public function __construct( - private AddedFileWithNodesFactory $addedFileWithNodesFactory, - private ValueObjectClassAnalyzer $valueObjectClassAnalyzer - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Move value object to ValueObject namespace/directory', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -// app/Exception/Name.php -class Name -{ - private $name; - - public function __construct(string $name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -// app/ValueObject/Name.php -class Name -{ - private $name; - - public function __construct(string $name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } -} -CODE_SAMPLE - , - [ - self::TYPES => ['ValueObjectInterfaceClassName'], - self::SUFFIXES => ['Search'], - self::ENABLE_VALUE_OBJECT_GUESSING => true, - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isValueObjectMatch($node)) { - return null; - } - - $smartFileInfo = $this->file->getSmartFileInfo(); - - $addedFileWithNodes = $this->addedFileWithNodesFactory->createWithDesiredGroup( - $smartFileInfo, - $this->file, - 'ValueObject' - ); - - if (! $addedFileWithNodes instanceof AddedFileWithNodes) { - return null; - } - - $this->removedAndAddedFilesCollector->removeFile($smartFileInfo); - $this->removedAndAddedFilesCollector->addAddedFile($addedFileWithNodes); - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $this->types = $configuration[self::TYPES] ?? []; - $this->suffixes = $configuration[self::SUFFIXES] ?? []; - $this->enableValueObjectGuessing = $configuration[self::ENABLE_VALUE_OBJECT_GUESSING] ?? false; - } - - private function isValueObjectMatch(Class_ $class): bool - { - if ($this->isSuffixMatch($class)) { - return true; - } - - $className = $this->getName($class); - if ($className === null) { - return false; - } - - $classObjectType = new ObjectType($className); - - foreach ($this->types as $type) { - $desiredObjectType = new ObjectType($type); - if ($desiredObjectType->isSuperTypeOf($classObjectType)->yes()) { - return true; - } - } - - if ($this->isKnownServiceType($className)) { - return false; - } - - if (! $this->enableValueObjectGuessing) { - return false; - } - - return $this->valueObjectClassAnalyzer->isValueObjectClass($class); - } - - private function isSuffixMatch(Class_ $class): bool - { - $className = $class->getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return false; - } - - foreach ($this->suffixes as $suffix) { - if (\str_ends_with($className, $suffix)) { - return true; - } - } - - return false; - } - - private function isKnownServiceType(string $className): bool - { - foreach (self::COMMON_SERVICE_SUFFIXES as $commonServiceSuffix) { - if (\str_ends_with($className, $commonServiceSuffix)) { - return true; - } - } - - return false; - } -} diff --git a/rules/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector.php b/rules/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector.php deleted file mode 100644 index 0010ea88f49..00000000000 --- a/rules/Autodiscovery/Rector/Interface_/MoveInterfacesToContractNamespaceDirectoryRector.php +++ /dev/null @@ -1,88 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Interface_::class]; - } - - /** - * @param Interface_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->netteControlFactoryInterfaceAnalyzer->isComponentFactoryInterface($node)) { - return null; - } - - $addedFileWithNodes = $this->addedFileWithNodesFactory->createWithDesiredGroup( - $this->file->getSmartFileInfo(), - $this->file, - 'Contract' - ); - - if (! $addedFileWithNodes instanceof AddedFileWithNodes) { - return null; - } - - $this->removedAndAddedFilesCollector->removeFile($this->file->getSmartFileInfo()); - $this->removedAndAddedFilesCollector->addAddedFile($addedFileWithNodes); - - return null; - } -} diff --git a/rules/Carbon/NodeFactory/CarbonCallFactory.php b/rules/Carbon/NodeFactory/CarbonCallFactory.php new file mode 100644 index 00000000000..3d0180a2ee2 --- /dev/null +++ b/rules/Carbon/NodeFactory/CarbonCallFactory.php @@ -0,0 +1,134 @@ +\+|-)(\\s+)?(?\\d+)(\s+)?(?seconds|second|sec|minutes|minute|min|hours|hour|days|day|weeks|week|months|month|years|year)#'; + + /** + * @see https://regex101.com/r/IhxHTO/1 + */ + private const string STATIC_DATE_REGEX = '#now|yesterday|today|tomorrow#'; + + public function createFromDateTimeString( + FullyQualified $carbonFullyQualified, + String_ $string + ): MethodCall|StaticCall { + $carbonCall = $this->createStaticCall($carbonFullyQualified, $string); + $string->value = Strings::replace($string->value, self::STATIC_DATE_REGEX); + + // Handle add/sub multiple times + while ($match = Strings::match($string->value, self::PLUS_MINUS_COUNT_REGEX)) { + $methodCall = $this->createModifyMethodCall( + $carbonCall, + new Int_((int) $match['count']), + $match['unit'], + $match['operator'] + ); + if ($methodCall instanceof MethodCall) { + $carbonCall = $methodCall; + $string->value = Strings::replace($string->value, self::PLUS_MINUS_COUNT_REGEX, '', 1); + } + } + + // If we still have something in the string, we go back to the first method and replace this with a parse + if (($rest = Strings::trim($string->value)) !== '') { + $currentCall = $carbonCall; + $callStack = []; + while ($currentCall instanceof MethodCall) { + $callStack[] = $currentCall; + $currentCall = $currentCall->var; + } + + if (! $currentCall instanceof StaticCall) { + return $carbonCall; + } + + // If we fallback to a parse we want to include tomorrow/today/yesterday etc + if ($currentCall->name instanceof Identifier && $currentCall->name->name !== 'now') { + $rest .= ' ' . $currentCall->name->name; + } + + $currentCall->name = new Identifier('parse'); + $currentCall->args = [new Arg(new String_($rest))]; + + // Rebuild original call from callstack + $carbonCall = $this->rebuildCallStack($currentCall, $callStack); + } + + return $carbonCall; + } + + private function createStaticCall(FullyQualified $carbonFullyQualified, String_ $string): StaticCall + { + $startDate = Strings::match($string->value, self::STATIC_DATE_REGEX)[0] ?? 'now'; + + return new StaticCall($carbonFullyQualified, new Identifier($startDate)); + } + + private function createModifyMethodCall( + MethodCall|StaticCall $carbonCall, + Int_ $int, + string $unit, + string $operator + ): ?MethodCall { + $unit = match ($unit) { + 'sec', 'second', 'seconds' => 'seconds', + 'min', 'minute', 'minutes' => 'minutes', + 'hour', 'hours' => 'hours', + 'day', 'days' => 'days', + 'week', 'weeks' => 'weeks', + 'month', 'months' => 'months', + 'year', 'years' => 'years', + default => null, + }; + + $operator = match ($operator) { + '+' => 'add', + '-' => 'sub', + default => null, + }; + + if ($unit === null || $operator === null) { + return null; + } + + $methodName = $operator . ucfirst($unit); + + return new MethodCall($carbonCall, new Identifier($methodName), [new Arg($int)]); + } + + /** + * @param MethodCall[] $callStack + */ + private function rebuildCallStack(StaticCall $staticCall, array $callStack): MethodCall|StaticCall + { + if ($callStack === []) { + return $staticCall; + } + + $currentCall = $staticCall; + $callStack = array_reverse($callStack); + foreach ($callStack as $call) { + $call->var = $currentCall; + $currentCall = $call; + } + + return $currentCall; + } +} diff --git a/rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php b/rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php new file mode 100644 index 00000000000..e0d311b8604 --- /dev/null +++ b/rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php @@ -0,0 +1,263 @@ +format(*)`', [ + new CodeSample( + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $date = date('Y-m-d'); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $date = \Carbon\Carbon::now()->format('Y-m-d'); + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Minus::class, FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node instanceof Minus) { + $left = $node->left; + if ($left instanceof FuncCall && ! $left->isFirstClassCallable() && $this->isName($left->name, 'time')) { + $timeUnit = $this->detectTimeUnit($node->right); + if ($timeUnit !== null) { + return $this->createCarbonSubtract($timeUnit); + } + } + + return null; + } + + if (! $node instanceof FuncCall) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + if ($this->isName($node->name, 'date') && isset($node->args[1]) && $node->args[1] instanceof Arg) { + $format = $this->getArgValue($node, 0); + if (! $format instanceof Expr) { + return null; + } + + $timestamp = $node->args[1]->value; + if ($timestamp instanceof FuncCall + && $this->isName($timestamp->name, 'strtotime') + && isset($timestamp->args[0]) && $timestamp->args[0] instanceof Arg + ) { + $dateExpr = $timestamp->args[0]->value; + return $this->createCarbonParseFormat($dateExpr, $format); + } + + // @phpstan-ignore if.alwaysTrue + if ($this->getType($timestamp)->isInteger()) { + return $this->createCarbonFromTimestamp($timestamp, $format); + } + } + + if ($this->isName($node->name, 'date') && isset($node->args[0])) { + $format = $this->getArgValue($node, 0); + if ($format instanceof String_) { + return $this->createCarbonNowFormat($format); + } + } + + if ($this->isName($node->name, 'strtotime') && isset($node->args[0])) { + $dateExpr = $this->getArgValue($node, 0); + $baseTimestamp = $this->getArgValue($node, 1); + + if ($dateExpr instanceof Expr && ! $baseTimestamp instanceof Expr) { + return $this->createCarbonParseTimestamp($dateExpr); + } + + if ($dateExpr instanceof Expr && $baseTimestamp instanceof String_) { + $isRelative = str_starts_with($baseTimestamp->value, '+') || str_starts_with( + $baseTimestamp->value, + '-' + ); + + if ($isRelative) { + return null; // @todo implement relative changes based on second arg + } + } + } + + return null; + } + + private function getArgValue(FuncCall $funcCall, int $index): ?Expr + { + if (! isset($funcCall->args[$index]) || ! $funcCall->args[$index] instanceof Arg) { + return null; + } + + return $funcCall->args[$index]->value; + } + + private function createCarbonNowFormat(String_ $string): MethodCall + { + return new MethodCall($this->createCarbonNow(), 'format', [new Arg($string)]); + } + + private function createCarbonNow(): StaticCall + { + return new StaticCall(new FullyQualified('Carbon\\Carbon'), 'now'); + } + + private function createCarbonParseTimestamp(Expr $dateExpr): MethodCall + { + $staticCall = new StaticCall(new FullyQualified('Carbon\\Carbon'), 'parse', [new Arg($dateExpr)]); + + return new MethodCall($staticCall, 'getTimestamp'); + } + + private function createCarbonParseFormat(Expr $dateExpr, Expr $format): MethodCall + { + $staticCall = new StaticCall(new FullyQualified('Carbon\\Carbon'), 'parse', [new Arg($dateExpr)]); + + return new MethodCall($staticCall, 'format', [new Arg($format)]); + } + + private function createCarbonFromTimestamp(Expr $timestampExpr, Expr $format): MethodCall + { + $staticCall = new StaticCall(new FullyQualified('Carbon\\Carbon'), 'createFromTimestamp', [ + new Arg($timestampExpr), + ]); + + return new MethodCall($staticCall, 'format', [new Arg($format)]); + } + + /** + * @param array{unit: string, value: int} $timeUnit + */ + private function createCarbonSubtract(array $timeUnit): MethodCall + { + $staticCall = new StaticCall(new FullyQualified('Carbon\\Carbon'), 'now'); + $methodName = 'sub' . ucfirst($timeUnit['unit']); + $methodCall = new MethodCall($staticCall, $methodName, [new Arg(new Int_($timeUnit['value']))]); + return new MethodCall($methodCall, 'getTimestamp'); + } + + /** + * @return array{unit: string, value: int}|null + */ + private function detectTimeUnit(Expr $expr): ?array + { + $product = $this->calculateProduct($expr); + if ($product === null) { + return null; + } + + foreach (self::TIME_UNITS as [$unit, $seconds]) { + if ($product % $seconds === 0) { + return [ + 'unit' => (string) $unit, + 'value' => (int) ($product / $seconds), + ]; + } + } + + return null; + } + + private function calculateProduct(Expr $expr): float|int|null + { + if ($expr instanceof Int_) { + return $expr->value; + } + + if (! $expr instanceof Mul) { + return null; + } + + $multipliers = $this->extractMultipliers($expr); + if ($multipliers === []) { + return null; + } + + return array_product($multipliers); + } + + /** + * @return int[] + */ + private function extractMultipliers(Node $node): array + { + $multipliers = []; + if (! $node instanceof Mul) { + return $multipliers; + } + + if ($node->left instanceof Int_) { + $multipliers[] = $node->left->value; + } elseif ($node->left instanceof Mul) { + $multipliers = array_merge($multipliers, $this->extractMultipliers($node->left)); + } + + if ($node->right instanceof Int_) { + $multipliers[] = $node->right->value; + } elseif ($node->right instanceof Mul) { + $multipliers = array_merge($multipliers, $this->extractMultipliers($node->right)); + } + + return $multipliers; + } +} diff --git a/rules/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector.php b/rules/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector.php new file mode 100644 index 00000000000..9f5eca66687 --- /dev/null +++ b/rules/Carbon/Rector/FuncCall/TimeFuncCallToCarbonRector.php @@ -0,0 +1,86 @@ +getTimestamp()`', [ + new CodeSample( + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $time = time(); + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $time = \Carbon\Carbon::now()->getTimestamp(); + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node->name, 'time')) { + return null; + } + + $firstClassCallable = $node->isFirstClassCallable(); + + if (! $firstClassCallable && count($node->getArgs()) !== 0) { + return null; + } + + // create now and format() + $nowStaticCall = new StaticCall(new FullyQualified('Carbon\Carbon'), 'now'); + $methodCall = new MethodCall($nowStaticCall, 'getTimestamp'); + + if ($firstClassCallable) { + return new ArrowFunction([ + 'static' => true, + 'expr' => $methodCall, + ]); + } + + return $methodCall; + } +} diff --git a/rules/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector.php b/rules/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector.php deleted file mode 100644 index 6ee60fbf053..00000000000 --- a/rules/Carbon/Rector/MethodCall/ChangeCarbonSingularMethodCallToPluralRector.php +++ /dev/null @@ -1,104 +0,0 @@ - - */ - private const SINGULAR_TO_PLURAL_NAMES = [ - 'addSecond' => 'addSeconds', - 'subSecond' => 'subSeconds', - 'addMinute' => 'addMinutes', - 'subMinute' => 'subMinutes', - 'addDay' => 'addDays', - 'subDay' => 'subDays', - 'addHour' => 'addHours', - 'subHour' => 'subHours', - 'addWeek' => 'addWeeks', - 'subWeek' => 'subWeeks', - 'addMonth' => 'addMonths', - 'subMonth' => 'subMonths', - 'addYear' => 'addYears', - 'subYear' => 'subYears', - ]; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change setter methods with args to their plural names on Carbon\Carbon', - [ - new CodeSample( - <<<'CODE_SAMPLE' -use Carbon\Carbon; - -final class SomeClass -{ - public function run(Carbon $carbon, $value): void - { - $carbon->addMinute($value); - } -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -use Carbon\Carbon; - -final class SomeClass -{ - public function run(Carbon $carbon, $value): void - { - $carbon->addMinutes($value); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node->args === []) { - return null; - } - - foreach (self::SINGULAR_TO_PLURAL_NAMES as $singularName => $pluralName) { - if (! $this->isName($node->name, $singularName)) { - continue; - } - - $node->name = new Identifier($pluralName); - return $node; - } - - return null; - } -} diff --git a/rules/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector.php b/rules/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector.php deleted file mode 100644 index 45f285fb94b..00000000000 --- a/rules/Carbon/Rector/MethodCall/ChangeDiffForHumansArgsRector.php +++ /dev/null @@ -1,104 +0,0 @@ -diffForHumans(null, true); - - $carbon->diffForHumans(null, false); - } -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -use Carbon\Carbon; - -final class SomeClass -{ - public function run(Carbon $carbon): void - { - $carbon->diffForHumans(null, \Carbon\CarbonInterface::DIFF_ABSOLUTE); - - $carbon->diffForHumans(null, \Carbon\CarbonInterface::DIFF_RELATIVE_AUTO); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isObjectType($node->var, new ObjectType('Carbon\Carbon'))) { - return null; - } - - if (! $this->isName($node->name, 'diffForHumans')) { - return null; - } - - if (! isset($node->args[1])) { - return null; - } - - $secondArgValue = $node->args[1]->value; - if ($this->valueResolver->isTrue($secondArgValue)) { - $node->args[1]->value = $this->nodeFactory->createClassConstFetch( - 'Carbon\CarbonInterface', - 'DIFF_ABSOLUTE' - ); - return $node; - } - - if ($this->valueResolver->isFalse($secondArgValue)) { - $node->args[1]->value = $this->nodeFactory->createClassConstFetch( - 'Carbon\CarbonInterface', - 'DIFF_RELATIVE_AUTO' - ); - return $node; - } - - return null; - } -} diff --git a/rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php b/rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php new file mode 100644 index 00000000000..15fc7e503d1 --- /dev/null +++ b/rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php @@ -0,0 +1,108 @@ +format('Y-m-d'); + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $date = \Carbon\Carbon::today()->addDays(20)->format('Y-m-d') + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + /** + * @param MethodCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->var instanceof New_) { + return null; + } + + $new = $node->var; + if (! $new->class instanceof Name) { + return null; + } + + if (! $this->isName($new->class, 'DateTime') && ! $this->isName($new->class, 'DateTimeImmutable')) { + return null; + } + + if ($new->isFirstClassCallable()) { + return null; + } + + if (count($new->getArgs()) !== 1) { + // @todo handle in separate static call + return null; + } + + $firstArg = $new->getArgs()[0]; + if (! $firstArg->value instanceof String_) { + return null; + } + + if ($this->isName($new->class, 'DateTime')) { + $carbonFullyQualified = new FullyQualified('Carbon\Carbon'); + } else { + $carbonFullyQualified = new FullyQualified('Carbon\CarbonImmutable'); + } + + $carbonCall = $this->carbonCallFactory->createFromDateTimeString($carbonFullyQualified, $firstArg->value); + + $node->var = $carbonCall; + + return $node; + } +} diff --git a/rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php b/rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php new file mode 100644 index 00000000000..b63705e5796 --- /dev/null +++ b/rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php @@ -0,0 +1,92 @@ +> + */ + public function getNodeTypes(): array + { + return [New_::class]; + } + + /** + * @param New_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + if ($this->isName($node->class, 'DateTime')) { + return $this->refactorWithClass($node, 'Carbon\\Carbon'); + } + + if ($this->isName($node->class, 'DateTimeImmutable')) { + return $this->refactorWithClass($node, 'Carbon\\CarbonImmutable'); + } + + return null; + } + + private function refactorWithClass(New_ $new, string $className): MethodCall|StaticCall|null + { + // no arg? ::now() + $carbonFullyQualified = new FullyQualified($className); + + if ($new->args === []) { + return new StaticCall($carbonFullyQualified, new Identifier('now')); + } + + if (count($new->getArgs()) === 1) { + $firstArg = $new->getArgs()[0]; + + if ($firstArg->value instanceof String_) { + return $this->carbonCallFactory->createFromDateTimeString($carbonFullyQualified, $firstArg->value); + } + } + + return null; + } +} diff --git a/rules/CodeQuality/CompactConverter.php b/rules/CodeQuality/CompactConverter.php index 94a30fcd27d..13c1f5636b8 100644 --- a/rules/CodeQuality/CompactConverter.php +++ b/rules/CodeQuality/CompactConverter.php @@ -4,15 +4,16 @@ namespace Rector\CodeQuality; +use PhpParser\Node\Arg; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Scalar\String_; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\PhpParser\Node\Value\ValueResolver; +use Rector\Exception\ShouldNotHappenException; +use Rector\PhpParser\Node\Value\ValueResolver; -final class CompactConverter +final readonly class CompactConverter { public function __construct( private ValueResolver $valueResolver @@ -22,6 +23,11 @@ public function __construct( public function hasAllArgumentsNamed(FuncCall $funcCall): bool { foreach ($funcCall->args as $arg) { + // VariadicPlaceholder doesn't has name, so it return false directly + if (! $arg instanceof Arg) { + return false; + } + /** @var string|null $variableName */ $variableName = $this->valueResolver->getValue($arg->value); if (! is_string($variableName)) { @@ -37,6 +43,10 @@ public function convertToArray(FuncCall $funcCall): Array_ $array = new Array_(); foreach ($funcCall->args as $arg) { + if (! $arg instanceof Arg) { + throw new ShouldNotHappenException(); + } + /** @var string|null $variableName */ $variableName = $this->valueResolver->getValue($arg->value); if (! is_string($variableName)) { diff --git a/rules/CodeQuality/NodeAnalyzer/ArrayCompacter.php b/rules/CodeQuality/NodeAnalyzer/ArrayCompacter.php deleted file mode 100644 index 5f4bd820e3c..00000000000 --- a/rules/CodeQuality/NodeAnalyzer/ArrayCompacter.php +++ /dev/null @@ -1,34 +0,0 @@ -items as $arrayItem) { - if (! $arrayItem instanceof ArrayItem) { - continue; - } - - if ($arrayItem->key !== null) { - continue; - } - - if (! $arrayItem->value instanceof String_) { - continue; - } - - $variableName = $arrayItem->value->value; - $arrayItem->key = new String_($variableName); - $arrayItem->value = new Variable($variableName); - } - } -} diff --git a/rules/CodeQuality/NodeAnalyzer/ArrayItemsAnalyzer.php b/rules/CodeQuality/NodeAnalyzer/ArrayItemsAnalyzer.php deleted file mode 100644 index 50d9a4b8f07..00000000000 --- a/rules/CodeQuality/NodeAnalyzer/ArrayItemsAnalyzer.php +++ /dev/null @@ -1,60 +0,0 @@ -items as $arrayItem) { - $variableName = $this->resolveStringValue($arrayItem); - if ($variableName === null) { - continue; - } - - // the variable must not be defined here - if ($scope->hasVariableType($variableName)->no()) { - return false; - } - } - - return true; - } - - public function hasArrayExclusiveUndefinedVariableNames(Array_ $array, Scope $scope): bool - { - foreach ($array->items as $arrayItem) { - $variableName = $this->resolveStringValue($arrayItem); - if ($variableName === null) { - continue; - } - - // the variable must not be defined here - if ($scope->hasVariableType($variableName)->yes()) { - return false; - } - } - - return true; - } - - private function resolveStringValue(?ArrayItem $arrayItem): ?string - { - if (! $arrayItem instanceof ArrayItem) { - return null; - } - - if (! $arrayItem->value instanceof String_) { - return null; - } - - return $arrayItem->value->value; - } -} diff --git a/rules/CodeQuality/NodeAnalyzer/ClassLikeAnalyzer.php b/rules/CodeQuality/NodeAnalyzer/ClassLikeAnalyzer.php index 04c1dcbb155..99aff2f6b50 100644 --- a/rules/CodeQuality/NodeAnalyzer/ClassLikeAnalyzer.php +++ b/rules/CodeQuality/NodeAnalyzer/ClassLikeAnalyzer.php @@ -5,15 +5,9 @@ namespace Rector\CodeQuality\NodeAnalyzer; use PhpParser\Node\Stmt\Class_; -use Rector\NodeNameResolver\NodeNameResolver; -final class ClassLikeAnalyzer +final readonly class ClassLikeAnalyzer { - public function __construct( - private NodeNameResolver $nodeNameResolver - ) { - } - /** * @return string[] */ @@ -22,7 +16,9 @@ public function resolvePropertyNames(Class_ $class): array $propertyNames = []; foreach ($class->getProperties() as $property) { - $propertyNames[] = $this->nodeNameResolver->getName($property); + foreach ($property->props as $prop) { + $propertyNames[] = $prop->name->toString(); + } } return $propertyNames; diff --git a/rules/CodeQuality/NodeAnalyzer/ForAnalyzer.php b/rules/CodeQuality/NodeAnalyzer/ForAnalyzer.php deleted file mode 100644 index d49e0c73b69..00000000000 --- a/rules/CodeQuality/NodeAnalyzer/ForAnalyzer.php +++ /dev/null @@ -1,191 +0,0 @@ -nodeNameResolver->isName($condExprs[0]->left, $keyValueName)) { - return false; - } - - return $this->nodeNameResolver->isName($condExprs[0]->right, $countValueName); - } - - // $i > $count - if ($condExprs[0] instanceof Greater) { - if (! $this->nodeNameResolver->isName($condExprs[0]->left, $countValueName)) { - return false; - } - - return $this->nodeNameResolver->isName($condExprs[0]->right, $keyValueName); - } - - return false; - } - - /** - * @param Expr[] $loopExprs - * $param - */ - public function isLoopMatch(array $loopExprs, ?string $keyValueName): bool - { - if (count($loopExprs) !== 1) { - return false; - } - - if ($keyValueName === null) { - return false; - } - - $prePostInc = $loopExprs[0]; - - if ($prePostInc instanceof PreInc || $prePostInc instanceof PostInc) { - return $this->nodeNameResolver->isName($prePostInc->var, $keyValueName); - } - - return false; - } - - public function isCountValueVariableUsedInsideForStatements(For_ $for, ?Expr $expr): bool - { - return (bool) $this->betterNodeFinder->findFirst( - $for->stmts, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $expr) - ); - } - - public function isArrayWithKeyValueNameUnsetted(For_ $for): bool - { - return (bool) $this->betterNodeFinder->findFirst( - $for->stmts, - function (Node $node): bool { - /** @var Node $parent */ - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Unset_) { - return false; - } - return $node instanceof ArrayDimFetch; - } - ); - } - - public function isAssignmentWithArrayDimFetchAsVariableInsideForStatements(For_ $for, string $keyValueName): bool - { - return (bool) $this->betterNodeFinder->findFirst( - $for->stmts, - function (Node $node) use ($keyValueName): bool { - if (! $node instanceof Assign) { - return false; - } - - if (! $node->var instanceof ArrayDimFetch) { - return false; - } - - $arrayDimFetch = $node->var; - if ($arrayDimFetch->dim === null) { - return false; - } - - if (! $arrayDimFetch->dim instanceof Variable) { - return false; - } - - return $this->nodeNameResolver->isName($arrayDimFetch->dim, $keyValueName); - } - ); - } - - public function isValueVarUsedNext(For_ $for, string $iteratedVariableSingle): bool - { - return (bool) $this->betterNodeFinder->findFirstNext($for, function (Node $node) use ( - $iteratedVariableSingle - ): bool { - if (! $node instanceof Variable) { - return false; - } - return $this->nodeNameResolver->isName($node, $iteratedVariableSingle); - }); - } - - /** - * @param Expr[] $condExprs - */ - public function isCondExprOneOrKeyValueNameNotNull(array $condExprs, ?string $keyValueName): bool - { - if (count($condExprs) !== 1) { - return true; - } - - return $keyValueName === null; - } - - public function isArrayDimFetchPartOfAssignOrArgParentCount(ArrayDimFetch $arrayDimFetch): bool - { - $parentNode = $arrayDimFetch->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - return false; - } - - if ($this->assignManipulator->isNodePartOfAssign($parentNode)) { - return true; - } - - return $this->isArgParentCount($parentNode); - } - - private function isArgParentCount(Node $node): bool - { - if (! $node instanceof Arg) { - return false; - } - - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof FuncCall) { - return false; - } - - return $this->nodeNameResolver->isName($parent, self::COUNT); - } -} diff --git a/rules/CodeQuality/NodeAnalyzer/ForeachAnalyzer.php b/rules/CodeQuality/NodeAnalyzer/ForeachAnalyzer.php index 5a11df5f9ce..993a41d9913 100644 --- a/rules/CodeQuality/NodeAnalyzer/ForeachAnalyzer.php +++ b/rules/CodeQuality/NodeAnalyzer/ForeachAnalyzer.php @@ -4,27 +4,17 @@ namespace Rector\CodeQuality\NodeAnalyzer; -use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Foreach_; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\NodeNameResolver\NodeNameResolver; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Comparing\NodeComparator; -final class ForeachAnalyzer +final readonly class ForeachAnalyzer { public function __construct( private NodeComparator $nodeComparator, - private ForAnalyzer $forAnalyzer, - private NodeNameResolver $nodeNameResolver, - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private BetterNodeFinder $betterNodeFinder ) { } @@ -53,7 +43,7 @@ public function matchAssignItemsOnlyForeachArrayVariable(Foreach_ $foreach): ?Ex return null; } - if ($onlyStatement->var->dim !== null) { + if ($onlyStatement->var->dim instanceof Expr) { return null; } @@ -63,70 +53,4 @@ public function matchAssignItemsOnlyForeachArrayVariable(Foreach_ $foreach): ?Ex return $onlyStatement->var->var; } - - /** - * @param Stmt[] $stmts - */ - public function useForeachVariableInStmts( - Expr $foreachedValue, - Expr $singleValue, - array $stmts, - string $keyValueName - ): void { - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - $stmts, - function (Node $node) use ($foreachedValue, $singleValue, $keyValueName): ?Expr { - if (! $node instanceof ArrayDimFetch) { - return null; - } - - // must be the same as foreach value - if (! $this->nodeComparator->areNodesEqual($node->var, $foreachedValue)) { - return null; - } - - if ($this->forAnalyzer->isArrayDimFetchPartOfAssignOrArgParentCount($node)) { - return null; - } - - // is dim same as key value name, ...[$i] - if (! $node->dim instanceof Variable) { - return null; - } - - if (! $this->nodeNameResolver->isName($node->dim, $keyValueName)) { - return null; - } - - return $singleValue; - } - ); - } - - public function isValueVarUsed(Foreach_ $foreach, string $singularValueVarName): bool - { - $isUsedInStmts = (bool) $this->betterNodeFinder->findFirst($foreach->stmts, function (Node $node) use ( - $singularValueVarName - ): bool { - if (! $node instanceof Variable) { - return false; - } - - return $this->nodeNameResolver->isName($node, $singularValueVarName); - }); - - if ($isUsedInStmts) { - return true; - } - - return (bool) $this->betterNodeFinder->findFirstNext($foreach, function (Node $node) use ( - $singularValueVarName - ): bool { - if (! $node instanceof Variable) { - return false; - } - - return $this->nodeNameResolver->isName($node, $singularValueVarName); - }); - } } diff --git a/rules/CodeQuality/NodeAnalyzer/LocalPropertyAnalyzer.php b/rules/CodeQuality/NodeAnalyzer/LocalPropertyAnalyzer.php index ca66f0a9f6b..07332188075 100644 --- a/rules/CodeQuality/NodeAnalyzer/LocalPropertyAnalyzer.php +++ b/rules/CodeQuality/NodeAnalyzer/LocalPropertyAnalyzer.php @@ -11,147 +11,185 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\FunctionLike; +use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\NodeTraverser; +use PhpParser\Node\Stmt\Function_; +use PhpParser\NodeVisitor; +use PHPStan\Analyser\Scope; use PHPStan\Type\MixedType; -use PHPStan\Type\Type; use Rector\CodeQuality\TypeResolver\ArrayDimFetchTypeResolver; -use Rector\Core\NodeAnalyzer\ClassAnalyzer; -use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use Rector\CodeQuality\ValueObject\DefinedPropertyWithType; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeNestingScope\ParentFinder; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; -final class LocalPropertyAnalyzer +final readonly class LocalPropertyAnalyzer { - /** - * @var string - */ - private const LARAVEL_COLLECTION_CLASS = 'Illuminate\Support\Collection'; + private const string LARAVEL_COLLECTION_CLASS = 'Illuminate\Support\Collection'; public function __construct( private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private ClassAnalyzer $classAnalyzer, private NodeNameResolver $nodeNameResolver, - private BetterNodeFinder $betterNodeFinder, private ArrayDimFetchTypeResolver $arrayDimFetchTypeResolver, private NodeTypeResolver $nodeTypeResolver, private PropertyFetchAnalyzer $propertyFetchAnalyzer, private TypeFactory $typeFactory, - private ParentFinder $parentFinder ) { } /** - * @return array + * @return DefinedPropertyWithType[] */ public function resolveFetchedPropertiesToTypesFromClass(Class_ $class): array { - $fetchedLocalPropertyNameToTypes = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($class->stmts, function (Node $node) use ( - &$fetchedLocalPropertyNameToTypes - ): ?int { - // skip anonymous class scope - $isAnonymousClass = $this->classAnalyzer->isAnonymousClass($node); - if ($isAnonymousClass) { - return NodeTraverser::DONT_TRAVERSE_CHILDREN; - } - - if (! $node instanceof PropertyFetch) { - return null; - } - - if (! $this->propertyFetchAnalyzer->isLocalPropertyFetch($node)) { - return null; - } - - if ($this->shouldSkipPropertyFetch($node)) { - return null; - } - - $propertyName = $this->nodeNameResolver->getName($node->name); - if ($propertyName === null) { - return null; - } - - $parentFunctionLike = $this->betterNodeFinder->findParentType($node, FunctionLike::class); - if (! $parentFunctionLike instanceof ClassMethod) { - return null; - } - - $propertyFetchType = $this->resolvePropertyFetchType($node); - $fetchedLocalPropertyNameToTypes[$propertyName][] = $propertyFetchType; - - return null; - }); + $definedPropertiesWithTypes = []; + + foreach ($class->getMethods() as $classMethod) { + $methodName = $this->nodeNameResolver->getName($classMethod); + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $classMethod->getStmts(), + function (Node $node) use (&$definedPropertiesWithTypes, $methodName): ?int { + if ($this->shouldSkip($node)) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($node instanceof Assign && ($node->var instanceof PropertyFetch || $node->var instanceof ArrayDimFetch)) { + $propertyFetch = $node->var; + + $propertyName = $this->resolvePropertyName( + $propertyFetch instanceof ArrayDimFetch ? $propertyFetch->var : $propertyFetch + ); + + if ($propertyName === null) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($propertyFetch instanceof ArrayDimFetch) { + $propertyType = $this->arrayDimFetchTypeResolver->resolve($propertyFetch, $node); + + $definedPropertiesWithTypes[] = new DefinedPropertyWithType( + $propertyName, + $propertyType, + $methodName + ); + + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + $propertyType = $this->nodeTypeResolver->getType($node->expr); + + $definedPropertiesWithTypes[] = new DefinedPropertyWithType( + $propertyName, + $propertyType, + $methodName + ); + + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + $propertyName = $this->resolvePropertyName($node); + if ($propertyName === null) { + return null; + } + + $definedPropertiesWithTypes[] = new DefinedPropertyWithType( + $propertyName, + new MixedType(), + $methodName + ); + + return null; + } + ); + } - return $this->normalizeToSingleType($fetchedLocalPropertyNameToTypes); + return $this->normalizeToSingleType($definedPropertiesWithTypes); } - private function shouldSkipPropertyFetch(PropertyFetch $propertyFetch): bool + private function shouldSkip(Node $node): bool { - // special Laravel collection scope - if ($this->shouldSkipForLaravelCollection($propertyFetch)) { + // skip anonymous classes and inner function + if ($node instanceof Class_ || $node instanceof Function_) { return true; } - if ($this->isPartOfClosureBind($propertyFetch)) { + // skip closure call + if ($node instanceof MethodCall && $node->var instanceof Closure) { return true; } - return $this->isPartOfClosureBindTo($propertyFetch); + if ($node instanceof StaticCall) { + return $this->nodeNameResolver->isName($node->class, self::LARAVEL_COLLECTION_CLASS); + } + + return false; } - private function resolvePropertyFetchType(PropertyFetch $propertyFetch): Type + private function resolvePropertyName(Node $node): string|null { - $parentNode = $propertyFetch->getAttribute(AttributeKey::PARENT_NODE); + if (! $node instanceof PropertyFetch) { + return null; + } - // possible get type - if ($parentNode instanceof Assign) { - return $this->nodeTypeResolver->getStaticType($parentNode->expr); + if (! $this->propertyFetchAnalyzer->isLocalPropertyFetch($node)) { + return null; } - if ($parentNode instanceof ArrayDimFetch) { - return $this->arrayDimFetchTypeResolver->resolve($parentNode); + if ($this->shouldSkipPropertyFetch($node)) { + return null; } - return new MixedType(); + return $this->nodeNameResolver->getName($node->name); } - /** - * @param array $propertyNameToTypes - * @return array - */ - private function normalizeToSingleType(array $propertyNameToTypes): array + private function shouldSkipPropertyFetch(PropertyFetch $propertyFetch): bool { - // normalize types to union - $propertyNameToType = []; - foreach ($propertyNameToTypes as $name => $types) { - $propertyNameToType[$name] = $this->typeFactory->createMixedPassedOrUnionType($types); + if ($this->isPartOfClosureBind($propertyFetch)) { + return true; } - return $propertyNameToType; + return ! $propertyFetch->name instanceof Identifier; } - private function shouldSkipForLaravelCollection(PropertyFetch $propertyFetch): bool + /** + * @param DefinedPropertyWithType[] $definedPropertiesWithTypes + * @return DefinedPropertyWithType[] + */ + private function normalizeToSingleType(array $definedPropertiesWithTypes): array { - $staticCallOrClassMethod = $this->parentFinder->findByTypes( - $propertyFetch, - [ClassMethod::class, StaticCall::class] - ); + $definedPropertiesWithTypesByPropertyName = []; + foreach ($definedPropertiesWithTypes as $definedPropertyWithType) { + $definedPropertiesWithTypesByPropertyName[$definedPropertyWithType->getName()][] = $definedPropertyWithType; + } - if (! $staticCallOrClassMethod instanceof StaticCall) { - return false; + $normalizedDefinedPropertiesWithTypes = []; + + foreach ($definedPropertiesWithTypesByPropertyName as $propertyName => $definedPropertiesWithTypes) { + if (count($definedPropertiesWithTypes) === 1) { + $normalizedDefinedPropertiesWithTypes[] = $definedPropertiesWithTypes[0]; + continue; + } + + $propertyTypes = []; + foreach ($definedPropertiesWithTypes as $definedPropertyWithType) { + /** @var DefinedPropertyWithType $definedPropertyWithType */ + $propertyTypes[] = $definedPropertyWithType->getType(); + } + + $normalizePropertyType = $this->typeFactory->createMixedPassedOrUnionType($propertyTypes); + $normalizedDefinedPropertiesWithTypes[] = new DefinedPropertyWithType( + $propertyName, + $normalizePropertyType, + // skip as multiple places can define the same property + null + ); } - return $this->nodeNameResolver->isName($staticCallOrClassMethod->class, self::LARAVEL_COLLECTION_CLASS); + return $normalizedDefinedPropertiesWithTypes; } /** @@ -160,29 +198,11 @@ private function shouldSkipForLaravelCollection(PropertyFetch $propertyFetch): b */ private function isPartOfClosureBind(PropertyFetch $propertyFetch): bool { - $parentStaticCall = $this->betterNodeFinder->findParentType($propertyFetch, StaticCall::class); - if (! $parentStaticCall instanceof StaticCall) { - return false; - } - - if (! $this->nodeNameResolver->isName($parentStaticCall->class, 'Closure')) { - return true; - } - - return $this->nodeNameResolver->isName($parentStaticCall->name, 'bind'); - } - - private function isPartOfClosureBindTo(PropertyFetch $propertyFetch): bool - { - $parentMethodCall = $this->betterNodeFinder->findParentType($propertyFetch, MethodCall::class); - if (! $parentMethodCall instanceof MethodCall) { - return false; - } - - if (! $parentMethodCall->var instanceof Closure) { + $scope = $propertyFetch->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { return false; } - return $this->nodeNameResolver->isName($parentMethodCall->name, 'bindTo'); + return $scope->isInClosureBind(); } } diff --git a/rules/CodeQuality/NodeAnalyzer/MissingPropertiesResolver.php b/rules/CodeQuality/NodeAnalyzer/MissingPropertiesResolver.php new file mode 100644 index 00000000000..65c9b1aacab --- /dev/null +++ b/rules/CodeQuality/NodeAnalyzer/MissingPropertiesResolver.php @@ -0,0 +1,57 @@ +classLikeAnalyzer->resolvePropertyNames($class); + + $missingPropertiesWithTypes = []; + + foreach ($definedPropertiesWithTypes as $definedPropertyWithType) { + // 1. property already exists, skip it + if (in_array($definedPropertyWithType->getName(), $existingPropertyNames, true)) { + continue; + } + + // 2. is part of class docblock or another magic, skip it + if ($classReflection->hasInstanceProperty($definedPropertyWithType->getName())) { + continue; + } + + // 3. is fetched by parent class on non-private property etc., skip it + $hasClassContextProperty = $this->propertyPresenceChecker->hasClassContextProperty( + $class, + $definedPropertyWithType + ); + + if ($hasClassContextProperty) { + continue; + } + + // it's most likely missing! + $missingPropertiesWithTypes[] = $definedPropertyWithType; + } + + return $missingPropertiesWithTypes; + } +} diff --git a/rules/CodeQuality/NodeAnalyzer/VariableDimFetchAssignResolver.php b/rules/CodeQuality/NodeAnalyzer/VariableDimFetchAssignResolver.php new file mode 100644 index 00000000000..5e64b79acf6 --- /dev/null +++ b/rules/CodeQuality/NodeAnalyzer/VariableDimFetchAssignResolver.php @@ -0,0 +1,101 @@ + + */ + public function resolveFromStmtsAndVariable(array $stmts, ?Assign $emptyArrayAssign): array + { + $exprs = []; + + $key = 0; + + foreach ($stmts as $stmt) { + if ($stmt instanceof Expression && $stmt->expr === $emptyArrayAssign) { + continue; + } + + if ($stmt instanceof Return_) { + continue; + } + + if (! $stmt instanceof Expression) { + return []; + } + + $stmtExpr = $stmt->expr; + if (! $stmtExpr instanceof Assign) { + return []; + } + + $assign = $stmtExpr; + + $dimValues = []; + + $arrayDimFetch = $assign->var; + while ($arrayDimFetch instanceof ArrayDimFetch) { + if ($arrayDimFetch->dim instanceof Expr && $this->exprAnalyzer->isDynamicExpr($arrayDimFetch->dim)) { + return []; + } + + $dimValues[] = $arrayDimFetch->dim instanceof Expr ? $this->valueResolver->getValue( + $arrayDimFetch->dim + ) : $key; + + $arrayDimFetch = $arrayDimFetch->var; + } + + ++$key; + + $this->setNestedKeysExpr($exprs, $dimValues, $assign->expr); + } + + return $exprs; + } + + /** + * @param mixed[] $exprsByKeys + * @param array $keys + */ + private function setNestedKeysExpr(array &$exprsByKeys, array $keys, Expr $expr): void + { + $reference = &$exprsByKeys; + + $keys = array_reverse($keys); + + foreach ($keys as $key) { + if ($reference instanceof Array_) { + // currently it fails here with Cannot use object of type PhpParser\Node\Expr\Array_ as array + throw new NotImplementedYetException(); + } + + $reference = &$reference[$key]; + } + + $reference = $expr; + } +} diff --git a/rules/CodeQuality/NodeFactory/ForeachFactory.php b/rules/CodeQuality/NodeFactory/ForeachFactory.php deleted file mode 100644 index 73a91faa61b..00000000000 --- a/rules/CodeQuality/NodeFactory/ForeachFactory.php +++ /dev/null @@ -1,35 +0,0 @@ -stmts = $for->stmts; - $foreach->keyVar = new Variable($keyValueName); - - return $foreach; - } -} diff --git a/rules/CodeQuality/NodeFactory/MissingPropertiesFactory.php b/rules/CodeQuality/NodeFactory/MissingPropertiesFactory.php index 37b4488af70..8b534032c7d 100644 --- a/rules/CodeQuality/NodeFactory/MissingPropertiesFactory.php +++ b/rules/CodeQuality/NodeFactory/MissingPropertiesFactory.php @@ -4,37 +4,72 @@ namespace Rector\CodeQuality\NodeFactory; +use PhpParser\Modifiers; +use PhpParser\Node; +use PhpParser\Node\PropertyItem; use PhpParser\Node\Stmt\Property; -use PHPStan\Type\Type; -use Rector\Core\PhpParser\Node\NodeFactory; +use Rector\CodeQuality\ValueObject\DefinedPropertyWithType; +use Rector\Php\PhpVersionProvider; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; -final class MissingPropertiesFactory +final readonly class MissingPropertiesFactory { public function __construct( - private NodeFactory $nodeFactory, - private PropertyTypeDecorator $propertyTypeDecorator + private PropertyTypeDecorator $propertyTypeDecorator, + private PhpVersionProvider $phpVersionProvider, + private StaticTypeMapper $staticTypeMapper ) { } /** - * @param array $fetchedLocalPropertyNameToTypes - * @param string[] $propertyNamesToComplete + * @param DefinedPropertyWithType[] $definedPropertiesWithType * @return Property[] */ - public function create(array $fetchedLocalPropertyNameToTypes, array $propertyNamesToComplete): array + public function create(array $definedPropertiesWithType): array { $newProperties = []; - foreach ($fetchedLocalPropertyNameToTypes as $propertyName => $propertyType) { - if (! in_array($propertyName, $propertyNamesToComplete, true)) { - continue; + foreach ($definedPropertiesWithType as $definedPropertyWithType) { + $visibilityModifier = $this->isFromAlwaysDefinedMethod($definedPropertyWithType) + ? Modifiers::PRIVATE + : Modifiers::PUBLIC; + + $property = new Property($visibilityModifier, [ + new PropertyItem($definedPropertyWithType->getName()), + ]); + + if ($this->isFromAlwaysDefinedMethod( + $definedPropertyWithType + ) && $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::TYPED_PROPERTIES)) { + $propertyType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $definedPropertyWithType->getType(), + TypeKind::PROPERTY + ); + if ($propertyType instanceof Node) { + $property->type = $propertyType; + $newProperties[] = $property; + + continue; + } } - $property = $this->nodeFactory->createPublicProperty($propertyName); - $this->propertyTypeDecorator->decorateProperty($property, $propertyType); + // fallback to docblock + $this->propertyTypeDecorator->decorateProperty($property, $definedPropertyWithType->getType()); $newProperties[] = $property; } return $newProperties; } + + private function isFromAlwaysDefinedMethod(DefinedPropertyWithType $definedPropertyWithType): bool + { + return in_array( + $definedPropertyWithType->getDefinedInMethodName(), + [MethodName::CONSTRUCT, MethodName::SET_UP], + true + ); + } } diff --git a/rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php b/rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php index f0076146322..bd0b22941d0 100644 --- a/rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php +++ b/rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php @@ -4,84 +4,29 @@ namespace Rector\CodeQuality\NodeFactory; -use PhpParser\Node; -use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\Property; -use PHPStan\Type\ArrayType; -use PHPStan\Type\MixedType; use PHPStan\Type\Type; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; -use Rector\Core\Php\PhpVersionProvider; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; -use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\Privatization\TypeManipulator\TypeNormalizer; -final class PropertyTypeDecorator +final readonly class PropertyTypeDecorator { public function __construct( - private PhpVersionProvider $phpVersionProvider, - private StaticTypeMapper $staticTypeMapper, private PhpDocTypeChanger $phpDocTypeChanger, - private PhpDocInfoFactory $phpDocInfoFactory + private PhpDocInfoFactory $phpDocInfoFactory, + private TypeNormalizer $typeNormalizer ) { } public function decorateProperty(Property $property, Type $propertyType): void { - $this->decoratePropertyWithVarDoc($property, $propertyType); - $this->decoratePropertyWithType($property, $propertyType); - } + // generalize false/true type to bool, as mostly default value but accepts both + $propertyType = $this->typeNormalizer->generalizeConstantTypes($propertyType); - private function decoratePropertyWithVarDoc(Property $property, Type $propertyType): void - { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); $phpDocInfo->makeMultiLined(); - if ($this->isNonMixedArrayType($propertyType)) { - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $propertyType); - $property->type = new Identifier('array'); - return; - } - - if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::TYPED_PROPERTIES)) { - $phpParserNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $propertyType, - TypeKind::PROPERTY() - ); - if (! $phpParserNode instanceof Node) { - // fallback to doc type in PHP 7.4 - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $propertyType); - } - } else { - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $propertyType); - } - } - - private function decoratePropertyWithType(Property $property, Type $propertyType): void - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::TYPED_PROPERTIES)) { - return; - } - - $phpParserNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyType, TypeKind::PROPERTY()); - if (! $phpParserNode instanceof Node) { - return; - } - - $property->type = $phpParserNode; - } - - private function isNonMixedArrayType(Type $type): bool - { - if (! $type instanceof ArrayType) { - return false; - } - - if ($type->getKeyType() instanceof MixedType) { - return false; - } - - return ! $type->getItemType() instanceof MixedType; + $this->phpDocTypeChanger->changeVarType($property, $phpDocInfo, $propertyType); } } diff --git a/rules/CodeQuality/NodeFactory/TypedPropertyFactory.php b/rules/CodeQuality/NodeFactory/TypedPropertyFactory.php new file mode 100644 index 00000000000..f62365e32fd --- /dev/null +++ b/rules/CodeQuality/NodeFactory/TypedPropertyFactory.php @@ -0,0 +1,56 @@ +createPropertyTypeNode($propertyTagValueNode, $class); + + return new Property(Modifiers::PRIVATE, [$propertyItem], [], $propertyTypeNode); + } + + public function createPropertyTypeNode( + PropertyTagValueNode $propertyTagValueNode, + Class_ $class, + bool $isNullable = true + ): Name|ComplexType|Identifier|null { + $propertyType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( + $propertyTagValueNode->type, + $class + ); + + $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyType, TypeKind::PROPERTY); + + if ($isNullable && ! $typeNode instanceof NullableType && ! $typeNode instanceof ComplexType && $typeNode instanceof Node) { + return new NullableType($typeNode); + } + + return $typeNode; + } +} diff --git a/rules/CodeQuality/NodeManipulator/ClassMethodParameterTypeManipulator.php b/rules/CodeQuality/NodeManipulator/ClassMethodParameterTypeManipulator.php deleted file mode 100644 index f525964fb1e..00000000000 --- a/rules/CodeQuality/NodeManipulator/ClassMethodParameterTypeManipulator.php +++ /dev/null @@ -1,176 +0,0 @@ -getParams() as $param) { - if (! $this->nodeTypeResolver->isObjectType($param, $objectType)) { - continue; - } - - $paramType = $this->nodeTypeResolver->resolve($param); - if (! $paramType->isSuperTypeOf($objectType)->yes()) { - continue; - } - - $this->refactorParamTypeHint($param, $replaceIntoType); - $this->refactorParamDocBlock($param, $classMethod, $phpDocType); - $this->refactorMethodCalls($param, $classMethod, $methodsReturningClassInstance); - } - } - - private function refactorParamTypeHint(Param $param, Identifier|Name|NullableType $replaceIntoType): void - { - if ($this->paramAnalyzer->isNullable($param) && ! $replaceIntoType instanceof NullableType) { - $replaceIntoType = new NullableType($replaceIntoType); - } - - $param->type = $replaceIntoType; - } - - private function refactorParamDocBlock(Param $param, ClassMethod $classMethod, Type $phpDocType): void - { - $paramName = $this->nodeNameResolver->getName($param->var); - if ($paramName === null) { - throw new ShouldNotHappenException(); - } - - if ($this->paramAnalyzer->isNullable($param)) { - if ($phpDocType instanceof UnionType) { - // Adding a UnionType into a new UnionType throws an exception so we need to "unpack" the types - $phpDocType = new UnionType([...$phpDocType->getTypes(), new NullType()]); - } else { - $phpDocType = new UnionType([$phpDocType, new NullType()]); - } - } - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - $this->phpDocTypeChanger->changeParamType($phpDocInfo, $phpDocType, $param, $paramName); - } - - /** - * @param string[] $methodsReturningClassInstance - */ - private function refactorMethodCalls( - Param $param, - ClassMethod $classMethod, - array $methodsReturningClassInstance - ): void { - if ($classMethod->stmts === null) { - return; - } - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod->stmts, function (Node $node) use ( - $param, - $methodsReturningClassInstance - ): void { - if (! ($node instanceof MethodCall)) { - return; - } - - $this->refactorMethodCall($param, $node, $methodsReturningClassInstance); - }); - } - - /** - * @param string[] $methodsReturningClassInstance - */ - private function refactorMethodCall( - Param $param, - MethodCall $methodCall, - array $methodsReturningClassInstance - ): void { - $paramName = $this->nodeNameResolver->getName($param->var); - if ($paramName === null) { - return; - } - if ($this->shouldSkipMethodCallRefactor($paramName, $methodCall, $methodsReturningClassInstance)) { - return; - } - - $assign = new Assign(new Variable($paramName), $methodCall); - - /** @var Node $parent */ - $parent = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Arg) { - $parent->value = $assign; - return; - } - - if (! $parent instanceof Expression) { - return; - } - - $parent->expr = $assign; - } - - /** - * @param string[] $methodsReturningClassInstance - */ - private function shouldSkipMethodCallRefactor( - string $paramName, - MethodCall $methodCall, - array $methodsReturningClassInstance - ): bool { - if (! $this->nodeNameResolver->isName($methodCall->var, $paramName)) { - return true; - } - - if (! $this->nodeNameResolver->isNames($methodCall->name, $methodsReturningClassInstance)) { - return true; - } - - $parentNode = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - return true; - } - - return $parentNode instanceof Assign; - } -} diff --git a/rules/CodeQuality/NodeManipulator/ClassMethodReturnTypeManipulator.php b/rules/CodeQuality/NodeManipulator/ClassMethodReturnTypeManipulator.php deleted file mode 100644 index da42abb055b..00000000000 --- a/rules/CodeQuality/NodeManipulator/ClassMethodReturnTypeManipulator.php +++ /dev/null @@ -1,69 +0,0 @@ -returnType; - if ($returnType === null) { - return; - } - - $isNullable = false; - if ($returnType instanceof NullableType) { - $isNullable = true; - $returnType = $returnType->type; - } - if (! $this->nodeTypeResolver->isObjectType($returnType, $objectType)) { - return; - } - - $paramType = $this->nodeTypeResolver->resolve($returnType); - if (! $paramType->isSuperTypeOf($objectType)->yes()) { - return; - } - - if ($isNullable) { - if ($phpDocType instanceof UnionType) { - // Adding a UnionType into a new UnionType throws an exception so we need to "unpack" the types - $phpDocType = new UnionType([...$phpDocType->getTypes(), new NullType()]); - } else { - $phpDocType = new UnionType([$phpDocType, new NullType()]); - } - if (! $replaceIntoType instanceof NullableType) { - $replaceIntoType = new NullableType($replaceIntoType); - } - } - $classMethod->returnType = $replaceIntoType; - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - $this->phpDocTypeChanger->changeReturnType($phpDocInfo, $phpDocType); - } -} diff --git a/rules/CodeQuality/NodeManipulator/ExprBoolCaster.php b/rules/CodeQuality/NodeManipulator/ExprBoolCaster.php index 633e2609b9f..d51d9a3ccf5 100644 --- a/rules/CodeQuality/NodeManipulator/ExprBoolCaster.php +++ b/rules/CodeQuality/NodeManipulator/ExprBoolCaster.php @@ -9,14 +9,15 @@ use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Cast\Bool_; -use PHPStan\Type\BooleanType; +use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; -use Rector\Core\PhpParser\Node\NodeFactory; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\NodeTypeResolver\PHPStan\Type\StaticTypeAnalyzer; +use Rector\PhpParser\Node\NodeFactory; use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper; -final class ExprBoolCaster +final readonly class ExprBoolCaster { public function __construct( private NodeTypeResolver $nodeTypeResolver, @@ -28,15 +29,15 @@ public function __construct( public function boolCastOrNullCompareIfNeeded(Expr $expr): Expr { - if (! $this->nodeTypeResolver->isNullableType($expr)) { - if (! $this->isBoolCastNeeded($expr)) { + $exprStaticType = $this->nodeTypeResolver->getType($expr); + if (! TypeCombinator::containsNull($exprStaticType)) { + if (! $this->isBoolCastNeeded($expr, $exprStaticType)) { return $expr; } return new Bool_($expr); } - $exprStaticType = $this->nodeTypeResolver->getStaticType($expr); // if we remove null type, still has to be trueable if ($exprStaticType instanceof UnionType) { $unionTypeWithoutNullType = $this->typeUnwrapper->removeNullTypeFromUnionType($exprStaticType); @@ -47,20 +48,20 @@ public function boolCastOrNullCompareIfNeeded(Expr $expr): Expr return new NotIdentical($expr, $this->nodeFactory->createNull()); } - if (! $this->isBoolCastNeeded($expr)) { + if (! $this->isBoolCastNeeded($expr, $exprStaticType)) { return $expr; } return new Bool_($expr); } - private function isBoolCastNeeded(Expr $expr): bool + private function isBoolCastNeeded(Expr $expr, Type $exprType): bool { if ($expr instanceof BooleanNot) { return false; } - if ($this->nodeTypeResolver->isStaticType($expr, BooleanType::class)) { + if ($exprType->isBoolean()->yes()) { return false; } diff --git a/rules/CodeQuality/NodeManipulator/NamedArgsSorter.php b/rules/CodeQuality/NodeManipulator/NamedArgsSorter.php new file mode 100644 index 00000000000..22821486aaf --- /dev/null +++ b/rules/CodeQuality/NodeManipulator/NamedArgsSorter.php @@ -0,0 +1,62 @@ + + */ + public function sortArgsToMatchReflectionParameters( + array $currentArgs, + FunctionReflection | MethodReflection $functionLikeReflection, + ): array { + $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors( + $functionLikeReflection->getVariants() + ); + + $parameters = $extendedParametersAcceptor->getParameters(); + + $order = []; + foreach ($parameters as $key => $parameter) { + $order[$parameter->getName()] = $key; + } + + $sortedArgs = []; + $toSortArgs = []; + foreach ($currentArgs as $currentArg) { + if (! $currentArg->name instanceof Identifier) { + $sortedArgs[] = $currentArg; + continue; + } + + $toSortArgs[] = $currentArg; + } + + usort( + $toSortArgs, + static function (Arg $arg1, Arg $arg2) use ($order): int { + /** @var Identifier $argName1 */ + $argName1 = $arg1->name; + /** @var Identifier $argName2 */ + $argName2 = $arg2->name; + + $order1 = $order[$argName1->name] ?? PHP_INT_MAX; + $order2 = $order[$argName2->name] ?? PHP_INT_MAX; + + return $order1 <=> $order2; + } + ); + + return [...$sortedArgs, ...$toSortArgs]; + } +} diff --git a/rules/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector.php b/rules/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector.php deleted file mode 100644 index c87eefe7c94..00000000000 --- a/rules/CodeQuality/Rector/Array_/ArrayThisCallToThisMethodCallRector.php +++ /dev/null @@ -1,152 +0,0 @@ -someMethod()', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $values = [$this, 'giveMeMore']; - } - - public function giveMeMore() - { - return 'more'; - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $values = $this->giveMeMore(); - } - - public function giveMeMore() - { - return 'more'; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Array_::class]; - } - - /** - * @param Array_ $node - */ - public function refactor(Node $node): ?Node - { - $arrayCallable = $this->arrayCallableMethodMatcher->match($node); - if (! $arrayCallable instanceof ArrayCallable) { - return null; - } - - if ($this->isAssignedToNetteMagicOnProperty($node)) { - return null; - } - if ($this->isInsideProperty($node)) { - return null; - } - - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - // skip if part of method - if ($parentNode instanceof Arg) { - return null; - } - - if (! $this->reflectionProvider->hasClass($arrayCallable->getClass())) { - return null; - } - - $classReflection = $this->reflectionProvider->getClass($arrayCallable->getClass()); - if (! $classReflection->hasMethod($arrayCallable->getMethod())) { - return null; - } - - $nativeReflectionClass = $classReflection->getNativeReflection(); - - $nativeReflectionMethod = $nativeReflectionClass->getMethod($arrayCallable->getMethod()); - if ($nativeReflectionMethod->getNumberOfParameters() === 0) { - return new MethodCall(new Variable('this'), $arrayCallable->getMethod()); - } - - $methodReflection = $classReflection->getNativeMethod($arrayCallable->getMethod()); - return $this->nodeFactory->createClosureFromMethodReflection($methodReflection); - } - - private function isAssignedToNetteMagicOnProperty(Array_ $array): bool - { - $parent = $array->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Assign) { - return false; - } - - if (! $parent->var instanceof ArrayDimFetch) { - return false; - } - - if (! $parent->var->var instanceof PropertyFetch) { - return false; - } - - /** @var PropertyFetch $propertyFetch */ - $propertyFetch = $parent->var->var; - return $this->isName($propertyFetch->name, 'on*'); - } - - private function isInsideProperty(Array_ $array): bool - { - $parentProperty = $this->betterNodeFinder->findParentType($array, Property::class); - - return $parentProperty !== null; - } -} diff --git a/rules/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector.php b/rules/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector.php deleted file mode 100644 index 354913a213d..00000000000 --- a/rules/CodeQuality/Rector/Array_/CallableThisArrayToAnonymousFunctionRector.php +++ /dev/null @@ -1,118 +0,0 @@ - $second; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $values = [1, 5, 3]; - usort($values, function ($first, $second) { - return $this->compareSize($first, $second); - }); - - return $values; - } - - private function compareSize($first, $second) - { - return $first <=> $second; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Array_::class]; - } - - /** - * @param Array_ $node - */ - public function refactor(Node $node): ?Node - { - $arrayCallable = $this->arrayCallableMethodMatcher->match($node); - if (! $arrayCallable instanceof ArrayCallable) { - return null; - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - - $phpMethodReflection = $this->reflectionResolver->resolveMethodReflection( - $arrayCallable->getClass(), - $arrayCallable->getMethod(), - $scope - ); - - if (! $phpMethodReflection instanceof PhpMethodReflection) { - return null; - } - - return $this->anonymousFunctionFactory->createFromPhpMethodReflection( - $phpMethodReflection, - $arrayCallable->getCallerExpr() - ); - } -} diff --git a/rules/CodeQuality/Rector/Assign/CombinedAssignRector.php b/rules/CodeQuality/Rector/Assign/CombinedAssignRector.php index bb42fa66ff6..6288a466267 100644 --- a/rules/CodeQuality/Rector/Assign/CombinedAssignRector.php +++ b/rules/CodeQuality/Rector/Assign/CombinedAssignRector.php @@ -5,10 +5,12 @@ namespace Rector\CodeQuality\Rector\Assign; use PhpParser\Node; +use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp; -use Rector\Core\PhpParser\Node\AssignAndBinaryMap; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Expr\BinaryOp\BitwiseXor; +use Rector\PhpParser\Node\AssignAndBinaryMap; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,7 +20,7 @@ final class CombinedAssignRector extends AbstractRector { public function __construct( - private AssignAndBinaryMap $assignAndBinaryMap + private readonly AssignAndBinaryMap $assignAndBinaryMap ) { } @@ -47,13 +49,18 @@ public function refactor(Node $node): ?Node return null; } - /** @var BinaryOp $binaryNode */ $binaryNode = $node->expr; if (! $this->nodeComparator->areNodesEqual($node->var, $binaryNode->left)) { return null; } + if ($binaryNode->left instanceof ArrayDimFetch && + $node->expr instanceof BitwiseXor + ) { + return null; + } + $assignClass = $this->assignAndBinaryMap->getAlternative($binaryNode); if ($assignClass === null) { return null; diff --git a/rules/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector.php b/rules/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector.php deleted file mode 100644 index f608ac90f8b..00000000000 --- a/rules/CodeQuality/Rector/Assign/SplitListAssignToSeparateLineRector.php +++ /dev/null @@ -1,148 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Assign::class]; - } - - /** - * @param Assign $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - /** @var Array_|List_ $leftArray */ - $leftArray = $node->var; - - /** @var Array_ $rightArray */ - $rightArray = $node->expr; - - $standaloneAssigns = $this->createStandaloneAssigns($leftArray, $rightArray); - $this->addNodesAfterNode($standaloneAssigns, $node); - - $this->removeNode($node); - - return $node; - } - - private function shouldSkip(Assign $assign): bool - { - if (! $assign->var instanceof Array_ && ! $assign->var instanceof List_) { - return true; - } - - $assignExpr = $assign->expr; - if (! $assignExpr instanceof Array_) { - return true; - } - - if (count($assign->var->items) !== count($assignExpr->items)) { - return true; - } - - // is value swap - return $this->isValueSwap($assign->var, $assignExpr); - } - - /** - * @return Assign[] - */ - private function createStandaloneAssigns(Array_ | List_ $expr, Array_ $rightArray): array - { - $standaloneAssigns = []; - foreach ($expr->items as $key => $leftArrayItem) { - if ($leftArrayItem === null) { - continue; - } - - $rightArrayItem = $rightArray->items[$key]; - - if (! $rightArrayItem instanceof ArrayItem) { - continue; - } - - $standaloneAssigns[] = new Assign($leftArrayItem->value, $rightArrayItem); - } - - return $standaloneAssigns; - } - - private function isValueSwap(Array_ | List_ $expr, Array_ $secondArray): bool - { - $firstArrayItemsHash = $this->getArrayItemsHash($expr); - $secondArrayItemsHash = $this->getArrayItemsHash($secondArray); - - return $firstArrayItemsHash === $secondArrayItemsHash; - } - - private function getArrayItemsHash(Array_ | List_ $node): string - { - $arrayItemsHashes = []; - foreach ($node->items as $arrayItem) { - $arrayItemsHashes[] = $this->nodeComparator->printWithoutComments($arrayItem); - } - - sort($arrayItemsHashes); - - $arrayItemsHash = implode('', $arrayItemsHashes); - - return sha1($arrayItemsHash); - } -} diff --git a/rules/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector.php b/rules/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector.php new file mode 100644 index 00000000000..3610c9659a9 --- /dev/null +++ b/rules/CodeQuality/Rector/Attribute/SortAttributeNamedArgsRector.php @@ -0,0 +1,102 @@ +args; + if (count($args) <= 1) { + return null; + } + + if (! $this->argsAnalyzer->hasNamedArg($args)) { + return null; + } + + $functionLikeReflection = $this->reflectionResolver->resolveConstructorReflectionFromAttribute($node); + if (! $functionLikeReflection instanceof MethodReflection) { + return null; + } + + $args = $this->namedArgsSorter->sortArgsToMatchReflectionParameters($args, $functionLikeReflection); + if ($node->args === $args) { + return null; + } + + $node->args = $args; + + return $node; + + } +} diff --git a/rules/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector.php b/rules/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector.php new file mode 100644 index 00000000000..865a4646c20 --- /dev/null +++ b/rules/CodeQuality/Rector/BooleanAnd/RemoveUselessIsObjectCheckRector.php @@ -0,0 +1,79 @@ +> + */ + public function getNodeTypes(): array + { + return [BooleanAnd::class]; + } + + /** + * @param BooleanAnd $node + */ + public function refactor(Node $node): ?Node + { + if ($node->left instanceof FuncCall + && $this->isName($node->left, 'is_object') + && $node->right instanceof Instanceof_) { + return $this->processRemoveUselessIsObject($node->left, $node->right); + } + + if (! $node->left instanceof Instanceof_) { + return null; + } + + if (! $node->right instanceof FuncCall) { + return null; + } + + if (! $this->isName($node->right, 'is_object')) { + return null; + } + + return $this->processRemoveUselessIsObject($node->right, $node->left); + } + + private function processRemoveUselessIsObject(FuncCall $funcCall, Instanceof_ $instanceof): ?Instanceof_ + { + if ($funcCall->isFirstClassCallable()) { + return null; + } + + $args = $funcCall->getArgs(); + if (! isset($args[0])) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($args[0]->value, $instanceof->expr)) { + return null; + } + + return $instanceof; + } +} diff --git a/rules/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector.php b/rules/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector.php new file mode 100644 index 00000000000..6078257de27 --- /dev/null +++ b/rules/CodeQuality/Rector/BooleanAnd/RepeatedAndNotEqualToNotInArrayRector.php @@ -0,0 +1,195 @@ +> + */ + public function getNodeTypes(): array + { + return [BooleanAnd::class]; + } + + /** + * @param BooleanAnd $node + */ + public function refactor(Node $node): ?BooleanNot + { + if (! $this->isNotEqualOrNotIdentical($node->right)) { + return null; + } + + // match compared variable and expr + if (! $node->left instanceof BooleanAnd && ! $this->isNotEqualOrNotIdentical($node->left)) { + return null; + } + + $comparedExprAndValueExprs = $this->matchComparedAndDesiredValues($node); + if ($comparedExprAndValueExprs === null) { + return null; + } + + if (count($comparedExprAndValueExprs) < 3) { + return null; + } + + // ensure all compared expr are the same + $valueExprs = $this->resolveValueExprs($comparedExprAndValueExprs); + + /** @var ComparedExprAndValueExpr $firstComparedExprAndValue */ + $firstComparedExprAndValue = array_pop($comparedExprAndValueExprs); + + // all compared expr must be equal + foreach ($comparedExprAndValueExprs as $comparedExprAndValueExpr) { + if (! $this->nodeComparator->areNodesEqual( + $firstComparedExprAndValue->getComparedExpr(), + $comparedExprAndValueExpr->getComparedExpr() + )) { + return null; + } + } + + $array = $this->nodeFactory->createArray($valueExprs); + + $args = $this->nodeFactory->createArgs([$firstComparedExprAndValue->getComparedExpr(), $array]); + + if ($this->isStrictComparison($node)) { + $args[] = new Arg(new ConstFetch(new Name('true'))); + } + + $inArrayFuncCall = new FuncCall(new Name('in_array'), $args); + + return new BooleanNot($inArrayFuncCall); + } + + private function isNotEqualOrNotIdentical(Expr $expr): bool + { + if ($expr instanceof NotIdentical) { + return true; + } + + return $expr instanceof NotEqual; + } + + private function matchComparedExprAndValueExpr(NotIdentical|NotEqual $expr): ComparedExprAndValueExpr + { + return new ComparedExprAndValueExpr($expr->left, $expr->right); + } + + /** + * @param ComparedExprAndValueExpr[] $comparedExprAndValueExprs + * @return Expr[] + */ + private function resolveValueExprs(array $comparedExprAndValueExprs): array + { + $valueExprs = []; + + foreach ($comparedExprAndValueExprs as $comparedExprAndValueExpr) { + $valueExprs[] = $comparedExprAndValueExpr->getValueExpr(); + } + + return $valueExprs; + } + + /** + * @return null|ComparedExprAndValueExpr[] + */ + private function matchComparedAndDesiredValues(BooleanAnd $booleanAnd): ?array + { + /** @var NotIdentical|NotEqual $rightCompare */ + $rightCompare = $booleanAnd->right; + + // match compared expr and desired value + $comparedExprAndValueExprs = [$this->matchComparedExprAndValueExpr($rightCompare)]; + + $currentBooleanAnd = $booleanAnd; + + while ($currentBooleanAnd->left instanceof BooleanAnd) { + if (! $this->isNotEqualOrNotIdentical($currentBooleanAnd->left->right)) { + return null; + } + + /** @var NotIdentical|NotEqual $leftRight */ + $leftRight = $currentBooleanAnd->left->right; + $comparedExprAndValueExprs[] = $this->matchComparedExprAndValueExpr($leftRight); + $currentBooleanAnd = $currentBooleanAnd->left; + } + + if (! $this->isNotEqualOrNotIdentical($currentBooleanAnd->left)) { + return null; + } + + /** @var NotIdentical|NotEqual $leftCompare */ + $leftCompare = $currentBooleanAnd->left; + + $comparedExprAndValueExprs[] = $this->matchComparedExprAndValueExpr($leftCompare); + + // keep original natural order, as left/right goes from bottom up + return array_reverse($comparedExprAndValueExprs); + } + + private function isStrictComparison(BooleanAnd $booleanAnd): bool + { + $notIdenticals = $this->betterNodeFinder->findInstanceOf($booleanAnd, NotIdentical::class); + $notEquals = $this->betterNodeFinder->findInstanceOf($booleanAnd, NotEqual::class); + + if ($notIdenticals !== []) { + // mix not identical and not equals, keep as is + // @see https://3v4l.org/2SoHZ + return $notEquals === []; + } + + return false; + } +} diff --git a/rules/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector.php b/rules/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector.php index ec24cfb9009..9b403b60cf5 100644 --- a/rules/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector.php +++ b/rules/CodeQuality/Rector/BooleanAnd/SimplifyEmptyArrayCheckRector.php @@ -10,21 +10,20 @@ use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\Empty_; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\NodeManipulator\BinaryOpManipulator; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Expr\Variable; +use Rector\NodeManipulator\BinaryOpManipulator; use Rector\Php71\ValueObject\TwoNodeMatch; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/EZ2P4 - * @see https://3v4l.org/egtb5 * @see \Rector\Tests\CodeQuality\Rector\BooleanAnd\SimplifyEmptyArrayCheckRector\SimplifyEmptyArrayCheckRectorTest */ final class SimplifyEmptyArrayCheckRector extends AbstractRector { public function __construct( - private BinaryOpManipulator $binaryOpManipulator + private readonly BinaryOpManipulator $binaryOpManipulator ) { } @@ -49,25 +48,69 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $twoNodeMatch = $this->binaryOpManipulator->matchFirstAndSecondConditionNode( - $node, - // is_array(...) - function (Node $node): bool { - if (! $node instanceof FuncCall) { - return false; - } - return $this->isName($node, 'is_array'); - }, - Empty_::class - ); + $twoNodeMatch = $this->resolveTwoNodeMatch($node); if (! $twoNodeMatch instanceof TwoNodeMatch) { return null; } + /** @var FuncCall $isArrayExpr */ + $isArrayExpr = $twoNodeMatch->getFirstExpr(); + + $firstArgValue = $isArrayExpr->getArgs()[0] + ->value; + /** @var Empty_ $emptyOrNotIdenticalNode */ $emptyOrNotIdenticalNode = $twoNodeMatch->getSecondExpr(); + if ($emptyOrNotIdenticalNode->expr instanceof FuncCall && $this->nodeComparator->areNodesEqual( + $emptyOrNotIdenticalNode->expr->getArgs()[0] + ->value, + $firstArgValue + )) { + return new Identical($emptyOrNotIdenticalNode->expr, new Array_()); + } + + if (! $this->nodeComparator->areNodesEqual($emptyOrNotIdenticalNode->expr, $firstArgValue)) { + return null; + } + return new Identical($emptyOrNotIdenticalNode->expr, new Array_()); } + + private function resolveTwoNodeMatch(BooleanAnd $booleanAnd): ?TwoNodeMatch + { + return $this->binaryOpManipulator->matchFirstAndSecondConditionNode( + $booleanAnd, + // is_array(...) + function (Node $node): bool { + if (! $node instanceof FuncCall) { + return false; + } + + if ($node->isFirstClassCallable()) { + return false; + } + + if (! $this->isName($node, 'is_array')) { + return false; + } + + if (isset($node->getArgs()[0])) { + return $node->getArgs()[0] + ->value instanceof Variable; + } + + return false; + }, + // empty(...) + function (Node $node): bool { + if (! $node instanceof Empty_) { + return false; + } + + return $node->expr instanceof Variable; + } + ); + } } diff --git a/rules/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector.php b/rules/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector.php new file mode 100644 index 00000000000..0d395974f3a --- /dev/null +++ b/rules/CodeQuality/Rector/BooleanNot/ReplaceConstantBooleanNotRector.php @@ -0,0 +1,82 @@ +> + */ + public function getNodeTypes(): array + { + return [BooleanNot::class]; + } + + /** + * @param BooleanNot $node + */ + public function refactor(Node $node): ?Node + { + if ($this->valueResolver->isFalse($node->expr)) { + return new ConstFetch(new Name('true')); + } + + if ($this->valueResolver->isTrue($node->expr)) { + return new ConstFetch(new Name('false')); + } + + return null; + } +} diff --git a/rules/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector.php b/rules/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector.php new file mode 100644 index 00000000000..7ebd4cb53bf --- /dev/null +++ b/rules/CodeQuality/Rector/BooleanNot/ReplaceMultipleBooleanNotRector.php @@ -0,0 +1,68 @@ +> + */ + public function getNodeTypes(): array + { + return [BooleanNot::class]; + } + + /** + * @param BooleanNot $node + */ + public function refactor(Node $node): ?Node + { + $depth = 0; + $expr = $node->expr; + while ($expr instanceof BooleanNot) { + ++$depth; + $expr = $expr->expr; + } + + if ($depth === 0) { + return null; + } + + if ($depth % 2 === 0) { + $node->expr = $expr; + return $node; + } + + return new Bool_($expr); + } +} diff --git a/rules/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector.php b/rules/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector.php index c0a14754e02..487f3e71af0 100644 --- a/rules/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector.php +++ b/rules/CodeQuality/Rector/BooleanNot/SimplifyDeMorganBinaryRector.php @@ -5,21 +5,21 @@ namespace Rector\CodeQuality\Rector\BooleanNot; use PhpParser\Node; +use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BooleanNot; -use Rector\Core\NodeManipulator\BinaryOpManipulator; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeManipulator\BinaryOpManipulator; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/questions/20043664/de-morgans-law * @see \Rector\Tests\CodeQuality\Rector\BooleanNot\SimplifyDeMorganBinaryRector\SimplifyDeMorganBinaryRectorTest */ final class SimplifyDeMorganBinaryRector extends AbstractRector { public function __construct( - private BinaryOpManipulator $binaryOpManipulator + private readonly BinaryOpManipulator $binaryOpManipulator ) { } @@ -56,12 +56,12 @@ public function getNodeTypes(): array /** * @param BooleanNot $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?BinaryOp { if (! $node->expr instanceof BooleanOr) { return null; } - return $this->binaryOpManipulator->inverseBinaryOp($node->expr); + return $this->binaryOpManipulator->inverseBooleanOr($node->expr); } } diff --git a/rules/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector.php b/rules/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector.php new file mode 100644 index 00000000000..f6f9372a11b --- /dev/null +++ b/rules/CodeQuality/Rector/BooleanOr/RepeatedOrEqualToInArrayRector.php @@ -0,0 +1,187 @@ +> + */ + public function getNodeTypes(): array + { + return [BooleanOr::class]; + } + + /** + * @param BooleanOr $node + */ + public function refactor(Node $node): ?FuncCall + { + if (! $this->isEqualOrIdentical($node->right)) { + return null; + } + + // match compared variable and expr + if (! $node->left instanceof BooleanOr && ! $this->isEqualOrIdentical($node->left)) { + return null; + } + + $comparedExprAndValueExprs = $this->matchComparedAndDesiredValues($node); + if ($comparedExprAndValueExprs === null) { + return null; + } + + if (count($comparedExprAndValueExprs) < 3) { + return null; + } + + // ensure all compared expr are the same + $valueExprs = $this->resolveValueExprs($comparedExprAndValueExprs); + + /** @var ComparedExprAndValueExpr $firstComparedExprAndValue */ + $firstComparedExprAndValue = array_pop($comparedExprAndValueExprs); + + // all compared expr must be equal + foreach ($comparedExprAndValueExprs as $comparedExprAndValueExpr) { + if (! $this->nodeComparator->areNodesEqual( + $firstComparedExprAndValue->getComparedExpr(), + $comparedExprAndValueExpr->getComparedExpr() + )) { + return null; + } + } + + $array = $this->nodeFactory->createArray($valueExprs); + + $args = $this->nodeFactory->createArgs([$firstComparedExprAndValue->getComparedExpr(), $array]); + + $identicals = $this->betterNodeFinder->findInstanceOf($node, Identical::class); + $equals = $this->betterNodeFinder->findInstanceOf($node, Equal::class); + + if ($identicals !== []) { + if ($equals !== []) { + // mix identical and equals, keep as is + // @see https://3v4l.org/24cFl + return null; + } + + $args[] = new Arg(new ConstFetch(new Name('true'))); + } + + return new FuncCall(new Name('in_array'), $args); + } + + private function isEqualOrIdentical(Expr $expr): bool + { + if ($expr instanceof Identical) { + return true; + } + + return $expr instanceof Equal; + } + + private function matchComparedExprAndValueExpr(Identical|Equal $expr): ComparedExprAndValueExpr + { + return new ComparedExprAndValueExpr($expr->left, $expr->right); + } + + /** + * @param ComparedExprAndValueExpr[] $comparedExprAndValueExprs + * @return Expr[] + */ + private function resolveValueExprs(array $comparedExprAndValueExprs): array + { + $valueExprs = []; + + foreach ($comparedExprAndValueExprs as $comparedExprAndValueExpr) { + $valueExprs[] = $comparedExprAndValueExpr->getValueExpr(); + } + + return $valueExprs; + } + + /** + * @return null|ComparedExprAndValueExpr[] + */ + private function matchComparedAndDesiredValues(BooleanOr $booleanOr): ?array + { + /** @var Identical|Equal $rightCompare */ + $rightCompare = $booleanOr->right; + + // match compared expr and desired value + $comparedExprAndValueExprs = [$this->matchComparedExprAndValueExpr($rightCompare)]; + + $currentBooleanOr = $booleanOr; + + while ($currentBooleanOr->left instanceof BooleanOr) { + if (! $this->isEqualOrIdentical($currentBooleanOr->left->right)) { + return null; + } + + /** @var Identical|Equal $leftRight */ + $leftRight = $currentBooleanOr->left->right; + $comparedExprAndValueExprs[] = $this->matchComparedExprAndValueExpr($leftRight); + $currentBooleanOr = $currentBooleanOr->left; + } + + if (! $this->isEqualOrIdentical($currentBooleanOr->left)) { + return null; + } + + /** @var Identical|Equal $leftCompare */ + $leftCompare = $currentBooleanOr->left; + + $comparedExprAndValueExprs[] = $this->matchComparedExprAndValueExpr($leftCompare); + + // keep original natural order, as left/right goes from bottom up + return array_reverse($comparedExprAndValueExprs); + } +} diff --git a/rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php b/rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php index c77815c18da..ba36f610026 100644 --- a/rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php +++ b/rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php @@ -8,35 +8,32 @@ use PhpParser\Node\Arg; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\New_; +use PhpParser\Node\Expr\Throw_; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Catch_; -use PhpParser\Node\Stmt\Throw_; -use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\ObjectType; -use PHPStan\Type\TypeWithClassName; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; +use PHPStan\Type\TypeCombinator; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://github.com/thecodingmachine/phpstan-strict-rules/blob/e3d746a61d38993ca2bc2e2fcda7012150de120c/src/Rules/Exceptions/ThrowMustBundlePreviousExceptionRule.php#L83 * @see \Rector\Tests\CodeQuality\Rector\Catch_\ThrowWithPreviousExceptionRector\ThrowWithPreviousExceptionRectorTest */ final class ThrowWithPreviousExceptionRector extends AbstractRector { - /** - * @var int - */ - private const DEFAULT_EXCEPTION_ARGUMENT_POSITION = 2; + private const int DEFAULT_EXCEPTION_ARGUMENT_POSITION = 2; public function __construct( - private ReflectionProvider $reflectionProvider + private readonly ReflectionProvider $reflectionProvider ) { } @@ -54,7 +51,7 @@ public function run() try { $someCode = 1; } catch (Throwable $throwable) { - throw new AnotherException('ups'); + throw new \RuntimeException('ups'); } } } @@ -68,7 +65,7 @@ public function run() try { $someCode = 1; } catch (Throwable $throwable) { - throw new AnotherException('ups', $throwable->getCode(), $throwable); + throw new \RuntimeException('ups', $throwable->getCode(), $throwable); } } } @@ -96,18 +93,27 @@ public function refactor(Node $node): ?Node return null; } - $this->traverseNodesWithCallable($node->stmts, function (Node $node) use ($caughtThrowableVariable): ?int { + $isChanged = false; + $this->traverseNodesWithCallable($node->stmts, function (Node $node) use ( + $caughtThrowableVariable, + &$isChanged + ): ?int { if (! $node instanceof Throw_) { return null; } - return $this->refactorThrow($node, $caughtThrowableVariable); + $isChanged = $this->refactorThrow($node, $caughtThrowableVariable); + return $isChanged; }); + if (! (bool) $isChanged) { + return null; + } + return $node; } - private function refactorThrow(Throw_ $throw, Variable $catchedThrowableVariable): ?int + private function refactorThrow(Throw_ $throw, Variable $caughtThrowableVariable): ?int { if (! $throw->expr instanceof New_) { return null; @@ -123,45 +129,123 @@ private function refactorThrow(Throw_ $throw, Variable $catchedThrowableVariable return null; } + if ($new->isFirstClassCallable()) { + return null; + } + // exception is bundled - if (isset($new->args[$exceptionArgumentPosition])) { + if (isset($new->getArgs()[$exceptionArgumentPosition])) { return null; } + /** @var Arg|null $messageArgument */ + $messageArgument = $new->args[0] ?? null; + $shouldUseNamedArguments = $messageArgument instanceof Arg && $messageArgument->name instanceof Identifier; + + $hasChanged = false; if (! isset($new->args[0])) { // get previous message - $new->args[0] = new Arg(new MethodCall($catchedThrowableVariable, 'getMessage')); + $getMessageMethodCall = new MethodCall($caughtThrowableVariable, 'getMessage'); + $new->args[0] = new Arg($getMessageMethodCall); + $hasChanged = true; + } elseif ($new->args[0] instanceof Arg && $new->args[0]->name instanceof Identifier && $new->args[0]->name->toString() === 'previous' && $this->nodeComparator->areNodesEqual( + $new->args[0]->value, + $caughtThrowableVariable + )) { + $new->args[0]->name->name = 'message'; + $new->args[0]->value = new MethodCall($caughtThrowableVariable, 'getMessage'); + $hasChanged = true; } - if (! isset($new->args[1])) { - // get previous code - $new->args[1] = new Arg(new MethodCall($catchedThrowableVariable, 'getCode')); + if (! isset($new->getArgs()[1])) { + if ($this->hasParameter($new, 'code') && ! $this->hasArgument($new, 'code')) { + // get previous code + $new->args[1] = new Arg( + new MethodCall($caughtThrowableVariable, 'getCode'), + name: $shouldUseNamedArguments ? new Identifier('code') : null + ); + $hasChanged = true; + } else { + return null; + } } + /** @var Arg $arg1 */ $arg1 = $new->args[1]; if ($arg1->name instanceof Identifier && $arg1->name->toString() === 'previous') { - $new->args[1] = new Arg(new MethodCall($catchedThrowableVariable, 'getCode')); - $new->args[$exceptionArgumentPosition] = $arg1; - } else { - $new->args[$exceptionArgumentPosition] = new Arg($catchedThrowableVariable); + if ($this->hasParameter($new, 'code') && ! $this->hasArgument($new, 'code')) { + $new->args[1] = new Arg( + new MethodCall($caughtThrowableVariable, 'getCode'), + name: $shouldUseNamedArguments ? new Identifier('code') : null + ); + $new->args[$exceptionArgumentPosition] = $arg1; + $hasChanged = true; + } elseif (! $hasChanged) { + return null; + } + } elseif ($this->hasParameter($new, 'previous') && ! $this->hasArgument($new, 'previous')) { + $new->args[$exceptionArgumentPosition] = new Arg( + $caughtThrowableVariable, + name: $shouldUseNamedArguments ? new Identifier('previous') : null + ); + $hasChanged = true; + } elseif (! $hasChanged) { + return null; } // null the node, to fix broken format preserving printers, see https://github.com/rectorphp/rector/issues/5576 $new->setAttribute(AttributeKey::ORIGINAL_NODE, null); // nothing more to add - return NodeTraverser::DONT_TRAVERSE_CHILDREN; + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } - private function resolveExceptionArgumentPosition(Name $exceptionName): ?int + private function hasParameter(New_ $new, string $parameterName): bool { - $className = $this->getName($exceptionName); + $className = $this->getName($new->class); + if ($className === null) { + return false; + } - // is native exception? - if (! \str_contains($className, '\\')) { - return self::DEFAULT_EXCEPTION_ARGUMENT_POSITION; + if (! $this->reflectionProvider->hasClass($className)) { + return false; + } + + $classReflection = $this->reflectionProvider->getClass($className); + $construct = $classReflection->hasMethod(MethodName::CONSTRUCT); + + if (! $construct) { + return false; + } + + $extendedMethodReflection = $classReflection->getConstructor(); + $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors( + $extendedMethodReflection->getVariants() + ); + + foreach ($extendedParametersAcceptor->getParameters() as $extendedParameterReflection) { + if ($extendedParameterReflection->getName() === $parameterName) { + return true; + } } + return false; + } + + private function hasArgument(New_ $new, string $argumentName): bool + { + foreach ($new->getArgs() as $arg) { + if ($arg->name instanceof Identifier && $arg->name->toString() === $argumentName) { + return true; + } + } + + return false; + } + + private function resolveExceptionArgumentPosition(Name $exceptionName): ?int + { + $className = $this->getName($exceptionName); if (! $this->reflectionProvider->hasClass($className)) { return self::DEFAULT_EXCEPTION_ARGUMENT_POSITION; } @@ -172,16 +256,25 @@ private function resolveExceptionArgumentPosition(Name $exceptionName): ?int return self::DEFAULT_EXCEPTION_ARGUMENT_POSITION; } - $constructorReflectionMethod = $classReflection->getConstructor(); - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($constructorReflectionMethod->getVariants()); + $extendedMethodReflection = $classReflection->getConstructor(); + $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors( + $extendedMethodReflection->getVariants() + ); + + foreach ($extendedParametersAcceptor->getParameters() as $position => $extendedParameterReflection) { + $parameterType = $extendedParameterReflection->getType(); + $className = ClassNameFromObjectTypeResolver::resolve($extendedParameterReflection->getType()); + + $objectType = new ObjectType('Throwable'); + if ($className === null) { + $parameterType = TypeCombinator::removeNull($parameterType); + if ($objectType->isSuperTypeOf($parameterType)->yes()) { + return $position; + } - foreach ($parametersAcceptor->getParameters() as $position => $parameterReflection) { - $parameterType = $parameterReflection->getType(); - if (! $parameterType instanceof TypeWithClassName) { continue; } - $objectType = new ObjectType('Throwable'); if ($objectType->isSuperTypeOf($parameterType)->no()) { continue; } diff --git a/rules/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector.php b/rules/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector.php new file mode 100644 index 00000000000..8e0c4c0b7b2 --- /dev/null +++ b/rules/CodeQuality/Rector/ClassConstFetch/VariableConstFetchToClassConstFetchRector.php @@ -0,0 +1,121 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassConstFetch::class]; + } + + /** + * @param ClassConstFetch $node + */ + public function refactor(Node $node): ?ClassConstFetch + { + if (! $node->class instanceof Variable) { + return null; + } + + if (! $node->name instanceof Identifier) { + return null; + } + + $constantName = $this->getName($node->name); + if (! is_string($constantName)) { + return null; + } + + $classObjectType = $this->nodeTypeResolver->getNativeType($node->class); + if (! $classObjectType instanceof ObjectType) { + return null; + } + + if (! $classObjectType->hasConstant($constantName)->yes()) { + return null; + } + + if (! $this->reflectionProvider->hasClass($classObjectType->getClassName())) { + return null; + } + + $classReflection = $this->reflectionProvider->getClass($classObjectType->getClassName()); + if (! $classReflection->isFinalByKeyword()) { + if (! $classReflection->hasConstant($constantName)) { + return null; + } + + $constant = $classReflection->getConstant($constantName); + if (! $constant->isFinalByKeyword()) { + return null; + } + } + + $node->class = new FullyQualified($classObjectType->getClassName()); + + return $node; + } +} diff --git a/rules/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector.php b/rules/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector.php deleted file mode 100644 index 13465171377..00000000000 --- a/rules/CodeQuality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector.php +++ /dev/null @@ -1,211 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, Property::class]; - } - - /** - * @param ClassMethod|Property $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof ClassMethod) { - $this->refactorClassMethod($node); - return $node; - } - - return $this->refactorProperty($node); - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::DATE_TIME_INTERFACE; - } - - private function refactorProperty(Property $property): ?Node - { - $type = $property->type; - if ($type === null) { - return null; - } - - $isNullable = false; - if ($type instanceof NullableType) { - $isNullable = true; - $type = $type->type; - } - if (! $this->isObjectType($type, new ObjectType(self::DATE_TIME))) { - return null; - } - - $types = $this->determinePhpDocTypes($property->type); - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - $this->phpDocTypeChanger->changeVarType($phpDocInfo, new UnionType($types)); - - $property->type = new FullyQualified('DateTimeInterface'); - if ($isNullable) { - $property->type = new NullableType($property->type); - } - return $property; - } - - /** - * @return Type[] - */ - private function determinePhpDocTypes(?Node $node): array - { - $types = [new ObjectType(self::DATE_TIME), new ObjectType('DateTimeImmutable')]; - - if ($this->canHaveNullType($node)) { - $types[] = new NullType(); - } - - return $types; - } - - private function canHaveNullType(?Node $node): bool - { - if ($node instanceof Param) { - return $this->paramAnalyzer->isNullable($node); - } - - return $node instanceof NullableType; - } - - private function refactorClassMethod(ClassMethod $classMethod): void - { - if ($this->shouldSkipExactlyReturnDateTime($classMethod)) { - return; - } - - $fromObjectType = new ObjectType(self::DATE_TIME); - $fullyQualified = new FullyQualified('DateTimeInterface'); - $unionType = new UnionType([new ObjectType(self::DATE_TIME), new ObjectType('DateTimeImmutable')]); - - $this->classMethodParameterTypeManipulator->refactorFunctionParameters( - $classMethod, - $fromObjectType, - $fullyQualified, - $unionType, - self::METHODS_RETURNING_CLASS_INSTANCE_MAP - ); - if (! $classMethod->returnType instanceof Node) { - return; - } - $this->classMethodReturnTypeManipulator->refactorFunctionReturnType( - $classMethod, - $fromObjectType, - $fullyQualified, - $unionType - ); - } - - private function shouldSkipExactlyReturnDateTime(ClassMethod $classMethod): bool - { - $return = $this->betterNodeFinder->findFirst( - (array) $classMethod->stmts, - fn (Node $node): bool => $node instanceof Return_ - ); - if (! $return instanceof Return_) { - return false; - } - - if (! $return->expr instanceof Expr) { - return false; - } - - if (! $this->callAnalyzer->isNewInstance($this->betterNodeFinder, $return->expr)) { - return false; - } - - $type = $this->nodeTypeResolver->resolve($return->expr); - return $type instanceof ObjectType && $type->getClassName() === self::DATE_TIME; - } -} diff --git a/rules/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector.php b/rules/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector.php new file mode 100644 index 00000000000..cc193e975ca --- /dev/null +++ b/rules/CodeQuality/Rector/ClassMethod/ExplicitReturnNullRector.php @@ -0,0 +1,192 @@ + 50) { + return 'yes'; + } + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + /** + * @return string|null + */ + public function run(int $number) + { + if ($number > 50) { + return 'yes'; + } + + return null; + } +} +CODE_SAMPLE + ), + + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + // known return type, nothing to improve + if ($node->returnType instanceof Node) { + return null; + } + + if ($node instanceof ClassMethod && $this->isName($node, MethodName::CONSTRUCT)) { + return null; + } + + $returnType = $this->returnTypeInferer->inferFunctionLike($node); + if (! $returnType instanceof UnionType) { + return null; + } + + $hasGoto = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped( + $node, + fn (Node $node): bool => $node instanceof Goto_ + ); + + if ($hasGoto) { + return null; + } + + $hasChanged = false; + $this->traverseNodesWithCallable((array) $node->stmts, static function (Node $node) use ( + &$hasChanged + ): int|null|Return_ { + if ($node instanceof Class_ || $node instanceof Function_ || $node instanceof Closure) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($node instanceof Return_ && ! $node->expr instanceof Expr) { + $hasChanged = true; + $node->expr = new ConstFetch(new Name('null')); + return $node; + } + + return null; + }); + + // allow non native @return never type use + // to avoid noise for addition returns + if (! $this->silentVoidResolver->hasSilentVoid($node, false)) { + if ($hasChanged) { + $this->transformDocUnionVoidToUnionNull($node); + return $node; + } + + return null; + } + + $node->stmts[] = new Return_(new ConstFetch(new Name('null'))); + + $this->transformDocUnionVoidToUnionNull($node); + + return $node; + } + + private function transformDocUnionVoidToUnionNull(ClassMethod|Function_ $node): void + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + $returnType = $phpDocInfo->getReturnType(); + if (! $returnType instanceof UnionType) { + return; + } + + $newTypes = []; + $hasChanged = false; + foreach ($returnType->getTypes() as $type) { + if ($type->isVoid()->yes()) { + $type = new NullType(); + $hasChanged = true; + } + + $newTypes[] = $type; + } + + if (! $hasChanged) { + return; + } + + $type = $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($newTypes); + if (! $type instanceof UnionType) { + return; + } + + $this->phpDocTypeChanger->changeReturnType($node, $phpDocInfo, $type); + } +} diff --git a/rules/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector.php b/rules/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector.php new file mode 100644 index 00000000000..a992e660924 --- /dev/null +++ b/rules/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector.php @@ -0,0 +1,251 @@ + 'Timmy', + 'surname' => 'Back', + ]; +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + $stmts = $node->stmts; + + // skip primitive cases, as may be on purpose + if (count($stmts) < 3) { + return null; + } + + $lastStmt = $node->stmts[count($stmts) - 1] ?? null; + if (! $lastStmt instanceof Return_) { + return null; + } + + if (! $lastStmt->expr instanceof Variable) { + return null; + } + + $returnedVariableName = $this->getName($lastStmt->expr); + if (! is_string($returnedVariableName)) { + return null; + } + + if ($node instanceof FunctionLike) { + foreach ($node->getParams() as $param) { + if ($this->isName($param->var, $returnedVariableName)) { + return null; + } + } + + if ($node instanceof Closure) { + foreach ($node->uses as $use) { + if (! $use->byRef) { + continue; + } + + if ($this->isName($use->var, $returnedVariableName)) { + return null; + } + } + } + } + + $emptyArrayAssign = $this->resolveDefaultEmptyArrayAssign($stmts, $returnedVariableName); + if (! $this->areAssignExclusiveToDimFetchVariable($stmts, $emptyArrayAssign, $returnedVariableName)) { + return null; + } + + // init maybe from before if + if (! $emptyArrayAssign instanceof Assign && ! $node instanceof FunctionLike) { + return null; + } + + try { + $keysAndExprsByKey = $this->variableDimFetchAssignResolver->resolveFromStmtsAndVariable( + $stmts, + $emptyArrayAssign, + ); + } catch (NotImplementedYetException) { + // dim fetch assign of nested arrays is hard to resolve + return null; + } + + if ($keysAndExprsByKey === []) { + + return null; + } + + $array = $this->nodeFactory->createArray($keysAndExprsByKey); + $node->stmts = [new Return_($array)]; + + return $node; + } + + /** + * Only: + * $items['...'] = $result; + * + * @param Stmt[] $stmts + */ + private function areAssignExclusiveToDimFetchVariable( + array $stmts, + ?Assign $emptyArrayAssign, + string $variableName + ): bool { + $lastKey = array_key_last($stmts); + + foreach ($stmts as $key => $stmt) { + if ($key === $lastKey) { + // skip last item + continue; + } + + if (! $stmt instanceof Expression) { + return false; + } + + if (! $stmt->expr instanceof Assign) { + return false; + } + + $assign = $stmt->expr; + + // skip initial assign + if ($assign === $emptyArrayAssign) { + continue; + } + + // skip new X instance with args to keep complex assign readable + if ($assign->expr instanceof New_ && ! $assign->expr->isFirstClassCallable() && $assign->expr->getArgs() !== []) { + return false; + } + + if (! $assign->var instanceof ArrayDimFetch) { + return false; + } + + $arrayDimFetch = $assign->var; + + // traverse all nested variables up + while ($arrayDimFetch instanceof ArrayDimFetch) { + $arrayDimFetch = $arrayDimFetch->var; + } + + if (! $arrayDimFetch instanceof Variable) { + return false; + } + + if (! $this->isName($arrayDimFetch, $variableName)) { + return false; + } + } + + return true; + } + + /** + * @param Stmt[] $stmts + */ + private function resolveDefaultEmptyArrayAssign(array $stmts, string $returnedVariableName): ?Assign + { + foreach ($stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof Assign) { + continue; + } + + $assign = $stmt->expr; + if (! $assign->var instanceof Variable) { + continue; + } + + if (! $this->isName($assign->var, $returnedVariableName)) { + continue; + } + + if (! $assign->expr instanceof Array_) { + continue; + } + + if ($assign->expr->items !== []) { + continue; + } + + return $assign; + } + + return null; + } +} diff --git a/rules/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector.php b/rules/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector.php new file mode 100644 index 00000000000..17be6159c5e --- /dev/null +++ b/rules/CodeQuality/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector.php @@ -0,0 +1,291 @@ +someStatic(); + } + + private function someStatic() + { + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if (! $classMethod->isPrivate()) { + continue; + } + + $changedClassMethod = $this->refactorClassMethod($node, $classMethod); + if ($changedClassMethod instanceof ClassMethod) { + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function refactorClassMethod(Class_ $class, ClassMethod $classMethod): ?ClassMethod + { + if (! $classMethod->isStatic()) { + return null; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + if ($this->classMethodVisibilityGuard->isClassMethodVisibilityGuardedByParent($classMethod, $classReflection)) { + return null; + } + + if ($this->isClassMethodCalledInAnotherStaticClassMethod($class, $classMethod)) { + return null; + } + + if ($this->isNeverCalled($class, $classMethod)) { + return null; + } + + // replace all the calls + $classMethodName = $this->getName($classMethod); + $className = $this->getName($class) ?? ''; + $shouldSkip = false; + + $this->traverseNodesWithCallable($class->getMethods(), function (Node $node) use ( + &$shouldSkip, + $classMethodName, + $className + ): int|null { + if (($node instanceof Closure || $node instanceof ArrowFunction) && $node->static) { + $this->traverseNodesWithCallable($node->getStmts(), function (Node $subNode) use ( + &$shouldSkip, + $classMethodName, + $className + ): ?int { + if (! $subNode instanceof StaticCall) { + return null; + } + + if (! $this->isNames($subNode->class, ['self', 'static', $className])) { + return null; + } + + if (! $this->isName($subNode->name, $classMethodName)) { + return null; + } + + $shouldSkip = true; + return NodeVisitor::STOP_TRAVERSAL; + }); + + if ($shouldSkip) { + return NodeVisitor::STOP_TRAVERSAL; + } + + return null; + } + + return null; + }); + + if ($shouldSkip) { + return null; + } + + $this->traverseNodesWithCallable($class->getMethods(), function (Node $node) use ( + $classMethodName, + $className + ): ?MethodCall { + if (! $node instanceof StaticCall) { + return null; + } + + if (! $this->isNames($node->class, ['self', 'static', $className])) { + return null; + } + + if (! $this->isName($node->name, $classMethodName)) { + return null; + } + + return new MethodCall(new Variable('this'), $classMethodName, $node->args); + }); + + // change static calls to non-static ones, but only if in non-static method!!! + $this->visibilityManipulator->makeNonStatic($classMethod); + return $classMethod; + } + + /** + * If the static class method is called in another static class method, + * we should keep it to avoid calling $this in static + */ + private function isClassMethodCalledInAnotherStaticClassMethod(Class_ $class, ClassMethod $classMethod): bool + { + $currentClassNamespacedName = (string) $this->getName($class); + $currentClassMethodName = $this->getName($classMethod); + + $isInsideStaticClassMethod = false; + + // check if called static call somewhere in class, but only in static methods + foreach ($class->getMethods() as $checkedClassMethod) { + // not a problem + if (! $checkedClassMethod->isStatic()) { + continue; + } + + $this->traverseNodesWithCallable($checkedClassMethod, function (Node $node) use ( + $currentClassNamespacedName, + $currentClassMethodName, + &$isInsideStaticClassMethod + ): ?int { + if ($node instanceof Array_) { + $scope = $node->getAttribute(AttributeKey::SCOPE); + if ($scope instanceof Scope && $this->arrayCallableMethodMatcher->match( + $node, + $scope, + $currentClassMethodName + )) { + $isInsideStaticClassMethod = true; + return NodeVisitor::STOP_TRAVERSAL; + } + } + + if (! $node instanceof StaticCall) { + return null; + } + + if (! $this->isNames($node->class, ['self', 'static', $currentClassNamespacedName])) { + return null; + } + + if (! $this->isName($node->name, $currentClassMethodName)) { + return null; + } + + $isInsideStaticClassMethod = true; + return NodeVisitor::STOP_TRAVERSAL; + }); + + if ($isInsideStaticClassMethod) { + return $isInsideStaticClassMethod; + } + } + + return false; + } + + /** + * In case of never called method call, + * it should be skipped and handled by another dead-code rule + */ + private function isNeverCalled(Class_ $class, ClassMethod $classMethod): bool + { + $currentMethodName = $this->getName($classMethod); + + $nodeFinder = new NodeFinder(); + + $methodCall = $nodeFinder->findFirst($class, function (Node $node) use ($currentMethodName): bool { + if ($node instanceof MethodCall && $node->var instanceof Variable && $this->isName( + $node->var, + 'this' + ) && $this->isName($node->name, $currentMethodName)) { + return true; + } + + return $node instanceof StaticCall && $this->isNames($node->class, ['self', 'static']) && $this->isName( + $node->name, + $currentMethodName + ); + }); + + return ! $methodCall instanceof Node; + } +} diff --git a/rules/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector.php b/rules/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector.php deleted file mode 100644 index 085ce4fae6c..00000000000 --- a/rules/CodeQuality/Rector/ClassMethod/NarrowUnionTypeDocRector.php +++ /dev/null @@ -1,128 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $params = $node->getParams(); - - foreach ($params as $key => $param) { - /** @var string $paramName */ - $paramName = $this->getName($param->var); - $paramType = $phpDocInfo->getParamType($paramName); - - if (! $paramType instanceof UnionType) { - continue; - } - - if ($this->unionTypeAnalyzer->isScalar($paramType)) { - $this->changeDocObjectScalar($key, $phpDocInfo); - continue; - } - - if ($this->unionTypeAnalyzer->hasObjectWithoutClassType($paramType)) { - $this->changeDocObjectWithoutClassType($paramType, $key, $phpDocInfo); - } - } - - if ($phpDocInfo->hasChanged()) { - $node->setAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED, true); - return $node; - } - - return null; - } - - private function changeDocObjectWithoutClassType( - UnionType $unionType, - int $key, - PhpDocInfo $phpDocInfo - ): void { - if (! $this->unionTypeAnalyzer->hasObjectWithoutClassTypeWithOnlyFullyQualifiedObjectType($unionType)) { - return; - } - - $types = $unionType->getTypes(); - $resultType = ''; - foreach ($types as $type) { - if ($type instanceof FullyQualifiedObjectType) { - $resultType .= $type->getClassName() . '|'; - } - } - - $resultType = rtrim($resultType, '|'); - $paramTagValueNodes = $phpDocInfo->getParamTagValueNodes(); - $paramTagValueNodes[$key]->type = new IdentifierTypeNode($resultType); - } - - private function changeDocObjectScalar(int $key, PhpDocInfo $phpDocInfo): void - { - $paramTagValueNodes = $phpDocInfo->getParamTagValueNodes(); - $paramTagValueNodes[$key]->type = new IdentifierTypeNode('scalar'); - } -} diff --git a/rules/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php b/rules/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php new file mode 100644 index 00000000000..b9be5cbff33 --- /dev/null +++ b/rules/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php @@ -0,0 +1,201 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class, Closure::class]; + } + + /** + * @param ClassMethod|Function_|Closure $node + */ + public function refactor(Node $node): ClassMethod|Function_|Closure|null + { + if ($node->params === []) { + return null; + } + + $hasChanged = false; + foreach ($node->params as $key => $param) { + if ($param->default instanceof Expr) { + continue; + } + + if ($param->variadic) { + continue; + } + + $previousParam = $node->params[$key - 1] ?? null; + if ($previousParam instanceof Param && $previousParam->default instanceof Expr) { + $hasChanged = true; + $this->processParam($param); + } + } + + return $hasChanged ? $node : null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NULLABLE_TYPE; + } + + /** + * Look first found type reasonable value + * + * @param Node[] $types + */ + private function mapReasonableParamValue(array $types): Expr + { + foreach ($types as $type) { + if ($this->isName($type, 'string')) { + return new String_(''); + } + + if ($this->isName($type, 'int')) { + return new Int_(0); + } + + if ($this->isName($type, 'float')) { + return new Float_(0.0); + } + + if ($this->isName($type, 'bool')) { + return $this->nodeFactory->createFalse(); + } + + if ($this->isName($type, 'array')) { + return $this->nodeFactory->createArray([]); + } + + if ($this->isName($type, 'true')) { + return $this->nodeFactory->createTrue(); + } + + if ($this->isName($type, 'false')) { + return $this->nodeFactory->createFalse(); + } + } + + return new ConstFetch(new Name('null')); + } + + private function processParam(Param $param): void + { + if (! $param->type instanceof Node) { + $param->default = new ConstFetch(new Name('null')); + return; + } + + if ($param->type instanceof NullableType) { + $param->default = new ConstFetch(new Name('null')); + return; + } + + if ($param->type instanceof IntersectionType) { + $param->default = new ConstFetch(new Name('null')); + $param->type = new UnionType([$param->type, new Identifier('null')]); + return; + } + + if ($param->type instanceof UnionType) { + foreach ($param->type->types as $unionedType) { + if ($unionedType instanceof Identifier && $this->isName($unionedType, 'null')) { + $param->default = new ConstFetch(new Name('null')); + return; + } + } + + $reasonableValue = $this->mapReasonableParamValue($param->type->types); + if ($this->valueResolver->isNull($reasonableValue)) { + $param->default = new ConstFetch(new Name('null')); + $param->type->types[] = new Identifier('null'); + return; + } + + $param->default = $reasonableValue; + return; + } + + if ($param->type instanceof ComplexType) { + return; + } + + $reasonableValue = $this->mapReasonableParamValue([$param->type]); + if ($this->valueResolver->isNull($reasonableValue)) { + if (! $param->type instanceof Identifier || ! $this->isNames($param->type, ['null', 'mixed'])) { + $param->type = new NullableType($param->type); + } + + $param->default = new ConstFetch(new Name('null')); + return; + } + + $param->default = $reasonableValue; + } +} diff --git a/rules/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector.php b/rules/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector.php index f170a20747f..3ca7963115c 100644 --- a/rules/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector.php +++ b/rules/CodeQuality/Rector/Class_/CompleteDynamicPropertiesRector.php @@ -5,37 +5,31 @@ namespace Rector\CodeQuality\Rector\Class_; use PhpParser\Node; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Type\ObjectType; -use PHPStan\Type\Type; -use Rector\CodeQuality\NodeAnalyzer\ClassLikeAnalyzer; use Rector\CodeQuality\NodeAnalyzer\LocalPropertyAnalyzer; +use Rector\CodeQuality\NodeAnalyzer\MissingPropertiesResolver; use Rector\CodeQuality\NodeFactory\MissingPropertiesFactory; -use Rector\Core\NodeAnalyzer\ClassAnalyzer; -use Rector\Core\NodeAnalyzer\PropertyPresenceChecker; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\ValueObject\PropertyMetadata; +use Rector\NodeAnalyzer\ClassAnalyzer; +use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/GL6II - * @see https://3v4l.org/eTrhZ - * @see https://3v4l.org/C554W - * * @see \Rector\Tests\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector\CompleteDynamicPropertiesRectorTest */ final class CompleteDynamicPropertiesRector extends AbstractRector { public function __construct( - private MissingPropertiesFactory $missingPropertiesFactory, - private LocalPropertyAnalyzer $localPropertyAnalyzer, - private ClassLikeAnalyzer $classLikeAnalyzer, - private ReflectionProvider $reflectionProvider, - private ClassAnalyzer $classAnalyzer, - private PropertyPresenceChecker $propertyPresenceChecker + private readonly MissingPropertiesFactory $missingPropertiesFactory, + private readonly LocalPropertyAnalyzer $localPropertyAnalyzer, + private readonly ReflectionProvider $reflectionProvider, + private readonly ClassAnalyzer $classAnalyzer, + private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer, + private readonly MissingPropertiesResolver $missingPropertiesResolver, ) { } @@ -88,34 +82,27 @@ public function refactor(Node $node): ?Node return null; } - $className = $this->getName($node); - if ($className === null) { - return null; - } - - if (! $this->reflectionProvider->hasClass($className)) { + $classReflection = $this->matchClassReflection($node); + if (! $classReflection instanceof ClassReflection) { return null; } - $classReflection = $this->reflectionProvider->getClass($className); - // special case for Laravel Collection macro magic - $fetchedLocalPropertyNameToTypes = $this->localPropertyAnalyzer->resolveFetchedPropertiesToTypesFromClass( + $definedLocalPropertiesWithTypes = $this->localPropertyAnalyzer->resolveFetchedPropertiesToTypesFromClass( $node ); - $propertiesToComplete = $this->resolvePropertiesToComplete($node, $fetchedLocalPropertyNameToTypes); - if ($propertiesToComplete === []) { + $propertiesToComplete = $this->missingPropertiesResolver->resolve( + $node, + $classReflection, + $definedLocalPropertiesWithTypes + ); + + $newProperties = $this->missingPropertiesFactory->create($propertiesToComplete); + if ($newProperties === []) { return null; } - $propertiesToComplete = $this->filterOutExistingProperties($node, $classReflection, $propertiesToComplete); - - $newProperties = $this->missingPropertiesFactory->create( - $fetchedLocalPropertyNameToTypes, - $propertiesToComplete - ); - $node->stmts = array_merge($newProperties, $node->stmts); return $node; @@ -127,12 +114,13 @@ private function shouldSkipClass(Class_ $class): bool return true; } - $className = $this->nodeNameResolver->getName($class); - if ($className === null) { + $className = (string) $this->getName($class); + if (! $this->reflectionProvider->hasClass($className)) { return true; } - if (! $this->reflectionProvider->hasClass($className)) { + // dynamic property on purpose + if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, 'AllowDynamicProperties')) { return true; } @@ -143,57 +131,26 @@ private function shouldSkipClass(Class_ $class): bool return true; } - return $classReflection->hasMethod('__get'); + if ($classReflection->hasMethod('__get')) { + return true; + } + + return $class->extends instanceof FullyQualified && ! $this->reflectionProvider->hasClass( + $class->extends->toString() + ); } - /** - * @param array $fetchedLocalPropertyNameToTypes - * @return string[] - */ - private function resolvePropertiesToComplete(Class_ $class, array $fetchedLocalPropertyNameToTypes): array + private function matchClassReflection(Class_ $class): ?ClassReflection { - $propertyNames = $this->classLikeAnalyzer->resolvePropertyNames($class); - - /** @var string[] $fetchedLocalPropertyNames */ - $fetchedLocalPropertyNames = array_keys($fetchedLocalPropertyNameToTypes); - - return array_diff($fetchedLocalPropertyNames, $propertyNames); - } + $className = $this->getName($class); + if ($className === null) { + return null; + } - /** - * @param string[] $propertiesToComplete - * @return string[] - */ - private function filterOutExistingProperties( - Class_ $class, - ClassReflection $classReflection, - array $propertiesToComplete - ): array { - $missingPropertyNames = []; - - $className = $classReflection->getName(); - // remove other properties that are accessible from this scope - foreach ($propertiesToComplete as $propertyToComplete) { - if ($classReflection->hasProperty($propertyToComplete)) { - continue; - } - - $propertyMetadata = new PropertyMetadata( - $propertyToComplete, - new ObjectType($className), - Class_::MODIFIER_PRIVATE - ); - $hasClassContextProperty = $this->propertyPresenceChecker->hasClassContextProperty( - $class, - $propertyMetadata - ); - if ($hasClassContextProperty) { - continue; - } - - $missingPropertyNames[] = $propertyToComplete; + if (! $this->reflectionProvider->hasClass($className)) { + return null; } - return $missingPropertyNames; + return $this->reflectionProvider->getClass($className); } } diff --git a/rules/CodeQuality/Rector/Class_/ConvertStaticToSelfRector.php b/rules/CodeQuality/Rector/Class_/ConvertStaticToSelfRector.php new file mode 100644 index 00000000000..7690d6f8db6 --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/ConvertStaticToSelfRector.php @@ -0,0 +1,185 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + $hasChanged = false; + $isFinal = $node->isFinal() || FeatureFlags::treatClassesAsFinal($node); + $scope = ScopeFetcher::fetch($node); + $classReflection = $scope->getClassReflection(); + + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $this->traverseNodesWithCallable($node->stmts, function (Node $subNode) use ( + &$hasChanged, + $classReflection, + $isFinal, + $scope, + ): ?Node { + if ( + ! $subNode instanceof StaticPropertyFetch + && ! $subNode instanceof StaticCall + && ! $subNode instanceof ClassConstFetch + ) { + return null; + } + + if ($this->shouldSkip($subNode, $classReflection, $isFinal, $scope)) { + return null; + } + + $hasChanged = true; + $subNode->class = new Name('self'); + return $subNode; + }); + + return $hasChanged ? $node : null; + } + + private function shouldSkip( + StaticPropertyFetch | StaticCall | ClassConstFetch $node, + ClassReflection $classReflection, + bool $isFinal, + Scope $scope, + ): bool { + if (! $node->class instanceof Name) { + return true; + } + + if (! $this->isName($node->class, ObjectReference::STATIC)) { + return true; + } + + if (! $node->name instanceof Identifier) { + return true; + } + + $name = (string) $this->getName($node->name); + + // For final classes, we can safely convert all static:: to self::, even + // it's virtual. For non-final classes, we can only convert private or final + // native members. + $hasMember = match (true) { + $node instanceof StaticPropertyFetch => $isFinal + ? $classReflection->hasStaticProperty($name) + : $classReflection->hasNativeProperty($name), + $node instanceof StaticCall => $isFinal + ? $classReflection->hasMethod($name) + : $classReflection->hasNativeMethod($name), + $node instanceof ClassConstFetch => $classReflection->hasConstant($name), + }; + + if (! $hasMember) { + return true; + } + + $reflection = match (true) { + $node instanceof StaticPropertyFetch => $isFinal + ? $classReflection->getStaticProperty($name) + : $classReflection->getNativeProperty($name), + $node instanceof StaticCall => $isFinal + ? $classReflection->getMethod($name, $scope) + : $classReflection->getNativeMethod($name), + $node instanceof ClassConstFetch => $classReflection->getConstant($name), + }; + + // avoid overlapped change + if (! $reflection->isStatic()) { + return true; + } + + if (! $isFinal) { + if ($reflection instanceof ClassConstantReflection) { + $memberIsFinal = $reflection->isFinalByKeyword(); + } else { + $memberIsFinal = $reflection->isFinalByKeyword() + ->yes(); + } + + // Final native members can be safely converted + if ($memberIsFinal) { + return false; + } + + // Otherwise, only convert private native members + return ! $reflection->isPrivate(); + } + + // For final classes, can safely convert all members + return false; + } +} diff --git a/rules/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector.php b/rules/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector.php new file mode 100644 index 00000000000..e37d3eae69a --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/DynamicDocBlockPropertyToNativePropertyRector.php @@ -0,0 +1,233 @@ +someDependency = new SomeDependency(); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private SomeDependency $someDependency; + + public function __construct() + { + $this->someDependency = new SomeDependency(); + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->phpAttributeAnalyzer->hasPhpAttribute($node, 'AllowDynamicProperties')) { + return null; + } + + if ($this->shouldSkipClass($node)) { + return null; + } + + // 2. add defined @property explicitly + $classPhpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $classPhpDocInfo instanceof PhpDocInfo) { + return null; + } + + $propertyPhpDocTagNodes = $classPhpDocInfo->getTagsByName('property'); + if ($propertyPhpDocTagNodes === []) { + return null; + } + + // 1. remove dynamic attribute, most likely any + foreach ($node->attrGroups as $key => $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + if ($attr->name->toString() === 'AllowDynamicProperties') { + unset($node->attrGroups[$key]); + continue 2; + } + } + } + + $node->attrGroups = array_values($node->attrGroups); + $newProperties = $this->createNewPropertyFromPropertyTagValueNodes($propertyPhpDocTagNodes, $node); + + // remove property tags + foreach ($propertyPhpDocTagNodes as $propertyPhpDocTagNode) { + // remove from docblock + $this->phpDocTagRemover->removeTagValueFromNode($classPhpDocInfo, $propertyPhpDocTagNode); + } + + // merge new properties to start of the file + $node->stmts = array_merge($newProperties, $node->stmts); + + // update doc info + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_DYNAMIC_PROPERTIES; + } + + /** + * @param PhpDocTagNode[] $propertyPhpDocTagNodes + * @return Property[] + */ + private function createNewPropertyFromPropertyTagValueNodes(array $propertyPhpDocTagNodes, Class_ $class): array + { + $newProperties = []; + + foreach ($propertyPhpDocTagNodes as $propertyPhpDocTagNode) { + // add explicit native property + $propertyTagValueNode = $propertyPhpDocTagNode->value; + if (! $propertyTagValueNode instanceof PropertyTagValueNode) { + continue; + } + + $propertyName = ltrim($propertyTagValueNode->propertyName, '$'); + + if ($this->isPromotedProperty($class, $propertyName)) { + continue; + } + + // is property already defined? + if ($class->getProperty($propertyName) instanceof Property) { + // improve existing one type if needed + + $existingProperty = $class->getProperty($propertyName); + if ($existingProperty->type instanceof Node) { + continue; + } + + $defaultValue = $existingProperty->props[0]->default; + $isNullable = $defaultValue instanceof Expr && $this->valueResolver->isNull($defaultValue); + + $existingProperty->type = $this->typedPropertyFactory->createPropertyTypeNode( + $propertyTagValueNode, + $class, + $isNullable, + ); + + continue; + } + + $newProperties[] = $this->typedPropertyFactory->createFromPropertyTagValueNode( + $propertyTagValueNode, + $class, + $propertyName + ); + } + + return $newProperties; + } + + private function shouldSkipClass(Class_ $class): bool + { + // skip magic + $getClassMethod = $class->getMethod('__get'); + if ($getClassMethod instanceof ClassMethod) { + return true; + } + + $setClassMethod = $class->getMethod('__set'); + if ($setClassMethod instanceof ClassMethod) { + return true; + } + + if (! $class->extends instanceof Node) { + return false; + } + + return ! $this->testsNodeAnalyzer->isInTestClass($class); + } + + private function isPromotedProperty(Class_ $class, string $propertyName): bool + { + $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); + if ($constructClassMethod instanceof ClassMethod) { + foreach ($constructClassMethod->params as $param) { + if (! $param->isPromoted()) { + continue; + } + + $paramName = $this->getName($param); + if ($paramName === $propertyName) { + return true; + } + } + } + + return false; + } +} diff --git a/rules/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector.php b/rules/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector.php new file mode 100644 index 00000000000..e952fb1806f --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/InlineConstructorDefaultToPropertyRector.php @@ -0,0 +1,228 @@ +name = 'John'; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private $name = 'John'; + + public function __construct() + { + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { + return null; + } + + if ($constructClassMethod->stmts === null) { + return null; + } + + foreach ($constructClassMethod->stmts as $key => $stmt) { + // code that is possibly breaking flow + if ($stmt instanceof If_) { + return null; + } + + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof Assign) { + continue; + } + + $assign = $stmt->expr; + + $propertyName = $this->matchAssignedLocalPropertyName($assign); + if (! is_string($propertyName)) { + continue; + } + + $defaultExpr = $assign->expr; + if ($this->exprAnalyzer->isDynamicExpr($defaultExpr)) { + continue; + } + + $hasPropertyChanged = $this->refactorProperty( + $node, + $propertyName, + $defaultExpr, + $constructClassMethod, + $key + ); + + if ($hasPropertyChanged) { + $hasChanged = true; + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + private function matchAssignedLocalPropertyName(Assign $assign): ?string + { + if (! $assign->var instanceof PropertyFetch) { + return null; + } + + $propertyFetch = $assign->var; + if (! $this->isName($propertyFetch->var, 'this')) { + return null; + } + + $propertyName = $this->getName($propertyFetch->name); + if (! is_string($propertyName)) { + return null; + } + + return $propertyName; + } + + private function isFoundInAnyPropertyHooks(Class_ $class, string $propertyName): bool + { + $propertyHooks = array_reduce( + $class->getProperties(), + static fn (array $hooks, Property $property): array => [...$hooks, ...$property->hooks], + [] + ); + + return (bool) $this->betterNodeFinder->findFirst($propertyHooks, function (Node $subNode) use ( + $class, + $propertyName + ): bool { + if (! $subNode instanceof PropertyFetch) { + return false; + } + + return $this->propertyFetchFinder->isLocalPropertyFetchByName($subNode, $class, $propertyName); + }); + } + + private function refactorProperty( + Class_ $class, + string $propertyName, + Expr $defaultExpr, + ClassMethod $constructClassMethod, + int $key + ): bool { + if ($class->isReadonly()) { + return false; + } + + if ($this->isFoundInAnyPropertyHooks($class, $propertyName)) { + return false; + } + + foreach ($class->stmts as $classStmt) { + if (! $classStmt instanceof Property) { + continue; + } + + // readonly property cannot have default value + if ($classStmt->isReadonly()) { + continue; + } + + foreach ($classStmt->props as $propertyProperty) { + if (! $this->isName($propertyProperty, $propertyName)) { + continue; + } + + $propertyProperty->default = $defaultExpr; + + $classStmt->setAttribute( + AttributeKey::COMMENTS, + array_merge( + $classStmt->getComments(), + isset($constructClassMethod->stmts[$key]) + ? $constructClassMethod->stmts[$key]->getComments() + : [], + ) + ); + + // remove assign + unset($constructClassMethod->stmts[$key]); + + return true; + } + } + + return false; + } +} diff --git a/rules/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector.php b/rules/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector.php new file mode 100644 index 00000000000..5bfa5aca8a5 --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/RemoveReadonlyPropertyVisibilityOnReadonlyClassRector.php @@ -0,0 +1,106 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->isReadonly()) { + return null; + } + + $hasChanged = false; + $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + + if ($constructClassMethod instanceof ClassMethod) { + foreach ($constructClassMethod->getParams() as $param) { + if (! $param->isReadonly()) { + continue; + } + + $this->visibilityManipulator->removeReadonly($param); + $hasChanged = true; + } + } + + foreach ($node->getProperties() as $property) { + if (! $property->isReadonly()) { + continue; + } + + $this->visibilityManipulator->removeReadonly($property); + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/CodeQuality/Rector/Class_/ReturnIteratorInDataProviderRector.php b/rules/CodeQuality/Rector/Class_/ReturnIteratorInDataProviderRector.php new file mode 100644 index 00000000000..2ecba009a54 --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/ReturnIteratorInDataProviderRector.php @@ -0,0 +1,123 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $dataProviderClassMethods = $this->dataProviderMethodsFinder->findDataProviderNodesInClass($node); + + $hasChanged = false; + + foreach ($dataProviderClassMethods as $dataProviderClassMethod) { + if ($dataProviderClassMethod->returnType instanceof Node) { + continue; + } + + if (! $this->hasYields($dataProviderClassMethod)) { + continue; + } + + $dataProviderClassMethod->returnType = new FullyQualified('Iterator'); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function hasYields(ClassMethod $classMethod): bool + { + $yields = $this->betterNodeFinder->findInstancesOfScoped((array) $classMethod->stmts, Yield_::class); + + return $yields !== []; + } +} diff --git a/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php b/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php new file mode 100644 index 00000000000..2c8c95f00e2 --- /dev/null +++ b/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php @@ -0,0 +1,97 @@ +> + */ + public function getNodeTypes(): array + { + return [Coalesce::class]; + } + + /** + * @param Coalesce $node + */ + public function refactor(Node $node): ?Node + { + /** + * indexed data maybe false positive + */ + if ($node->left instanceof ArrayDimFetch) { + return null; + } + + /** + * Scope needs to use parent Coalesce to properly get type from left side of coalesce + */ + $scope = ScopeFetcher::fetch($node); + $nativeType = $scope->getNativeType($node->left); + + if ($nativeType instanceof ErrorType) { + return null; + } + + if ($nativeType instanceof MixedType) { + return null; + } + + if ($nativeType instanceof NullType) { + return null; + } + + if ($nativeType instanceof UnionType) { + foreach ($nativeType->getTypes() as $unionedType) { + if ($unionedType instanceof NullType) { + return null; + } + } + } + + return new Ternary($node->left, null, $node->right); + } +} diff --git a/rules/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector.php b/rules/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector.php new file mode 100644 index 00000000000..4a72194a090 --- /dev/null +++ b/rules/CodeQuality/Rector/Concat/DirnameDirConcatStringToDirectStringPathRector.php @@ -0,0 +1,99 @@ +left instanceof FuncCall || ! $this->isName($node->left, 'dirname')) { + return null; + } + + if (! $node->right instanceof String_) { + return null; + } + + $dirnameFuncCall = $node->left; + if ($dirnameFuncCall->isFirstClassCallable()) { + return null; + } + + // avoid multiple dir nesting for now + if (count($dirnameFuncCall->getArgs()) !== 1) { + return null; + } + + $firstArg = $dirnameFuncCall->getArgs()[0]; + if (! $firstArg->value instanceof Dir) { + return null; + } + + $string = $node->right; + if (str_contains($string->value, '/')) { + // linux paths + $string->value = '/../' . ltrim($string->value, '/'); + $node->left = new Dir(); + return $node; + } + + if (str_contains($string->value, '\\')) { + // windows paths + $string->value = '\\..\\' . ltrim($string->value, '\\'); + $node->left = new Dir(); + return $node; + } + + return null; + } +} diff --git a/rules/CodeQuality/Rector/Concat/JoinStringConcatRector.php b/rules/CodeQuality/Rector/Concat/JoinStringConcatRector.php index f6c5f456136..60103f05bba 100644 --- a/rules/CodeQuality/Rector/Concat/JoinStringConcatRector.php +++ b/rules/CodeQuality/Rector/Concat/JoinStringConcatRector.php @@ -8,8 +8,8 @@ use PhpParser\Node; use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\Util\StringUtils; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,12 +18,13 @@ */ final class JoinStringConcatRector extends AbstractRector { + private const int LINE_BREAK_POINT = 100; + /** - * @var int + * @see https://regex101.com/r/VaXM1t/1 + * @see https://stackoverflow.com/questions/4147646/determine-if-utf-8-text-is-all-ascii */ - private const LINE_BREAK_POINT = 100; - - private bool $nodeReplacementIsRestricted = false; + private const string ASCII_REGEX = '#[^\x00-\x7F]#'; public function getRuleDefinition(): RuleDefinition { @@ -68,69 +69,42 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $this->nodeReplacementIsRestricted = false; - - if (! $this->isTopMostConcatNode($node)) { + if (! $node->left instanceof String_) { return null; } - $joinedNode = $this->joinConcatIfStrings($node); - if (! $joinedNode instanceof String_) { + if (! $node->right instanceof String_) { return null; } - if ($this->nodeReplacementIsRestricted) { + $leftStartLine = $node->left->getStartLine(); + $rightStartLine = $node->right->getStartLine(); + + if ($leftStartLine > 0 && $rightStartLine > 0 && $rightStartLine > $leftStartLine) { return null; } - return $joinedNode; - } - - private function isTopMostConcatNode(Concat $concat): bool - { - $parent = $concat->getAttribute(AttributeKey::PARENT_NODE); - return ! $parent instanceof Concat; + return $this->joinConcatIfStrings($node->left, $node->right); } - private function joinConcatIfStrings(Concat $node): Concat | String_ + private function joinConcatIfStrings(String_ $leftString, String_ $rightString): ?String_ { - $concat = clone $node; + $leftValue = $leftString->value; + $rightValue = $rightString->value; - if ($concat->left instanceof Concat) { - $concat->left = $this->joinConcatIfStrings($concat->left); - } - - if ($concat->right instanceof Concat) { - $concat->right = $this->joinConcatIfStrings($concat->right); - } - - if (! $concat->left instanceof String_) { - return $node; - } - - if (! $concat->right instanceof String_) { - return $node; - } - - $leftValue = $concat->left->value; - $rightValue = $concat->right->value; - - if ($leftValue === "\n") { - $this->nodeReplacementIsRestricted = true; - return $node; + if (str_contains($leftValue, "\n") || str_contains($rightValue, "\n")) { + return null; } - if ($rightValue === "\n") { - $this->nodeReplacementIsRestricted = true; - return $node; + $joinedStringValue = $leftValue . $rightValue; + if (StringUtils::isMatch($joinedStringValue, self::ASCII_REGEX)) { + return null; } - $resultString = new String_($leftValue . $rightValue); - if (Strings::length($resultString->value) >= self::LINE_BREAK_POINT) { - $this->nodeReplacementIsRestricted = true; - return $node; + if (Strings::length($joinedStringValue) >= self::LINE_BREAK_POINT) { + return null; } - return $resultString; + return new String_($joinedStringValue); } } diff --git a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php new file mode 100644 index 00000000000..0f36b480e0f --- /dev/null +++ b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php @@ -0,0 +1,163 @@ +> + */ + public function getNodeTypes(): array + { + return [Empty_::class, BooleanNot::class]; + } + + /** + * @param Empty_|BooleanNot $node $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($node instanceof BooleanNot) { + if ($node->expr instanceof Empty_ && $this->isAllowedExpr($node->expr->expr, $scope)) { + return new NotIdentical($node->expr->expr, new Array_()); + } + + return null; + } + + if (! $this->isAllowedExpr($node->expr, $scope)) { + return null; + } + + return new Identical($node->expr, new Array_()); + } + + private function isAllowedVariable(Variable $variable): bool + { + if (is_string($variable->name) && $this->reservedKeywordAnalyzer->isNativeVariable($variable->name)) { + return false; + } + + return ! $this->exprAnalyzer->isNonTypedFromParam($variable); + } + + private function isAllowedExpr(Expr $expr, Scope $scope): bool + { + if (! $scope->getType($expr)->isArray()->yes()) { + return false; + } + + if ($expr instanceof Variable) { + return $this->isAllowedVariable($expr); + } + + if (! $expr instanceof PropertyFetch && ! $expr instanceof StaticPropertyFetch) { + return false; + } + + if (! $expr->name instanceof Identifier) { + return false; + } + + $classReflection = $this->reflectionResolver->resolveClassReflectionSourceObject($expr); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + $propertyName = $expr->name->toString(); + if (! $classReflection->hasNativeProperty($propertyName)) { + return false; + } + + $phpPropertyReflection = $classReflection->getNativeProperty($propertyName); + $nativeType = $phpPropertyReflection->getNativeType(); + + if (! $nativeType instanceof MixedType) { + return $nativeType->isArray() + ->yes(); + } + + $property = $this->astResolver->resolvePropertyFromPropertyReflection($phpPropertyReflection); + + /** + * Skip property promotion mixed type for now, as: + * + * - require assign in default param check + * - check all assign of property promotion params under the class + */ + if (! $property instanceof Property) { + return false; + } + + $type = $this->allAssignNodePropertyTypeInferer->inferProperty($property, $classReflection, $this->getFile()); + if (! $type instanceof Type) { + return false; + } + + return $type->isArray() + ->yes(); + } +} diff --git a/rules/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector.php b/rules/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector.php index 3893183a8c3..3c8e87d016f 100644 --- a/rules/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector.php +++ b/rules/CodeQuality/Rector/Equal/UseIdenticalOverEqualWithSameTypeRector.php @@ -5,13 +5,21 @@ namespace Rector\CodeQuality\Rector\Equal; use PhpParser\Node; +use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; +use PhpParser\Node\Expr\Ternary; +use PHPStan\Type\BooleanType; +use PHPStan\Type\FloatType; +use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; -use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; +use PHPStan\Type\StringType; +use PHPStan\Type\Type; +use PHPStan\Type\TypeTraverser; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,7 +31,7 @@ final class UseIdenticalOverEqualWithSameTypeRector extends AbstractRector public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Use ===/!== over ==/!=, it values have the same type', + 'Use ===/!== over ==/!=, if values have the same type', [ new CodeSample( <<<'CODE_SAMPLE' @@ -32,7 +40,7 @@ class SomeClass public function run(int $firstValue, int $secondValue) { $isSame = $firstValue == $secondValue; - $isDiffernt = $firstValue != $secondValue; + $isDifferent = $firstValue != $secondValue; } } CODE_SAMPLE @@ -43,7 +51,7 @@ class SomeClass public function run(int $firstValue, int $secondValue) { $isSame = $firstValue === $secondValue; - $isDiffernt = $firstValue !== $secondValue; + $isDifferent = $firstValue !== $secondValue; } } CODE_SAMPLE @@ -65,23 +73,76 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $leftStaticType = $this->getStaticType($node->left); - $rightStaticType = $this->getStaticType($node->right); + if ($node->left instanceof ArrayDimFetch || $node->right instanceof ArrayDimFetch) { + return null; + } + + $leftStaticType = $this->nodeTypeResolver->getNativeType($node->left); + $rightStaticType = $this->nodeTypeResolver->getNativeType($node->right); // objects can be different by content - if ($leftStaticType instanceof ObjectType) { + if ($this->hasObjectType($leftStaticType) || $this->hasObjectType($rightStaticType)) { return null; } - if ($leftStaticType instanceof MixedType) { + + if ($leftStaticType instanceof MixedType || $rightStaticType instanceof MixedType) { return null; } - if ($rightStaticType instanceof MixedType) { + + $normalizedLeftType = $this->normalizeScalarType($leftStaticType); + $normalizedRightType = $this->normalizeScalarType($rightStaticType); + + if (! $normalizedLeftType->equals($normalizedRightType)) { return null; } - // different types - if (! $leftStaticType->equals($rightStaticType)) { - return null; + return $this->processIdenticalOrNotIdentical($node); + } + + private function hasObjectType(Type $type): bool + { + $hasObjectType = false; + TypeTraverser::map($type, function (Type $type, callable $traverseCallback) use (&$hasObjectType): Type { + // maybe has object type? mark as object type + if (! $type->isObject()->no()) { + $hasObjectType = true; + } + + return $traverseCallback($type); + }); + + return $hasObjectType; + } + + private function normalizeScalarType(Type $type): Type + { + if ($type->isString()->yes()) { + return new StringType(); + } + + if ($type->isBoolean()->yes()) { + return new BooleanType(); + } + + if ($type->isInteger()->yes()) { + return new IntegerType(); + } + + if ($type->isFloat()->yes()) { + return new FloatType(); + } + + return $type; + } + + private function processIdenticalOrNotIdentical(Equal|NotEqual $node): Identical|NotIdentical + { + if ($node->left instanceof Ternary) { + $node->left->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + } + + if ($node->right instanceof Ternary) { + $node->right->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); } if ($node instanceof Equal) { diff --git a/rules/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector.php b/rules/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector.php index ba8d70bbf07..d99eaa94bd1 100644 --- a/rules/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector.php +++ b/rules/CodeQuality/Rector/Expression/InlineIfToExplicitIfRector.php @@ -12,21 +12,18 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; -use PHPStan\Type\BooleanType; -use Rector\Core\NodeManipulator\BinaryOpManipulator; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeManipulator\BinaryOpManipulator; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/dmHCC - * * @see \Rector\Tests\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector\InlineIfToExplicitIfRectorTest */ final class InlineIfToExplicitIfRector extends AbstractRector { public function __construct( - private BinaryOpManipulator $binaryOpManipulator + private readonly BinaryOpManipulator $binaryOpManipulator ) { } @@ -45,7 +42,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -79,9 +76,11 @@ public function refactor(Node $node): ?Node if ($node->expr instanceof BooleanAnd) { return $this->processExplicitIf($node); } + if ($node->expr instanceof BooleanOr) { return $this->processExplicitIf($node); } + return null; } @@ -90,8 +89,8 @@ private function processExplicitIf(Expression $expression): ?Node /** @var BooleanAnd|BooleanOr $booleanExpr */ $booleanExpr = $expression->expr; - $leftStaticType = $this->getStaticType($booleanExpr->left); - if (! $leftStaticType instanceof BooleanType) { + $leftStaticType = $this->getType($booleanExpr->left); + if (! $leftStaticType->isBoolean()->yes()) { return null; } diff --git a/rules/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector.php b/rules/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector.php new file mode 100644 index 00000000000..b0f738faa2b --- /dev/null +++ b/rules/CodeQuality/Rector/Expression/TernaryFalseExpressionToIfRector.php @@ -0,0 +1,89 @@ +call($value) : false; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + public function run($value, $someMethod) + { + if ($value) { + $someMethod->call($value); + } + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Expression::class]; + } + + /** + * @param Expression $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->expr instanceof Ternary) { + return null; + } + + $ternary = $node->expr; + if (! $ternary->if instanceof Expr) { + return null; + } + + if (! $ternary->else instanceof Variable && $this->exprAnalyzer->isDynamicExpr($ternary->else)) { + return null; + } + + return new If_($ternary->cond, [ + 'stmts' => [new Expression($ternary->if)], + ]); + } +} diff --git a/rules/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector.php b/rules/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector.php index 77816eb993f..0b751bcd5c9 100644 --- a/rules/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector.php +++ b/rules/CodeQuality/Rector/For_/ForRepeatedCountToOwnVariableRector.php @@ -5,13 +5,17 @@ namespace Rector\CodeQuality\Rector\For_; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\BinaryOp\Smaller; +use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\For_; -use Rector\Core\Rector\AbstractRector; -use Rector\Naming\Naming\VariableNaming; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,10 +24,7 @@ */ final class ForRepeatedCountToOwnVariableRector extends AbstractRector { - public function __construct( - private VariableNaming $variableNaming - ) { - } + private const string COUNTER_NAME = 'counter'; public function getRuleDefinition(): RuleDefinition { @@ -42,7 +43,7 @@ public function run($items) } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -70,48 +71,43 @@ public function getNodeTypes(): array /** * @param For_ $node + * @return Stmt[]|null */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - $countInCond = null; - $variableName = null; + $scope = ScopeFetcher::fetch($node); - $forScope = $node->getAttribute(AttributeKey::SCOPE); + if ($scope->hasVariableType(self::COUNTER_NAME)->yes()) { + return null; + } - $this->traverseNodesWithCallable($node->cond, function (Node $node) use ( - &$countInCond, - &$variableName, - $forScope - ): ?Variable { - if (! $node instanceof FuncCall) { - return null; - } + $countInCond = null; + $counterVariable = new Variable(self::COUNTER_NAME); - if (! $this->isName($node, 'count')) { - return null; + foreach ($node->cond as $condExpr) { + if (! $condExpr instanceof Smaller && ! $condExpr instanceof SmallerOrEqual) { + continue; } - $countInCond = $node; + if (! $condExpr->right instanceof FuncCall) { + continue; + } - $variableName = $this->variableNaming->resolveFromFuncCallFirstArgumentWithSuffix( - $node, - 'Count', - 'itemsCount', - $forScope - ); + $funcCall = $condExpr->right; + if (! $this->isName($funcCall, 'count')) { + continue; + } - return new Variable($variableName); - }); - if ($countInCond === null) { - return null; + $countInCond = $condExpr->right; + $condExpr->right = $counterVariable; } - if ($variableName === null) { + + if (! $countInCond instanceof Expr) { return null; } - $countAssign = new Assign(new Variable($variableName), $countInCond); - $this->addNodeBeforeNode($countAssign, $node); + $countAssign = new Assign($counterVariable, $countInCond); - return $node; + return [new Expression($countAssign), $node]; } } diff --git a/rules/CodeQuality/Rector/For_/ForToForeachRector.php b/rules/CodeQuality/Rector/For_/ForToForeachRector.php deleted file mode 100644 index 179d433961a..00000000000 --- a/rules/CodeQuality/Rector/For_/ForToForeachRector.php +++ /dev/null @@ -1,259 +0,0 @@ - $token) { - if ($token[0] === T_STRING && $token[1] === 'fn') { - $tokens[$i][0] = self::T_FN; - } - } - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [For_::class]; - } - - /** - * @param For_ $node - */ - public function refactor(Node $node): ?Node - { - $this->reset(); - - $this->matchInit($node->init); - - if (! $this->isConditionMatch($node->cond)) { - return null; - } - - if (! $this->forAnalyzer->isLoopMatch($node->loop, $this->keyValueName)) { - return null; - } - - if ($this->iteratedExpr === null) { - return null; - } - - if ($this->keyValueName === null) { - return null; - } - - $iteratedVariable = $this->getName($this->iteratedExpr); - if ($iteratedVariable === null) { - return null; - } - - $init = $node->init; - if (count($init) > 2) { - return null; - } - - if ($this->forAnalyzer->isCountValueVariableUsedInsideForStatements($node, $this->countValueVariableExpr)) { - return null; - } - - if ($this->forAnalyzer->isAssignmentWithArrayDimFetchAsVariableInsideForStatements( - $node, - $this->keyValueName - )) { - return null; - } - - if ($this->forAnalyzer->isArrayWithKeyValueNameUnsetted($node)) { - return null; - } - - return $this->processForToForeach($node, $iteratedVariable); - } - - private function processForToForeach(For_ $for, string $iteratedVariable): ?Foreach_ - { - $originalVariableSingle = $this->inflector->singularize($iteratedVariable); - $iteratedVariableSingle = $originalVariableSingle; - if ($iteratedVariableSingle === $iteratedVariable) { - $iteratedVariableSingle = 'single' . ucfirst($iteratedVariableSingle); - } - - if (! $this->forAnalyzer->isValueVarUsedNext($for, $iteratedVariableSingle)) { - return $this->createForeachFromForWithIteratedVariableSingle($for, $iteratedVariableSingle); - } - - if ($iteratedVariableSingle === $originalVariableSingle) { - return null; - } - - if (! $this->forAnalyzer->isValueVarUsedNext($for, $originalVariableSingle)) { - return $this->createForeachFromForWithIteratedVariableSingle($for, $originalVariableSingle); - } - - return null; - } - - private function createForeachFromForWithIteratedVariableSingle(For_ $for, string $iteratedVariableSingle): Foreach_ - { - $foreach = $this->foreachFactory->createFromFor( - $for, - $iteratedVariableSingle, - $this->iteratedExpr, - $this->keyValueName - ); - $this->mirrorComments($foreach, $for); - - if ($this->keyValueName === null) { - throw new ShouldNotHappenException(); - } - - $this->foreachAnalyzer->useForeachVariableInStmts( - $foreach->expr, - $foreach->valueVar, - $foreach->stmts, - $this->keyValueName - ); - - return $foreach; - } - - private function reset(): void - { - $this->keyValueName = null; - $this->countValueVariableExpr = null; - $this->countValueName = null; - $this->iteratedExpr = null; - } - - /** - * @param Expr[] $initExprs - */ - private function matchInit(array $initExprs): void - { - foreach ($initExprs as $initExpr) { - if (! $initExpr instanceof Assign) { - continue; - } - - if ($this->valueResolver->isValue($initExpr->expr, 0)) { - $this->keyValueName = $this->getName($initExpr->var); - } - - if (! $initExpr->expr instanceof FuncCall) { - continue; - } - - $funcCall = $initExpr->expr; - - if ($this->nodeNameResolver->isName($funcCall, self::COUNT)) { - $this->countValueVariableExpr = $initExpr->var; - $this->countValueName = $this->getName($initExpr->var); - $this->iteratedExpr = $funcCall->args[0]->value; - } - } - } - - /** - * @param Expr[] $condExprs - */ - private function isConditionMatch(array $condExprs): bool - { - if ($this->forAnalyzer->isCondExprOneOrKeyValueNameNotNull($condExprs, $this->keyValueName)) { - return false; - } - - /** @var string $keyValueName */ - $keyValueName = $this->keyValueName; - if ($this->countValueName !== null) { - return $this->forAnalyzer->isCondExprSmallerOrGreater($condExprs, $keyValueName, $this->countValueName); - } - - if (! $condExprs[0] instanceof BinaryOp) { - return false; - } - - $funcCall = $condExprs[0]->right; - if (! $funcCall instanceof FuncCall) { - return false; - } - - if ($this->nodeNameResolver->isName($funcCall, self::COUNT)) { - $this->iteratedExpr = $funcCall->args[0]->value; - return true; - } - - return false; - } -} diff --git a/rules/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector.php b/rules/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector.php index 79bd121e58a..debfef05f43 100644 --- a/rules/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector.php +++ b/rules/CodeQuality/Rector/Foreach_/ForeachItemsAssignToEmptyArrayToAssignRector.php @@ -6,15 +6,19 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Array_; +use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Foreach_; -use PHPStan\Analyser\Scope; -use PHPStan\Type\ObjectType; -use PHPStan\Type\ThisType; +use PhpParser\NodeVisitor; use Rector\CodeQuality\NodeAnalyzer\ForeachAnalyzer; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\ReadWrite\NodeFinder\NodeUsageFinder; +use Rector\NodeAnalyzer\ExprAnalyzer; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -24,8 +28,9 @@ final class ForeachItemsAssignToEmptyArrayToAssignRector extends AbstractRector { public function __construct( - private NodeUsageFinder $nodeUsageFinder, - private ForeachAnalyzer $foreachAnalyzer + private readonly ForeachAnalyzer $foreachAnalyzer, + private readonly ValueResolver $valueResolver, + private readonly ExprAnalyzer $exprAnalyzer ) { } @@ -70,74 +75,137 @@ public function run($items) */ public function getNodeTypes(): array { - return [Foreach_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Foreach_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + if ($node->stmts === null) { return null; } - /** @var Expr $assignVariable */ - $assignVariable = $this->foreachAnalyzer->matchAssignItemsOnlyForeachArrayVariable($node); + $emptyArrayVariables = []; - return new Assign($assignVariable, $node->expr); + foreach ($node->stmts as $key => $stmt) { + $variableName = $this->matchEmptyArrayVariableAssign($stmt); + if (is_string($variableName)) { + $emptyArrayVariables[] = $variableName; + } + + if (! $stmt instanceof Foreach_) { + if ($this->isAppend($stmt, $emptyArrayVariables)) { + return null; + } + + continue; + } + + if ($this->shouldSkip($stmt, $emptyArrayVariables)) { + if ($this->isAppend($stmt, $emptyArrayVariables)) { + return null; + } + + continue; + } + + $assignVariable = $this->foreachAnalyzer->matchAssignItemsOnlyForeachArrayVariable($stmt); + if (! $assignVariable instanceof Expr) { + continue; + } + + $directAssign = new Assign($assignVariable, $stmt->expr); + $node->stmts[$key] = new Expression($directAssign); + + return $node; + } + + return null; } - private function shouldSkip(Foreach_ $foreach): bool + /** + * @param string[] $emptyArrayVariables + */ + private function isAppend(Stmt $stmt, array $emptyArrayVariables): bool { - $assignVariable = $this->foreachAnalyzer->matchAssignItemsOnlyForeachArrayVariable($foreach); - if (! $assignVariable instanceof Expr) { - return true; - } + $isAppend = false; + + $this->traverseNodesWithCallable( + $stmt, + function (Node $subNode) use ($emptyArrayVariables, &$isAppend): ?int { + if ($subNode instanceof Assign && $subNode->var instanceof ArrayDimFetch) { + $isAppend = $this->isNames($subNode->var->var, $emptyArrayVariables); + + if ($isAppend) { + return NodeVisitor::STOP_TRAVERSAL; + } + } + + if ($subNode instanceof Assign && + $subNode->var instanceof Variable && + $this->isNames($subNode->var, $emptyArrayVariables) && + ! $this->valueResolver->isValue($subNode->expr, [])) { + $isAppend = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + return null; + } + ); - if ($this->shouldSkipAsPartOfNestedForeach($foreach)) { - return true; - } + return $isAppend; + } - $previousDeclaration = $this->nodeUsageFinder->findPreviousForeachNodeUsage($foreach, $assignVariable); - if (! $previousDeclaration instanceof Node) { + /** + * @param string[] $emptyArrayVariables + */ + private function shouldSkip(Foreach_ $foreach, array $emptyArrayVariables): bool + { + $assignVariableExpr = $this->foreachAnalyzer->matchAssignItemsOnlyForeachArrayVariable($foreach); + if (! $assignVariableExpr instanceof Expr) { return true; } - $previousDeclarationParentNode = $previousDeclaration->getAttribute(AttributeKey::PARENT_NODE); - if (! $previousDeclarationParentNode instanceof Assign) { + $foreachedExprType = $this->nodeTypeResolver->getNativeType($foreach->expr); + + // only arrays, not traversable/iterable + if (! $foreachedExprType->isArray()->yes()) { return true; } - // must be empty array, otherwise it will false override - $defaultValue = $this->valueResolver->getValue($previousDeclarationParentNode->expr); - if ($defaultValue !== []) { - return true; + return ! $this->isNames($assignVariableExpr, $emptyArrayVariables); + } + + private function matchEmptyArrayVariableAssign(Stmt $stmt): ?string + { + if (! $stmt instanceof Expression) { + return null; } - $scope = $foreach->expr->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return true; + if (! $stmt->expr instanceof Assign) { + return null; } - $type = $scope->getType($foreach->expr); + $assign = $stmt->expr; + if (! $assign->var instanceof Variable) { + return null; + } - if ($type instanceof ObjectType) { - return $type->isIterable() - ->yes(); + if (! $assign->expr instanceof Array_) { + return null; } - if ($type instanceof ThisType) { - return $type->isIterable() - ->yes(); + // must be assign of empty array + if (! $this->valueResolver->isValue($assign->expr, [])) { + return null; } - return false; - } + if ($this->exprAnalyzer->isDynamicArray($assign->expr)) { + return null; + } - private function shouldSkipAsPartOfNestedForeach(Foreach_ $foreach): bool - { - $foreachParent = $this->betterNodeFinder->findParentType($foreach, Foreach_::class); - return $foreachParent !== null; + return $this->getName($assign->var); } } diff --git a/rules/CodeQuality/Rector/Foreach_/ForeachToInArrayRector.php b/rules/CodeQuality/Rector/Foreach_/ForeachToInArrayRector.php index d97f0925321..8e7571d7ac3 100644 --- a/rules/CodeQuality/Rector/Foreach_/ForeachToInArrayRector.php +++ b/rules/CodeQuality/Rector/Foreach_/ForeachToInArrayRector.php @@ -6,7 +6,6 @@ use PhpParser\Node; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BooleanNot; @@ -15,12 +14,12 @@ use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use PHPStan\Type\ObjectType; -use Rector\BetterPhpDocParser\Comment\CommentsMerger; -use Rector\Core\NodeManipulator\BinaryOpManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\NodeFinder; +use Rector\NodeManipulator\BinaryOpManipulator; use Rector\Php71\ValueObject\TwoNodeMatch; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -30,8 +29,9 @@ final class ForeachToInArrayRector extends AbstractRector { public function __construct( - private BinaryOpManipulator $binaryOpManipulator, - private CommentsMerger $commentsMerger + private readonly BinaryOpManipulator $binaryOpManipulator, + private readonly ValueResolver $valueResolver, + private readonly NodeFinder $nodeFinder ) { } @@ -64,106 +64,115 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Foreach_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Foreach_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkipForeach($node)) { + if ($node->stmts === null) { return null; } - /** @var If_ $firstNodeInsideForeach */ - $firstNodeInsideForeach = $node->stmts[0]; - if ($this->shouldSkipIf($firstNodeInsideForeach)) { - return null; - } + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Return_) { + continue; + } - /** @var Identical|Equal $ifCondition */ - $ifCondition = $firstNodeInsideForeach->cond; - $foreachValueVar = $node->valueVar; + $prevStmt = $node->stmts[$key - 1] ?? null; + if (! $prevStmt instanceof Foreach_) { + continue; + } - $twoNodeMatch = $this->matchNodes($ifCondition, $foreachValueVar); - if (! $twoNodeMatch instanceof TwoNodeMatch) { - return null; - } + $return = $stmt; + $foreach = $prevStmt; - $comparedNode = $twoNodeMatch->getSecondExpr(); - if (! $this->isIfBodyABoolReturnNode($firstNodeInsideForeach)) { - return null; - } + if ($this->shouldSkipForeach($foreach)) { + return null; + } - $funcCall = $this->createInArrayFunction($comparedNode, $ifCondition, $node); + /** @var If_ $firstNodeInsideForeach */ + $firstNodeInsideForeach = $foreach->stmts[0]; + if ($this->shouldSkipIf($firstNodeInsideForeach)) { + return null; + } - /** @var Return_ $returnToRemove */ - $returnToRemove = $node->getAttribute(AttributeKey::NEXT_NODE); + /** @var Identical|Equal $ifCondition */ + $ifCondition = $firstNodeInsideForeach->cond; - /** @var Return_ $return */ - $return = $firstNodeInsideForeach->stmts[0]; - if ($returnToRemove->expr === null) { - return null; - } + $twoNodeMatch = $this->matchNodes($ifCondition, $foreach->valueVar); + if (! $twoNodeMatch instanceof TwoNodeMatch) { + return null; + } - if (! $this->valueResolver->isTrueOrFalse($returnToRemove->expr)) { - return null; - } + $variableNodes = $this->nodeFinder->findInstanceOf($twoNodeMatch->getSecondExpr(), Variable::class); - $returnedExpr = $return->expr; - if (! $returnedExpr instanceof Expr) { - return null; - } + foreach ($variableNodes as $variableNode) { + if ($this->nodeComparator->areNodesEqual($variableNode, $foreach->valueVar)) { + return null; + } + } - // cannot be "return true;" + "return true;" - if ($this->nodeComparator->areNodesEqual($return, $returnToRemove)) { - return null; - } + $comparedExpr = $twoNodeMatch->getSecondExpr(); + if (! $this->isIfBodyABoolReturnNode($firstNodeInsideForeach)) { + return null; + } - $this->removeNode($returnToRemove); + $foreachReturn = $firstNodeInsideForeach->stmts[0]; + if (! $foreachReturn instanceof Return_) { + return null; + } - $return = $this->createReturn($returnedExpr, $funcCall); + if (! $return->expr instanceof Expr) { + return null; + } - $this->commentsMerger->keepChildren($return, $node); + if (! $this->valueResolver->isTrueOrFalse($return->expr)) { + return null; + } - return $return; - } + if (! $foreachReturn->expr instanceof Expr) { + return null; + } - private function shouldSkipForeach(Foreach_ $foreach): bool - { - if ($foreach->keyVar !== null) { - return true; - } + // cannot be "return true;" + "return true;" + if ($this->nodeComparator->areNodesEqual($return, $foreachReturn)) { + return null; + } - if (count($foreach->stmts) > 1) { - return true; - } + // 1. remove foreach + unset($node->stmts[$key - 1]); - $nextNode = $foreach->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Node) { - return true; - } - if (! $nextNode instanceof Return_) { - return true; + // 2. make return of in_array() + $funcCall = $this->createInArrayFunction($comparedExpr, $ifCondition, $foreach); + $return = $this->createReturn($foreachReturn->expr, $funcCall); + $node->stmts[$key] = $return; + + return $node; } - $returnExpression = $nextNode->expr; + return null; + } - if (! $returnExpression instanceof Expr) { + private function shouldSkipForeach(Foreach_ $foreach): bool + { + if ($foreach->keyVar instanceof Expr) { return true; } - if (! $this->valueResolver->isTrueOrFalse($returnExpression)) { + if (count($foreach->stmts) > 1) { return true; } - $foreachValueStaticType = $this->getStaticType($foreach->expr); - if ($foreachValueStaticType instanceof ObjectType) { + if (! $foreach->stmts[0] instanceof If_) { return true; } - return ! $foreach->stmts[0] instanceof If_; + return ! $this->nodeTypeResolver->getNativeType($foreach->expr) + ->isArray() + ->yes(); } private function shouldSkipIf(If_ $if): bool @@ -172,10 +181,11 @@ private function shouldSkipIf(If_ $if): bool if ($ifCondition instanceof Identical) { return false; } + return ! $ifCondition instanceof Equal; } - private function matchNodes(BinaryOp $binaryOp, Expr $expr): ?TwoNodeMatch + private function matchNodes(Equal|Identical $binaryOp, Expr $expr): ?TwoNodeMatch { return $this->binaryOpManipulator->matchFirstAndSecondConditionNode( $binaryOp, @@ -186,16 +196,16 @@ private function matchNodes(BinaryOp $binaryOp, Expr $expr): ?TwoNodeMatch private function isIfBodyABoolReturnNode(If_ $if): bool { - $ifStatment = $if->stmts[0]; - if (! $ifStatment instanceof Return_) { + $ifStatement = $if->stmts[0]; + if (! $ifStatement instanceof Return_) { return false; } - if ($ifStatment->expr === null) { + if (! $ifStatement->expr instanceof Expr) { return false; } - return $this->valueResolver->isTrueOrFalse($ifStatment->expr); + return $this->valueResolver->isTrueOrFalse($ifStatement->expr); } private function createInArrayFunction(Expr $expr, Identical | Equal $binaryOp, Foreach_ $foreach): FuncCall diff --git a/rules/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector.php b/rules/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector.php deleted file mode 100644 index 5d4854669c0..00000000000 --- a/rules/CodeQuality/Rector/Foreach_/SimplifyForeachToArrayFilterRector.php +++ /dev/null @@ -1,169 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Foreach_::class]; - } - - /** - * @param Foreach_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - /** @var If_ $ifNode */ - $ifNode = $node->stmts[0]; - - /** @var FuncCall $funcCallNode */ - $funcCallNode = $ifNode->cond; - if (count($ifNode->stmts) !== 1) { - return null; - } - - if (! $this->isSimpleCall($funcCallNode, $node)) { - return null; - } - - if (! $ifNode->stmts[0] instanceof Expression) { - return null; - } - - $onlyNodeInIf = $ifNode->stmts[0]->expr; - if (! $onlyNodeInIf instanceof Assign) { - return null; - } - - $arrayDimFetch = $onlyNodeInIf->var; - if (! $arrayDimFetch instanceof ArrayDimFetch) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($onlyNodeInIf->expr, $node->valueVar)) { - return null; - } - - $name = $this->getName($funcCallNode); - if ($name === null) { - return null; - } - - if (! $this->isArrayDimFetchInForLoop($node, $arrayDimFetch)) { - return null; - } - - return $this->createAssignNode($node, $name, $arrayDimFetch); - } - - private function shouldSkip(Foreach_ $foreach): bool - { - if (count($foreach->stmts) !== 1) { - return true; - } - - if (! $foreach->stmts[0] instanceof If_) { - return true; - } - - /** @var If_ $ifNode */ - $ifNode = $foreach->stmts[0]; - - if ($ifNode->else !== null) { - return true; - } - - if ($ifNode->elseifs !== []) { - return true; - } - - return ! $ifNode->cond instanceof FuncCall; - } - - private function createAssignNode(Foreach_ $foreach, string $name, ArrayDimFetch $arrayDimFetch): Assign - { - $string = new String_($name); - - $args = [new Arg($foreach->expr), new Arg($string)]; - $arrayFilterFuncCall = new FuncCall(new Name('array_filter'), $args); - - return new Assign($arrayDimFetch->var, $arrayFilterFuncCall); - } - - private function isArrayDimFetchInForLoop(Foreach_ $foreach, ArrayDimFetch $arrayDimFetch): bool - { - $loopVar = $foreach->expr; - if (! $loopVar instanceof Variable) { - return false; - } - - $varThatIsModified = $arrayDimFetch->var; - if (! $varThatIsModified instanceof Variable) { - return false; - } - - return $loopVar->name !== $varThatIsModified->name; - } - - private function isSimpleCall(FuncCall $funcCall, Foreach_ $foreach): bool - { - if (count($funcCall->args) !== 1) { - return false; - } - - return $this->nodeComparator->areNodesEqual($funcCall->args[0], $foreach->valueVar); - } -} diff --git a/rules/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector.php b/rules/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector.php index 230957373d5..70945844046 100644 --- a/rules/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector.php +++ b/rules/CodeQuality/Rector/Foreach_/SimplifyForeachToCoalescingRector.php @@ -5,36 +5,29 @@ namespace Rector\CodeQuality\Rector\Foreach_; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp\Coalesce; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\NodeManipulator\ForeachManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/bfsdY - * * @see \Rector\Tests\CodeQuality\Rector\Foreach_\SimplifyForeachToCoalescingRector\SimplifyForeachToCoalescingRectorTest */ final class SimplifyForeachToCoalescingRector extends AbstractRector implements MinPhpVersionInterface { - private ?Return_ $return = null; - - public function __construct( - private ForeachManipulator $foreachManipulator - ) { - } - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -60,41 +53,87 @@ public function getRuleDefinition(): RuleDefinition } /** - * @return array> + * @innerForeachReturn array> */ public function getNodeTypes(): array { - return [Foreach_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Foreach_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - $this->return = null; - - if ($node->keyVar === null) { + if ($node->stmts === null) { return null; } - /** @var Return_|Assign|null $returnOrAssignNode */ - $returnOrAssignNode = $this->matchReturnOrAssignNode($node); - if ($returnOrAssignNode === null) { - return null; - } + $hasChanged = false; - // return $newValue; - // we don't return the node value - if (! $this->nodeComparator->areNodesEqual($node->valueVar, $returnOrAssignNode->expr)) { - return null; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } + + $foreach = $stmt; + if (! $foreach->keyVar instanceof Expr) { + continue; + } + + $foreachReturnOrAssign = $this->matchForeachReturnOrAssign($foreach); + + if ($foreachReturnOrAssign instanceof Expression) { + /** @var Assign $innerAssign */ + $innerAssign = $foreachReturnOrAssign->expr; + + if (! $this->nodeComparator->areNodesEqual($foreach->valueVar, $innerAssign->expr)) { + return null; + } + + $assign = $this->processForeachNodeWithAssignInside($foreach, $innerAssign); + if (! $assign instanceof Assign) { + return null; + } + + $node->stmts[$key] = new Expression($assign); + + $hasChanged = true; + + continue; + } + + if ($foreachReturnOrAssign instanceof Return_) { + if (! $this->nodeComparator->areNodesEqual($foreach->valueVar, $foreachReturnOrAssign->expr)) { + return null; + } + + $nextStmt = $node->stmts[$key + 1] ?? null; + + if (! $nextStmt instanceof Return_) { + continue; + } + + $return = $this->processForeachNodeWithReturnInside($foreach, $foreachReturnOrAssign, $nextStmt); + + if (! $return instanceof Return_) { + continue; + } + + $node->stmts[$key] = $return; + + // cleanup next return + unset($node->stmts[$key + 1]); + + $hasChanged = true; + } } - if ($returnOrAssignNode instanceof Return_) { - return $this->processForeachNodeWithReturnInside($node, $returnOrAssignNode); + if ($hasChanged) { + return $node; } - return $this->processForeachNodeWithAssignInside($node, $returnOrAssignNode); + return null; } public function provideMinPhpVersion(): int @@ -103,35 +142,65 @@ public function provideMinPhpVersion(): int } /** - * @return Assign|Return_|null + * @todo make assign expr generic + * @return Expression|Return_|null */ - private function matchReturnOrAssignNode(Foreach_ $foreach): ?Node + private function matchForeachReturnOrAssign(Foreach_ $foreach): Expression|Return_|null { - return $this->foreachManipulator->matchOnlyStmt($foreach, function (Node $node): ?Node { - if (! $node instanceof If_) { - return null; - } + if (count($foreach->stmts) !== 1) { + return null; + } - if (! $node->cond instanceof Identical) { - return null; - } + $onlyForeachStmt = $foreach->stmts[0]; + if (! $onlyForeachStmt instanceof If_) { + return null; + } + + $if = $onlyForeachStmt; + if (! $if->cond instanceof Identical) { + return null; + } + + if (count($if->stmts) !== 1) { + return null; + } + + if ($if->else instanceof Else_ || $if->elseifs !== []) { + return null; + } + + $foreachExprType = $this->nodeTypeResolver->getNativeType($foreach->expr); + if (! $foreachExprType->isArray()->yes()) { + return null; + } - if (count($node->stmts) !== 1) { + $innerStmt = $if->stmts[0]; + if ($innerStmt instanceof Return_) { + return $innerStmt; + } + + if (! $innerStmt instanceof Expression) { + return null; + } + + $innerNode = $innerStmt->expr; + if ($innerNode instanceof Assign) { + if ($innerNode->var instanceof ArrayDimFetch) { return null; } - $innerNode = $node->stmts[0] instanceof Expression ? $node->stmts[0]->expr : $node->stmts[0]; - if ($innerNode instanceof Assign || $innerNode instanceof Return_) { - return $innerNode; - } + return $innerStmt; + } - return null; - }); + return null; } - private function processForeachNodeWithReturnInside(Foreach_ $foreach, Return_ $return): ?Node - { - if (! $this->nodeComparator->areNodesEqual($foreach->valueVar, $return->expr)) { + private function processForeachNodeWithReturnInside( + Foreach_ $foreach, + Return_ $innerForeachReturn, + ?Stmt $nextStmt + ): Return_|null { + if (! $this->nodeComparator->areNodesEqual($foreach->valueVar, $innerForeachReturn->expr)) { return null; } @@ -148,27 +217,16 @@ private function processForeachNodeWithReturnInside(Foreach_ $foreach, Return_ $ } else { return null; } - $nextNode = $foreach->getAttribute(AttributeKey::NEXT_NODE); - - // is next node Return? - if ($nextNode instanceof Return_) { - $this->return = $nextNode; - $this->removeNode($this->return); - } $coalesce = new Coalesce(new ArrayDimFetch( $foreach->expr, $checkedNode - ), $this->return && $this->return->expr !== null ? $this->return->expr : $checkedNode); + ), $nextStmt instanceof Return_ && $nextStmt->expr instanceof Expr ? $nextStmt->expr : $checkedNode); - if ($this->return !== null) { - return new Return_($coalesce); - } - - return null; + return new Return_($coalesce); } - private function processForeachNodeWithAssignInside(Foreach_ $foreach, Assign $assign): ?Node + private function processForeachNodeWithAssignInside(Foreach_ $foreach, Assign $assign): ?Assign { /** @var If_ $ifNode */ $ifNode = $foreach->stmts[0]; diff --git a/rules/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector.php b/rules/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector.php index 53cfb5d7444..51b3d304f29 100644 --- a/rules/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector.php +++ b/rules/CodeQuality/Rector/Foreach_/UnusedForeachValueToArrayKeysRector.php @@ -5,14 +5,16 @@ namespace Rector\CodeQuality\Rector\Foreach_; use PhpParser\Node; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\Expr\List_; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Foreach_; -use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; use Rector\DeadCode\NodeAnalyzer\ExprUsedInNodeAnalyzer; +use Rector\NodeManipulator\StmtsManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,7 +24,9 @@ final class UnusedForeachValueToArrayKeysRector extends AbstractRector { public function __construct( - private ExprUsedInNodeAnalyzer $exprUsedInNodeAnalyzer + private readonly ExprUsedInNodeAnalyzer $exprUsedInNodeAnalyzer, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly StmtsManipulator $stmtsManipulator ) { } @@ -44,7 +48,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -67,61 +71,133 @@ public function run() */ public function getNodeTypes(): array { - return [Foreach_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Foreach_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if ($node->keyVar === null) { + $stmts = $node->stmts; + + if ($stmts === null) { return null; } - // special case of nested array items - if ($node->valueVar instanceof Array_) { - $node->valueVar = $this->refactorArrayForeachValue($node->valueVar, $node); - if ($node->valueVar->items !== []) { - return null; + $hasChanged = false; + foreach ($stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; } - } elseif ($node->valueVar instanceof Variable) { - if ($this->isVariableUsedInForeach($node->valueVar, $node)) { - return null; + + if (! $stmt->keyVar instanceof Expr) { + continue; } - } else { - return null; + + if (! $this->nodeTypeResolver->getNativeType($stmt->expr)->isArray()->yes()) { + continue; + } + + // special case of nested array items + if ($stmt->valueVar instanceof List_) { + $valueArray = $this->refactorArrayForeachValue($stmt->valueVar, $stmt); + if (! $valueArray instanceof List_) { + continue; + } + + $stmt->valueVar = $valueArray; + + // not sure what does this mean :) + if ($valueArray->items !== []) { + continue; + } + + $hasChanged = true; + $this->removeForeachValueAndUseArrayKeys($stmt, $stmt->keyVar); + continue; + } + + if (! $stmt->valueVar instanceof Variable) { + continue; + } + + if ($this->isVariableUsedInForeach($stmt->valueVar, $stmt)) { + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt( + $node, + $key + 1, + (string) $this->getName($stmt->valueVar) + )) { + continue; + } + + $hasChanged = true; + $this->removeForeachValueAndUseArrayKeys($stmt, $stmt->keyVar); } - if (! $this->isArrayType($node->expr)) { + if (! $hasChanged) { return null; } - $this->removeForeachValueAndUseArrayKeys($node); - return $node; } - private function refactorArrayForeachValue(Array_ $array, Foreach_ $foreach): Array_ + /** + * @param int[] $removedKeys + */ + private function isArrayItemsRemovalWithoutChangingOrder(List_ $list, array $removedKeys): bool + { + $hasRemovingStarted = false; + + foreach (array_keys($list->items) as $key) { + if (in_array($key, $removedKeys, true)) { + $hasRemovingStarted = true; + } elseif ($hasRemovingStarted) { + // we cannot remove the previous item, and not remove the next one, because that would change the order + return false; + } + } + + return true; + } + + private function refactorArrayForeachValue(List_ $list, Foreach_ $foreach): ?List_ { - foreach ($array->items as $key => $arrayItem) { + // only last items can be removed, without changing the order + $removedKeys = []; + + foreach ($list->items as $key => $arrayItem) { if (! $arrayItem instanceof ArrayItem) { - continue; + // only known values can be processes + return null; } $value = $arrayItem->value; if (! $value instanceof Variable) { - return $array; + // only variables can be processed + return null; } if ($this->isVariableUsedInForeach($value, $foreach)) { continue; } - unset($array->items[$key]); + $removedKeys[] = $key; + } + + if (! $this->isArrayItemsRemovalWithoutChangingOrder($list, $removedKeys)) { + return null; + } + + // clear removed items + foreach ($removedKeys as $removedKey) { + unset($list->items[$removedKey]); } - return $array; + return $list; } private function isVariableUsedInForeach(Variable $variable, Foreach_ $foreach): bool @@ -132,23 +208,12 @@ private function isVariableUsedInForeach(Variable $variable, Foreach_ $foreach): ); } - private function removeForeachValueAndUseArrayKeys(Foreach_ $foreach): void + private function removeForeachValueAndUseArrayKeys(Foreach_ $foreach, Expr $keyVarExpr): void { // remove key value - $foreach->valueVar = $foreach->keyVar; + $foreach->valueVar = $keyVarExpr; $foreach->keyVar = null; $foreach->expr = $this->nodeFactory->createFuncCall('array_keys', [$foreach->expr]); } - - private function isArrayType(Expr $expr): bool - { - $exprType = $this->getStaticType($expr); - if ($exprType instanceof ObjectType) { - return false; - } - - return $exprType->isArray() - ->yes(); - } } diff --git a/rules/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector.php b/rules/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector.php deleted file mode 100644 index b02e468d6c6..00000000000 --- a/rules/CodeQuality/Rector/FuncCall/AddPregQuoteDelimiterRector.php +++ /dev/null @@ -1,120 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'preg_quote')) { - return null; - } - - // already completed - if (isset($node->args[1])) { - return null; - } - - $delimiter = $this->determineDelimiter($node); - if ($delimiter === null) { - return null; - } - - $node->args[1] = new Arg(new String_($delimiter)); - - return $node; - } - - private function determineDelimiter(FuncCall $funcCall): ?string - { - $concat = $this->getUppermostConcat($funcCall); - if (! $concat instanceof Concat) { - return null; - } - - $leftMostConcatNode = $concat->left; - while ($leftMostConcatNode instanceof Concat) { - $leftMostConcatNode = $leftMostConcatNode->left; - } - - $rightMostConcatNode = $concat->right; - while ($rightMostConcatNode instanceof Concat) { - $rightMostConcatNode = $rightMostConcatNode->right; - } - - if (! $leftMostConcatNode instanceof String_) { - return null; - } - - $possibleLeftDelimiter = Strings::substring($leftMostConcatNode->value, 0, 1); - if (! $rightMostConcatNode instanceof String_) { - return null; - } - - $possibleRightDelimiter = Strings::substring(rtrim($rightMostConcatNode->value, self::ALL_MODIFIERS), -1, 1); - if ($possibleLeftDelimiter === $possibleRightDelimiter) { - return $possibleLeftDelimiter; - } - - return null; - } - - private function getUppermostConcat(FuncCall $funcCall): ?Concat - { - $upperMostConcat = null; - $parent = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - while ($parent instanceof Concat) { - $upperMostConcat = $parent; - $parent = $parent->getAttribute(AttributeKey::PARENT_NODE); - } - return $upperMostConcat; - } -} diff --git a/rules/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector.php b/rules/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector.php deleted file mode 100644 index 86dad5465ab..00000000000 --- a/rules/CodeQuality/Rector/FuncCall/ArrayKeysAndInArrayToArrayKeyExistsRector.php +++ /dev/null @@ -1,116 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->nodeNameResolver->isName($node, 'in_array')) { - return null; - } - - $arrayVariable = $node->args[1]->value; - - /** @var Assign|Node|null $previousAssignArraysKeysFuncCall */ - $previousAssignArraysKeysFuncCall = $this->betterNodeFinder->findFirstPreviousOfNode($node, function ( - Node $node - ) use ($arrayVariable): bool { - // breaking out of scope - if ($node instanceof FunctionLike) { - return true; - } - - if (! $node instanceof Assign) { - return ! (bool) $this->betterNodeFinder->find( - $node, - fn (Node $n): bool => $this->nodeComparator->areNodesEqual($arrayVariable, $n) - ); - } - - if (! $this->nodeComparator->areNodesEqual($arrayVariable, $node->var)) { - return false; - } - - if (! $node->expr instanceof FuncCall) { - return false; - } - - return $this->nodeNameResolver->isName($node->expr, 'array_keys'); - }); - - if (! $previousAssignArraysKeysFuncCall instanceof Assign) { - return null; - } - - /** @var FuncCall $arrayKeysFuncCall */ - $arrayKeysFuncCall = $previousAssignArraysKeysFuncCall->expr; - - $this->removeNode($previousAssignArraysKeysFuncCall); - - return $this->createArrayKeyExists($node, $arrayKeysFuncCall); - } - - private function createArrayKeyExists(FuncCall $inArrayFuncCall, FuncCall $arrayKeysFuncCall): FuncCall - { - $arguments = [$inArrayFuncCall->args[0], $arrayKeysFuncCall->args[0]]; - - return new FuncCall(new Name('array_key_exists'), $arguments); - } -} diff --git a/rules/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector.php b/rules/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector.php index a3216826052..357d8bea05b 100644 --- a/rules/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector.php +++ b/rules/CodeQuality/Rector/FuncCall/ArrayMergeOfNonArraysToSimpleArrayRector.php @@ -5,18 +5,15 @@ namespace Rector\CodeQuality\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/aLf96 - * @see https://3v4l.org/2r26K - * @see https://3v4l.org/anks3 - * * @see \Rector\Tests\CodeQuality\Rector\FuncCall\ArrayMergeOfNonArraysToSimpleArrayRector\ArrayMergeOfNonArraysToSimpleArrayRectorTest */ final class ArrayMergeOfNonArraysToSimpleArrayRector extends AbstractRector @@ -39,7 +36,7 @@ public function go() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -75,21 +72,34 @@ public function refactor(Node $node): ?Node } $array = new Array_(); + $isAssigned = false; foreach ($node->args as $arg) { + // found non Arg? return early + if (! $arg instanceof Arg) { + return null; + } + $nestedArrayItem = $arg->value; if (! $nestedArrayItem instanceof Array_) { return null; } foreach ($nestedArrayItem->items as $nestedArrayItemItem) { - if ($nestedArrayItemItem === null) { + if (! $nestedArrayItemItem instanceof ArrayItem) { continue; } - $array->items[] = new ArrayItem($nestedArrayItemItem->value, $nestedArrayItemItem->key); + $array->items[] = $nestedArrayItemItem->unpack + ? $nestedArrayItemItem + : new ArrayItem($nestedArrayItemItem->value, $nestedArrayItemItem->key); + $isAssigned = true; } } + if (! $isAssigned) { + return null; + } + return $array; } } diff --git a/rules/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector.php b/rules/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector.php index 2f3a3b1ae5c..53c714c9723 100644 --- a/rules/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector.php +++ b/rules/CodeQuality/Rector/FuncCall/CallUserFuncWithArrowFunctionToInlineRector.php @@ -5,29 +5,28 @@ namespace Rector\CodeQuality\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; use Rector\Php74\NodeAnalyzer\ClosureArrowFunctionAnalyzer; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://3v4l.org/fuuEF - * * @see \Rector\Tests\CodeQuality\Rector\FuncCall\CallUserFuncWithArrowFunctionToInlineRector\CallUserFuncWithArrowFunctionToInlineRectorTest */ final class CallUserFuncWithArrowFunctionToInlineRector extends AbstractRector { public function __construct( - private ClosureArrowFunctionAnalyzer $closureArrowFunctionAnalyzer + private readonly ClosureArrowFunctionAnalyzer $closureArrowFunctionAnalyzer ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Refactor call_user_func() with arrow function to direct call', [ + return new RuleDefinition('Refactor `call_user_func()` with arrow function to direct call', [ new CodeSample( <<<'CODE_SAMPLE' final class SomeClass @@ -66,6 +65,10 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { + if ($node->isFirstClassCallable()) { + return null; + } + if (! $this->isName($node, 'call_user_func')) { return null; } @@ -75,7 +78,16 @@ public function refactor(Node $node): ?Node } // change the node - $firstArgValue = $node->args[0]->value; + if (! isset($node->getArgs()[0])) { + return null; + } + + $firstArg = $node->args[0]; + if (! $firstArg instanceof Arg) { + return null; + } + + $firstArgValue = $firstArg->value; if ($firstArgValue instanceof ArrowFunction) { return $firstArgValue->expr; } diff --git a/rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php b/rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php index df64730a767..d287ff519fe 100644 --- a/rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php +++ b/rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php @@ -5,19 +5,16 @@ namespace Rector\CodeQuality\Rector\FuncCall; use PhpParser\Node; -use PhpParser\Node\Arg; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/questions/559844/whats-better-to-use-in-php-array-value-or-array-pusharray-value - * * @see \Rector\Tests\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector\ChangeArrayPushToArrayAssignRectorTest */ final class ChangeArrayPushToArrayAssignRector extends AbstractRector @@ -29,25 +26,13 @@ public function getRuleDefinition(): RuleDefinition [ new CodeSample( <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $items = []; - array_push($items, $item); - } -} +$items = []; +array_push($items, $item); CODE_SAMPLE -, + , <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $items = []; - $items[] = $item; - } -} +$items = []; +$items[] = $item; CODE_SAMPLE ), ] @@ -59,53 +44,63 @@ public function run() */ public function getNodeTypes(): array { - return [FuncCall::class]; + return [Expression::class]; } /** - * @param FuncCall $node + * @param Expression $node + * @return Stmt[]|null */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - if (! $this->isName($node, 'array_push')) { + if (! $node->expr instanceof FuncCall) { return null; } - if ($this->hasArraySpread($node)) { + $funcCall = $node->expr; + if (! $this->isName($funcCall, 'array_push')) { return null; } - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Expression) { + if ($funcCall->isFirstClassCallable()) { return null; } - $arrayDimFetch = new ArrayDimFetch($node->args[0]->value); + if ($this->hasArraySpread($funcCall)) { + return null; + } - $position = 1; - while (isset($node->args[$position])) { - $assign = new Assign($arrayDimFetch, $node->args[$position]->value); + $args = $funcCall->getArgs(); + if ($args === []) { + return null; + } + + $firstArg = array_shift($args); + if ($args === []) { + return null; + } + + $arrayDimFetch = new ArrayDimFetch($firstArg->value); + + $newStmts = []; + + foreach ($args as $key => $arg) { + $assign = new Assign($arrayDimFetch, $arg->value); $assignExpression = new Expression($assign); + $newStmts[] = $assignExpression; // keep comments of first line - if ($position === 1) { + if ($key === 0) { $this->mirrorComments($assignExpression, $node); } - - $this->addNodeAfterNode($assignExpression, $node); - - ++$position; } - $this->removeNode($node); - - return null; + return $newStmts; } private function hasArraySpread(FuncCall $funcCall): bool { - foreach ($funcCall->args as $arg) { - /** @var Arg $arg */ + foreach ($funcCall->getArgs() as $arg) { if ($arg->unpack) { return true; } diff --git a/rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php b/rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php index 4b74e6cf14c..bd504202330 100644 --- a/rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php +++ b/rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php @@ -5,38 +5,32 @@ namespace Rector\CodeQuality\Rector\FuncCall; use PhpParser\Node; -use PhpParser\Node\Expr; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\FuncCall; -use PHPStan\Analyser\Scope; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Scalar\String_; use PHPStan\Type\Constant\ConstantArrayType; +use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\MixedType; use Rector\CodeQuality\CompactConverter; -use Rector\CodeQuality\NodeAnalyzer\ArrayCompacter; -use Rector\CodeQuality\NodeAnalyzer\ArrayItemsAnalyzer; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/a/16319909/1348344 - * @see https://3v4l.org/8GJEs * @see \Rector\Tests\CodeQuality\Rector\FuncCall\CompactToVariablesRector\CompactToVariablesRectorTest */ final class CompactToVariablesRector extends AbstractRector { public function __construct( - private CompactConverter $compactConverter, - private ArrayItemsAnalyzer $arrayItemsAnalyzer, - private ArrayCompacter $arrayCompacter + private readonly CompactConverter $compactConverter, ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change compact() call to own array', [ + return new RuleDefinition('Change `compact()` call to own array', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -88,70 +82,40 @@ public function refactor(Node $node): ?Node return $this->compactConverter->convertToArray($node); } - $firstValue = $node->args[0]->value; - $firstValueStaticType = $this->getStaticType($firstValue); - if (! $firstValueStaticType instanceof ConstantArrayType) { + if ($node->isFirstClassCallable()) { return null; } - if ($firstValueStaticType->getItemType() instanceof MixedType) { + + $firstArg = $node->getArgs()[0]; + + $firstValue = $firstArg->value; + $firstValueStaticType = $this->getType($firstValue); + if (! $firstValueStaticType instanceof ConstantArrayType) { return null; } - return $this->refactorAssignArray($firstValue, $node); - } - private function refactorAssignedArray(Assign $assign, FuncCall $funcCall, Expr $expr): ?Expr - { - if (! $assign->expr instanceof Array_) { + if ($firstValueStaticType->getIterableValueType() instanceof MixedType) { return null; } - $array = $assign->expr; + return $this->refactorAssignArray($firstValueStaticType); + } - $assignScope = $assign->getAttribute(AttributeKey::SCOPE); - if (! $assignScope instanceof Scope) { - return null; - } + private function refactorAssignArray(ConstantArrayType $constantArrayType): ?Array_ + { + $arrayItems = []; - $isCompactOfUndefinedVariables = $this->arrayItemsAnalyzer->hasArrayExclusiveDefinedVariableNames( - $array, - $assignScope - ); - if ($isCompactOfUndefinedVariables) { - $funcCallScope = $funcCall->getAttribute(AttributeKey::SCOPE); - if (! $funcCallScope instanceof Scope) { + foreach ($constantArrayType->getValueTypes() as $valueType) { + if (! $valueType instanceof ConstantStringType) { return null; } - $isCompactOfDefinedVariables = $this->arrayItemsAnalyzer->hasArrayExclusiveUndefinedVariableNames( - $array, - $funcCallScope - ); - if ($isCompactOfDefinedVariables) { - $this->arrayCompacter->compactStringToVariableArray($array); - return $expr; - } - } - - $this->removeNode($assign); - - $this->arrayCompacter->compactStringToVariableArray($array); - - $assignVariable = $funcCall->args[0]->value; - $preAssign = new Assign($assignVariable, $array); + $variableName = $valueType->getValue(); + $variable = new Variable($variableName); - $currentStatement = $funcCall->getAttribute(AttributeKey::CURRENT_STATEMENT); - $this->addNodeBeforeNode($preAssign, $currentStatement); - - return $expr; - } - - private function refactorAssignArray(Expr $expr, FuncCall $funcCall): ?Expr - { - $previousAssign = $this->betterNodeFinder->findPreviousAssignToExpr($expr); - if (! $previousAssign instanceof Assign) { - return null; + $arrayItems[] = new ArrayItem($variable, new String_($variableName)); } - return $this->refactorAssignedArray($previousAssign, $funcCall, $expr); + return new Array_($arrayItems); } } diff --git a/rules/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector.php b/rules/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector.php deleted file mode 100644 index 4dcf0de5068..00000000000 --- a/rules/CodeQuality/Rector/FuncCall/InArrayAndArrayKeysToArrayKeyExistsRector.php +++ /dev/null @@ -1,69 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'in_array')) { - return null; - } - - $secondArgument = $node->args[1]->value; - if (! $secondArgument instanceof FuncCall) { - return null; - } - - if (! $this->isName($secondArgument, 'array_keys')) { - return null; - } - - if (count($secondArgument->args) > 1) { - return null; - } - - $keyArg = $node->args[0]; - $arrayArg = $node->args[1]; - - /** @var FuncCall $innerFuncCallNode */ - $innerFuncCallNode = $arrayArg->value; - $arrayArg = $innerFuncCallNode->args[0]; - - $node->name = new Name('array_key_exists'); - $node->args = [$keyArg, $arrayArg]; - - return $node; - } -} diff --git a/rules/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector.php b/rules/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector.php new file mode 100644 index 00000000000..5a883c3de6b --- /dev/null +++ b/rules/CodeQuality/Rector/FuncCall/InlineIsAInstanceOfRector.php @@ -0,0 +1,121 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node->name, 'is_a')) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + $firstArgValue = $args[0]->value; + + if (! $this->isFirstObjectType($firstArgValue)) { + return null; + } + + /** + * instanceof with Variable is ok, while on FuncCal with instanceof cause fatal error, see https://3v4l.org/IHb30 + */ + if ($args[1]->value instanceof Variable) { + return new Instanceof_($firstArgValue, $args[1]->value); + } + + $className = $this->resolveClassName($args[1]->value); + if ($className === null) { + return null; + } + + return new Instanceof_($firstArgValue, new FullyQualified($className)); + } + + private function resolveClassName(Expr $expr): ?string + { + if (! $expr instanceof ClassConstFetch) { + return null; + } + + $type = $this->getType($expr); + + if ($type instanceof GenericClassStringType) { + $type = $type->getGenericType(); + } + + return ClassNameFromObjectTypeResolver::resolve($type); + } + + private function isFirstObjectType(Expr $expr): bool + { + $exprType = $this->getType($expr); + if ($exprType instanceof ObjectWithoutClassType) { + return true; + } + + return $exprType instanceof ObjectType; + } +} diff --git a/rules/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector.php b/rules/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector.php deleted file mode 100644 index 46c692b9a25..00000000000 --- a/rules/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector.php +++ /dev/null @@ -1,78 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'intval')) { - return null; - } - - if (isset($node->args[1])) { - $secondArgumentValue = $this->valueResolver->getValue($node->args[1]->value); - // default value - if ($secondArgumentValue !== 10) { - return null; - } - } - - return new Int_($node->args[0]->value); - } -} diff --git a/rules/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector.php b/rules/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector.php index cbb6c2f3c3c..aa13b1f5b9f 100644 --- a/rules/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector.php +++ b/rules/CodeQuality/Rector/FuncCall/IsAWithStringWithThirdArgumentRector.php @@ -7,8 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; -use PHPStan\Type\StringType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -64,12 +63,18 @@ public function refactor(Node $node): ?Node return null; } - if (isset($node->args[2])) { + if ($node->isFirstClassCallable()) { return null; } - $firstArgumentStaticType = $this->getStaticType($node->args[0]->value); - if (! $firstArgumentStaticType instanceof StringType) { + if (isset($node->getArgs()[2])) { + return null; + } + + $firstArg = $node->getArgs()[0]; + + $firstArgumentStaticType = $this->getType($firstArg->value); + if (! $firstArgumentStaticType->isString()->yes()) { return null; } diff --git a/rules/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector.php b/rules/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector.php index 5b77bc3fc2f..544f4adf6a1 100644 --- a/rules/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector.php +++ b/rules/CodeQuality/Rector/FuncCall/RemoveSoleValueSprintfRector.php @@ -7,8 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Scalar\String_; -use PHPStan\Type\StringType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,15 +18,13 @@ final class RemoveSoleValueSprintfRector extends AbstractRector { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Remove sprintf() wrapper if not needed', [ + return new RuleDefinition('Remove `sprintf()` wrapper if not needed', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass { public function run() { - $value = sprintf('%s', 'hi'); - $welcome = 'hello'; $value = sprintf('%s', $welcome); } @@ -39,8 +36,6 @@ class SomeClass { public function run() { - $value = 'hi'; - $welcome = 'hello'; $value = $welcome; } @@ -67,11 +62,16 @@ public function refactor(Node $node): ?Node return null; } - if (count($node->args) !== 2) { + if ($node->isFirstClassCallable()) { + return null; + } + + if (count($node->getArgs()) !== 2) { return null; } - $maskArgument = $node->args[0]->value; + $firstArg = $node->getArgs()[0]; + $maskArgument = $firstArg->value; if (! $maskArgument instanceof String_) { return null; } @@ -80,8 +80,11 @@ public function refactor(Node $node): ?Node return null; } - $valueArgument = $node->args[1]->value; - if (! $this->nodeTypeResolver->isStaticType($valueArgument, StringType::class)) { + $secondArg = $node->getArgs()[1]; + $valueArgument = $secondArg->value; + + $valueType = $this->getType($valueArgument); + if (! $valueType->isString()->yes()) { return null; } diff --git a/rules/CodeQuality/Rector/FuncCall/SetTypeToCastRector.php b/rules/CodeQuality/Rector/FuncCall/SetTypeToCastRector.php index 290c107c13b..9bb2e01fd80 100644 --- a/rules/CodeQuality/Rector/FuncCall/SetTypeToCastRector.php +++ b/rules/CodeQuality/Rector/FuncCall/SetTypeToCastRector.php @@ -5,8 +5,6 @@ namespace Rector\CodeQuality\Rector\FuncCall; use PhpParser\Node; -use PhpParser\Node\Arg; -use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Cast; use PhpParser\Node\Expr\Cast\Array_; @@ -17,14 +15,12 @@ use PhpParser\Node\Expr\Cast\String_; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/questions/5577003/using-settype-in-php-instead-of-typecasting-using-brackets-what-is-the-differen/5577068#5577068 - * * @see \Rector\Tests\CodeQuality\Rector\FuncCall\SetTypeToCastRector\SetTypeToCastRectorTest */ final class SetTypeToCastRector extends AbstractRector @@ -32,7 +28,7 @@ final class SetTypeToCastRector extends AbstractRector /** * @var array> */ - private const TYPE_TO_CAST = [ + private const array TYPE_TO_CAST = [ 'array' => Array_::class, 'bool' => Bool_::class, 'boolean' => Bool_::class, @@ -44,35 +40,40 @@ final class SetTypeToCastRector extends AbstractRector 'string' => String_::class, ]; + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Changes settype() to (type) where possible', [ - new CodeSample( - <<<'CODE_SAMPLE' + return new RuleDefinition( + 'Change `settype()` to `(type)` on standalone line. `settype()` returns always success/failure bool value', + [ + new CodeSample( + <<<'CODE_SAMPLE' class SomeClass { public function run($foo) { settype($foo, 'string'); - - return settype($foo, 'integer'); } } CODE_SAMPLE - , - <<<'CODE_SAMPLE' + , + <<<'CODE_SAMPLE' class SomeClass { public function run($foo) { $foo = (string) $foo; - - return (int) $foo; } } CODE_SAMPLE - ), - ]); + ), + + ] + ); } /** @@ -80,49 +81,65 @@ public function run($foo) */ public function getNodeTypes(): array { - return [FuncCall::class]; + return [Expression::class]; } /** - * @param FuncCall $node + * @param Expression $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): null|Expression { - if (! $this->isName($node, 'settype')) { + // skip expr that are not standalone line, as settype() returns success bool value + // and cannot be casted + if (! $node->expr instanceof FuncCall) { return null; } - $typeNode = $this->valueResolver->getValue($node->args[1]->value); - if ($typeNode === null) { + $assign = $this->refactorFuncCall($node->expr); + if (! $assign instanceof Assign) { return null; } - $typeNode = strtolower($typeNode); + return new Expression($assign); + } - $varNode = $node->args[0]->value; - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); + private function refactorFuncCall(FuncCall $funcCall): Assign|null + { + if (! $this->isName($funcCall, 'setType')) { + return null; + } - // result of function or probably used - if ($parentNode instanceof Expr || $parentNode instanceof Arg) { + if ($funcCall->isFirstClassCallable()) { return null; } - if (isset(self::TYPE_TO_CAST[$typeNode])) { - $castClass = self::TYPE_TO_CAST[$typeNode]; - $castNode = new $castClass($varNode); + if (count($funcCall->getArgs()) < 2) { + return null; + } + + $secondArg = $funcCall->getArgs()[1]; + + $typeValue = $this->valueResolver->getValue($secondArg->value); + if (! is_string($typeValue)) { + return null; + } + + $typeValue = strtolower($typeValue); + + $firstArg = $funcCall->getArgs()[0]; + $variable = $firstArg->value; - if ($parentNode instanceof Expression) { - // bare expression? → assign - return new Assign($varNode, $castNode); - } + if (isset(self::TYPE_TO_CAST[$typeValue])) { + $castClass = self::TYPE_TO_CAST[$typeValue]; + $castNode = new $castClass($variable); - return $castNode; + return new Assign($variable, $castNode); } - if ($typeNode === 'null') { - return new Assign($varNode, $this->nodeFactory->createNull()); + if ($typeValue === 'null') { + return new Assign($variable, $this->nodeFactory->createNull()); } - return $node; + return null; } } diff --git a/rules/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php b/rules/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php index 4ee1d51f5e3..b1dc06a072b 100644 --- a/rules/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php +++ b/rules/CodeQuality/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php @@ -6,7 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -40,15 +40,20 @@ public function refactor(Node $node): ?Node return null; } - if (! $node->args[0]->value instanceof FuncCall) { + if ($node->isFirstClassCallable()) { return null; } - /** @var FuncCall $innerFuncCall */ - $innerFuncCall = $node->args[0]->value; + $firstArg = $node->getArgs()[0]; + + if (! $firstArg->value instanceof FuncCall) { + return null; + } + + $innerFuncCall = $firstArg->value; if (! $this->isName($innerFuncCall, 'func_get_args')) { - return $node; + return null; } return $this->nodeFactory->createFuncCall('func_num_args'); diff --git a/rules/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector.php b/rules/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector.php index 04360fc4530..70abeba0ec1 100644 --- a/rules/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector.php +++ b/rules/CodeQuality/Rector/FuncCall/SimplifyInArrayValuesRector.php @@ -5,8 +5,9 @@ namespace Rector\CodeQuality\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -40,17 +41,32 @@ public function refactor(Node $node): ?Node return null; } + if ($node->isFirstClassCallable()) { + return null; + } + + if (! isset($node->args[1])) { + return null; + } + + if (! $node->args[1] instanceof Arg) { + return null; + } + if (! $node->args[1]->value instanceof FuncCall) { return null; } - /** @var FuncCall $innerFunCall */ $innerFunCall = $node->args[1]->value; if (! $this->isName($innerFunCall, 'array_values')) { return null; } - $node->args[1] = $innerFunCall->args[0]; + if (! isset($node->getArgs()[0])) { + return null; + } + + $node->args[1] = $innerFunCall->getArgs()[0]; return $node; } diff --git a/rules/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector.php b/rules/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector.php index 262671ff20e..4c29d7a9853 100644 --- a/rules/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector.php +++ b/rules/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector.php @@ -6,34 +6,34 @@ use Nette\Utils\Strings; use PhpParser\Node; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\StaticCall; -use Rector\Core\Php\Regex\RegexPatternArgumentManipulator; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Scalar\String_; +use Rector\NodeNameResolver\Regex\RegexPatternDetector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog http://php.net/manual/en/function.preg-match.php#105924 - * * @see \Rector\Tests\CodeQuality\Rector\FuncCall\SimplifyRegexPatternRector\SimplifyRegexPatternRectorTest */ final class SimplifyRegexPatternRector extends AbstractRector { /** + * Using double quote "\d", "\w", "\s" to avoid unescaped issue on scoped build + * Reproduced with php-scoper 0.18.17, @see https://github.com/rectorphp/rector/issues/9395 + * * @var array */ - private const COMPLEX_PATTERN_TO_SIMPLE = [ - '[0-9]' => '\d', - '[a-zA-Z0-9_]' => '\w', - '[A-Za-z0-9_]' => '\w', - '[0-9a-zA-Z_]' => '\w', - '[0-9A-Za-z_]' => '\w', - '[\r\n\t\f\v ]' => '\s', + private const array COMPLEX_PATTERN_TO_SIMPLE = [ + '[0-9]' => "\d", + '[a-zA-Z0-9_]' => "\w", + '[A-Za-z0-9_]' => "\w", + '[0-9a-zA-Z_]' => "\w", + '[0-9A-Za-z_]' => "\w", + '[\r\n\t\f\v ]' => "\s", ]; public function __construct( - private RegexPatternArgumentManipulator $regexPatternArgumentManipulator + private readonly RegexPatternDetector $regexPatternDetector ) { } @@ -69,29 +69,42 @@ public function run($value) */ public function getNodeTypes(): array { - return [FuncCall::class, StaticCall::class]; + return [String_::class]; } /** - * @param FuncCall|StaticCall $node + * @param String_ $node */ public function refactor(Node $node): ?Node { - $patterns = $this->regexPatternArgumentManipulator->matchCallArgumentWithRegexPattern($node); - if ($patterns === []) { + if (! $this->regexPatternDetector->isRegexPattern($node->value)) { return null; } - foreach ($patterns as $pattern) { - foreach (self::COMPLEX_PATTERN_TO_SIMPLE as $complexPattern => $simple) { - $pattern->value = Strings::replace( - $pattern->value, - '#' . preg_quote($complexPattern, '#') . '#', - $simple - ); + foreach (self::COMPLEX_PATTERN_TO_SIMPLE as $complexPattern => $simple) { + $originalValue = $node->value; + $simplifiedValue = Strings::replace( + $node->value, + '#' . preg_quote($complexPattern, '#') . '#', + $simple + ); + + if ($originalValue === $simplifiedValue) { + continue; + } + + if (str_contains($originalValue, '[^' . $complexPattern)) { + continue; } + + if ($complexPattern === $node->value) { + continue; + } + + $node->value = $simplifiedValue; + return $node; } - return $node; + return null; } } diff --git a/rules/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector.php b/rules/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector.php index f72fb5b5658..aa49de95840 100644 --- a/rules/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector.php +++ b/rules/CodeQuality/Rector/FuncCall/SimplifyStrposLowerRector.php @@ -4,10 +4,12 @@ namespace Rector\CodeQuality\Rector\FuncCall; +use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Scalar\String_; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -16,6 +18,11 @@ */ final class SimplifyStrposLowerRector extends AbstractRector { + /** + * @see https://regex101.com/r/Jokjt8/1 + */ + private const string UPPERCASE_REGEX = '#[A-Z]#'; + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -41,22 +48,36 @@ public function refactor(Node $node): ?Node return null; } - if (! isset($node->args[0])) { + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + if (! isset($args[0], $args[1])) { return null; } - if (! $node->args[0]->value instanceof FuncCall) { + $firstArg = $args[0]; + if (! $firstArg->value instanceof FuncCall) { return null; } - /** @var FuncCall $innerFuncCall */ - $innerFuncCall = $node->args[0]->value; + $innerFuncCall = $firstArg->value; if (! $this->isName($innerFuncCall, 'strtolower')) { return null; } + $secondArg = $args[1]; + if (! $secondArg->value instanceof String_) { + return null; + } + + if (Strings::match($secondArg->value->value, self::UPPERCASE_REGEX) !== null) { + return null; + } + // pop 1 level up - $node->args[0] = $innerFuncCall->args[0]; + $node->args[0] = $innerFuncCall->getArgs()[0]; $node->name = new Name('stripos'); return $node; diff --git a/rules/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector.php b/rules/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector.php index d4f1a682778..373b43c242f 100644 --- a/rules/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector.php +++ b/rules/CodeQuality/Rector/FuncCall/SingleInArrayToCompareRector.php @@ -5,12 +5,16 @@ namespace Rector\CodeQuality\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotEqual; +use PhpParser\Node\Expr\BinaryOp\NotIdentical; +use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -58,24 +62,58 @@ public function run() */ public function getNodeTypes(): array { - return [FuncCall::class]; + return [BooleanNot::class, FuncCall::class]; } /** - * @param FuncCall $node + * @param BooleanNot|FuncCall $node */ public function refactor(Node $node): ?Node { - if (! $this->isName($node, 'in_array')) { + if ($node instanceof BooleanNot) { + if (! $node->expr instanceof FuncCall) { + return null; + } + + $firstArrayItem = $this->resolveArrayItem($node->expr); + if (! $firstArrayItem instanceof ArrayItem) { + return null; + } + + return $this->processCompare($firstArrayItem, $node->expr, true); + } + + $firstArrayItem = $this->resolveArrayItem($node); + if (! $firstArrayItem instanceof ArrayItem) { + return null; + } + + return $this->processCompare($firstArrayItem, $node); + } + + private function resolveArrayItem(FuncCall $funcCall): ?ArrayItem + { + if (! $this->isName($funcCall, 'in_array')) { + return null; + } + + if ($funcCall->isFirstClassCallable()) { return null; } - if (! $node->args[1]->value instanceof Array_) { + if (! isset($funcCall->args[1])) { return null; } - /** @var Array_ $arrayNode */ - $arrayNode = $node->args[1]->value; + if (! $funcCall->args[1] instanceof Arg) { + return null; + } + + if (! $funcCall->args[1]->value instanceof Array_) { + return null; + } + + $arrayNode = $funcCall->args[1]->value; if (count($arrayNode->items) !== 1) { return null; } @@ -85,12 +123,32 @@ public function refactor(Node $node): ?Node return null; } + if ($firstArrayItem->unpack) { + return null; + } + + if (! isset($funcCall->getArgs()[0])) { + return null; + } + + return $firstArrayItem; + } + + private function processCompare(ArrayItem $firstArrayItem, FuncCall $funcCall, bool $isNegated = false): Node + { $firstArrayItemValue = $firstArrayItem->value; + + $firstArg = $funcCall->getArgs()[0]; + // strict - if (isset($node->args[2])) { - return new Identical($node->args[0]->value, $firstArrayItemValue); + if (isset($funcCall->getArgs()[2])) { + return $isNegated + ? new NotIdentical($firstArg->value, $firstArrayItemValue) + : new Identical($firstArg->value, $firstArrayItemValue); } - return new Equal($node->args[0]->value, $firstArrayItemValue); + return $isNegated + ? new NotEqual($firstArg->value, $firstArrayItemValue) + : new Equal($firstArg->value, $firstArrayItemValue); } } diff --git a/rules/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector.php b/rules/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector.php new file mode 100644 index 00000000000..de840c38697 --- /dev/null +++ b/rules/CodeQuality/Rector/FuncCall/SortCallLikeNamedArgsRector.php @@ -0,0 +1,100 @@ +> + */ + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class, New_::class, FuncCall::class]; + } + + /** + * @param MethodCall|StaticCall|New_|FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + if (count($args) <= 1) { + return null; + } + + if (! $this->argsAnalyzer->hasNamedArg($args)) { + return null; + } + + if ($node instanceof New_) { + $functionLikeReflection = $this->reflectionResolver->resolveMethodReflectionFromNew($node); + } else { + $functionLikeReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + } + + if (! $functionLikeReflection instanceof MethodReflection && ! $functionLikeReflection instanceof FunctionReflection) { + return null; + } + + $args = $this->namedArgsSorter->sortArgsToMatchReflectionParameters($args, $functionLikeReflection); + if ($node->args === $args) { + return null; + } + + $node->args = $args; + + return $node; + } +} diff --git a/rules/CodeQuality/Rector/FuncCall/SortNamedParamRector.php b/rules/CodeQuality/Rector/FuncCall/SortNamedParamRector.php new file mode 100644 index 00000000000..f6d8c7e2f3b --- /dev/null +++ b/rules/CodeQuality/Rector/FuncCall/SortNamedParamRector.php @@ -0,0 +1,64 @@ +> + */ + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class, New_::class, FuncCall::class]; + } + + /** + * @param MethodCall|StaticCall|New_|FuncCall $node + */ + public function refactor(Node $node): ?Node + { + throw new ShouldNotHappenException(sprintf( + '%s is deprecated as renamed to "%s".', + self::class, + SortCallLikeNamedArgsRector::class + )); + } +} diff --git a/rules/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector.php b/rules/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector.php index 1a71acd4661..7ba33034c88 100644 --- a/rules/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector.php +++ b/rules/CodeQuality/Rector/FuncCall/UnwrapSprintfOneArgumentRector.php @@ -6,19 +6,18 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/5280 * @see \Rector\Tests\CodeQuality\Rector\FuncCall\UnwrapSprintfOneArgumentRector\UnwrapSprintfOneArgumentRectorTest */ final class UnwrapSprintfOneArgumentRector extends AbstractRector { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('unwrap sprintf() with one argument', [ + return new RuleDefinition('Unwrap `sprintf()` with one argument', [ new CodeSample( <<<'CODE_SAMPLE' echo sprintf('value'); @@ -44,18 +43,23 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { + if ($node->isFirstClassCallable()) { + return null; + } + if (! $this->isName($node, 'sprintf')) { return null; } - if (count($node->args) > 1) { + if (count($node->getArgs()) > 1) { return null; } - if ($node->args[0]->unpack) { + $firstArg = $node->getArgs()[0]; + if ($firstArg->unpack) { return null; } - return $node->args[0]->value; + return $firstArg->value; } } diff --git a/rules/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector.php b/rules/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector.php deleted file mode 100644 index fbaf7ea663d..00000000000 --- a/rules/CodeQuality/Rector/FunctionLike/RemoveAlwaysTrueConditionSetInConstructorRector.php +++ /dev/null @@ -1,268 +0,0 @@ -value = $value; - } - - public function go() - { - if ($this->value) { - return 'yes'; - } - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - private $value; - - public function __construct(stdClass $value) - { - $this->value = $value; - } - - public function go() - { - return 'yes'; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, Closure::class]; - } - - /** - * @param ClassMethod|Closure $node - */ - public function refactor(Node $node): ?Node - { - if ($node->stmts === null) { - return null; - } - if ($node->stmts === []) { - return null; - } - - $haveNodeChanged = false; - foreach ($node->stmts as $key => $stmt) { - if ($stmt instanceof Expression) { - $stmt = $stmt->expr; - } - - if (! $this->isAlwaysTruableNode($stmt)) { - continue; - } - - /** @var If_ $stmt */ - if (count($stmt->stmts) === 1) { - $node->stmts[$key] = $stmt->stmts[0]; - continue; - } - - $haveNodeChanged = true; - // move all nodes one level up - array_splice($node->stmts, $key, count($stmt->stmts) - 1, $stmt->stmts); - } - - if ($haveNodeChanged) { - return $node; - } - - return null; - } - - private function isAlwaysTruableNode(Node $node): bool - { - if (! $node instanceof If_) { - return false; - } - - // just one if - if ($node->elseifs !== []) { - return false; - } - - // there is some else - if ($node->else !== null) { - return false; - } - - // only property fetch, because of constructor set - if (! $node->cond instanceof PropertyFetch) { - return false; - } - - $propertyFetchType = $this->resolvePropertyFetchType($node->cond); - return $this->staticTypeAnalyzer->isAlwaysTruableType($propertyFetchType); - } - - private function resolvePropertyFetchType(PropertyFetch $propertyFetch): Type - { - $classLike = $propertyFetch->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return new MixedType(); - } - - $propertyName = $this->getName($propertyFetch); - if ($propertyName === null) { - return new MixedType(); - } - - $property = $classLike->getProperty($propertyName); - if (! $property instanceof Property) { - return new MixedType(); - } - - // anything but private can be changed from outer scope - if (! $property->isPrivate()) { - return new MixedType(); - } - - // set in constructor + changed in class - $propertyTypeFromConstructor = $this->resolvePropertyTypeAfterConstructor($classLike, $propertyName); - - $resolvedTypes = []; - $resolvedTypes[] = $propertyTypeFromConstructor; - - $defaultValue = $property->props[0]->default; - if ($defaultValue !== null) { - $resolvedTypes[] = $this->getStaticType($defaultValue); - } - - $resolveAssignedType = $this->resolveAssignedTypeInStmtsByPropertyName($classLike->stmts, $propertyName); - if ($resolveAssignedType !== null) { - $resolvedTypes[] = $resolveAssignedType; - } - - return $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($resolvedTypes); - } - - private function resolvePropertyTypeAfterConstructor(Class_ $class, string $propertyName): Type - { - $propertyTypeFromConstructor = null; - - $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); - if ($constructClassMethod !== null) { - $propertyTypeFromConstructor = $this->resolveAssignedTypeInStmtsByPropertyName( - (array) $constructClassMethod->stmts, - $propertyName - ); - } - - if ($propertyTypeFromConstructor !== null) { - return $propertyTypeFromConstructor; - } - - // undefined property is null by default - return new NullType(); - } - - /** - * @param Stmt[] $stmts - */ - private function resolveAssignedTypeInStmtsByPropertyName(array $stmts, string $propertyName): ?Type - { - $resolvedTypes = []; - - $this->traverseNodesWithCallable($stmts, function (Node $node) use ($propertyName, &$resolvedTypes): ?int { - if ($node instanceof ClassMethod && $this->isName($node, MethodName::CONSTRUCT)) { - return NodeTraverser::DONT_TRAVERSE_CHILDREN; - } - - if (! $this->isPropertyFetchAssignOfPropertyName($node, $propertyName)) { - return null; - } - - if (! $node instanceof Assign) { - return null; - } - - $resolvedTypes[] = $this->getStaticType($node->expr); - return null; - }); - - if ($resolvedTypes === []) { - return null; - } - - return $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($resolvedTypes); - } - - /** - * E.g. $this->{value} = x - */ - private function isPropertyFetchAssignOfPropertyName(Node $node, string $propertyName): bool - { - if (! $node instanceof Assign) { - return false; - } - - if (! $node->var instanceof PropertyFetch) { - return false; - } - - return $this->isName($node->var, $propertyName); - } -} diff --git a/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php b/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php new file mode 100644 index 00000000000..ee5d6b8c685 --- /dev/null +++ b/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php @@ -0,0 +1,240 @@ + $configuration + */ + public function configure(array $configuration): void + { + $this->onlyDirectAssign = $configuration[self::ONLY_DIRECT_ASSIGN] ?? false; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Remove useless variable assigns', [ + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +function () { + $a = true; + return $a; +}; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +function () { + return true; +}; +CODE_SAMPLE + , + // default + [ + self::ONLY_DIRECT_ASSIGN => true, + ] + ), + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +function () { + $a = 'Hello, '; + $a .= 'World!'; + + return $a; +}; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +function () { + $a = 'Hello, '; + + return $a . 'World!'; +}; +CODE_SAMPLE + , + [ + self::ONLY_DIRECT_ASSIGN => false, + ] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + $stmts = $node->stmts; + if ($stmts === null) { + return null; + } + + foreach ($stmts as $key => $stmt) { + // has previous node? + if (! isset($stmts[$key - 1])) { + continue; + } + + if (! $stmt instanceof Return_) { + continue; + } + + $previousStmt = $stmts[$key - 1]; + if ($this->shouldSkipStmt($stmt, $previousStmt)) { + return null; + } + + if ($this->hasSomeComment($previousStmt)) { + return null; + } + + if ($this->isReturnWithVarAnnotation($stmt)) { + return null; + } + + /** @var Expression $previousStmt */ + $assign = $previousStmt->expr; + + return $this->processSimplifyUselessVariable($node, $stmt, $assign, $key); + } + + return null; + } + + /** + * @param StmtsAware $stmtsAware + * @return StmtsAware|null + */ + private function processSimplifyUselessVariable( + Node $stmtsAware, + Return_ $return, + Assign|AssignOp $assign, + int $key + ): ?Node { + if (! $assign instanceof Assign) { + $binaryClass = $this->assignAndBinaryMap->getAlternative($assign); + if ($binaryClass === null) { + return null; + } + + $return->expr = new $binaryClass($assign->var, $assign->expr); + } else { + $return->expr = $assign->expr; + } + + unset($stmtsAware->stmts[$key - 1]); + return $stmtsAware; + } + + private function shouldSkipStmt(Return_ $return, Stmt $previousStmt): bool + { + if (! $return->expr instanceof Variable) { + return true; + } + + if ($return->getAttribute(AttributeKey::IS_BYREF_RETURN) === true) { + return true; + } + + if (! $previousStmt instanceof Expression) { + return true; + } + + // is variable part of single assign + $previousNode = $previousStmt->expr; + + if (! $previousNode instanceof AssignOp && ! $previousNode instanceof Assign) { + return true; + } + + if ($this->onlyDirectAssign && $previousNode instanceof AssignOp) { + return true; + } + + if ($previousNode instanceof AssignOp && $previousNode->expr instanceof Ternary) { + return true; + } + + $variable = $return->expr; + // is the same variable + if (! $this->nodeComparator->areNodesEqual($previousNode->var, $variable)) { + return true; + } + + if ($this->variableAnalyzer->isStaticOrGlobal($variable)) { + return true; + } + + /** @var Variable $previousVar */ + $previousVar = $previousNode->var; + if ($this->callAnalyzer->isNewInstance($previousVar)) { + return true; + } + + return $this->variableAnalyzer->isUsedByReference($variable); + } + + private function hasSomeComment(Stmt $stmt): bool + { + if ($stmt->getComments() !== []) { + return true; + } + + return $stmt->getDocComment() instanceof Doc; + } + + private function isReturnWithVarAnnotation(Return_ $return): bool + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($return); + return ! $phpDocInfo->getVarType() instanceof MixedType; + } +} diff --git a/rules/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector.php b/rules/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector.php index ca006e1689d..f254210f11b 100644 --- a/rules/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector.php +++ b/rules/CodeQuality/Rector/Identical/BooleanNotIdenticalToNotIdenticalRector.php @@ -8,13 +8,11 @@ use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; -use PHPStan\Type\BooleanType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/GoEPq * @see \Rector\Tests\CodeQuality\Rector\Identical\BooleanNotIdenticalToNotIdenticalRector\BooleanNotIdenticalToNotIdenticalRectorTest */ final class BooleanNotIdenticalToNotIdenticalRector extends AbstractRector @@ -78,11 +76,14 @@ public function refactor(Node $node): ?Node if ($node->expr instanceof Identical) { $identical = $node->expr; - if (! $this->nodeTypeResolver->isStaticType($identical->left, BooleanType::class)) { + + $leftType = $this->getType($identical->left); + if (! $leftType->isBoolean()->yes()) { return null; } - if (! $this->nodeTypeResolver->isStaticType($identical->right, BooleanType::class)) { + $rightType = $this->getType($identical->right); + if (! $rightType->isBoolean()->yes()) { return null; } @@ -94,11 +95,13 @@ public function refactor(Node $node): ?Node private function processIdentical(Identical $identical): ?NotIdentical { - if (! $this->nodeTypeResolver->isStaticType($identical->left, BooleanType::class)) { + $leftType = $this->getType($identical->left); + if (! $leftType->isBoolean()->yes()) { return null; } - if (! $this->nodeTypeResolver->isStaticType($identical->right, BooleanType::class)) { + $rightType = $this->getType($identical->right); + if (! $rightType->isBoolean()->yes()) { return null; } diff --git a/rules/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector.php b/rules/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector.php index 95830d4bdd2..de30df87260 100644 --- a/rules/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector.php +++ b/rules/CodeQuality/Rector/Identical/FlipTypeControlToUseExclusiveTypeRector.php @@ -6,22 +6,17 @@ use PhpParser\Node; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node\Name\FullyQualified; -use PhpParser\Node\Stmt\Expression; -use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; -use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; -use PHPStan\Type\Type; -use PHPStan\Type\UnionType; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; -use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType; +use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType; +use Rector\TypeDeclaration\TypeAnalyzer\NullableTypeAnalyzer; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -31,45 +26,34 @@ final class FlipTypeControlToUseExclusiveTypeRector extends AbstractRector { public function __construct( - private PhpDocTagRemover $phpDocTagRemover + private readonly NullableTypeAnalyzer $nullableTypeAnalyzer, + private readonly ValueResolver $valueResolver, ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition( - 'Flip type control to use exclusive type', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass + return new RuleDefinition('Flip type control from null compare to use exclusive instanceof object', [ + new CodeSample( + <<<'CODE_SAMPLE' +function process(?DateTime $dateTime) { - public function __construct(array $values) - { - /** @var PhpDocInfo|null $phpDocInfo */ - $phpDocInfo = $functionLike->getAttribute(AttributeKey::PHP_DOC_INFO); - if ($phpDocInfo === null) { - return; - } + if ($dateTime === null) { + return; } } CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass + , + <<<'CODE_SAMPLE' +function process(?DateTime $dateTime) { - public function __construct(array $values) - { - $phpDocInfo = $functionLike->getAttribute(AttributeKey::PHP_DOC_INFO); - if (! $phpDocInfo instanceof PhpDocInfo) { - return; - } + if (! $dateTime instanceof DateTime) { + return; } } CODE_SAMPLE - ), - ] - ); + ), + ]); } /** @@ -77,113 +61,54 @@ public function __construct(array $values) */ public function getNodeTypes(): array { - return [Identical::class]; + return [Identical::class, NotIdentical::class]; } /** - * @param Identical $node + * @param Identical|NotIdentical $node */ public function refactor(Node $node): ?Node { - if (! $this->valueResolver->isNull($node->left) && ! $this->valueResolver->isNull($node->right)) { - return null; - } - - $variable = $this->valueResolver->isNull($node->left) - ? $node->right - : $node->left; - - $assign = $this->getVariableAssign($node, $variable); - if (! $assign instanceof Assign) { + $expr = $this->matchNullComparedExpr($node); + if (! $expr instanceof Expr) { return null; } - $expression = $assign->getAttribute(AttributeKey::PARENT_NODE); - if (! $expression instanceof Expression) { + $nullableObjectType = $this->nullableTypeAnalyzer->resolveNullableObjectType($expr); + if (! $nullableObjectType instanceof ObjectType) { return null; } - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($expression); - $type = $phpDocInfo->getVarType(); - - if (! $type instanceof UnionType) { - $type = $this->getObjectType($assign->expr); - } - - if (! $type instanceof UnionType) { - return null; - } - - /** @var Type[] $types */ - $types = $this->getTypes($type); - if ($this->isNotNullOneOf($types)) { - return null; - } - - return $this->processConvertToExclusiveType($types, $variable, $phpDocInfo); - } - - private function getVariableAssign(Identical $identical, Expr $expr): ?Node - { - return $this->betterNodeFinder->findFirstPrevious($identical, function (Node $node) use ($expr): bool { - if (! $node instanceof Assign) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node->var, $expr); - }); - } - - /** - * @return Type[] - */ - private function getTypes(UnionType $unionType): array - { - $types = $unionType->getTypes(); - if (count($types) > 2) { - return []; - } - - return $types; + return $this->processConvertToExclusiveType($nullableObjectType, $expr, $node); } - /** - * @param Type[] $types - */ - private function isNotNullOneOf(array $types): bool - { - if ($types === []) { - return true; - } - - if ($types[0] === $types[1]) { - return true; - } - if ($types[0] instanceof NullType) { - return false; + private function processConvertToExclusiveType( + ObjectType $objectType, + Expr $expr, + Identical|NotIdentical $binaryOp + ): BooleanNot|Instanceof_ { + $fullyQualifiedType = $objectType instanceof ShortenedObjectType || $objectType instanceof AliasedObjectType + ? $objectType->getFullyQualifiedName() + : $objectType->getClassName(); + + $instanceof = new Instanceof_($expr, new FullyQualified($fullyQualifiedType)); + if ($binaryOp instanceof NotIdentical) { + return $instanceof; } - return ! $types[1] instanceof NullType; + return new BooleanNot($instanceof); } - /** - * @param Type[] $types - */ - private function processConvertToExclusiveType(array $types, Expr $expr, PhpDocInfo $phpDocInfo): ?BooleanNot + private function matchNullComparedExpr(Identical|NotIdentical $binaryOp): ?Expr { - $type = $types[0] instanceof NullType - ? $types[1] - : $types[0]; - - if (! $type instanceof FullyQualifiedObjectType && ! $type instanceof ObjectType) { - return null; + if ($this->valueResolver->isNull($binaryOp->left)) { + return $binaryOp->right; } - $varTagValueNode = $phpDocInfo->getVarTagValueNode(); - if ($varTagValueNode instanceof VarTagValueNode) { - $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $varTagValueNode); + if ($this->valueResolver->isNull($binaryOp->right)) { + return $binaryOp->left; } - return new BooleanNot(new Instanceof_($expr, new FullyQualified($type->getClassName()))); + return null; } } diff --git a/rules/CodeQuality/Rector/Identical/GetClassToInstanceOfRector.php b/rules/CodeQuality/Rector/Identical/GetClassToInstanceOfRector.php deleted file mode 100644 index 2b642fe9e9b..00000000000 --- a/rules/CodeQuality/Rector/Identical/GetClassToInstanceOfRector.php +++ /dev/null @@ -1,121 +0,0 @@ -job)) { }', - 'if ($event->job instanceof EventsListener) { }' - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Identical::class, NotIdentical::class]; - } - - /** - * @param Identical|NotIdentical $node - */ - public function refactor(Node $node): ?Node - { - $twoNodeMatch = $this->binaryOpManipulator->matchFirstAndSecondConditionNode( - $node, - fn (Node $node): bool => $this->isClassReference($node), - fn (Node $node): bool => $this->isGetClassFuncCallNode($node) - ); - - if (! $twoNodeMatch instanceof TwoNodeMatch) { - return null; - } - - /** @var ClassConstFetch|String_ $firstExpr */ - $firstExpr = $twoNodeMatch->getFirstExpr(); - - /** @var FuncCall $funcCall */ - $funcCall = $twoNodeMatch->getSecondExpr(); - - $varNode = $funcCall->args[0]->value; - - if ($firstExpr instanceof String_) { - $className = $this->valueResolver->getValue($firstExpr); - } else { - $className = $this->getName($firstExpr->class); - } - - if ($className === null) { - return null; - } - - $class = in_array($className, self::NO_NAMESPACED_CLASSNAMES, true) - ? new Name($className) - : new FullyQualified($className); - $instanceof = new Instanceof_($varNode, $class); - if ($node instanceof NotIdentical) { - return new BooleanNot($instanceof); - } - - return $instanceof; - } - - private function isClassReference(Node $node): bool - { - if (! $node instanceof ClassConstFetch) { - // might be - return $node instanceof String_; - } - - return $this->isName($node->name, 'class'); - } - - private function isGetClassFuncCallNode(Node $node): bool - { - if (! $node instanceof FuncCall) { - return false; - } - - return $this->isName($node, 'get_class'); - } -} diff --git a/rules/CodeQuality/Rector/Identical/SimplifyArraySearchRector.php b/rules/CodeQuality/Rector/Identical/SimplifyArraySearchRector.php index 29ed6bc5a6e..48e978fc506 100644 --- a/rules/CodeQuality/Rector/Identical/SimplifyArraySearchRector.php +++ b/rules/CodeQuality/Rector/Identical/SimplifyArraySearchRector.php @@ -5,13 +5,15 @@ namespace Rector\CodeQuality\Rector\Identical; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\NodeManipulator\BinaryOpManipulator; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeManipulator\BinaryOpManipulator; use Rector\Php71\ValueObject\TwoNodeMatch; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,7 +23,8 @@ final class SimplifyArraySearchRector extends AbstractRector { public function __construct( - private BinaryOpManipulator $binaryOpManipulator + private readonly BinaryOpManipulator $binaryOpManipulator, + private readonly ValueResolver $valueResolver ) { } @@ -59,19 +62,19 @@ function (Node $node): bool { return false; } - return $this->nodeNameResolver->isName($node, 'array_search'); + return $this->isName($node, 'array_search'); }, - fn (Node $node): bool => $this->valueResolver->isFalse($node) + fn (Node $node): bool => $node instanceof Expr && $this->valueResolver->isFalse($node) ); if (! $twoNodeMatch instanceof TwoNodeMatch) { return null; } - /** @var FuncCall $arraySearchFuncCall */ - $arraySearchFuncCall = $twoNodeMatch->getFirstExpr(); + /** @var FuncCall $funcCallExpr */ + $funcCallExpr = $twoNodeMatch->getFirstExpr(); - $inArrayFuncCall = $this->nodeFactory->createFuncCall('in_array', $arraySearchFuncCall->args); + $inArrayFuncCall = $this->nodeFactory->createFuncCall('in_array', $funcCallExpr->args); if ($node instanceof Identical) { return new BooleanNot($inArrayFuncCall); diff --git a/rules/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector.php b/rules/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector.php index 9d7c3831877..8df17c82beb 100644 --- a/rules/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector.php +++ b/rules/CodeQuality/Rector/Identical/SimplifyBoolIdenticalTrueRector.php @@ -9,8 +9,8 @@ use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; -use PHPStan\Type\BooleanType; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,10 +19,15 @@ */ final class SimplifyBoolIdenticalTrueRector extends AbstractRector { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Symplify bool value compare to true or false', + 'Simplify bool value compare to true or false', [ new CodeSample( <<<'CODE_SAMPLE' @@ -31,6 +36,7 @@ class SomeClass public function run(bool $value, string $items) { $match = in_array($value, $items, TRUE) === TRUE; + $match = in_array($value, $items, TRUE) !== FALSE; } } @@ -42,6 +48,7 @@ class SomeClass public function run(bool $value, string $items) { $match = in_array($value, $items, TRUE); + $match = in_array($value, $items, TRUE); } } @@ -64,19 +71,15 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($this->nodeTypeResolver->isStaticType( - $node->left, - BooleanType::class - ) && ! $this->valueResolver->isTrueOrFalse($node->left)) { + if ($this->isBooleanButNotTrueAndFalse($node->left)) { return $this->processBoolTypeToNotBool($node, $node->left, $node->right); } - if (! $this->nodeTypeResolver->isStaticType($node->right, BooleanType::class)) { - return null; - } - if ($this->valueResolver->isTrueOrFalse($node->right)) { - return null; + + if ($this->isBooleanButNotTrueAndFalse($node->right)) { + return $this->processBoolTypeToNotBool($node, $node->right, $node->left); } - return $this->processBoolTypeToNotBool($node, $node->right, $node->left); + + return null; } private function processBoolTypeToNotBool(Node $node, Expr $leftExpr, Expr $rightExpr): ?Expr @@ -98,16 +101,16 @@ private function refactorIdentical(Expr $leftExpr, Expr $rightExpr): ?Expr return $leftExpr; } - if ($this->valueResolver->isFalse($rightExpr)) { - // prevent !! - if ($leftExpr instanceof BooleanNot) { - return $leftExpr->expr; - } + // prevent double negation !! + if (! $this->valueResolver->isFalse($rightExpr)) { + return null; + } - return new BooleanNot($leftExpr); + if (! $leftExpr instanceof BooleanNot) { + return null; } - return null; + return $leftExpr->expr; } private function refactorNotIdentical(Expr $leftExpr, Expr $rightExpr): ?Expr @@ -122,4 +125,15 @@ private function refactorNotIdentical(Expr $leftExpr, Expr $rightExpr): ?Expr return null; } + + private function isBooleanButNotTrueAndFalse(Expr $expr): bool + { + if ($this->valueResolver->isTrueOrFalse($expr)) { + return false; + } + + return $this->nodeTypeResolver->getNativeType($expr) + ->isBoolean() + ->yes(); + } } diff --git a/rules/CodeQuality/Rector/Identical/SimplifyConditionsRector.php b/rules/CodeQuality/Rector/Identical/SimplifyConditionsRector.php index 5fea5a9ad14..45af8787a59 100644 --- a/rules/CodeQuality/Rector/Identical/SimplifyConditionsRector.php +++ b/rules/CodeQuality/Rector/Identical/SimplifyConditionsRector.php @@ -5,15 +5,17 @@ namespace Rector\CodeQuality\Rector\Identical; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; -use Rector\Core\NodeManipulator\BinaryOpManipulator; -use Rector\Core\PhpParser\Node\AssignAndBinaryMap; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeManipulator\BinaryOpManipulator; use Rector\Php71\ValueObject\TwoNodeMatch; +use Rector\PhpParser\Node\AssignAndBinaryMap; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,8 +25,9 @@ final class SimplifyConditionsRector extends AbstractRector { public function __construct( - private AssignAndBinaryMap $assignAndBinaryMap, - private BinaryOpManipulator $binaryOpManipulator + private readonly AssignAndBinaryMap $assignAndBinaryMap, + private readonly BinaryOpManipulator $binaryOpManipulator, + private readonly ValueResolver $valueResolver ) { } @@ -73,23 +76,23 @@ private function processIdenticalAndNotIdentical(Identical $identical): ?Node { $twoNodeMatch = $this->binaryOpManipulator->matchFirstAndSecondConditionNode( $identical, - fn (Node $binaryOp): bool => $binaryOp instanceof Identical || $binaryOp instanceof NotIdentical, - fn (Node $binaryOp): bool => $this->valueResolver->isTrueOrFalse($binaryOp) + static fn (Node $node): bool => $node instanceof Identical || $node instanceof NotIdentical, + fn (Node $node): bool => $node instanceof Expr && $this->valueResolver->isTrueOrFalse($node) ); if (! $twoNodeMatch instanceof TwoNodeMatch) { return $twoNodeMatch; } - /** @var Identical|NotIdentical $subBinaryOp */ - $subBinaryOp = $twoNodeMatch->getFirstExpr(); + /** @var Identical|NotIdentical $firstExpr */ + $firstExpr = $twoNodeMatch->getFirstExpr(); - $otherNode = $twoNodeMatch->getSecondExpr(); - if ($this->valueResolver->isFalse($otherNode)) { - return $this->createInversedBooleanOp($subBinaryOp); + $otherExpr = $twoNodeMatch->getSecondExpr(); + if ($this->valueResolver->isFalse($otherExpr)) { + return $this->createInversedBooleanOp($firstExpr); } - return $subBinaryOp; + return $firstExpr; } /** @@ -104,6 +107,7 @@ private function shouldSkip(BinaryOp $binaryOp): bool if ($binaryOp->left instanceof BinaryOp) { return true; } + return $binaryOp->right instanceof BinaryOp; } diff --git a/rules/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector.php b/rules/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector.php index 44416170b9f..2043a5fb8b5 100644 --- a/rules/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector.php +++ b/rules/CodeQuality/Rector/Identical/StrlenZeroToIdenticalEmptyStringRector.php @@ -6,10 +6,14 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\BinaryOp\Greater; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotIdentical; +use PhpParser\Node\Expr\BinaryOp\Smaller; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,6 +22,11 @@ */ final class StrlenZeroToIdenticalEmptyStringRector extends AbstractRector { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -27,7 +36,7 @@ public function getRuleDefinition(): RuleDefinition <<<'CODE_SAMPLE' class SomeClass { - public function run($value) + public function run(string $value) { $empty = strlen($value) === 0; } @@ -37,7 +46,7 @@ public function run($value) <<<'CODE_SAMPLE' class SomeClass { - public function run($value) + public function run(string $value) { $empty = $value === ''; } @@ -53,40 +62,58 @@ public function run($value) */ public function getNodeTypes(): array { - return [Identical::class]; + return [Greater::class, Smaller::class, Identical::class]; } /** - * @param Identical $node + * @param Greater|Smaller|Identical $node */ public function refactor(Node $node): ?Node { - $variable = null; if ($node->left instanceof FuncCall) { - if (! $this->isName($node->left, 'strlen')) { - return null; - } + $funcCall = $node->left; + $expr = $node->right; + } elseif ($node->right instanceof FuncCall) { + $expr = $node->left; + $funcCall = $node->right; + } else { + return null; + } - if (! $this->valueResolver->isValue($node->right, 0)) { - return null; - } + if (! $this->isName($funcCall, 'strlen')) { + return null; + } - $variable = $node->left->args[0]->value; - } elseif ($node->right instanceof FuncCall) { - if (! $this->isName($node->right, 'strlen')) { - return null; - } + if ($funcCall->isFirstClassCallable()) { + return null; + } - if (! $this->valueResolver->isValue($node->left, 0)) { - return null; - } + if (! $this->valueResolver->isValue($expr, 0)) { + return null; + } - $variable = $node->right->args[0]->value; - } else { + if ( + ($node instanceof Greater && $node->right instanceof FuncCall) + || ($node instanceof Smaller && $node->left instanceof FuncCall) + ) { return null; } - /** @var Expr $variable */ - return new Identical($variable, new String_('')); + $variable = $funcCall->getArgs()[0] + ->value; + + // Needs string cast if variable type is not string + // see https://github.com/rectorphp/rector/issues/6700 + $isStringType = $this->nodeTypeResolver->getNativeType($variable) + ->isString() + ->yes(); + + if (! $isStringType) { + $variable = new Expr\Cast\String_($variable); + } + + return $node instanceof Identical + ? new Identical($variable, new String_('')) + : new NotIdentical($variable, new String_('')); } } diff --git a/rules/CodeQuality/Rector/If_/CombineIfRector.php b/rules/CodeQuality/Rector/If_/CombineIfRector.php index 2c73b7a8d51..b3e13d6f28c 100644 --- a/rules/CodeQuality/Rector/If_/CombineIfRector.php +++ b/rules/CodeQuality/Rector/If_/CombineIfRector.php @@ -5,10 +5,17 @@ namespace Rector\CodeQuality\Rector\If_; use PhpParser\Node; +use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PhpParser\Node\Expr\BooleanNot; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; +use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use Rector\BetterPhpDocParser\Comment\CommentsMerger; -use Rector\Core\Rector\AbstractRector; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,13 +25,14 @@ final class CombineIfRector extends AbstractRector { public function __construct( - private CommentsMerger $commentsMerger + private readonly CommentsMerger $commentsMerger, + private readonly PhpDocInfoFactory $phpDocInfoFactory ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Merges nested if statements', [ + return new RuleDefinition('Merge nested if statements', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -74,7 +82,33 @@ public function refactor(Node $node): ?Node /** @var If_ $subIf */ $subIf = $node->stmts[0]; + + if ($this->hasVarTag($subIf)) { + return null; + } + + $node->cond->setAttribute(AttributeKey::ORIGINAL_NODE, null); + + $cond = $node->cond; + + while ($cond instanceof BinaryOp) { + if (! $cond->right instanceof BinaryOp) { + $cond->right->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + + $cond = $cond->right; + } + + if ($subIf->cond instanceof BinaryOp && ! $subIf->cond->left instanceof BinaryOp) { + $subIf->cond->left->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + + if ($node->cond instanceof BooleanNot && $node->cond->expr instanceof BinaryOp) { + $node->cond->expr->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + $node->cond = new BooleanAnd($node->cond, $subIf->cond); + $node->stmts = $subIf->stmts; $this->commentsMerger->keepComments($node, [$subIf]); @@ -84,7 +118,7 @@ public function refactor(Node $node): ?Node private function shouldSkip(If_ $if): bool { - if ($if->else !== null) { + if ($if->else instanceof Else_) { return true; } @@ -100,10 +134,20 @@ private function shouldSkip(If_ $if): bool return true; } - if ($if->stmts[0]->else !== null) { + if ($if->stmts[0]->else instanceof Else_) { return true; } return (bool) $if->stmts[0]->elseifs; } + + private function hasVarTag(If_ $if): bool + { + $subIfPhpDocInfo = $this->phpDocInfoFactory->createFromNode($if); + if (! $subIfPhpDocInfo instanceof PhpDocInfo) { + return false; + } + + return $subIfPhpDocInfo->getVarTagValueNode() instanceof VarTagValueNode; + } } diff --git a/rules/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector.php b/rules/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector.php new file mode 100644 index 00000000000..be8e73a7458 --- /dev/null +++ b/rules/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector.php @@ -0,0 +1,130 @@ +> + */ + public function getNodeTypes(): array + { + return [If_::class, ElseIf_::class, Else_::class]; + } + + /** + * @param If_|ElseIf_|Else_ $node + */ + public function refactor(Node $node): ?Node + { + if ($this->isBareNewNode($node)) { + return null; + } + + $oldTokens = $this->getFile() + ->getOldTokens(); + if ($this->isIfConditionFollowedByOpeningCurlyBracket($node, $oldTokens)) { + return null; + } + + // invoke reprint with brackets + $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); + + return $node; + } + + /** + * @param Token[] $oldTokens + */ + private function isIfConditionFollowedByOpeningCurlyBracket(If_|ElseIf_|Else_ $if, array $oldTokens): bool + { + $startStmt = current($if->stmts); + if (! $startStmt instanceof Stmt) { + return true; + } + + $startTokenPos = $if->getStartTokenPos(); + $i = $startStmt->getStartTokenPos() - 1; + $condEndTokenPos = $if instanceof Else_ + ? $startTokenPos + : $if->cond->getEndTokenPos(); + + while (isset($oldTokens[$i])) { + if ($i === $condEndTokenPos) { + return false; + } + + if (in_array((string) $oldTokens[$i], ['{', ':'], true)) { + // all good + return true; + } + + if ($i === $startTokenPos) { + return false; + } + + --$i; + } + + return false; + } + + private function isBareNewNode(If_|ElseIf_|Else_ $if): bool + { + $originalNode = $if->getAttribute(AttributeKey::ORIGINAL_NODE); + if (! $originalNode instanceof Node) { + return true; + } + + // not defined, probably new if + return $if->getStartTokenPos() === -1; + } +} diff --git a/rules/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector.php b/rules/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector.php index a320a1046a5..913c6ef4b3a 100644 --- a/rules/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector.php +++ b/rules/CodeQuality/Rector/If_/ConsecutiveNullCompareReturnsToNullCoalesceQueueRector.php @@ -7,12 +7,16 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Coalesce; +use PhpParser\Node\Expr\Throw_; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeManipulator\IfManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,18 +26,9 @@ */ final class ConsecutiveNullCompareReturnsToNullCoalesceQueueRector extends AbstractRector implements MinPhpVersionInterface { - /** - * @var Node[] - */ - private array $nodesToRemove = []; - - /** - * @var Expr[] - */ - private array $coalescingNodes = []; - public function __construct( - private IfManipulator $ifManipulator + private readonly IfManipulator $ifManipulator, + private readonly ValueResolver $valueResolver ) { } @@ -46,11 +41,11 @@ class SomeClass { public function run() { - if (null !== $this->orderItem) { + if ($this->orderItem !== null) { return $this->orderItem; } - if (null !== $this->orderItemUnit) { + if ($this->orderItemUnit !== null) { return $this->orderItemUnit; } @@ -77,44 +72,85 @@ public function run() */ public function getNodeTypes(): array { - return [If_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param If_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - $this->reset(); + if ($node->stmts === null) { + return null; + } - $currentNode = $node; - while ($currentNode !== null) { - if ($currentNode instanceof If_) { - $comparedNode = $this->ifManipulator->matchIfNotNullReturnValue($currentNode); - if ($comparedNode !== null) { - $this->coalescingNodes[] = $comparedNode; - $this->nodesToRemove[] = $currentNode; + $coalescingExprs = []; + $ifKeys = []; - $currentNode = $currentNode->getAttribute(AttributeKey::NEXT_NODE); - continue; - } - return null; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof If_) { + continue; } - if ($this->isReturnNull($currentNode)) { - $this->nodesToRemove[] = $currentNode; - break; + $comparedExpr = $this->ifManipulator->matchIfNotNullReturnValue($stmt); + if (! $comparedExpr instanceof Expr) { + continue; } - return null; + + if (! isset($node->stmts[$key + 1])) { + return null; + } + + $coalescingExprs[] = $comparedExpr; + $ifKeys[] = $key; } // at least 2 coalescing nodes are needed - if (count($this->coalescingNodes) < 2) { + if (count($coalescingExprs) < 2) { return null; } - $this->removeNodes($this->nodesToRemove); - return $this->createReturnCoalesceNode($this->coalescingNodes); + // remove last return null + $appendExpr = null; + $hasChanged = false; + $originalStmts = $node->stmts; + foreach ($node->stmts as $key => $stmt) { + if (in_array($key, $ifKeys, true)) { + unset($node->stmts[$key]); + $hasChanged = true; + + continue; + } + + if (! $hasChanged) { + continue; + } + + if ($stmt instanceof Expression && $stmt->expr instanceof Throw_) { + unset($node->stmts[$key]); + $appendExpr = $stmt->expr; + + continue; + } + + if (! $this->isReturnNull($stmt)) { + if ($stmt instanceof Return_ && $stmt->expr instanceof Expr) { + unset($node->stmts[$key]); + $appendExpr = $stmt->expr; + + continue; + } + + $node->stmts = $originalStmts; + return null; + } + + unset($node->stmts[$key]); + } + + $node->stmts[] = $this->createCoalesceReturn($coalescingExprs, $appendExpr); + + return $node; } public function provideMinPhpVersion(): int @@ -122,41 +158,40 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::NULL_COALESCE; } - private function reset(): void - { - $this->coalescingNodes = []; - $this->nodesToRemove = []; - } - - private function isReturnNull(Node $node): bool + private function isReturnNull(Stmt $stmt): bool { - if (! $node instanceof Return_) { + if (! $stmt instanceof Return_) { return false; } - if ($node->expr === null) { + if (! $stmt->expr instanceof Expr) { return false; } - return $this->valueResolver->isNull($node->expr); + return $this->valueResolver->isNull($stmt->expr); } /** - * @param Expr[] $coalescingNodes + * @param Expr[] $coalescingExprs */ - private function createReturnCoalesceNode(array $coalescingNodes): Return_ + private function createCoalesceReturn(array $coalescingExprs, ?Expr $appendExpr): Return_ { - /** @var Expr $left */ - $left = array_shift($coalescingNodes); + /** @var Expr $leftExpr */ + $leftExpr = array_shift($coalescingExprs); + + /** @var Expr $rightExpr */ + $rightExpr = array_shift($coalescingExprs); - /** @var Expr $right */ - $right = array_shift($coalescingNodes); + $coalesce = new Coalesce($leftExpr, $rightExpr); + + foreach ($coalescingExprs as $coalescingExpr) { + $coalesce = new Coalesce($coalesce, $coalescingExpr); + } - $coalesceNode = new Coalesce($left, $right); - foreach ($coalescingNodes as $coalescingNode) { - $coalesceNode = new Coalesce($coalesceNode, $coalescingNode); + if ($appendExpr instanceof Expr) { + return new Return_(new Coalesce($coalesce, $appendExpr)); } - return new Return_($coalesceNode); + return new Return_($coalesce); } } diff --git a/rules/CodeQuality/Rector/If_/ExplicitBoolCompareRector.php b/rules/CodeQuality/Rector/If_/ExplicitBoolCompareRector.php index b51e6c70bb4..3cbeaeb7c05 100644 --- a/rules/CodeQuality/Rector/If_/ExplicitBoolCompareRector.php +++ b/rules/CodeQuality/Rector/If_/ExplicitBoolCompareRector.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; use PhpParser\Node\Expr\BinaryOp\BooleanOr; @@ -16,35 +17,35 @@ use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Cast\Bool_; use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Scalar\DNumber; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Name\FullyQualified; +use PhpParser\Node\Scalar\Float_; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ElseIf_; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; -use PHPStan\Type\BooleanType; -use PHPStan\Type\Constant\ConstantIntegerType; -use PHPStan\Type\FloatType; -use PHPStan\Type\IntegerType; +use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; use Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer; use Rector\NodeTypeResolver\TypeAnalyzer\StringTypeAnalyzer; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://www.reddit.com/r/PHP/comments/aqk01p/is_there_a_situation_in_which_if_countarray_0/ - * @see https://3v4l.org/UCd1b - * * @see \Rector\Tests\CodeQuality\Rector\If_\ExplicitBoolCompareRector\ExplicitBoolCompareRectorTest */ final class ExplicitBoolCompareRector extends AbstractRector { public function __construct( - private StringTypeAnalyzer $stringTypeAnalyzer, - private ArrayTypeAnalyzer $arrayTypeAnalyzer + private readonly StringTypeAnalyzer $stringTypeAnalyzer, + private readonly ArrayTypeAnalyzer $arrayTypeAnalyzer, + private readonly ValueResolver $valueResolver, ) { } @@ -89,11 +90,12 @@ public function getNodeTypes(): array /** * @param If_|ElseIf_|Ternary $node + * @return null|Stmt[]|Node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): null|array|Node { // skip short ternary - if ($node instanceof Ternary && $node->if === null) { + if ($node instanceof Ternary && ! $node->if instanceof Expr) { return null; } @@ -109,24 +111,34 @@ public function refactor(Node $node): ?Node return null; } - $conditionStaticType = $this->getStaticType($conditionNode); - if ($conditionStaticType instanceof BooleanType || $conditionStaticType instanceof ConstantIntegerType) { + $conditionStaticType = $this->nodeTypeResolver->getNativeType($conditionNode); + if ($conditionStaticType instanceof MixedType || $conditionStaticType->isBoolean()->yes()) { return null; } - $newConditionNode = $this->resolveNewConditionNode($conditionNode, $isNegated); - if (! $newConditionNode instanceof BinaryOp) { + $binaryOp = $this->resolveNewConditionNode($conditionNode, $isNegated); + if (! $binaryOp instanceof Expr) { return null; } - $node->cond = $newConditionNode; + if ($node instanceof If_ && $node->cond instanceof Assign && $binaryOp instanceof BinaryOp && $binaryOp->left instanceof NotIdentical && $binaryOp->right instanceof NotIdentical) { + $expression = new Expression($node->cond); + $binaryOp->left->left = $node->cond->var; + $binaryOp->right->left = $node->cond->var; + + $node->cond = $binaryOp; + + return [$expression, $node]; + } + + $node->cond = $binaryOp; return $node; } - private function resolveNewConditionNode(Expr $expr, bool $isNegated): ?BinaryOp + private function resolveNewConditionNode(Expr $expr, bool $isNegated): BinaryOp|Instanceof_|BooleanNot|null { - if ($expr instanceof FuncCall && $this->nodeNameResolver->isName($expr, 'count')) { + if ($expr instanceof FuncCall && $this->isName($expr, 'count')) { return $this->resolveCount($isNegated, $expr); } @@ -138,35 +150,47 @@ private function resolveNewConditionNode(Expr $expr, bool $isNegated): ?BinaryOp return $this->resolveString($isNegated, $expr); } - if ($this->nodeTypeResolver->isStaticType($expr, IntegerType::class)) { + $exprType = $this->getType($expr); + if ($exprType->isInteger()->yes()) { return $this->resolveInteger($isNegated, $expr); } - if ($this->nodeTypeResolver->isStaticType($expr, FloatType::class)) { + if ($exprType->isFloat()->yes()) { return $this->resolveFloat($isNegated, $expr); } - if ($this->nodeTypeResolver->isNullableTypeOfSpecificType($expr, ObjectType::class)) { - return $this->resolveNullable($isNegated, $expr); + $objectType = $this->nodeTypeResolver->matchNullableTypeOfSpecificType($expr, ObjectType::class); + if ($objectType instanceof ObjectType) { + return $this->resolveNullable($isNegated, $expr, $objectType); } return null; } - private function resolveCount(bool $isNegated, FuncCall $funcCall): Identical | Greater + private function resolveCount(bool $isNegated, FuncCall $funcCall): Identical | Greater | null { - $lNumber = new LNumber(0); + if ($funcCall->isFirstClassCallable()) { + return null; + } + + $countedType = $this->getType($funcCall->getArgs()[0]->value); + + if ($countedType->isArray()->yes()) { + return null; + } + + $int = new Int_(0); // compare === 0, assumption if ($isNegated) { - return new Identical($funcCall, $lNumber); + return new Identical($funcCall, $int); } - return new Greater($funcCall, $lNumber); + return new Greater($funcCall, $int); } /** - * @return Identical|NotIdentical + * @return Identical|NotIdentical|null */ private function resolveArray(bool $isNegated, Expr $expr): ?BinaryOp { @@ -186,22 +210,22 @@ private function resolveArray(bool $isNegated, Expr $expr): ?BinaryOp private function resolveString(bool $isNegated, Expr $expr): Identical | NotIdentical | BooleanAnd | BooleanOr { - $string = new String_(''); + $emptyString = new String_(''); - $identical = $this->resolveIdentical($expr, $isNegated, $string); + $identical = $this->resolveIdentical($expr, $isNegated, $emptyString); $value = $this->valueResolver->getValue($expr); // unknown value. may be from parameter if ($value === null) { - return $this->resolveZeroIdenticalstring($identical, $isNegated, $expr); + return $this->resolveZeroIdenticalString($identical, $isNegated, $expr); } - $length = strlen($value); + $length = strlen((string) $value); if ($length === 1) { - $string = new String_('0'); - return $this->resolveIdentical($expr, $isNegated, $string); + $zeroString = new String_('0'); + return $this->resolveIdentical($expr, $isNegated, $zeroString); } return $identical; @@ -211,8 +235,6 @@ private function resolveIdentical(Expr $expr, bool $isNegated, String_ $string): { /** * // compare === '' - * - * @var Identical|NotIdentical $identical */ $identical = $isNegated ? new Identical($expr, $string) @@ -221,56 +243,47 @@ private function resolveIdentical(Expr $expr, bool $isNegated, String_ $string): return $identical; } - private function resolveZeroIdenticalstring( + private function resolveZeroIdenticalString( Identical | NotIdentical $identical, bool $isNegated, Expr $expr ): BooleanAnd | BooleanOr { $string = new String_('0'); - $zeroIdentical = $isNegated - ? new Identical($expr, $string) - : new NotIdentical($expr, $string); - - /** - * @var BooleanAnd|BooleanOr $result - */ - $result = $isNegated - ? new BooleanOr($identical, $zeroIdentical) - : new BooleanAnd($identical, $zeroIdentical); - return $result; + $zeroIdentical = $isNegated ? new Identical($expr, $string) : new NotIdentical($expr, $string); + return $isNegated ? new BooleanOr($identical, $zeroIdentical) : new BooleanAnd($identical, $zeroIdentical); } private function resolveInteger(bool $isNegated, Expr $expr): Identical | NotIdentical { - $lNumber = new LNumber(0); + $int = new Int_(0); if ($isNegated) { - return new Identical($expr, $lNumber); + return new Identical($expr, $int); } - return new NotIdentical($expr, $lNumber); + return new NotIdentical($expr, $int); } private function resolveFloat(bool $isNegated, Expr $expr): Identical | NotIdentical { - $dNumber = new DNumber(0.0); + $float = new Float_(0.0); if ($isNegated) { - return new Identical($expr, $dNumber); + return new Identical($expr, $float); } - return new NotIdentical($expr, $dNumber); + return new NotIdentical($expr, $float); } - private function resolveNullable(bool $isNegated, Expr $expr): Identical | NotIdentical - { - $constFetch = $this->nodeFactory->createNull(); - - if ($isNegated) { - return new Identical($expr, $constFetch); - } + private function resolveNullable( + bool $isNegated, + Expr $expr, + ObjectType $objectType + ): BooleanNot | Instanceof_ { + $fullyQualified = new FullyQualified($objectType->getClassName()); + $instanceof = new Instanceof_($expr, $fullyQualified); - return new NotIdentical($expr, $constFetch); + return $isNegated ? new BooleanNot($instanceof) : $instanceof; } } diff --git a/rules/CodeQuality/Rector/If_/ShortenElseIfRector.php b/rules/CodeQuality/Rector/If_/ShortenElseIfRector.php index 20612d26a97..9296af9cd7b 100644 --- a/rules/CodeQuality/Rector/If_/ShortenElseIfRector.php +++ b/rules/CodeQuality/Rector/If_/ShortenElseIfRector.php @@ -8,18 +8,21 @@ use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\ElseIf_; use PhpParser\Node\Stmt\If_; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Stmt\Nop; +use Rector\Contract\Rector\HTMLAverseRectorInterface; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\CodeQuality\Rector\If_\ShortenElseIfRector\ShortenElseIfRectorTest */ -final class ShortenElseIfRector extends AbstractRector +final class ShortenElseIfRector extends AbstractRector implements HTMLAverseRectorInterface { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Shortens else/if to elseif', [ + return new RuleDefinition('Shorten `else`/`if` to `elseif`', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -89,10 +92,20 @@ private function shortenElseIf(If_ $node): ?If_ // Try to shorten the nested if before transforming it to elseif $refactored = $this->shortenElseIf($if); - if ($refactored !== null) { + if ($refactored instanceof If_) { $if = $refactored; } + if ($if->stmts === []) { + $nop = new Nop(); + $nop->setAttribute(AttributeKey::COMMENTS, $if->getComments()); + $if->stmts[] = $nop; + } else { + $currentStmt = current($if->stmts); + $mergedComments = array_merge($if->getComments(), $currentStmt->getComments()); + $currentStmt->setAttribute(AttributeKey::COMMENTS, $mergedComments); + } + $node->elseifs[] = new ElseIf_($if->cond, $if->stmts); $node->else = $if->else; diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector.php index 5af56c92f8c..808ac5d7136 100644 --- a/rules/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector.php +++ b/rules/CodeQuality/Rector/If_/SimplifyIfElseToTernaryRector.php @@ -4,15 +4,19 @@ namespace Rector\CodeQuality\Rector\If_; -use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,10 +25,13 @@ */ final class SimplifyIfElseToTernaryRector extends AbstractRector { - /** - * @var int - */ - private const LINE_LENGTH_LIMIT = 120; + private const int LINE_LENGTH_LIMIT = 120; + + public function __construct( + private readonly BetterStandardPrinter $betterStandardPrinter, + private readonly BetterNodeFinder $betterNodeFinder + ) { + } public function getRuleDefinition(): RuleDefinition { @@ -73,7 +80,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($node->else === null) { + if (! $node->else instanceof Else_) { return null; } @@ -81,41 +88,47 @@ public function refactor(Node $node): ?Node return null; } - $ifAssignVar = $this->resolveOnlyStmtAssignVar($node->stmts); - $elseAssignVar = $this->resolveOnlyStmtAssignVar($node->else->stmts); - if (! $ifAssignVar instanceof Expr) { + $ifAssignVarExpr = $this->resolveOnlyStmtAssignVar($node->stmts); + if (! $ifAssignVarExpr instanceof Expr) { return null; } - if (! $elseAssignVar instanceof Expr) { + + $elseAssignExpr = $this->resolveOnlyStmtAssignVar($node->else->stmts); + if (! $elseAssignExpr instanceof Expr) { return null; } - if (! $this->nodeComparator->areNodesEqual($ifAssignVar, $elseAssignVar)) { + if (! $this->nodeComparator->areNodesEqual($ifAssignVarExpr, $elseAssignExpr)) { return null; } - $ternaryIf = $this->resolveOnlyStmtAssignExpr($node->stmts); - $ternaryElse = $this->resolveOnlyStmtAssignExpr($node->else->stmts); - if (! $ternaryIf instanceof Expr) { + $ternaryIfExpr = $this->resolveOnlyStmtAssignExpr($node->stmts); + $expr = $this->resolveOnlyStmtAssignExpr($node->else->stmts); + if (! $ternaryIfExpr instanceof Expr) { return null; } - if (! $ternaryElse instanceof Expr) { + + if (! $expr instanceof Expr) { return null; } // has nested ternary → skip, it's super hard to read - if ($this->haveNestedTernary([$node->cond, $ternaryIf, $ternaryElse])) { + if ($this->haveNestedTernary([$node->cond, $ternaryIfExpr, $expr])) { return null; } - $ternary = new Ternary($node->cond, $ternaryIf, $ternaryElse); - $assign = new Assign($ifAssignVar, $ternary); + $ternary = new Ternary($node->cond, $ternaryIfExpr, $expr); + $assign = new Assign($ifAssignVarExpr, $ternary); // do not create super long lines if ($this->isNodeTooLong($assign)) { return null; } + if ($ternary->cond instanceof BinaryOp || $ternary->cond instanceof Assign) { + $ternary->cond->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + $expression = new Expression($assign); $this->mirrorComments($expression, $node); @@ -131,12 +144,17 @@ private function resolveOnlyStmtAssignVar(array $stmts): ?Expr return null; } - $onlyStmt = $this->unwrapExpression($stmts[0]); - if (! $onlyStmt instanceof Assign) { + $stmt = $stmts[0]; + if (! $stmt instanceof Expression) { return null; } - return $onlyStmt->var; + $stmtExpr = $stmt->expr; + if (! $stmtExpr instanceof Assign) { + return null; + } + + return $stmtExpr->var; } /** @@ -148,12 +166,21 @@ private function resolveOnlyStmtAssignExpr(array $stmts): ?Expr return null; } - $onlyStmt = $this->unwrapExpression($stmts[0]); - if (! $onlyStmt instanceof Assign) { + $stmt = $stmts[0]; + if (! $stmt instanceof Expression) { + return null; + } + + if ($stmt->getComments() !== []) { + return null; + } + + $stmtExpr = $stmt->expr; + if (! $stmtExpr instanceof Assign) { return null; } - return $onlyStmt->expr; + return $stmtExpr->expr; } /** @@ -162,8 +189,8 @@ private function resolveOnlyStmtAssignExpr(array $stmts): ?Expr private function haveNestedTernary(array $nodes): bool { foreach ($nodes as $node) { - $betterNodeFinderFindInstanceOf = $this->betterNodeFinder->findInstanceOf($node, Ternary::class); - if ($betterNodeFinderFindInstanceOf !== []) { + $ternary = $this->betterNodeFinder->findFirstInstanceOf($node, Ternary::class); + if ($ternary instanceof Ternary) { return true; } } @@ -173,6 +200,7 @@ private function haveNestedTernary(array $nodes): bool private function isNodeTooLong(Assign $assign): bool { - return Strings::length($this->print($assign)) > self::LINE_LENGTH_LIMIT; + $assignContent = $this->betterStandardPrinter->print($assign); + return strlen($assignContent) > self::LINE_LENGTH_LIMIT; } } diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector.php deleted file mode 100644 index 298874f92a8..00000000000 --- a/rules/CodeQuality/Rector/If_/SimplifyIfIssetToNullCoalescingRector.php +++ /dev/null @@ -1,184 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [If_::class]; - } - - /** - * @param If_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - /** @var Isset_ $issetNode */ - $issetNode = $node->cond; - - $valueNode = $issetNode->vars[0]; - - // various scenarios - $ifFirstStmt = $node->stmts[0]; - if (! $ifFirstStmt instanceof Expression) { - return null; - } - - $else = $node->else; - if (! $else instanceof Else_) { - return null; - } - - $elseFirstStmt = $else->stmts[0]; - if (! $elseFirstStmt instanceof Expression) { - return null; - } - - /** @var Assign $firstAssign */ - $firstAssign = $ifFirstStmt->expr; - - /** @var Assign $secondAssign */ - $secondAssign = $elseFirstStmt->expr; - - // 1. array_merge - if (! $firstAssign->expr instanceof FuncCall) { - return null; - } - - if (! $this->isName($firstAssign->expr, 'array_merge')) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($firstAssign->expr->args[0]->value, $valueNode)) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($secondAssign->expr, $firstAssign->expr->args[1]->value)) { - return null; - } - - $args = [new Arg(new Coalesce($valueNode, new Array_([]))), new Arg($secondAssign->expr)]; - $funcCall = new FuncCall(new Name('array_merge'), $args); - - return new Assign($valueNode, $funcCall); - } - - private function shouldSkip(If_ $if): bool - { - if ($if->else === null) { - return true; - } - - if (count($if->elseifs) > 1) { - return true; - } - - if (! $if->cond instanceof Isset_) { - return true; - } - - if (! $this->hasOnlyStatementAssign($if)) { - return true; - } - - if (! $this->hasOnlyStatementAssign($if->else)) { - return true; - } - - $ifStmt = $if->stmts[0]; - if (! $ifStmt instanceof Expression) { - return true; - } - - if (! $ifStmt->expr instanceof Assign) { - return true; - } - - if (! $this->nodeComparator->areNodesEqual($if->cond->vars[0], $ifStmt->expr->var)) { - return true; - } - - $firstElseStmt = $if->else->stmts[0]; - if (! $firstElseStmt instanceof Expression) { - return false; - } - - if (! $firstElseStmt->expr instanceof Assign) { - return false; - } - - return ! $this->nodeComparator->areNodesEqual($if->cond->vars[0], $firstElseStmt->expr->var); - } - - private function hasOnlyStatementAssign(If_ | Else_ $node): bool - { - if (count($node->stmts) !== 1) { - return false; - } - - if (! $node->stmts[0] instanceof Expression) { - return false; - } - - return $node->stmts[0]->expr instanceof Assign; - } -} diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php index b5ce6b3b04c..7fb2afc9af7 100644 --- a/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php +++ b/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php @@ -5,11 +5,14 @@ namespace Rector\CodeQuality\Rector\If_; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeManipulator\IfManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,7 +22,8 @@ final class SimplifyIfNotNullReturnRector extends AbstractRector { public function __construct( - private IfManipulator $ifManipulator + private readonly IfManipulator $ifManipulator, + private readonly ValueResolver $valueResolver ) { } @@ -52,47 +56,55 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [If_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param If_ $node + * @param StmtsAware $node + * @return StmtsAware */ public function refactor(Node $node): ?Node { - $comparedNode = $this->ifManipulator->matchIfNotNullReturnValue($node); - if ($comparedNode !== null) { - $insideIfNode = $node->stmts[0]; + foreach ((array) $node->stmts as $key => $stmt) { + if (! $stmt instanceof If_) { + continue; + } - $nextNode = $node->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Return_) { - return null; + if ($stmt->else instanceof Else_) { + continue; } - if ($nextNode->expr === null) { - return null; + + if ($stmt->elseifs !== []) { + continue; } - if (! $this->valueResolver->isNull($nextNode->expr)) { + if (! isset($node->stmts[$key + 1])) { return null; } - $this->removeNode($nextNode); - return $insideIfNode; - } - - $comparedNode = $this->ifManipulator->matchIfValueReturnValue($node); - if ($comparedNode !== null) { - $nextNode = $node->getAttribute(AttributeKey::NEXT_NODE); + $nextNode = $node->stmts[$key + 1]; if (! $nextNode instanceof Return_) { - return null; + continue; } - if (! $this->nodeComparator->areNodesEqual($comparedNode, $nextNode->expr)) { - return null; + $expr = $this->ifManipulator->matchIfNotNullReturnValue($stmt); + if (! $expr instanceof Expr) { + continue; + } + + $insideIfNode = $stmt->stmts[0]; + if (! $nextNode->expr instanceof Expr) { + continue; } - $this->removeNode($nextNode); - return clone $nextNode; + if (! $this->valueResolver->isNull($nextNode->expr)) { + continue; + } + + unset($node->stmts[$key]); + $node->stmts[$key + 1] = $insideIfNode; + + return $node; } return null; diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector.php index b342464bb44..eeb34c85776 100644 --- a/rules/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector.php +++ b/rules/CodeQuality/Rector/If_/SimplifyIfNullableReturnRector.php @@ -10,18 +10,19 @@ use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node\Name; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; use Rector\CodeQuality\TypeResolver\AssignVariableTypeResolver; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Rector\AbstractRector; use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeManipulator\IfManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -32,9 +33,10 @@ final class SimplifyIfNullableReturnRector extends AbstractRector { public function __construct( - private IfManipulator $ifManipulator, - private AssignVariableTypeResolver $assignVariableTypeResolver, - private VarTagRemover $varTagRemover + private readonly IfManipulator $ifManipulator, + private readonly AssignVariableTypeResolver $assignVariableTypeResolver, + private readonly VarTagRemover $varTagRemover, + private readonly ValueResolver $valueResolver ) { } @@ -47,14 +49,16 @@ class SomeClass { public function run() { - /** @var \stdClass|null $value */ - $value = $this->foo->bar(); + $value = $this->get(); if (! $value instanceof \stdClass) { return null; } return $value; } + + public function get(): ?stdClass { + } } CODE_SAMPLE , @@ -63,7 +67,10 @@ class SomeClass { public function run() { - return $this->foo->bar(); + return $this->get(); + } + + public function get(): ?stdClass { } } CODE_SAMPLE @@ -76,77 +83,108 @@ public function run() */ public function getNodeTypes(): array { - return [If_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param If_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + if ($node->stmts === null) { return null; } - /** @var BooleanNot|Instanceof_ $cond */ - $cond = $node->cond; - /** @var Instanceof_ $instanceof */ - $instanceof = $cond instanceof BooleanNot - ? $cond->expr - : $cond; - $variable = $instanceof->expr; - $class = $instanceof->class; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Return_) { + continue; + } - if (! $class instanceof Name) { - return null; - } + $previousStmt = $node->stmts[$key - 1] ?? null; + if (! $previousStmt instanceof If_) { + continue; + } - /** @var Return_ $returnIfStmt */ - $returnIfStmt = $node->stmts[0]; + $if = $previousStmt; + if ($this->shouldSkip($if, $stmt)) { + continue; + } - if ($this->isIfStmtReturnIncorrect($cond, $variable, $returnIfStmt)) { - return null; - } + /** @var BooleanNot|Instanceof_ $cond */ + $cond = $if->cond; - $previous = $node->getAttribute(AttributeKey::PREVIOUS_NODE); - if (! $previous instanceof Expression) { - return null; - } + /** @var Instanceof_ $instanceof */ + $instanceof = $cond instanceof BooleanNot ? $cond->expr : $cond; - $previousAssign = $previous->expr; - if (! $previousAssign instanceof Assign) { - return null; - } + // @todo allow property as well + $variable = $instanceof->expr; - if (! $this->nodeComparator->areNodesEqual($previousAssign->var, $variable)) { - return null; - } + $class = $instanceof->class; - /** @var Return_ $next */ - $next = $node->getAttribute(AttributeKey::NEXT_NODE); - if ($this->isNextReturnIncorrect($cond, $variable, $next)) { - return null; - } + if (! $class instanceof Name) { + continue; + } - $variableType = $this->assignVariableTypeResolver->resolve($previousAssign); - if (! $variableType instanceof UnionType) { - return null; - } + /** @var Return_ $returnIfStmt */ + $returnIfStmt = $if->stmts[0]; + + if ($this->isIfStmtReturnIncorrect($cond, $variable, $returnIfStmt)) { + continue; + } + + $previousPreviousStmt = $node->stmts[$key - 2] ?? null; + if (! $previousPreviousStmt instanceof Expression) { + continue; + } + + if (! $previousPreviousStmt->expr instanceof Assign) { + continue; + } + + $previousPreviousAssign = $previousPreviousStmt->expr; + if (! $this->nodeComparator->areNodesEqual($previousPreviousAssign->var, $variable)) { + continue; + } + + if ($this->isNextReturnIncorrect($cond, $variable, $stmt)) { + continue; + } + + $variableType = $this->assignVariableTypeResolver->resolve($previousPreviousAssign); + if (! $variableType instanceof UnionType) { + continue; + } - $className = $class->toString(); - $types = $variableType->getTypes(); + $className = $class->toString(); + $types = $variableType->getTypes(); + + $directReturn = $this->processSimplifyNullableReturn( + $variableType, + $types, + $className, + $previousPreviousStmt, + $previousPreviousAssign->expr + ); + + if (! $directReturn instanceof Return_) { + continue; + } + + // unset previous assign + unset($node->stmts[$key - 2]); + + // unset previous if + unset($node->stmts[$key - 1]); + + $node->stmts[$key] = $directReturn; + + return $node; + } - return $this->processSimplifyNullableReturn( - $variableType, - $types, - $className, - $next, - $previous, - $previousAssign->expr - ); + return null; } - private function isIfStmtReturnIncorrect(Expr $expr, Expr $variable, Return_ $return): bool + private function isIfStmtReturnIncorrect(BooleanNot|Instanceof_ $expr, Expr $variable, Return_ $return): bool { if (! $return->expr instanceof Expr) { return true; @@ -159,7 +197,7 @@ private function isIfStmtReturnIncorrect(Expr $expr, Expr $variable, Return_ $re return $expr instanceof Instanceof_ && ! $this->nodeComparator->areNodesEqual($variable, $return->expr); } - private function isNextReturnIncorrect(Expr $expr, Expr $variable, Return_ $return): bool + private function isNextReturnIncorrect(BooleanNot|Instanceof_ $expr, Expr $variable, Return_ $return): bool { if (! $return->expr instanceof Expr) { return true; @@ -179,7 +217,6 @@ private function processSimplifyNullableReturn( UnionType $unionType, array $types, string $className, - Return_ $return, Expression $expression, Expr $expr ): ?Return_ { @@ -187,19 +224,19 @@ private function processSimplifyNullableReturn( return null; } - if ($types[0] instanceof FullyQualifiedObjectType && $types[1] instanceof NullType && $className === $types[0]->getClassName()) { - return $this->removeAndReturn($return, $expression, $expr, $unionType); + if ($types[0] instanceof FullyQualifiedObjectType && $types[1]->isNull()->yes() && $className === $types[0]->getClassName()) { + return $this->createDirectReturn($expression, $expr, $unionType); } - if ($types[0] instanceof NullType && $types[1] instanceof FullyQualifiedObjectType && $className === $types[1]->getClassName()) { - return $this->removeAndReturn($return, $expression, $expr, $unionType); + if ($types[0]->isNull()->yes() && $types[1] instanceof FullyQualifiedObjectType && $className === $types[1]->getClassName()) { + return $this->createDirectReturn($expression, $expr, $unionType); } if ($this->isNotTypedNullable($types, $className)) { return null; } - return $this->removeAndReturn($return, $expression, $expr, $unionType); + return $this->createDirectReturn($expression, $expr, $unionType); } /** @@ -211,33 +248,30 @@ private function isNotTypedNullable(array $types, string $className): bool return true; } - if (! $types[1] instanceof NullType) { + if (! $types[1]->isNull()->yes()) { return true; } return $className !== $types[0]->getClassName(); } - private function removeAndReturn(Return_ $return, Expression $expression, Expr $expr, UnionType $unionType): Return_ + private function createDirectReturn(Expression $expression, Expr $expr, UnionType $unionType): Return_ { - $this->removeNode($return); - $this->removeNode($expression); + $exprReturn = new Return_($expr); - $return = new Return_($expr); $this->varTagRemover->removeVarPhpTagValueNodeIfNotComment($expression, $unionType); - $this->mirrorComments($return, $expression); + $this->mirrorComments($exprReturn, $expression); - return $return; + return $exprReturn; } - private function shouldSkip(If_ $if): bool + private function shouldSkip(If_ $if, Stmt $stmt): bool { if (! $this->ifManipulator->isIfWithOnly($if, Return_::class)) { return true; } - $next = $if->getAttribute(AttributeKey::NEXT_NODE); - if (! $next instanceof Return_) { + if (! $stmt instanceof Return_) { return true; } diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php index 18403e21391..f0129cd4ee3 100644 --- a/rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php +++ b/rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php @@ -6,15 +6,20 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; use Rector\BetterPhpDocParser\Comment\CommentsMerger; use Rector\CodeQuality\NodeManipulator\ExprBoolCaster; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -24,8 +29,10 @@ final class SimplifyIfReturnBoolRector extends AbstractRector { public function __construct( - private CommentsMerger $commentsMerger, - private ExprBoolCaster $exprBoolCaster + private readonly CommentsMerger $commentsMerger, + private readonly ExprBoolCaster $exprBoolCaster, + private readonly BetterStandardPrinter $betterStandardPrinter, + private readonly ValueResolver $valueResolver ) { } @@ -43,7 +50,9 @@ public function getRuleDefinition(): RuleDefinition return false; CODE_SAMPLE , - 'return strpos($docToken->getContent(), "\n") === false;' + <<<'CODE_SAMPLE' +return strpos($docToken->getContent(), "\n") === false; +CODE_SAMPLE ), ] ); @@ -54,55 +63,66 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [If_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param If_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + if ($node->stmts === null) { return null; } - /** @var Return_ $ifInnerNode */ - $ifInnerNode = $node->stmts[0]; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Return_) { + continue; + } - /** @var Return_ $nextNode */ - $nextNode = $node->getAttribute(AttributeKey::NEXT_NODE); + $previousStmt = $node->stmts[$key - 1] ?? null; + if (! $previousStmt instanceof If_) { + continue; + } - /** @var Node $innerIfInnerNode */ - $innerIfInnerNode = $ifInnerNode->expr; + $if = $previousStmt; + if ($this->shouldSkipIfAndReturn($previousStmt, $stmt)) { + continue; + } - if ($this->valueResolver->isTrue($innerIfInnerNode)) { - $newReturnNode = $this->processReturnTrue($node, $nextNode); - } elseif ($this->valueResolver->isFalse($innerIfInnerNode)) { - $newReturnNode = $this->processReturnFalse($node, $nextNode); - } else { - return null; - } + $return = $stmt; - if ($newReturnNode === null) { - return null; - } + /** @var Return_ $ifInnerNode */ + $ifInnerNode = $if->stmts[0]; + + $innerIfInnerNode = $ifInnerNode->expr; + if (! $innerIfInnerNode instanceof Expr) { + continue; + } + + $newReturn = $this->resolveReturn($innerIfInnerNode, $if, $return); + if (! $newReturn instanceof Return_) { + continue; + } - $this->commentsMerger->keepComments($newReturnNode, [$node, $ifInnerNode, $nextNode, $newReturnNode]); - $this->removeNode($nextNode); + $this->commentsMerger->keepComments($newReturn, [$if, $return, $ifInnerNode]); - return $newReturnNode; + // remove previous IF + unset($node->stmts[$key - 1]); + $node->stmts[$key] = $newReturn; + + return $node; + } + + return null; } - private function shouldSkip(If_ $if): bool + private function shouldSkipIfAndReturn(If_ $if, Return_ $return): bool { if ($if->elseifs !== []) { return true; } - if ($this->isElseSeparatedThenIf($if)) { - return true; - } - if (! $this->isIfWithSingleReturnExpr($if)) { return true; } @@ -117,30 +137,27 @@ private function shouldSkip(If_ $if): bool return true; } - $nextNode = $if->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Return_) { - return true; - } - if ($nextNode->expr === null) { + if (! $return->expr instanceof Expr) { return true; } + // negate + negate → skip for now if (! $this->valueResolver->isFalse($returnedExpr)) { - return ! $this->valueResolver->isTrueOrFalse($nextNode->expr); + return ! $this->valueResolver->isTrueOrFalse($return->expr); } - $condString = $this->print($if->cond); + $condString = $this->betterStandardPrinter->print($if->cond); if (! \str_contains($condString, '!=')) { - return ! $this->valueResolver->isTrueOrFalse($nextNode->expr); + return ! $this->valueResolver->isTrueOrFalse($return->expr); } - return true; + return ! $if->cond instanceof NotIdentical && ! $if->cond instanceof NotEqual; } - private function processReturnTrue(If_ $if, Return_ $nextReturnNode): Return_ + private function processReturnTrue(If_ $if, Return_ $nextReturn): Return_ { - if ($if->cond instanceof BooleanNot && $nextReturnNode->expr !== null && $this->valueResolver->isTrue( - $nextReturnNode->expr + if ($if->cond instanceof BooleanNot && $nextReturn->expr instanceof Expr && $this->valueResolver->isTrue( + $nextReturn->expr )) { return new Return_($this->exprBoolCaster->boolCastOrNullCompareIfNeeded($if->cond->expr)); } @@ -148,7 +165,7 @@ private function processReturnTrue(If_ $if, Return_ $nextReturnNode): Return_ return new Return_($this->exprBoolCaster->boolCastOrNullCompareIfNeeded($if->cond)); } - private function processReturnFalse(If_ $if, Return_ $nextReturnNode): ?Return_ + private function processReturnFalse(If_ $if, Return_ $nextReturn): ?Return_ { if ($if->cond instanceof Identical) { $notIdentical = new NotIdentical($if->cond->left, $if->cond->right); @@ -156,11 +173,17 @@ private function processReturnFalse(If_ $if, Return_ $nextReturnNode): ?Return_ return new Return_($this->exprBoolCaster->boolCastOrNullCompareIfNeeded($notIdentical)); } - if ($nextReturnNode->expr === null) { + if ($if->cond instanceof Equal) { + $notIdentical = new NotEqual($if->cond->left, $if->cond->right); + + return new Return_($this->exprBoolCaster->boolCastOrNullCompareIfNeeded($notIdentical)); + } + + if (! $nextReturn->expr instanceof Expr) { return null; } - if (! $this->valueResolver->isTrue($nextReturnNode->expr)) { + if (! $this->valueResolver->isTrue($nextReturn->expr)) { return null; } @@ -171,40 +194,47 @@ private function processReturnFalse(If_ $if, Return_ $nextReturnNode): ?Return_ return new Return_($this->exprBoolCaster->boolCastOrNullCompareIfNeeded(new BooleanNot($if->cond))); } - /** - * Matches: "else if" - */ - private function isElseSeparatedThenIf(If_ $if): bool + private function isIfWithSingleReturnExpr(If_ $if): bool { - if ($if->else === null) { + if (count($if->stmts) !== 1) { return false; } - if (count($if->else->stmts) !== 1) { + if ($if->else instanceof Else_ || $if->elseifs !== []) { return false; } - $onlyStmt = $if->else->stmts[0]; + $ifInnerNode = $if->stmts[0]; + if (! $ifInnerNode instanceof Return_) { + return false; + } - return $onlyStmt instanceof If_; + // return must have value + return $ifInnerNode->expr instanceof Expr; } - private function isIfWithSingleReturnExpr(If_ $if): bool + private function resolveReturn(Expr $innerExpr, If_ $if, Return_ $return): ?Return_ { - if (count($if->stmts) !== 1) { - return false; + if ($this->valueResolver->isTrue($innerExpr)) { + return $this->processReturnTrue($if, $return); } - if ($if->elseifs !== []) { - return false; - } + if ($this->valueResolver->isFalse($innerExpr)) { + /** @var Expr $expr */ + $expr = $return->expr; + if ($if->cond instanceof NotIdentical && $this->valueResolver->isTrue($expr)) { + $if->cond = new Identical($if->cond->left, $if->cond->right); + return $this->processReturnTrue($if, $return); + } - $ifInnerNode = $if->stmts[0]; - if (! $ifInnerNode instanceof Return_) { - return false; + if ($if->cond instanceof NotEqual && $this->valueResolver->isTrue($expr)) { + $if->cond = new Equal($if->cond->left, $if->cond->right); + return $this->processReturnTrue($if, $return); + } + + return $this->processReturnFalse($if, $return); } - // return must have value - return $ifInnerNode->expr !== null; + return null; } } diff --git a/rules/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector.php b/rules/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector.php index be67622a609..d34a8294716 100644 --- a/rules/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector.php +++ b/rules/CodeQuality/Rector/Include_/AbsolutizeRequireAndIncludePathRector.php @@ -10,21 +10,31 @@ use PhpParser\Node\Expr\Include_; use PhpParser\Node\Scalar\MagicConst\Dir; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\Util\StringUtils; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://github.com/symplify/CodingStandard#includerequire-should-be-followed-by-absolute-path - * * @see \Rector\Tests\CodeQuality\Rector\Include_\AbsolutizeRequireAndIncludePathRector\AbsolutizeRequireAndIncludePathRectorTest */ final class AbsolutizeRequireAndIncludePathRector extends AbstractRector { + /** + * @see https://regex101.com/r/N8oLqv/1 + */ + private const string WINDOWS_DRIVE_REGEX = '#^[a-zA-z]\:[\/\\\]#'; + + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'include/require to absolute path. This Rector might introduce backwards incompatible code, when the include/require beeing changed depends on the current working directory.', + 'Migrate include/require to absolute path. This Rector might introduce backwards incompatible code, when the include/require being changed depends on the current working directory.', [ new CodeSample( <<<'CODE_SAMPLE' @@ -68,10 +78,22 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { + if ($node->expr instanceof Concat && $node->expr->left instanceof String_ && $this->isRefactorableStringPath( + $node->expr->left + )) { + $node->expr->left = $this->prefixWithDirConstant($node->expr->left); + + return $node; + } + if (! $node->expr instanceof String_) { return null; } + if (! $this->isRefactorableStringPath($node->expr)) { + return null; + } + /** @var string $includeValue */ $includeValue = $this->valueResolver->getValue($node->expr); @@ -81,19 +103,60 @@ public function refactor(Node $node): ?Node } // skip absolute paths - if (\str_starts_with($includeValue, '/')) { + if (\str_starts_with($includeValue, '/') || \str_starts_with($includeValue, '\\')) { return null; } - // add preslash to string - if (\str_starts_with($includeValue, './')) { - $node->expr->value = Strings::substring($includeValue, 1); - } else { - $node->expr->value = '/' . $includeValue; + if (str_contains($includeValue, 'config/')) { + return null; } - $node->expr = new Concat(new Dir(), $node->expr); + if (StringUtils::isMatch($includeValue, self::WINDOWS_DRIVE_REGEX)) { + return null; + } + + // add preslash to string + $node->expr->value = \str_starts_with($includeValue, './') ? Strings::substring( + $includeValue, + 1 + ) : '/' . $includeValue; + + $node->expr = $this->prefixWithDirConstant($node->expr); return $node; } + + private function isRefactorableStringPath(String_ $string): bool + { + return ! \str_starts_with($string->value, 'phar://'); + } + + private function prefixWithDirConstant(String_ $string): Concat + { + $this->removeExtraDotSlash($string); + $this->prependSlashIfMissing($string); + + return new Concat(new Dir(), $string); + } + + /** + * Remove "./" which would break the path + */ + private function removeExtraDotSlash(String_ $string): void + { + if (! \str_starts_with($string->value, './')) { + return; + } + + $string->value = Strings::replace($string->value, '#^\.\/#', '/'); + } + + private function prependSlashIfMissing(String_ $string): void + { + if (\str_starts_with($string->value, '/')) { + return; + } + + $string->value = '/' . $string->value; + } } diff --git a/rules/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector.php b/rules/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector.php index 0be54ec9fb2..52f0b4374b1 100644 --- a/rules/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector.php +++ b/rules/CodeQuality/Rector/Isset_/IssetOnPropertyObjectToPropertyExistsRector.php @@ -7,36 +7,44 @@ use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PhpParser\Node\Expr\BinaryOp\BooleanOr; +use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; +use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Isset_; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Identifier; use PhpParser\Node\Scalar\String_; +use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\Php\PhpPropertyReflection; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\MixedType; -use PHPStan\Type\TypeWithClassName; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Reflection\ReflectionResolver; +use PHPStan\Type\TypeCombinator; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\CodeQuality\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector\IssetOnPropertyObjectToPropertyExistsRectorTest - * - * @see https://3v4l.org/TI8XL Change isset on property object to property_exists() with not null check */ final class IssetOnPropertyObjectToPropertyExistsRector extends AbstractRector { public function __construct( - private ReflectionProvider $reflectionProvider, - private ReflectionResolver $reflectionResolver + private readonly ReflectionProvider $reflectionProvider, + private readonly ReflectionResolver $reflectionResolver, + private readonly ValueResolver $valueResolver ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change isset on property object to property_exists() and not null check', [ + return new RuleDefinition('Change isset on property object to `property_exists()` and not null check', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -49,7 +57,7 @@ public function run(): void } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -70,55 +78,70 @@ public function run(): void */ public function getNodeTypes(): array { - return [Isset_::class]; + return [Isset_::class, BooleanNot::class]; } /** - * @param Isset_ $node + * @param Isset_|BooleanNot $node */ public function refactor(Node $node): ?Node { + $isNegated = false; + + if ($node instanceof BooleanNot) { + if ($node->expr instanceof Isset_) { + $isNegated = true; + $isset = $node->expr; + } else { + return null; + } + } else { + $isset = $node; + } + $newNodes = []; - foreach ($node->vars as $issetVar) { - if (! $issetVar instanceof PropertyFetch) { + foreach ($isset->vars as $issetExpr) { + if (! $issetExpr instanceof PropertyFetch) { continue; } // has property PHP 7.4 type? - if ($this->hasPropertyTypeDeclaration($issetVar)) { + if ($this->shouldSkipForPropertyTypeDeclaration($issetExpr)) { continue; } - $propertyFetchName = $this->getName($issetVar->name); - if ($propertyFetchName === null) { + // Ignore dynamically accessed properties ($o->$p) + $propertyFetchName = $this->getName($issetExpr->name); + if (! is_string($propertyFetchName)) { continue; } - $propertyFetchVarType = $this->getObjectType($issetVar->var); + $classReflection = $this->matchPropertyTypeClassReflection($issetExpr); + if (! $classReflection instanceof ClassReflection) { + continue; + } - if ($propertyFetchVarType instanceof TypeWithClassName) { - if (! $this->reflectionProvider->hasClass($propertyFetchVarType->getClassName())) { - continue; - } + if ($classReflection->hasNativeMethod(MethodName::ISSET)) { + continue; + } - $classReflection = $this->reflectionProvider->getClass($propertyFetchVarType->getClassName()); + // possibly by docblock + if ($issetExpr->var instanceof ArrayDimFetch) { + continue; + } - if (! $classReflection->hasProperty($propertyFetchName) || $classReflection->isBuiltIn()) { - $newNodes[] = $this->replaceToPropertyExistsWithNullCheck( - $issetVar->var, - $propertyFetchName, - $issetVar - ); - } else { - $newNodes[] = $this->createNotIdenticalToNull($issetVar); - } - } else { + if (! $classReflection->hasInstanceProperty($propertyFetchName) || $classReflection->isBuiltin()) { $newNodes[] = $this->replaceToPropertyExistsWithNullCheck( - $issetVar->var, + $issetExpr->var, $propertyFetchName, - $issetVar + $issetExpr, + $isNegated ); + } elseif ($isNegated) { + $newNodes[] = $this->createIdenticalToNull($issetExpr); + } else { + $newNodes[] = $this->createNotIdenticalToNull($issetExpr); } } @@ -128,26 +151,76 @@ public function refactor(Node $node): ?Node private function replaceToPropertyExistsWithNullCheck( Expr $expr, string $property, - PropertyFetch $propertyFetch - ): BooleanAnd { + PropertyFetch $propertyFetch, + bool $isNegated + ): BooleanAnd|BooleanOr { $args = [new Arg($expr), new Arg(new String_($property))]; $propertyExistsFuncCall = $this->nodeFactory->createFuncCall('property_exists', $args); + if ($isNegated) { + $booleanNot = new BooleanNot($propertyExistsFuncCall); + return new BooleanOr($booleanNot, $this->createIdenticalToNull($propertyFetch)); + } + return new BooleanAnd($propertyExistsFuncCall, $this->createNotIdenticalToNull($propertyFetch)); } - private function createNotIdenticalToNull(Expr $expr): NotIdentical + private function createNotIdenticalToNull(PropertyFetch $propertyFetch): NotIdentical { - return new NotIdentical($expr, $this->nodeFactory->createNull()); + return new NotIdentical($propertyFetch, $this->nodeFactory->createNull()); } - private function hasPropertyTypeDeclaration(PropertyFetch $propertyFetch): bool + private function shouldSkipForPropertyTypeDeclaration(PropertyFetch $propertyFetch): bool { + if (! $propertyFetch->name instanceof Identifier) { + return true; + } + $phpPropertyReflection = $this->reflectionResolver->resolvePropertyReflectionFromPropertyFetch($propertyFetch); if (! $phpPropertyReflection instanceof PhpPropertyReflection) { return false; } - return ! $phpPropertyReflection->getNativeType() instanceof MixedType; + $propertyType = $phpPropertyReflection->getNativeType(); + if ($propertyType instanceof MixedType) { + return false; + } + + if (! TypeCombinator::containsNull($propertyType)) { + return true; + } + + $nativeReflectionProperty = $phpPropertyReflection->getNativeReflection(); + if (! $nativeReflectionProperty->hasDefaultValue()) { + return true; + } + + $defaultValueExpr = $nativeReflectionProperty->getDefaultValueExpression(); + return ! $this->valueResolver->isNull($defaultValueExpr); + } + + private function createIdenticalToNull(PropertyFetch $propertyFetch): Identical + { + return new Identical($propertyFetch, $this->nodeFactory->createNull()); + } + + private function matchPropertyTypeClassReflection(PropertyFetch $propertyFetch): ?ClassReflection + { + $propertyFetchVarType = $this->getType($propertyFetch->var); + $className = ClassNameFromObjectTypeResolver::resolve($propertyFetchVarType); + + if ($className === null) { + return null; + } + + if ($className === 'stdClass') { + return null; + } + + if (! $this->reflectionProvider->hasClass($className)) { + return null; + } + + return $this->reflectionProvider->getClass($className); } } diff --git a/rules/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector.php b/rules/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector.php index 5b277d4d51d..99aa7e084d1 100644 --- a/rules/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector.php +++ b/rules/CodeQuality/Rector/LogicalAnd/AndAssignsToSeparateLinesRector.php @@ -8,20 +8,18 @@ use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp\LogicalAnd; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/ji8bX * @see \Rector\Tests\CodeQuality\Rector\LogicalAnd\AndAssignsToSeparateLinesRector\AndAssignsToSeparateLinesRectorTest */ final class AndAssignsToSeparateLinesRector extends AbstractRector { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Split 2 assigns ands to separate line', [ + return new RuleDefinition('Split 2 assigns with "and" to separate lines', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -54,27 +52,31 @@ public function run() */ public function getNodeTypes(): array { - return [LogicalAnd::class]; + return [Expression::class]; } /** - * @param LogicalAnd $node + * @param Expression $node + * @return Expression[]|null */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - if (! $node->left instanceof Assign) { + if (! $node->expr instanceof LogicalAnd) { return null; } - if (! $node->right instanceof Assign) { + + $logicalAnd = $node->expr; + if (! $logicalAnd->left instanceof Assign) { return null; } - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Expression) { + + if (! $logicalAnd->right instanceof Assign) { return null; } - $this->addNodeAfterNode($node->right, $node); + $leftAssignExpression = new Expression($logicalAnd->left); + $rightAssignExpression = new Expression($logicalAnd->right); - return $node->left; + return [$leftAssignExpression, $rightAssignExpression]; } } diff --git a/rules/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector.php b/rules/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector.php index f3aff4d44cc..aea7cced284 100644 --- a/rules/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector.php +++ b/rules/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector.php @@ -5,17 +5,16 @@ namespace Rector\CodeQuality\Rector\LogicalAnd; use PhpParser\Node; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BinaryOp\LogicalAnd; use PhpParser\Node\Expr\BinaryOp\LogicalOr; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/a/5998330/1348344 - * * @see \Rector\Tests\CodeQuality\Rector\LogicalAnd\LogicalToBooleanRector\LogicalToBooleanRectorTest */ final class LogicalToBooleanRector extends AbstractRector @@ -53,8 +52,27 @@ public function getNodeTypes(): array /** * @param LogicalOr|LogicalAnd $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): BooleanAnd|BooleanOr|null { + $type = $this->nodeTypeResolver->getNativeType($node->left); + + if ($node->left instanceof Assign && ! $type->isBoolean()->yes()) { + return null; + } + + return $this->refactorLogicalToBoolean($node); + } + + private function refactorLogicalToBoolean(LogicalOr|LogicalAnd $node): BooleanAnd|BooleanOr + { + if ($node->left instanceof LogicalOr || $node->left instanceof LogicalAnd) { + $node->left = $this->refactorLogicalToBoolean($node->left); + } + + if ($node->right instanceof LogicalOr || $node->right instanceof LogicalAnd) { + $node->right = $this->refactorLogicalToBoolean($node->right); + } + if ($node instanceof LogicalOr) { return new BooleanOr($node->left, $node->right); } diff --git a/rules/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector.php b/rules/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector.php deleted file mode 100644 index 7359287f867..00000000000 --- a/rules/CodeQuality/Rector/Name/FixClassCaseSensitivityNameRector.php +++ /dev/null @@ -1,142 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Name::class]; - } - - /** - * @param Name $node - */ - public function refactor(Node $node): ?Node - { - $fullyQualifiedName = $this->resolveFullyQualifiedName($node); - if (! $this->reflectionProvider->hasClass($fullyQualifiedName)) { - return null; - } - - $classReflection = $this->reflectionProvider->getClass($fullyQualifiedName); - if ($classReflection->isBuiltin()) { - // skip built-in classes - return null; - } - - $realClassName = $classReflection->getName(); - if (strtolower($realClassName) !== strtolower($fullyQualifiedName)) { - // skip class alias - return null; - } - - if ($realClassName === $fullyQualifiedName) { - return null; - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - $hasFunction = $this->reflectionProvider->hasFunction(new FullyQualified($fullyQualifiedName), $scope); - if ($hasFunction) { - return null; - } - - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - - // do not FQN use imports - if ($parent instanceof UseUse) { - return new Name($realClassName); - } - - return new FullyQualified($realClassName); - } - - private function resolveFullyQualifiedName(Name $name): string - { - $parent = $name->getAttribute(AttributeKey::PARENT_NODE); - // for some reason, Param gets already corrected name - if (! $parent instanceof Param && ! $parent instanceof ClassConstFetch) { - return $this->getName($name); - } - - $originalName = $name->getAttribute(AttributeKey::ORIGINAL_NAME); - if (! $originalName instanceof Name) { - return $this->getName($name); - } - - // replace parts from the old one - $originalReversedParts = array_reverse($originalName->parts); - $resolvedReversedParts = array_reverse($name->parts); - - $mergedReversedParts = $originalReversedParts + $resolvedReversedParts; - $mergedParts = array_reverse($mergedReversedParts); - - return implode('\\', $mergedParts); - } -} diff --git a/rules/CodeQuality/Rector/New_/NewStaticToNewSelfRector.php b/rules/CodeQuality/Rector/New_/NewStaticToNewSelfRector.php index 9e2205d75a2..a32a99324c5 100644 --- a/rules/CodeQuality/Rector/New_/NewStaticToNewSelfRector.php +++ b/rules/CodeQuality/Rector/New_/NewStaticToNewSelfRector.php @@ -8,24 +8,23 @@ use PhpParser\Node\Expr\New_; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Configuration\Parameter\FeatureFlags; +use Rector\Enum\ObjectReference; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://github.com/phpstan/phpstan-src/blob/699c420f8193da66927e54494a0afa0c323c6458/src/Rules/Classes/NewStaticRule.php - * * @see \Rector\Tests\CodeQuality\Rector\New_\NewStaticToNewSelfRector\NewStaticToNewSelfRectorTest */ final class NewStaticToNewSelfRector extends AbstractRector { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change unsafe new static() to new self()', [ + return new RuleDefinition('Change unsafe `new static()` to `new self()`', [ new CodeSample( <<<'CODE_SAMPLE' -class SomeClass +final class SomeClass { public function build() { @@ -36,7 +35,7 @@ public function build() , <<<'CODE_SAMPLE' -class SomeClass +final class SomeClass { public function build() { @@ -53,29 +52,39 @@ public function build() */ public function getNodeTypes(): array { - return [New_::class]; + return [Class_::class]; } /** - * @param New_ $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - $class = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { + if (! $node->isFinal() && FeatureFlags::treatClassesAsFinal($node) === false) { return null; } - if (! $class->isFinal()) { - return null; - } + $hasChanged = false; - if (! $this->isName($node->class, 'static')) { - return null; - } + $this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged): ?New_ { + if (! $node instanceof New_) { + return null; + } - $node->class = new Name('self'); + if (! $this->isName($node->class, ObjectReference::STATIC)) { + return null; + } + + $hasChanged = true; + + $node->class = new Name(ObjectReference::SELF); + return $node; + }); + + if ($hasChanged) { + return $node; + } - return $node; + return null; } } diff --git a/rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php b/rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php index 269fbf818eb..cea0b7c876d 100644 --- a/rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php +++ b/rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php @@ -6,13 +6,11 @@ use PhpParser\Node; use PhpParser\Node\Expr\BinaryOp\NotEqual; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/a/4294663/1348344 * @see \Rector\Tests\CodeQuality\Rector\NotEqual\CommonNotEqualRector\CommonNotEqualRectorTest */ final class CommonNotEqualRector extends AbstractRector @@ -58,11 +56,21 @@ public function getNodeTypes(): array /** * @param NotEqual $node */ - public function refactor(Node $node): NotEqual + public function refactor(Node $node): ?NotEqual { - // invoke override to default "!=" - $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); + $tokenStartPos = $node->getStartTokenPos(); + $tokenEndPos = $node->getEndTokenPos(); - return $node; + for ($i = $tokenStartPos; $i < $tokenEndPos; ++$i) { + $token = $this->getFile() + ->getOldTokens()[$i]; + + if ((string) $token === '<>') { + $token->text = '!='; + return $node; + } + } + + return null; } } diff --git a/rules/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php b/rules/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php new file mode 100644 index 00000000000..26e19f0d1b9 --- /dev/null +++ b/rules/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php @@ -0,0 +1,108 @@ +getString(); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class HelloWorld { + public function getString(): string + { + return 'hello world'; + } +} + +function get(): HelloWorld +{ + return new HelloWorld(); +} + +echo get()->getString(); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [NullsafeMethodCall::class]; + } + + /** + * @param NullsafeMethodCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->name instanceof Identifier) { + return null; + } + + if (! $node->var instanceof FuncCall && ! $node->var instanceof MethodCall && ! $node->var instanceof StaticCall) { + return null; + } + + $returnType = $this->returnStrictTypeAnalyzer->resolveMethodCallReturnType($node->var); + if (! $returnType instanceof ObjectType) { + return null; + } + + return new MethodCall($node->var, $node->name, $node->args); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NULLSAFE_OPERATOR; + } +} diff --git a/rules/CodeQuality/Rector/Return_/SimplifyUselessVariableRector.php b/rules/CodeQuality/Rector/Return_/SimplifyUselessVariableRector.php deleted file mode 100644 index fe9ded9e2d6..00000000000 --- a/rules/CodeQuality/Rector/Return_/SimplifyUselessVariableRector.php +++ /dev/null @@ -1,187 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Return_::class]; - } - - /** - * @param Return_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - $previousNode = $node->getAttribute(AttributeKey::PREVIOUS_NODE); - if (! $previousNode instanceof Expression) { - return null; - } - - /** @var AssignOp|Assign $previousNode */ - $previousNode = $previousNode->expr; - $previousVariableNode = $previousNode->var; - - if ($this->hasSomeComment($previousVariableNode)) { - return null; - } - - if ($previousNode instanceof Assign) { - if ($this->isReturnWithVarAnnotation($node)) { - return null; - } - - $node->expr = $previousNode->expr; - } - - if ($previousNode instanceof AssignOp) { - $binaryClass = $this->assignAndBinaryMap->getAlternative($previousNode); - if ($binaryClass === null) { - return null; - } - - $node->expr = new $binaryClass($previousNode->var, $previousNode->expr); - } - - $this->removeNode($previousNode); - - return $node; - } - - private function hasByRefReturn(Return_ $return): bool - { - $node = $return; - - while ($node = $node->getAttribute(AttributeKey::PARENT_NODE)) { - if ($node instanceof FunctionLike) { - return $node->returnsByRef(); - } - } - - return false; - } - - private function shouldSkip(Return_ $return): bool - { - if (! $return->expr instanceof Variable) { - return true; - } - - if ($this->hasByRefReturn($return)) { - return true; - } - - /** @var Variable $variableNode */ - $variableNode = $return->expr; - - $previousExpression = $return->getAttribute(AttributeKey::PREVIOUS_NODE); - if (! $previousExpression instanceof Node) { - return true; - } - if (! $previousExpression instanceof Expression) { - return true; - } - - // is variable part of single assign - $previousNode = $previousExpression->expr; - if (! $previousNode instanceof AssignOp && ! $previousNode instanceof Assign) { - return true; - } - - // is the same variable - if (! $this->nodeComparator->areNodesEqual($previousNode->var, $variableNode)) { - return true; - } - - if ($this->isPreviousExpressionVisuallySimilar($previousExpression, $previousNode)) { - return true; - } - - return $this->variableAnalyzer->isStaticOrGlobal($variableNode); - } - - private function hasSomeComment(Expr $expr): bool - { - if ($expr->getComments() !== []) { - return true; - } - - return $expr->getDocComment() !== null; - } - - private function isReturnWithVarAnnotation(Return_ $return): bool - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($return); - return ! $phpDocInfo->getVarType() instanceof MixedType; - } - - private function isPreviousExpressionVisuallySimilar( - Expression $previousExpression, - AssignOp | Assign $previousNode - ): bool { - $prePreviousExpression = $previousExpression->getAttribute(AttributeKey::PREVIOUS_STATEMENT); - if (! $prePreviousExpression instanceof Expression) { - return false; - } - if (! $prePreviousExpression->expr instanceof AssignOp) { - return false; - } - return $this->nodeComparator->areNodesEqual($prePreviousExpression->expr->var, $previousNode->var); - } -} diff --git a/rules/CodeQuality/Rector/Switch_/SingularSwitchToIfRector.php b/rules/CodeQuality/Rector/Switch_/SingularSwitchToIfRector.php index 9c4bf18a2a4..939503c8447 100644 --- a/rules/CodeQuality/Rector/Switch_/SingularSwitchToIfRector.php +++ b/rules/CodeQuality/Rector/Switch_/SingularSwitchToIfRector.php @@ -5,10 +5,13 @@ namespace Rector\CodeQuality\Rector\Switch_; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Break_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Switch_; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Rector\Renaming\NodeManipulator\SwitchManipulator; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,13 +22,13 @@ final class SingularSwitchToIfRector extends AbstractRector { public function __construct( - private SwitchManipulator $switchManipulator + private readonly SwitchManipulator $switchManipulator ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change switch with only 1 check to if', [ + return new RuleDefinition('Change `switch` with only 1 check to `if`', [ new CodeSample( <<<'CODE_SAMPLE' class SomeObject @@ -74,7 +77,7 @@ public function getNodeTypes(): array * @param Switch_ $node * @return Node\Stmt[]|If_|null */ - public function refactor(Node $node) + public function refactor(Node $node): array|If_|null { if (count($node->cases) !== 1) { return null; @@ -83,8 +86,9 @@ public function refactor(Node $node) $onlyCase = $node->cases[0]; // only default → basically unwrap - if ($onlyCase->cond === null) { - return $onlyCase->stmts; + if (! $onlyCase->cond instanceof Expr) { + // remove default clause because it cause syntax error + return array_filter($onlyCase->stmts, static fn (Stmt $stmt): bool => ! $stmt instanceof Break_); } $if = new If_(new Identical($node->cond, $onlyCase->cond)); diff --git a/rules/CodeQuality/Rector/Switch_/SwitchTrueToIfRector.php b/rules/CodeQuality/Rector/Switch_/SwitchTrueToIfRector.php new file mode 100644 index 00000000000..c143fca8cc8 --- /dev/null +++ b/rules/CodeQuality/Rector/Switch_/SwitchTrueToIfRector.php @@ -0,0 +1,122 @@ +> + */ + public function getNodeTypes(): array + { + return [Switch_::class]; + } + + /** + * @param Switch_ $node + * @return Stmt[]|null + */ + public function refactor(Node $node): ?array + { + if (! $this->valueResolver->isTrue($node->cond)) { + return null; + } + + $newStmts = []; + + $defaultCase = null; + + foreach ($node->cases as $case) { + if (! end($case->stmts) instanceof Return_) { + return null; + } + + if (! $case->cond instanceof Expr) { + $defaultCase = $case; + continue; + } + + $if = new If_($case->cond); + $if->stmts = $case->stmts; + + $newStmts[] = $if; + } + + if ($defaultCase instanceof Case_) { + $newStmts = array_merge($newStmts, $defaultCase->stmts); + } + + if ($newStmts === []) { + return null; + } + + return $newStmts; + } +} diff --git a/rules/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector.php b/rules/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector.php index 25e6395ed31..8fcfc88a162 100644 --- a/rules/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector.php +++ b/rules/CodeQuality/Rector/Ternary/ArrayKeyExistsTernaryThenValueToCoalescingRector.php @@ -5,25 +5,30 @@ namespace Rector\CodeQuality\Rector\Ternary; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\BinaryOp\Coalesce; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Ternary; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/f7itn - * * @see \Rector\Tests\CodeQuality\Rector\Ternary\ArrayKeyExistsTernaryThenValueToCoalescingRector\ArrayKeyExistsTernaryThenValueToCoalescingRectorTest */ final class ArrayKeyExistsTernaryThenValueToCoalescingRector extends AbstractRector { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Change array_key_exists() ternary to coalesing', + 'Change array_key_exists() ternary to coalescing', [ new CodeSample( <<<'CODE_SAMPLE' @@ -35,7 +40,7 @@ public function run($values, $keyToMatch) } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -95,12 +100,24 @@ public function refactor(Node $node): ?Node */ private function areArrayKeysExistsArgsMatchingDimFetch(FuncCall $funcCall, ArrayDimFetch $arrayDimFetch): bool { - $keyExpr = $funcCall->args[0]->value; - $valuesExpr = $funcCall->args[1]->value; + $firstArg = $funcCall->args[0]; + if (! $firstArg instanceof Arg) { + return false; + } + + $keyExpr = $firstArg->value; + + $secondArg = $funcCall->args[1]; + if (! $secondArg instanceof Arg) { + return false; + } + + $valuesExpr = $secondArg->value; if (! $this->nodeComparator->areNodesEqual($arrayDimFetch->var, $valuesExpr)) { return false; } + return $this->nodeComparator->areNodesEqual($arrayDimFetch->dim, $keyExpr); } } diff --git a/rules/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector.php b/rules/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector.php new file mode 100644 index 00000000000..263c98546c4 --- /dev/null +++ b/rules/CodeQuality/Rector/Ternary/NumberCompareToMaxFuncCallRector.php @@ -0,0 +1,111 @@ + 100 ? $value : 100; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run($value) + { + return max($value, 100); + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Ternary::class]; + } + + /** + * @param Ternary $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->cond instanceof BinaryOp) { + return null; + } + + $binaryOp = $node->cond; + if (! $this->areIntegersCompared($binaryOp)) { + return null; + } + + if ($binaryOp instanceof Smaller || $binaryOp instanceof SmallerOrEqual) { + if (! $this->nodeComparator->areNodesEqual($binaryOp->left, $node->else)) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($binaryOp->right, $node->if)) { + return null; + } + + return $this->nodeFactory->createFuncCall('max', [$node->if, $node->else]); + } + + if ($binaryOp instanceof Greater || $binaryOp instanceof GreaterOrEqual) { + if (! $this->nodeComparator->areNodesEqual($binaryOp->left, $node->if)) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($binaryOp->right, $node->else)) { + return null; + } + + return $this->nodeFactory->createFuncCall('max', [$node->if, $node->else]); + } + + return null; + } + + private function areIntegersCompared(BinaryOp $binaryOp): bool + { + $leftType = $this->getType($binaryOp->left); + if (! $leftType->isInteger()->yes()) { + return false; + } + + $rightType = $this->getType($binaryOp->right); + return $rightType->isInteger() + ->yes(); + } +} diff --git a/rules/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector.php b/rules/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector.php deleted file mode 100644 index da2a574173d..00000000000 --- a/rules/CodeQuality/Rector/Ternary/SimplifyDuplicatedTernaryRector.php +++ /dev/null @@ -1,80 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Ternary::class]; - } - - /** - * @param Ternary $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->nodeTypeResolver->isStaticType($node->cond, BooleanType::class)) { - return null; - } - - if ($node->if === null) { - return null; - } - if (! $this->valueResolver->isTrue($node->if)) { - return null; - } - if (! $this->valueResolver->isFalse($node->else)) { - return null; - } - - return $node->cond; - } -} diff --git a/rules/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector.php b/rules/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector.php index 15aaf346236..67e6be187a7 100644 --- a/rules/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector.php +++ b/rules/CodeQuality/Rector/Ternary/SimplifyTautologyTernaryRector.php @@ -8,9 +8,9 @@ use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\Ternary; -use Rector\Core\NodeManipulator\BinaryOpManipulator; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeManipulator\BinaryOpManipulator; use Rector\Php71\ValueObject\TwoNodeMatch; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,7 +20,7 @@ final class SimplifyTautologyTernaryRector extends AbstractRector { public function __construct( - private BinaryOpManipulator $binaryOpManipulator + private readonly BinaryOpManipulator $binaryOpManipulator ) { } @@ -61,6 +61,6 @@ public function refactor(Node $node): ?Node return null; } - return $node->if; + return $node->cond instanceof NotIdentical ? $node->if : $node->else; } } diff --git a/rules/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector.php b/rules/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector.php index c9262b5d825..9f1d1c5ce63 100644 --- a/rules/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector.php +++ b/rules/CodeQuality/Rector/Ternary/SwitchNegatedTernaryRector.php @@ -5,10 +5,11 @@ namespace Rector\CodeQuality\Rector\Ternary; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Ternary; -use Rector\Core\PhpParser\Node\Value\TernaryBracketWrapper; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -17,15 +18,10 @@ */ final class SwitchNegatedTernaryRector extends AbstractRector { - public function __construct( - private TernaryBracketWrapper $ternaryBracketWrapper - ) { - } - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Switch negated ternary condition rector', + 'Switch negated ternary condition', [ new CodeSample( <<<'CODE_SAMPLE' @@ -73,15 +69,26 @@ public function refactor(Node $node): ?Node return null; } - if ($node->if === null) { + if (! $node->if instanceof Expr) { return null; } $node->cond = $node->cond->expr; - [$node->if, $node->else] = [$node->else, $node->if]; + $else = clone $node->else; + $if = clone $node->if; + + $node->else = $if; + $node->if = $else; if ($node->if instanceof Ternary) { - $this->ternaryBracketWrapper->wrapWithBracket($node->if); + $ternary = $node->if; + $ternary->setAttribute(AttributeKey::KIND, AttributeKey::WRAPPED_IN_PARENTHESES); + $ternary->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + + if ($node->else instanceof Ternary) { + $ternary = $node->else; + $ternary->setAttribute(AttributeKey::ORIGINAL_NODE, null); } return $node; diff --git a/rules/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector.php b/rules/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector.php new file mode 100644 index 00000000000..068e774798e --- /dev/null +++ b/rules/CodeQuality/Rector/Ternary/TernaryEmptyArrayArrayDimFetchToCoalesceRector.php @@ -0,0 +1,92 @@ +items) ? $this->items[0] : 'default'; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private array $items = []; + + public function run() + { + return $this->items[0] ?? 'default'; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Ternary::class]; + } + + /** + * @param Ternary $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->cond instanceof BooleanNot) { + return null; + } + + $negagedExpr = $node->cond->expr; + if (! $negagedExpr instanceof Empty_) { + return null; + } + + if (! $node->if instanceof ArrayDimFetch) { + return null; + } + + $emptyExprType = $this->getType($negagedExpr->expr); + if (! $emptyExprType->isArray()->yes()) { + return null; + } + + $dimFetchVar = $node->if->var; + if (! $this->nodeComparator->areNodesEqual($negagedExpr->expr, $dimFetchVar)) { + return null; + } + + return new Coalesce($node->if, $node->else); + } +} diff --git a/rules/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector.php b/rules/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector.php new file mode 100644 index 00000000000..9f5d9d0e5b3 --- /dev/null +++ b/rules/CodeQuality/Rector/Ternary/TernaryImplodeToImplodeRector.php @@ -0,0 +1,112 @@ +> + */ + public function getNodeTypes(): array + { + return [Ternary::class]; + } + + /** + * @param Ternary $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->cond instanceof Identical) { + return null; + } + + $identical = $node->cond; + if (! $identical->right instanceof Array_) { + return null; + } + + if ($identical->right->items !== []) { + return null; + } + + if (! $node->if instanceof String_) { + return null; + } + + if ($node->if->value !== '') { + return null; + } + + if (! $node->else instanceof FuncCall) { + return null; + } + + if (! $this->isNames($node->else, ['implode', 'join'])) { + return null; + } + + if ($node->else->isFirstClassCallable()) { + return null; + } + + $function = $node->else; + $secondArg = $function->getArgs()[1] ?? null; + + if (! $secondArg instanceof Arg) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($identical->left, $secondArg->value)) { + return null; + } + + return $node->else; + } +} diff --git a/rules/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector.php b/rules/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector.php index 7bc0f432790..c1e5ecfaa2d 100644 --- a/rules/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector.php +++ b/rules/CodeQuality/Rector/Ternary/UnnecessaryTernaryExpressionRector.php @@ -10,9 +10,9 @@ use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Cast\Bool_; use PhpParser\Node\Expr\Ternary; -use PHPStan\Type\BooleanType; -use Rector\Core\PhpParser\Node\AssignAndBinaryMap; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\AssignAndBinaryMap; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,14 +22,15 @@ final class UnnecessaryTernaryExpressionRector extends AbstractRector { public function __construct( - private AssignAndBinaryMap $assignAndBinaryMap + private readonly AssignAndBinaryMap $assignAndBinaryMap, + private readonly ValueResolver $valueResolver ) { } public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Remove unnecessary ternary expressions.', + 'Remove unnecessary ternary expressions', [new CodeSample('$foo === $bar ? true : false;', '$foo === $bar;')] ); } @@ -47,29 +48,29 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - /** @var Ternary $ternaryExpression */ - $ternaryExpression = $node; - if (! $ternaryExpression->if instanceof Expr) { + if (! $node->if instanceof Expr) { return null; } - $ifExpression = $ternaryExpression->if; + $ifExpression = $node->if; if (! $this->valueResolver->isTrueOrFalse($ifExpression)) { return null; } - $elseExpression = $ternaryExpression->else; + $elseExpression = $node->else; if (! $this->valueResolver->isTrueOrFalse($elseExpression)) { return null; } - $condition = $ternaryExpression->cond; + $condition = $node->cond; if (! $condition instanceof BinaryOp) { return $this->processNonBinaryCondition($ifExpression, $elseExpression, $condition); } + if ($this->valueResolver->isNull($ifExpression)) { return null; } + if ($this->valueResolver->isNull($elseExpression)) { return null; } @@ -94,18 +95,22 @@ private function processNonBinaryCondition(Expr $ifExpression, Expr $elseExpress if ($this->valueResolver->isTrue($ifExpression) && $this->valueResolver->isFalse($elseExpression)) { return $this->processTrueIfExpressionWithFalseElseExpression($condition); } + if (! $this->valueResolver->isFalse($ifExpression)) { return null; } + if (! $this->valueResolver->isTrue($elseExpression)) { return null; } + return $this->processFalseIfExpressionWithTrueElseExpression($condition); } private function processTrueIfExpressionWithFalseElseExpression(Expr $expr): Expr { - if ($this->nodeTypeResolver->isStaticType($expr, BooleanType::class)) { + $exprType = $this->getType($expr); + if ($exprType->isBoolean()->yes()) { return $expr; } @@ -115,14 +120,16 @@ private function processTrueIfExpressionWithFalseElseExpression(Expr $expr): Exp private function processFalseIfExpressionWithTrueElseExpression(Expr $expr): Expr { if ($expr instanceof BooleanNot) { - if ($this->nodeTypeResolver->isStaticType($expr->expr, BooleanType::class)) { + $negatedExprType = $this->getType($expr->expr); + if ($negatedExprType->isBoolean()->yes()) { return $expr->expr; } return new Bool_($expr->expr); } - if ($this->nodeTypeResolver->isStaticType($expr, BooleanType::class)) { + $exprType = $this->getType($expr); + if ($exprType->isBoolean()->yes()) { return new BooleanNot($expr); } diff --git a/rules/CodeQuality/TypeResolver/ArrayDimFetchTypeResolver.php b/rules/CodeQuality/TypeResolver/ArrayDimFetchTypeResolver.php index aacce8f6a8d..8f5938d6c02 100644 --- a/rules/CodeQuality/TypeResolver/ArrayDimFetchTypeResolver.php +++ b/rules/CodeQuality/TypeResolver/ArrayDimFetchTypeResolver.php @@ -4,43 +4,33 @@ namespace Rector\CodeQuality\TypeResolver; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PHPStan\Type\ArrayType; use PHPStan\Type\MixedType; use PHPStan\Type\Type; -use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; -final class ArrayDimFetchTypeResolver +final readonly class ArrayDimFetchTypeResolver { public function __construct( private NodeTypeResolver $nodeTypeResolver ) { } - public function resolve(ArrayDimFetch $arrayDimFetch): ArrayType + public function resolve(ArrayDimFetch $arrayDimFetch, Assign $assign): ArrayType { $keyStaticType = $this->resolveDimType($arrayDimFetch); - $valueStaticType = $this->resolveValueStaticType($arrayDimFetch); + $valueStaticType = $this->nodeTypeResolver->getType($assign->expr); return new ArrayType($keyStaticType, $valueStaticType); } private function resolveDimType(ArrayDimFetch $arrayDimFetch): Type { - if ($arrayDimFetch->dim !== null) { - return $this->nodeTypeResolver->getStaticType($arrayDimFetch->dim); - } - - return new MixedType(); - } - - private function resolveValueStaticType(ArrayDimFetch $arrayDimFetch): Type - { - $parentParent = $arrayDimFetch->getAttribute(AttributeKey::PARENT_NODE); - if ($parentParent instanceof Assign) { - return $this->nodeTypeResolver->getStaticType($parentParent->expr); + if ($arrayDimFetch->dim instanceof Expr) { + return $this->nodeTypeResolver->getType($arrayDimFetch->dim); } return new MixedType(); diff --git a/rules/CodeQuality/TypeResolver/AssignVariableTypeResolver.php b/rules/CodeQuality/TypeResolver/AssignVariableTypeResolver.php index c37433b707c..87a978e0b48 100644 --- a/rules/CodeQuality/TypeResolver/AssignVariableTypeResolver.php +++ b/rules/CodeQuality/TypeResolver/AssignVariableTypeResolver.php @@ -9,7 +9,7 @@ use PHPStan\Type\UnionType; use Rector\NodeTypeResolver\NodeTypeResolver; -final class AssignVariableTypeResolver +final readonly class AssignVariableTypeResolver { public function __construct( private NodeTypeResolver $nodeTypeResolver @@ -18,13 +18,11 @@ public function __construct( public function resolve(Assign $assign): Type { - $variableType = $this->nodeTypeResolver->resolve($assign->var); - $exprType = $this->nodeTypeResolver->resolve($assign->expr); - + $exprType = $this->nodeTypeResolver->getType($assign->expr); if ($exprType instanceof UnionType) { - $variableType = $exprType; + return $exprType; } - return $variableType; + return $this->nodeTypeResolver->getType($assign->var); } } diff --git a/rules/CodeQuality/ValueObject/ComparedExprAndValueExpr.php b/rules/CodeQuality/ValueObject/ComparedExprAndValueExpr.php new file mode 100644 index 00000000000..50ef45ae862 --- /dev/null +++ b/rules/CodeQuality/ValueObject/ComparedExprAndValueExpr.php @@ -0,0 +1,26 @@ +comparedExpr; + } + + public function getValueExpr(): Expr + { + return $this->valueExpr; + } +} diff --git a/rules/CodeQuality/ValueObject/DefinedPropertyWithType.php b/rules/CodeQuality/ValueObject/DefinedPropertyWithType.php new file mode 100644 index 00000000000..7105d8089d9 --- /dev/null +++ b/rules/CodeQuality/ValueObject/DefinedPropertyWithType.php @@ -0,0 +1,32 @@ +propertyName; + } + + public function getType(): Type + { + return $this->type; + } + + public function getDefinedInMethodName(): ?string + { + return $this->definedInMethodName; + } +} diff --git a/rules/CodeQuality/ValueObject/KeyAndExpr.php b/rules/CodeQuality/ValueObject/KeyAndExpr.php new file mode 100644 index 00000000000..d1d9a79ba3b --- /dev/null +++ b/rules/CodeQuality/ValueObject/KeyAndExpr.php @@ -0,0 +1,39 @@ +keyExpr; + } + + public function getExpr(): Expr + { + return $this->expr; + } + + /** + * @return Comment[] + */ + public function getComments(): array + { + return $this->comments; + } +} diff --git a/rules/CodeQualityStrict/NodeFactory/ClassConstFetchFactory.php b/rules/CodeQualityStrict/NodeFactory/ClassConstFetchFactory.php deleted file mode 100644 index a09c9754559..00000000000 --- a/rules/CodeQualityStrict/NodeFactory/ClassConstFetchFactory.php +++ /dev/null @@ -1,41 +0,0 @@ -getFullyQualifiedName()), 'class'); - } elseif ($type instanceof ObjectType) { - $classConstTypes[] = new ClassConstFetch(new FullyQualified($type->getClassName()), 'class'); - } - - if ($type instanceof UnionType) { - foreach ($type->getTypes() as $unionedType) { - if (! $unionedType instanceof TypeWithClassName) { - throw new ShouldNotHappenException(); - } - - $classConstTypes[] = new ClassConstFetch(new FullyQualified($unionedType->getClassName()), 'class'); - } - } - - return $classConstTypes; - } -} diff --git a/rules/CodeQualityStrict/TypeAnalyzer/SubTypeAnalyzer.php b/rules/CodeQualityStrict/TypeAnalyzer/SubTypeAnalyzer.php deleted file mode 100644 index 72e52fc5274..00000000000 --- a/rules/CodeQualityStrict/TypeAnalyzer/SubTypeAnalyzer.php +++ /dev/null @@ -1,30 +0,0 @@ -getClassName() === 'stdClass') { - return true; - } - - return $mainType->isSuperTypeOf($checkedType) - ->yes(); - } -} diff --git a/rules/CodingStyle/Application/UseImportsAdder.php b/rules/CodingStyle/Application/UseImportsAdder.php index cf1873813a4..1e1cc7c6529 100644 --- a/rules/CodingStyle/Application/UseImportsAdder.php +++ b/rules/CodingStyle/Application/UseImportsAdder.php @@ -4,19 +4,21 @@ namespace Rector\CodingStyle\Application; -use Nette\Utils\Strings; +use PhpParser\Node\Name; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Declare_; +use PhpParser\Node\Stmt\GroupUse; use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\Nop; use PhpParser\Node\Stmt\Use_; -use PHPStan\Type\ObjectType; use Rector\CodingStyle\ClassNameImport\UsedImportsResolver; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; +use Rector\PhpParser\Node\FileNode; use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; -final class UseImportsAdder +final readonly class UseImportsAdder { public function __construct( private UsedImportsResolver $usedImportsResolver, @@ -27,70 +29,165 @@ public function __construct( /** * @param Stmt[] $stmts * @param array $useImportTypes + * @param array $constantUseImportTypes * @param array $functionUseImportTypes - * @return Stmt[] */ - public function addImportsToStmts(array $stmts, array $useImportTypes, array $functionUseImportTypes): array - { - $existingUseImportTypes = $this->usedImportsResolver->resolveForStmts($stmts); - $existingFunctionUseImports = $this->usedImportsResolver->resolveFunctionImportsForStmts($stmts); + public function addImportsToStmts( + FileNode $fileNode, + array $stmts, + array $useImportTypes, + array $constantUseImportTypes, + array $functionUseImportTypes + ): bool { + $usedImports = $this->usedImportsResolver->resolveForStmts($stmts); + $existingUseImportTypes = $usedImports->getUseImports(); + $existingConstantUseImports = $usedImports->getConstantImports(); + $existingFunctionUseImports = $usedImports->getFunctionImports(); $useImportTypes = $this->diffFullyQualifiedObjectTypes($useImportTypes, $existingUseImportTypes); + + $constantUseImportTypes = $this->diffFullyQualifiedObjectTypes( + $constantUseImportTypes, + $existingConstantUseImports + ); + $functionUseImportTypes = $this->diffFullyQualifiedObjectTypes( $functionUseImportTypes, $existingFunctionUseImports ); - $newUses = $this->createUses($useImportTypes, $functionUseImportTypes, null); + $newUses = $this->createUses($useImportTypes, $constantUseImportTypes, $functionUseImportTypes, null); if ($newUses === []) { - return $stmts; + return false; } + $stmts = array_values(array_filter($stmts, static function (Stmt $stmt): bool { + if (! $stmt instanceof Use_) { + return true; + } + + return $stmt->uses !== []; + })); + // place after declare strict_types foreach ($stmts as $key => $stmt) { - if ($stmt instanceof Declare_) { - if (isset($stmts[$key + 1]) && $stmts[$key + 1] instanceof Use_) { - $nodesToAdd = $newUses; - } else { - // add extra space, if there are no new use imports to be added - $nodesToAdd = array_merge([new Nop()], $newUses); - } + // maybe just added a space + if ($stmt instanceof Nop) { + continue; + } + + // when we found a non-declare, directly stop + if (! $stmt instanceof Declare_) { + break; + } + + $nodesToAdd = array_merge([new Nop()], $newUses); - array_splice($stmts, $key + 1, 0, $nodesToAdd); + $this->mirrorUseComments($stmts, $newUses, $key + 1); - return $stmts; + // remove space before next use tweak + if (isset($stmts[$key + 1]) && ($stmts[$key + 1] instanceof Use_ || $stmts[$key + 1] instanceof GroupUse)) { + $stmts[$key + 1]->setAttribute(AttributeKey::ORIGINAL_NODE, null); } + + array_splice($stmts, $key + 1, 0, $nodesToAdd); + + $fileNode->stmts = $stmts; + $fileNode->stmts = array_values($fileNode->stmts); + + return true; } + $this->mirrorUseComments($stmts, $newUses); + // make use stmts first - return array_merge($newUses, $stmts); + $fileNode->stmts = array_merge($newUses, $this->resolveInsertNop($fileNode), $stmts); + $fileNode->stmts = array_values($fileNode->stmts); + + return true; } /** * @param FullyQualifiedObjectType[] $useImportTypes + * @param FullyQualifiedObjectType[] $constantUseImportTypes * @param FullyQualifiedObjectType[] $functionUseImportTypes */ public function addImportsToNamespace( Namespace_ $namespace, array $useImportTypes, + array $constantUseImportTypes, array $functionUseImportTypes - ): void { + ): bool { $namespaceName = $this->getNamespaceName($namespace); - $existingUseImportTypes = $this->usedImportsResolver->resolveForNode($namespace); - $existingFunctionUseImportTypes = $this->usedImportsResolver->resolveFunctionImportsForStmts($namespace->stmts); + $existingUsedImports = $this->usedImportsResolver->resolveForStmts($namespace->stmts); + $existingUseImportTypes = $existingUsedImports->getUseImports(); + $existingConstantUseImportTypes = $existingUsedImports->getConstantImports(); + $existingFunctionUseImportTypes = $existingUsedImports->getFunctionImports(); $existingUseImportTypes = $this->typeFactory->uniquateTypes($existingUseImportTypes); $useImportTypes = $this->diffFullyQualifiedObjectTypes($useImportTypes, $existingUseImportTypes); + $constantUseImportTypes = $this->diffFullyQualifiedObjectTypes( + $constantUseImportTypes, + $existingConstantUseImportTypes + ); + $functionUseImportTypes = $this->diffFullyQualifiedObjectTypes( $functionUseImportTypes, $existingFunctionUseImportTypes ); - $newUses = $this->createUses($useImportTypes, $functionUseImportTypes, $namespaceName); - $namespace->stmts = array_merge($newUses, $namespace->stmts); + $newUses = $this->createUses($useImportTypes, $constantUseImportTypes, $functionUseImportTypes, $namespaceName); + + if ($newUses === []) { + return false; + } + + $this->mirrorUseComments($namespace->stmts, $newUses); + + $namespace->stmts = array_merge($newUses, $this->resolveInsertNop($namespace), $namespace->stmts); + $namespace->stmts = array_values($namespace->stmts); + + return true; + } + + /** + * @return Nop[] + */ + private function resolveInsertNop(FileNode|Namespace_ $namespace): array + { + $currentStmt = $namespace->stmts[0] ?? null; + if (! $currentStmt instanceof Stmt || $currentStmt instanceof Use_ || $currentStmt instanceof GroupUse) { + return []; + } + + return [new Nop()]; + } + + /** + * @param Stmt[] $stmts + * @param Use_[] $newUses + */ + private function mirrorUseComments(array $stmts, array $newUses, int $indexStmt = 0): void + { + if ($stmts === []) { + return; + } + + if (isset($stmts[$indexStmt]) && $stmts[$indexStmt] instanceof Use_) { + $comments = (array) $stmts[$indexStmt]->getAttribute(AttributeKey::COMMENTS); + + if ($comments !== []) { + $newUses[0]->setAttribute( + AttributeKey::COMMENTS, + $stmts[$indexStmt]->getAttribute(AttributeKey::COMMENTS) + ); + + $stmts[$indexStmt]->setAttribute(AttributeKey::COMMENTS, []); + } + } } /** @@ -113,28 +210,41 @@ private function diffFullyQualifiedObjectTypes(array $mainTypes, array $typesToR /** * @param array $useImportTypes + * @param array $constantUseImportTypes * @param array $functionUseImportTypes * @return Use_[] */ - private function createUses(array $useImportTypes, array $functionUseImportTypes, ?string $namespaceName): array - { + private function createUses( + array $useImportTypes, + array $constantUseImportTypes, + array $functionUseImportTypes, + ?string $namespaceName + ): array { $newUses = []; - foreach ($useImportTypes as $useImportType) { - if ($namespaceName !== null && $this->isCurrentNamespace($namespaceName, $useImportType)) { - continue; - } - // already imported in previous cycle - $newUses[] = $useImportType->getUseNode(); - } + /** @var array> $importsMapping */ + $importsMapping = [ + Use_::TYPE_NORMAL => $useImportTypes, + Use_::TYPE_CONSTANT => $constantUseImportTypes, + Use_::TYPE_FUNCTION => $functionUseImportTypes, + ]; - foreach ($functionUseImportTypes as $functionUseImportType) { - if ($namespaceName !== null && $this->isCurrentNamespace($namespaceName, $functionUseImportType)) { - continue; - } + foreach ($importsMapping as $type => $importTypes) { + /** @var AliasedObjectType|FullyQualifiedObjectType $importType */ + foreach ($importTypes as $importType) { + if ($namespaceName !== null && $this->isCurrentNamespace($namespaceName, $importType)) { + continue; + } + + if ($namespaceName === null + && $importType instanceof FullyQualifiedObjectType + && substr_count(ltrim($importType->getClassName(), '\\'), '\\') === 0) { + continue; + } - // already imported in previous cycle - $newUses[] = $functionUseImportType->getFunctionUseNode(); + // already imported in previous cycle + $newUses[] = $importType->getUseNode($type); + } } return $newUses; @@ -142,20 +252,23 @@ private function createUses(array $useImportTypes, array $functionUseImportTypes private function getNamespaceName(Namespace_ $namespace): ?string { - if ($namespace->name === null) { + if (! $namespace->name instanceof Name) { return null; } return $namespace->name->toString(); } - private function isCurrentNamespace(string $namespaceName, ObjectType $objectType): bool - { - $afterCurrentNamespace = Strings::after($objectType->getClassName(), $namespaceName . '\\'); - if (! $afterCurrentNamespace) { + private function isCurrentNamespace( + string $namespaceName, + AliasedObjectType|FullyQualifiedObjectType $objectType + ): bool { + $className = $objectType->getClassName(); + + if (! str_starts_with($className, $namespaceName . '\\')) { return false; } - return ! \str_contains($afterCurrentNamespace, '\\'); + return $namespaceName . '\\' . $objectType->getShortName() === $className; } } diff --git a/rules/CodingStyle/Application/UseImportsRemover.php b/rules/CodingStyle/Application/UseImportsRemover.php index 0b69be0fcfd..904cf358e3a 100644 --- a/rules/CodingStyle/Application/UseImportsRemover.php +++ b/rules/CodingStyle/Application/UseImportsRemover.php @@ -4,69 +4,71 @@ namespace Rector\CodingStyle\Application; -use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\Use_; +use Rector\PhpParser\Node\FileNode; +use Rector\Renaming\Collector\RenamedNameCollector; -final class UseImportsRemover +final readonly class UseImportsRemover { + public function __construct( + private RenamedNameCollector $renamedNameCollector + ) { + } + /** - * @param Stmt[] $stmts - * @param string[] $removedShortUses - * @return Stmt[] + * @param string[] $removedUses */ - public function removeImportsFromStmts(array $stmts, array $removedShortUses): array + public function removeImportsFromStmts(FileNode|Namespace_ $node, array $removedUses): bool { - foreach ($stmts as $stmtKey => $stmt) { + $hasRemoved = false; + foreach ($node->stmts as $key => $stmt) { if (! $stmt instanceof Use_) { continue; } - $this->removeUseFromUse($removedShortUses, $stmt); + if ($this->removeUseFromUse($removedUses, $stmt)) { + $node->stmts[$key] = $stmt; + $hasRemoved = true; + } - // nothing left → remove + // remove empty uses if ($stmt->uses === []) { - unset($stmts[$stmtKey]); + unset($node->stmts[$key]); } } - return $stmts; + if ($hasRemoved) { + $node->stmts = array_values($node->stmts); + } + + return $hasRemoved; } /** - * @param string[] $removedShortUses + * @param string[] $removedUses */ - public function removeImportsFromNamespace(Namespace_ $namespace, array $removedShortUses): void + private function removeUseFromUse(array $removedUses, Use_ $use): bool { - foreach ($namespace->stmts as $namespaceKey => $stmt) { - if (! $stmt instanceof Use_) { + $hasChanged = false; + foreach ($use->uses as $usesKey => $useUse) { + $useName = $useUse->name->toString(); + if (! in_array($useName, $removedUses, true)) { continue; } - $this->removeUseFromUse($removedShortUses, $stmt); - - // nothing left → remove - if ($stmt->uses === []) { - unset($namespace->stmts[$namespaceKey]); + if (! $this->renamedNameCollector->has($useName)) { + continue; } - } - } - /** - * @param string[] $removedShortUses - */ - private function removeUseFromUse(array $removedShortUses, Use_ $use): void - { - foreach ($use->uses as $usesKey => $useUse) { - foreach ($removedShortUses as $removedShortUse) { - if ($useUse->name->getLast() === $removedShortUse) { - unset($use->uses[$usesKey]); - } + unset($use->uses[$usesKey]); + $hasChanged = true; + } - if ($useUse->name->toString() === $removedShortUse) { - unset($use->uses[$usesKey]); - } - } + if ($hasChanged) { + $use->uses = array_values($use->uses); } + + return $hasChanged; } } diff --git a/rules/CodingStyle/ClassNameImport/AliasUsesResolver.php b/rules/CodingStyle/ClassNameImport/AliasUsesResolver.php index b642fa0c2f6..2419c381f58 100644 --- a/rules/CodingStyle/ClassNameImport/AliasUsesResolver.php +++ b/rules/CodingStyle/ClassNameImport/AliasUsesResolver.php @@ -5,46 +5,61 @@ namespace Rector\CodingStyle\ClassNameImport; use PhpParser\Node; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Namespace_; -use PhpParser\Node\Stmt\UseUse; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use PhpParser\Node\Stmt\Use_; +use PhpParser\Node\UseItem; +use Rector\PhpParser\Node\FileNode; -final class AliasUsesResolver +final readonly class AliasUsesResolver { public function __construct( - private UseImportsTraverser $useImportsTraverser, - private BetterNodeFinder $betterNodeFinder + private UseImportsTraverser $useImportsTraverser ) { } /** + * @param Stmt[] $stmts * @return string[] */ - public function resolveForNode(Node $node): array + public function resolveFromNode(Node $node, array $stmts): array { - if (! $node instanceof Namespace_) { - $node = $this->betterNodeFinder->findParentType($node, Namespace_::class); - } + if (! $node instanceof Namespace_ && ! $node instanceof FileNode) { + /** @var Namespace_[]|FileNode[] $namespaces */ + $namespaces = array_filter( + $stmts, + static fn (Stmt $stmt): bool => $stmt instanceof Namespace_ || $stmt instanceof FileNode + ); + if (count($namespaces) !== 1) { + return []; + } - if ($node instanceof Namespace_) { - return $this->resolveForNamespace($node); + $node = current($namespaces); } - return []; + return $this->resolveFromStmts($node->stmts); } /** + * @param Stmt[] $stmts * @return string[] */ - private function resolveForNamespace(Namespace_ $namespace): array + public function resolveFromStmts(array $stmts): array { $aliasedUses = []; - $this->useImportsTraverser->traverserStmts($namespace->stmts, function ( - UseUse $useUse, + /** @param Use_::TYPE_* $useType */ + $this->useImportsTraverser->traverserStmts($stmts, static function ( + int $useType, + UseItem $useItem, string $name ) use (&$aliasedUses): void { - if ($useUse->alias === null) { + if ($useType !== Use_::TYPE_NORMAL) { + return; + } + + if (! $useItem->alias instanceof Identifier) { return; } diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/AliasClassNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/AliasClassNameImportSkipVoter.php index 1a9d32da4b3..c774a0c4e07 100644 --- a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/AliasClassNameImportSkipVoter.php +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/AliasClassNameImportSkipVoter.php @@ -7,8 +7,8 @@ use PhpParser\Node; use Rector\CodingStyle\ClassNameImport\AliasUsesResolver; use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; -use Rector\Core\ValueObject\Application\File; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +use Rector\ValueObject\Application\File; /** * Prevents adding: @@ -19,7 +19,7 @@ * * use App\Something as SomeClass; */ -final class AliasClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface +final readonly class AliasClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface { public function __construct( private AliasUsesResolver $aliasUsesResolver @@ -28,13 +28,19 @@ public function __construct( public function shouldSkip(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType, Node $node): bool { - $aliasedUses = $this->aliasUsesResolver->resolveForNode($node); + $aliasedUses = $this->aliasUsesResolver->resolveFromNode($node, $file->getNewStmts()); + $longNameLowered = strtolower($fullyQualifiedObjectType->getClassName()); + $shortNameLowered = $fullyQualifiedObjectType->getShortNameLowered(); foreach ($aliasedUses as $aliasedUse) { $aliasedUseLowered = strtolower($aliasedUse); // its aliased, we cannot just rename it - if (\str_ends_with($aliasedUseLowered, '\\' . $fullyQualifiedObjectType->getShortNameLowered())) { + if (\str_ends_with($aliasedUseLowered, '\\' . $shortNameLowered)) { + return true; + } + + if ($aliasedUseLowered === $shortNameLowered && $longNameLowered === $shortNameLowered) { return true; } } diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ClassLikeNameClassNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ClassLikeNameClassNameImportSkipVoter.php index ff215d96144..d7e9fd80045 100644 --- a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ClassLikeNameClassNameImportSkipVoter.php +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ClassLikeNameClassNameImportSkipVoter.php @@ -5,10 +5,13 @@ namespace Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter; use PhpParser\Node; +use PHPStan\Analyser\Scope; +use Rector\CodingStyle\ClassNameImport\NamespaceBeforeClassNameResolver; use Rector\CodingStyle\ClassNameImport\ShortNameResolver; use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; -use Rector\Core\ValueObject\Application\File; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +use Rector\ValueObject\Application\File; /** * Prevents adding: @@ -19,19 +22,45 @@ * * class SomeClass {} */ -final class ClassLikeNameClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface +final readonly class ClassLikeNameClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface { public function __construct( - private ShortNameResolver $shortNameResolver + private ShortNameResolver $shortNameResolver, + private NamespaceBeforeClassNameResolver $namespaceBeforeClassNameResolver ) { } public function shouldSkip(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType, Node $node): bool { - $classLikeNames = $this->shortNameResolver->resolveShortClassLikeNamesForNode($node); + $classLikeNames = $this->shortNameResolver->resolveShortClassLikeNames($file); + if ($classLikeNames === []) { + return false; + } + + /** + * Note: Don't use ScopeFetcher::fetch() on Name instance, + * Scope can be null on Name + * This is part of ScopeAnalyzer::NON_REFRESHABLE_NODES + * @see https://github.com/rectorphp/rector-src/blob/9929af7c0179929b4fde6915cb7a06c3141dde6c/src/NodeAnalyzer/ScopeAnalyzer.php#L17 + */ + $scope = $node->getAttribute(AttributeKey::SCOPE); + $namespace = $scope instanceof Scope ? $scope->getNamespace() : null; + $namespace = strtolower((string) $namespace); + + $shortNameLowered = $fullyQualifiedObjectType->getShortNameLowered(); + $subClassName = $this->namespaceBeforeClassNameResolver->resolve($fullyQualifiedObjectType); + $fullyQualifiedObjectTypeNamespace = strtolower($subClassName); foreach ($classLikeNames as $classLikeName) { - if (strtolower($classLikeName) === $fullyQualifiedObjectType->getShortNameLowered()) { + if (strtolower($classLikeName) !== $shortNameLowered) { + continue; + } + + if ($namespace === '') { + return true; + } + + if ($namespace !== $fullyQualifiedObjectTypeNamespace) { return true; } } diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/FullyQualifiedNameClassNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/FullyQualifiedNameClassNameImportSkipVoter.php index 145348f74c2..3cc572e64df 100644 --- a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/FullyQualifiedNameClassNameImportSkipVoter.php +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/FullyQualifiedNameClassNameImportSkipVoter.php @@ -4,11 +4,12 @@ namespace Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter; +use Nette\Utils\Strings; use PhpParser\Node; use Rector\CodingStyle\ClassNameImport\ShortNameResolver; use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; -use Rector\Core\ValueObject\Application\File; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +use Rector\ValueObject\Application\File; /** * Prevents adding: @@ -19,27 +20,41 @@ * * SomeClass::callThis(); */ -final class FullyQualifiedNameClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface +final readonly class FullyQualifiedNameClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface { public function __construct( - private ShortNameResolver $shortNameResolver, + private ShortNameResolver $shortNameResolver ) { } public function shouldSkip(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType, Node $node): bool { // "new X" or "X::static()" - $shortNamesToFullyQualifiedNames = $this->shortNameResolver->resolveForNode($file); + /** @var array $shortNamesToFullyQualifiedNames */ + $shortNamesToFullyQualifiedNames = $this->shortNameResolver->resolveFromFile($file); + $fullyQualifiedObjectTypeShortName = $fullyQualifiedObjectType->getShortName(); + $className = $fullyQualifiedObjectType->getClassName(); foreach ($shortNamesToFullyQualifiedNames as $shortName => $fullyQualifiedName) { - $shortNameLowered = strtolower($shortName); - if ($fullyQualifiedObjectType->getShortNameLowered() !== $shortNameLowered) { + if ($fullyQualifiedObjectTypeShortName !== $shortName) { + $shortName = $this->cleanShortName($shortName); + } + + if ($fullyQualifiedObjectTypeShortName !== $shortName) { continue; } - return $fullyQualifiedObjectType->getClassNameLowered() !== strtolower($fullyQualifiedName); + $fullyQualifiedName = ltrim($fullyQualifiedName, '\\'); + return $className !== $fullyQualifiedName; } return false; } + + private function cleanShortName(string $shortName): string + { + return str_starts_with($shortName, '\\') + ? ltrim((string) Strings::after($shortName, '\\', -1)) + : $shortName; + } } diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/OriginalNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/OriginalNameImportSkipVoter.php new file mode 100644 index 00000000000..e8569ab0572 --- /dev/null +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/OriginalNameImportSkipVoter.php @@ -0,0 +1,33 @@ +toCodeString(), '\\') === 1) { + return false; + } + + // verify long name, as short name verify may conflict + // see test PR: https://github.com/rectorphp/rector-src/pull/6208 + // ref https://3v4l.org/21H5j vs https://3v4l.org/GIHSB + $originalName = $node->getAttribute(AttributeKey::ORIGINAL_NAME); + return $originalName instanceof Name && $originalName->getLast() === $originalName->toString(); + } +} diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ReservedClassNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ReservedClassNameImportSkipVoter.php new file mode 100644 index 00000000000..7e618f832fe --- /dev/null +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ReservedClassNameImportSkipVoter.php @@ -0,0 +1,43 @@ +getShortNameLowered(); + + return in_array($shortName, self::RESERVED_CLASS_NAMES, true); + } +} diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ShortClassImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ShortClassImportSkipVoter.php new file mode 100644 index 00000000000..26f17091fbe --- /dev/null +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ShortClassImportSkipVoter.php @@ -0,0 +1,26 @@ +getClassName(), '\\'); + + if (substr_count($className, '\\') === 0) { + return ! SimpleParameterProvider::provideBoolParameter(Option::IMPORT_SHORT_CLASSES); + } + + return false; + } +} diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/UsesClassNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/UsesClassNameImportSkipVoter.php index d248ef23818..5c20de92ddd 100644 --- a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/UsesClassNameImportSkipVoter.php +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/UsesClassNameImportSkipVoter.php @@ -6,10 +6,9 @@ use PhpParser\Node; use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; -use Rector\Core\Configuration\RenamedClassesDataCollector; -use Rector\Core\ValueObject\Application\File; use Rector\PostRector\Collector\UseNodesToAddCollector; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +use Rector\ValueObject\Application\File; /** * This prevents importing: @@ -18,24 +17,18 @@ * if there is already: * - use App\Another\Product */ -final class UsesClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface +final readonly class UsesClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface { public function __construct( - private UseNodesToAddCollector $useNodesToAddCollector, - private RenamedClassesDataCollector $renamedClassesDataCollector + private UseNodesToAddCollector $useNodesToAddCollector ) { } public function shouldSkip(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType, Node $node): bool { - $useImportTypes = $this->useNodesToAddCollector->getUseImportTypesByNode($file, $node); + $useImportTypes = $this->useNodesToAddCollector->getUseImportTypesByNode($file); foreach ($useImportTypes as $useImportType) { - // if the class is renamed, the use import is no longer blocker - if ($this->renamedClassesDataCollector->hasOldClass($useImportType->getClassName())) { - continue; - } - if (! $useImportType->equals($fullyQualifiedObjectType) && $useImportType->areShortNamesEqual( $fullyQualifiedObjectType )) { diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipper.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipper.php index 963837b0df0..5812d47fa10 100644 --- a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipper.php +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipper.php @@ -5,22 +5,25 @@ namespace Rector\CodingStyle\ClassNameImport; use PhpParser\Node; -use PhpParser\Node\Name; +use PhpParser\Node\Identifier; +use PhpParser\Node\Name\FullyQualified; +use PhpParser\Node\Stmt\GroupUse; use PhpParser\Node\Stmt\Use_; -use PhpParser\Node\Stmt\UseUse; +use PhpParser\Node\UseItem; use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; -use Rector\Core\Configuration\RenamedClassesDataCollector; -use Rector\Core\ValueObject\Application\File; +use Rector\Naming\Naming\UseImportsResolver; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +use Rector\ValueObject\Application\File; -final class ClassNameImportSkipper +final readonly class ClassNameImportSkipper { /** * @param ClassNameImportSkipVoterInterface[] $classNameImportSkipVoters */ public function __construct( - private array $classNameImportSkipVoters, - private RenamedClassesDataCollector $renamedClassesDataCollector + private iterable $classNameImportSkipVoters, + private UseImportsResolver $useImportsResolver, ) { } @@ -39,72 +42,59 @@ public function shouldSkipNameForFullyQualifiedObjectType( } /** - * @param Use_[] $existingUses + * @param array $uses */ - public function isShortNameInUseStatement(Name $name, array $existingUses): bool + public function shouldSkipName(FullyQualified $fullyQualified, array $uses): bool { - $longName = $name->toString(); - if (\str_contains($longName, '\\')) { - return false; + if (substr_count($fullyQualified->toCodeString(), '\\') === 1) { + return $this->isFunctionOrConstantImport($fullyQualified); } - return $this->isFoundInUse($name, $existingUses); - } + $stringName = $fullyQualified->toString(); + $lastUseName = $fullyQualified->getLast(); + $nameLastName = strtolower($lastUseName); - /** - * @param Use_[] $uses - */ - public function isAlreadyImported(Name $name, array $uses): bool - { foreach ($uses as $use) { - foreach ($use->uses as $useUse) { - if ($useUse->name->toString() === $name->toString()) { - return true; - } - } - } + $prefix = $this->useImportsResolver->resolvePrefix($use); + $useName = $prefix . $stringName; - return false; - } - - /** - * @param Use_[] $uses - */ - public function isFoundInUse(Name $name, array $uses): bool - { - foreach ($uses as $use) { foreach ($use->uses as $useUse) { - if ($useUse->name->getLast() !== $name->getLast()) { + $useUseLastName = strtolower($useUse->name->getLast()); + + if ($useUseLastName !== $nameLastName) { continue; } - if ($this->isJustRenamedClass($name, $useUse)) { - continue; + if ($this->isConflictedShortNameInUse($useUse, $useName, $lastUseName, $stringName)) { + return true; } - return true; + return $prefix . $useUse->name->toString() !== $stringName; } } return false; } - private function isJustRenamedClass(Name $name, UseUse $useUse): bool + private function isFunctionOrConstantImport(FullyQualified $fullyQualified): bool { - // is in renamed classes? skip it - foreach ($this->renamedClassesDataCollector->getOldToNewClasses() as $oldClass => $newClass) { - // is class being renamed in use imports? - if ($name->toString() !== $newClass) { - continue; - } + if ($fullyQualified->getAttribute(AttributeKey::IS_CONSTFETCH_NAME) === true) { + return true; + } - if ($useUse->name->toString() !== $oldClass) { - continue; - } + return $fullyQualified->getAttribute(AttributeKey::IS_FUNCCALL_NAME) === true; + } + private function isConflictedShortNameInUse( + UseItem $useItem, + string $useName, + string $lastUseName, + string $stringName + ): bool { + if (! $useItem->alias instanceof Identifier && $useName !== $stringName && $lastUseName === $stringName) { return true; } - return false; + return $useItem->alias instanceof Identifier && $useItem->alias->toString() === $stringName; } } diff --git a/rules/CodingStyle/ClassNameImport/NamespaceBeforeClassNameResolver.php b/rules/CodingStyle/ClassNameImport/NamespaceBeforeClassNameResolver.php new file mode 100644 index 00000000000..5459b90210e --- /dev/null +++ b/rules/CodingStyle/ClassNameImport/NamespaceBeforeClassNameResolver.php @@ -0,0 +1,20 @@ +getClassName(); + $shortName = $fullyQualifiedObjectType->getShortName(); + + return $className === $shortName + ? '' + : substr($className, 0, -strlen($shortName) - 1); + } +} diff --git a/rules/CodingStyle/ClassNameImport/ShortNameResolver.php b/rules/CodingStyle/ClassNameImport/ShortNameResolver.php index cd5206cba37..7b53fab2602 100644 --- a/rules/CodingStyle/ClassNameImport/ShortNameResolver.php +++ b/rules/CodingStyle/ClassNameImport/ShortNameResolver.php @@ -4,77 +4,55 @@ namespace Rector\CodingStyle\ClassNameImport; -use Nette\Utils\Reflection; -use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ClassLike; -use PhpParser\Node\Stmt\Namespace_; -use PhpParser\NodeFinder; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; -use PHPStan\Reflection\ReflectionProvider; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\CodingStyle\NodeAnalyzer\UseImportNameMatcher; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\ValueObject\Application\File; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; -use ReflectionClass; -use Symfony\Contracts\Service\Attribute\Required; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; -use Symplify\SimplePhpDocParser\PhpDocNodeTraverser; - +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpDocParser\PhpDocParser\PhpDocNodeTraverser; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\ValueObject\Application\File; + +/** + * @see \Rector\Tests\CodingStyle\ClassNameImport\ShortNameResolver\ShortNameResolverTest + */ final class ShortNameResolver { /** - * @var string - * @see https://regex101.com/r/KphLd2/1 - */ - private const BIG_LETTER_START_REGEX = '#^[A-Z]#'; - - /** - * @var string[][] + * @var array */ private array $shortNamesByFilePath = []; - private PhpDocInfoFactory $phpDocInfoFactory; - public function __construct( - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private NodeNameResolver $nodeNameResolver, - private NodeFinder $nodeFinder, - private ReflectionProvider $reflectionProvider, - private BetterNodeFinder $betterNodeFinder, - private UseImportNameMatcher $useImportNameMatcher, + private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser, + private readonly NodeNameResolver $nodeNameResolver, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly UseImportNameMatcher $useImportNameMatcher, + private readonly PhpDocInfoFactory $phpDocInfoFactory ) { } - // Avoids circular reference - - #[Required] - public function autowireShortNameResolver(PhpDocInfoFactory $phpDocInfoFactory): void - { - $this->phpDocInfoFactory = $phpDocInfoFactory; - } - /** * @return array */ - public function resolveForNode(File $file): array + public function resolveFromFile(File $file): array { - $smartFileInfo = $file->getSmartFileInfo(); - $nodeRealPath = $smartFileInfo->getRealPath(); + $filePath = $file->getFilePath(); - if (isset($this->shortNamesByFilePath[$nodeRealPath])) { - return $this->shortNamesByFilePath[$nodeRealPath]; + if (isset($this->shortNamesByFilePath[$filePath])) { + return $this->shortNamesByFilePath[$filePath]; } $shortNamesToFullyQualifiedNames = $this->resolveForStmts($file->getNewStmts()); - $this->shortNamesByFilePath[$nodeRealPath] = $shortNamesToFullyQualifiedNames; + $this->shortNamesByFilePath[$filePath] = $shortNamesToFullyQualifiedNames; return $shortNamesToFullyQualifiedNames; } @@ -83,16 +61,17 @@ public function resolveForNode(File $file): array * Collects all "class ", "trait " and "interface " * @return string[] */ - public function resolveShortClassLikeNamesForNode(Node $node): array + public function resolveShortClassLikeNames(File $file): array { - $namespace = $this->betterNodeFinder->findParentType($node, Namespace_::class); - if (! $namespace instanceof Namespace_) { - // only handle namespace nodes + $rootNode = $file->getUseImportsRootNode(); + + // nothing to resolve + if (! $rootNode instanceof Node) { return []; } /** @var ClassLike[] $classLikes */ - $classLikes = $this->nodeFinder->findInstanceOf($namespace, ClassLike::class); + $classLikes = $this->betterNodeFinder->findInstanceOf($rootNode->stmts, ClassLike::class); $shortClassLikeNames = []; foreach ($classLikes as $classLike) { @@ -112,38 +91,41 @@ private function resolveForStmts(array $stmts): array $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmts, function (Node $node) use ( &$shortNamesToFullyQualifiedNames - ): void { + ): null { // class name is used! if ($node instanceof ClassLike && $node->name instanceof Identifier) { $fullyQualifiedName = $this->nodeNameResolver->getName($node); if ($fullyQualifiedName === null) { - return; + return null; } $shortNamesToFullyQualifiedNames[$node->name->toString()] = $fullyQualifiedName; - return; + return null; } if (! $node instanceof Name) { - return; + return null; } $originalName = $node->getAttribute(AttributeKey::ORIGINAL_NAME); if (! $originalName instanceof Name) { - return; + return null; } // already short if (\str_contains($originalName->toString(), '\\')) { - return; + return null; } - $fullyQualifiedName = $this->nodeNameResolver->getName($node); - $shortNamesToFullyQualifiedNames[$originalName->toString()] = $fullyQualifiedName; + $shortNamesToFullyQualifiedNames[$originalName->toString()] = $this->nodeNameResolver->getName($node); + + return null; }); $docBlockShortNamesToFullyQualifiedNames = $this->resolveFromStmtsDocBlocks($stmts); - return array_merge($shortNamesToFullyQualifiedNames, $docBlockShortNamesToFullyQualifiedNames); + /** @var array $result */ + $result = [...$shortNamesToFullyQualifiedNames, ...$docBlockShortNamesToFullyQualifiedNames]; + return $result; } /** @@ -152,26 +134,24 @@ private function resolveForStmts(array $stmts): array */ private function resolveFromStmtsDocBlocks(array $stmts): array { - $reflectionClass = $this->resolveNativeClassReflection($stmts); - $shortNames = []; $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmts, function (Node $node) use ( &$shortNames - ): void { + ): null { // speed up for nodes that are $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); if (! $phpDocInfo instanceof PhpDocInfo) { - return; + return null; } $phpDocNodeTraverser = new PhpDocNodeTraverser(); $phpDocNodeTraverser->traverseWithCallable( $phpDocInfo->getPhpDocNode(), '', - function ($node) use (&$shortNames) { + static function ($node) use (&$shortNames): null { if ($node instanceof PhpDocTagNode) { $shortName = trim($node->name, '@'); - if (Strings::match($shortName, self::BIG_LETTER_START_REGEX)) { + if (ucfirst($shortName) === $shortName) { $shortNames[] = $shortName; } @@ -185,32 +165,11 @@ function ($node) use (&$shortNames) { return null; } ); - }); - return $this->fqnizeShortNames($shortNames, $reflectionClass, $stmts); - } - - /** - * @param Node[] $stmts - */ - private function resolveNativeClassReflection(array $stmts): ?ReflectionClass - { - $firstClassLike = $this->nodeFinder->findFirstInstanceOf($stmts, ClassLike::class); - if (! $firstClassLike instanceof ClassLike) { - return null; - } - - $className = $this->nodeNameResolver->getName($firstClassLike); - if (! $className) { - return null; - } - - if (! $this->reflectionProvider->hasClass($className)) { return null; - } + }); - $classReflection = $this->reflectionProvider->getClass($className); - return $classReflection->getNativeReflection(); + return $this->fqnizeShortNames($shortNames, $stmts); } /** @@ -218,18 +177,18 @@ private function resolveNativeClassReflection(array $stmts): ?ReflectionClass * @param Stmt[] $stmts * @return array */ - private function fqnizeShortNames(array $shortNames, ?ReflectionClass $reflectionClass, array $stmts): array + private function fqnizeShortNames(array $shortNames, array $stmts): array { $shortNamesToFullyQualifiedNames = []; foreach ($shortNames as $shortName) { - if ($reflectionClass instanceof ReflectionClass) { - $fullyQualifiedName = Reflection::expandClassName($shortName, $reflectionClass); - } else { - $fullyQualifiedName = $this->useImportNameMatcher->matchNameWithStmts($shortName, $stmts) ?: $shortName; + $stmtsMatchedName = $this->useImportNameMatcher->matchNameWithStmts($shortName, $stmts); + + if ($stmtsMatchedName == null) { + continue; } - $shortNamesToFullyQualifiedNames[$shortName] = $fullyQualifiedName; + $shortNamesToFullyQualifiedNames[$shortName] = $stmtsMatchedName; } return $shortNamesToFullyQualifiedNames; diff --git a/rules/CodingStyle/ClassNameImport/UseImportsTraverser.php b/rules/CodingStyle/ClassNameImport/UseImportsTraverser.php index 1758a1b1209..0c12c361003 100644 --- a/rules/CodingStyle/ClassNameImport/UseImportsTraverser.php +++ b/rules/CodingStyle/ClassNameImport/UseImportsTraverser.php @@ -4,67 +4,56 @@ namespace Rector\CodingStyle\ClassNameImport; -use PhpParser\Node; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\GroupUse; +use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\Use_; +use PhpParser\Node\UseItem; use Rector\NodeNameResolver\NodeNameResolver; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Node\FileNode; -final class UseImportsTraverser +final readonly class UseImportsTraverser { public function __construct( - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, private NodeNameResolver $nodeNameResolver ) { } /** * @param Stmt[] $stmts - */ - public function traverserStmtsForFunctions(array $stmts, callable $callable): void - { - $this->traverseForType($stmts, $callable, Use_::TYPE_FUNCTION); - } - - /** - * @param Stmt[] $stmts + * @param callable(Use_::TYPE_* $useType, UseItem $useUse, string $name):void $callable */ public function traverserStmts(array $stmts, callable $callable): void { - $this->traverseForType($stmts, $callable, Use_::TYPE_NORMAL); - } - - /** - * @param Stmt[] $stmts - */ - private function traverseForType(array $stmts, callable $callable, int $desiredType): void - { - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmts, function (Node $node) use ( - $callable, - $desiredType - ) { - if ($node instanceof Use_) { - // only import uses - if ($node->type !== $desiredType) { - return null; - } + foreach ($stmts as $stmt) { + if ($stmt instanceof Namespace_ || $stmt instanceof FileNode) { + $this->traverserStmts($stmt->stmts, $callable); + continue; + } - foreach ($node->uses as $useUse) { + if ($stmt instanceof Use_) { + foreach ($stmt->uses as $useUse) { $name = $this->nodeNameResolver->getName($useUse); - $callable($useUse, $name); + if ($name === null) { + continue; + } + + $callable($stmt->type, $useUse, $name); } - } - if ($node instanceof GroupUse) { - $this->processGroupUse($node, $desiredType, $callable); + continue; } - return null; - }); + if ($stmt instanceof GroupUse) { + $this->processGroupUse($stmt, $callable); + } + } } - private function processGroupUse(GroupUse $groupUse, int $desiredType, callable $callable): void + /** + * @param callable(Use_::TYPE_* $useType, UseItem $useUse, string $name):void $callable + */ + private function processGroupUse(GroupUse $groupUse, callable $callable): void { if ($groupUse->type !== Use_::TYPE_UNKNOWN) { return; @@ -73,12 +62,8 @@ private function processGroupUse(GroupUse $groupUse, int $desiredType, callable $prefixName = $groupUse->prefix->toString(); foreach ($groupUse->uses as $useUse) { - if ($useUse->type !== $desiredType) { - continue; - } - $name = $prefixName . '\\' . $this->nodeNameResolver->getName($useUse); - $callable($useUse, $name); + $callable($useUse->type, $useUse, $name); } } } diff --git a/rules/CodingStyle/ClassNameImport/UsedImportsResolver.php b/rules/CodingStyle/ClassNameImport/UsedImportsResolver.php index bef4258191a..5e8d84bdc91 100644 --- a/rules/CodingStyle/ClassNameImport/UsedImportsResolver.php +++ b/rules/CodingStyle/ClassNameImport/UsedImportsResolver.php @@ -4,48 +4,30 @@ namespace Rector\CodingStyle\ClassNameImport; -use PhpParser\Node; +use PhpParser\Node\Identifier; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\Namespace_; -use PhpParser\Node\Stmt\UseUse; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use PhpParser\Node\Stmt\Use_; +use PhpParser\Node\UseItem; +use Rector\CodingStyle\ClassNameImport\ValueObject\UsedImports; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\PhpParser\Node\BetterNodeFinder; use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; -final class UsedImportsResolver +final readonly class UsedImportsResolver { public function __construct( private BetterNodeFinder $betterNodeFinder, - private NodeNameResolver $nodeNameResolver, - private UseImportsTraverser $useImportsTraverser + private UseImportsTraverser $useImportsTraverser, + private NodeNameResolver $nodeNameResolver ) { } - /** - * @return array - */ - public function resolveForNode(Node $node): array - { - if ($node instanceof Namespace_) { - $namespace = $node; - } else { - $namespace = $this->betterNodeFinder->findParentType($node, Namespace_::class); - } - - if ($namespace instanceof Namespace_) { - return $this->resolveForNamespace($namespace); - } - - return []; - } - /** * @param Stmt[] $stmts - * @return array */ - public function resolveForStmts(array $stmts): array + public function resolveForStmts(array $stmts): UsedImports { $usedImports = []; @@ -53,50 +35,37 @@ public function resolveForStmts(array $stmts): array $class = $this->betterNodeFinder->findFirstInstanceOf($stmts, Class_::class); // add class itself - if ($class !== null) { - $className = $this->nodeNameResolver->getName($class); - if ($className !== null) { - $usedImports[] = new FullyQualifiedObjectType($className); - } + // is not anonymous class + if ($class instanceof Class_) { + $className = (string) $this->nodeNameResolver->getName($class); + $usedImports[] = new FullyQualifiedObjectType($className); } - $this->useImportsTraverser->traverserStmts($stmts, function ( - UseUse $useUse, + $usedConstImports = []; + $usedFunctionImports = []; + /** @param Use_::TYPE_* $useType */ + $this->useImportsTraverser->traverserStmts($stmts, static function ( + int $useType, + UseItem $useItem, string $name - ) use (&$usedImports): void { - if ($useUse->alias !== null) { - $usedImports[] = new AliasedObjectType($useUse->alias->toString(), $name); - } else { - $usedImports[] = new FullyQualifiedObjectType($name); + ) use (&$usedImports, &$usedFunctionImports, &$usedConstImports): void { + if ($useType === Use_::TYPE_NORMAL) { + if ($useItem->alias instanceof Identifier) { + $usedImports[] = new AliasedObjectType($useItem->alias->toString(), $name); + } else { + $usedImports[] = new FullyQualifiedObjectType($name); + } } - }); - - return $usedImports; - } - /** - * @param Stmt[] $stmts - * @return FullyQualifiedObjectType[] - */ - public function resolveFunctionImportsForStmts(array $stmts): array - { - $usedFunctionImports = []; + if ($useType === Use_::TYPE_FUNCTION) { + $usedFunctionImports[] = new FullyQualifiedObjectType($name); + } - $this->useImportsTraverser->traverserStmtsForFunctions($stmts, function ( - UseUse $useUse, - string $name - ) use (&$usedFunctionImports): void { - $usedFunctionImports[] = new FullyQualifiedObjectType($name); + if ($useType === Use_::TYPE_CONSTANT) { + $usedConstImports[] = new FullyQualifiedObjectType($name); + } }); - return $usedFunctionImports; - } - - /** - * @return array - */ - private function resolveForNamespace(Namespace_ $namespace): array - { - return $this->resolveForStmts($namespace->stmts); + return new UsedImports($usedImports, $usedFunctionImports, $usedConstImports); } } diff --git a/rules/CodingStyle/ClassNameImport/ValueObject/UsedImports.php b/rules/CodingStyle/ClassNameImport/ValueObject/UsedImports.php new file mode 100644 index 00000000000..efec25e6397 --- /dev/null +++ b/rules/CodingStyle/ClassNameImport/ValueObject/UsedImports.php @@ -0,0 +1,47 @@ + $useImports + * @param FullyQualifiedObjectType[] $functionImports + * @param FullyQualifiedObjectType[] $constantImports + */ + public function __construct( + private array $useImports, + private array $functionImports, + private array $constantImports + ) { + } + + /** + * @return array + */ + public function getUseImports(): array + { + return $this->useImports; + } + + /** + * @return FullyQualifiedObjectType[] + */ + public function getFunctionImports(): array + { + return $this->functionImports; + } + + /** + * @return FullyQualifiedObjectType[] + */ + public function getConstantImports(): array + { + return $this->constantImports; + } +} diff --git a/rules/CodingStyle/Contract/ClassNameImport/ClassNameImportSkipVoterInterface.php b/rules/CodingStyle/Contract/ClassNameImport/ClassNameImportSkipVoterInterface.php index c46153181ed..48b13e55605 100644 --- a/rules/CodingStyle/Contract/ClassNameImport/ClassNameImportSkipVoterInterface.php +++ b/rules/CodingStyle/Contract/ClassNameImport/ClassNameImportSkipVoterInterface.php @@ -5,8 +5,8 @@ namespace Rector\CodingStyle\Contract\ClassNameImport; use PhpParser\Node; -use Rector\Core\ValueObject\Application\File; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +use Rector\ValueObject\Application\File; interface ClassNameImportSkipVoterInterface { diff --git a/rules/CodingStyle/Enum/PreferenceSelfThis.php b/rules/CodingStyle/Enum/PreferenceSelfThis.php deleted file mode 100644 index e8309dc66de..00000000000 --- a/rules/CodingStyle/Enum/PreferenceSelfThis.php +++ /dev/null @@ -1,26 +0,0 @@ -isFirstClassCallable()) { + return true; + } + + // use cheap checks first + if ($arrowFunctionOrClosure->getAttribute(AttributeKey::HAS_CLOSURE_WITH_VARIADIC_ARGS) === true) { + return true; + } + + if ($arrowFunctionOrClosure->getAttribute( + AttributeKey::IS_ASSIGNED_TO + ) === true || $arrowFunctionOrClosure->getAttribute(AttributeKey::IS_BEING_ASSIGNED)) { + return true; + } + + $params = $arrowFunctionOrClosure->getParams(); + + if (count($params) !== count($callLike->getArgs())) { + return true; + } + + $args = $callLike->getArgs(); + if ($this->isChainedCall($callLike)) { + return true; + } + + if ($this->isUsingNamedArgs($args)) { + return true; + } + + if ($this->isUsingByRef($params)) { + return true; + } + + if ($this->isNotUsingSameParamsForArgs($params, $args)) { + return true; + } + + if ($this->isDependantMethod($callLike, $params)) { + return true; + } + + if ($this->isUsingThisInNonObjectContext($callLike, $scope)) { + return true; + } + + $reflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($callLike); + + // does not exists, probably by magic method + if ($reflection === null) { + return true; + } + + // phpstan reports first class callables that are not native methods + if ($reflection instanceof MethodReflection && ! $reflection->getDeclaringClass()->hasNativeMethod( + $reflection->getName() + )) { + return true; + } + + // check if args require by reference + $parameters = ParametersAcceptorSelectorVariantsWrapper::select( + $reflection, + $callLike, + $scope + )->getParameters(); + foreach ($parameters as $parameter) { + if ($parameter->passedByReference()->yes()) { + return true; + } + } + + $functionLike = $this->astResolver->resolveClassMethodOrFunctionFromCall($callLike); + if (! $functionLike instanceof FunctionLike) { + return false; + } + + return count($functionLike->getParams()) > 1; + } + + /** + * @param Param[] $params + */ + private function isDependantMethod(StaticCall|MethodCall|FuncCall $expr, array $params): bool + { + if ($expr instanceof FuncCall) { + return false; + } + + $found = false; + $parentNode = $expr instanceof MethodCall ? $expr->var : $expr->class; + + foreach ($params as $param) { + SimpleCallableNodeTraverser::traverse($parentNode, function (Node $node) use ($param, &$found): ?int { + if ($this->nodeComparator->areNodesEqual($node, $param->var)) { + $found = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + return null; + }); + + if ($found) { + return true; + } + } + + return false; + } + + private function isUsingThisInNonObjectContext(FuncCall|MethodCall|StaticCall $callLike, Scope $scope): bool + { + if (! $callLike instanceof MethodCall) { + return false; + } + + if (in_array('this', $scope->getDefinedVariables(), true)) { + return false; + } + + $found = false; + + SimpleCallableNodeTraverser::traverse($callLike, function (Node $node) use (&$found): ?int { + if ($this->nodeNameResolver->isName($node, 'this')) { + $found = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + return null; + }); + + return $found; + } + + /** + * @param Param[] $params + */ + private function isUsingByRef(array $params): bool + { + foreach ($params as $param) { + if ($param->byRef) { + return true; + } + } + + return false; + } + + /** + * @param Arg[] $args + */ + private function isUsingNamedArgs(array $args): bool + { + foreach ($args as $arg) { + if ($arg->name instanceof Identifier) { + return true; + } + } + + return false; + } + + private function isChainedCall(FuncCall|MethodCall|StaticCall $callLike): bool + { + if ($callLike instanceof MethodCall) { + return $callLike->var instanceof CallLike; + } + + if ($callLike instanceof StaticCall) { + return $callLike->class instanceof CallLike; + } + + return false; + } + + /** + * @param Param[] $params + * @param Arg[] $args + */ + private function isNotUsingSameParamsForArgs(array $params, array $args): bool + { + if (count($args) > count($params)) { + return true; + } + + if (count($args) === 1 && $args[0]->unpack) { + return ! $params[0]->variadic; + } + + foreach ($args as $key => $arg) { + if (! $this->nodeComparator->areNodesEqual($arg->value, $params[$key]->var)) { + return true; + } + + if (! $arg->value instanceof Variable) { + continue; + } + + $variableName = (string) $this->nodeNameResolver->getName($arg->value); + + foreach ($params as $param) { + if ($param->var instanceof Variable + && $this->nodeNameResolver->isName($param->var, $variableName) + && $param->variadic + && ! $arg->unpack) { + return true; + } + } + } + + return false; + } +} diff --git a/rules/CodingStyle/Guard/StaticGuard.php b/rules/CodingStyle/Guard/StaticGuard.php new file mode 100644 index 00000000000..810d296053a --- /dev/null +++ b/rules/CodingStyle/Guard/StaticGuard.php @@ -0,0 +1,50 @@ +static) { + return false; + } + + $nodes = $node instanceof Closure + ? $node->stmts + : [$node->expr]; + + return ! (bool) $this->betterNodeFinder->findFirst( + $nodes, + function (Node $subNode): bool { + if (! $subNode instanceof StaticCall) { + return $subNode instanceof Variable && $subNode->name === 'this'; + } + + $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromStaticCall($subNode); + if (! $methodReflection instanceof MethodReflection) { + return false; + } + + return ! $methodReflection->isStatic(); + } + ); + } +} diff --git a/rules/CodingStyle/Naming/ClassNaming.php b/rules/CodingStyle/Naming/ClassNaming.php index 700450cc735..2a90030b3fc 100644 --- a/rules/CodingStyle/Naming/ClassNaming.php +++ b/rules/CodingStyle/Naming/ClassNaming.php @@ -8,29 +8,13 @@ use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Stmt\ClassLike; -use PhpParser\Node\Stmt\Function_; -use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment; -use Stringy\Stringy; -use Symplify\SmartFileSystem\SmartFileInfo; final class ClassNaming { - /** - * @see https://regex101.com/r/8BdrI3/1 - * @var string - */ - private const INPUT_HASH_NAMING_REGEX = '#input_(.*?)_#'; - - public function getVariableName(string | Name | Identifier $name): string - { - $shortName = $this->getShortName($name); - return lcfirst($shortName); - } - public function getShortName(string | Name | Identifier | ClassLike $name): string { if ($name instanceof ClassLike) { - if ($name->name === null) { + if (! $name->name instanceof Identifier) { return ''; } @@ -43,47 +27,11 @@ public function getShortName(string | Name | Identifier | ClassLike $name): stri $name = trim($name, '\\'); - return Strings::after($name, '\\', -1) ?: $name; - } - - public function getNamespace(string $fullyQualifiedName): ?string - { - $fullyQualifiedName = trim($fullyQualifiedName, '\\'); - - return Strings::before($fullyQualifiedName, '\\', -1) ?: null; - } - - public function getNameFromFileInfo(SmartFileInfo $smartFileInfo): string - { - $basenameWithoutSuffix = $smartFileInfo->getBasenameWithoutSuffix(); - - // remove PHPUnit fixture file prefix - if (StaticPHPUnitEnvironment::isPHPUnitRun()) { - $basenameWithoutSuffix = Strings::replace($basenameWithoutSuffix, self::INPUT_HASH_NAMING_REGEX, ''); - } - - $stringy = new Stringy($basenameWithoutSuffix); - return (string) $stringy->upperCamelize(); - } - - /** - * "some_function" → "someFunction" - */ - public function createMethodNameFromFunction(Function_ $function): string - { - $functionName = (string) $function->name; - - $stringy = new Stringy($functionName); - return (string) $stringy->camelize(); - } - - public function replaceSuffix(string $content, string $oldSuffix, string $newSuffix): string - { - if (! \str_ends_with($content, $oldSuffix)) { - return $content . $newSuffix; + $shortName = Strings::after($name, '\\', -1); + if (is_string($shortName)) { + return $shortName; } - $contentWithoutOldSuffix = Strings::substring($content, 0, -Strings::length($oldSuffix)); - return $contentWithoutOldSuffix . $newSuffix; + return $name; } } diff --git a/rules/CodingStyle/Naming/NameRenamer.php b/rules/CodingStyle/Naming/NameRenamer.php deleted file mode 100644 index 97a9cb241b0..00000000000 --- a/rules/CodingStyle/Naming/NameRenamer.php +++ /dev/null @@ -1,165 +0,0 @@ -getParentNode(); - $usedName = $usedNameNode->getNameNode(); - - if ($parentNode instanceof TraitUse) { - $this->renameTraitUse($lastName, $parentNode, $usedName); - } - - if ($parentNode instanceof Class_) { - $this->renameClass($lastName, $parentNode, $usedName); - } - - if ($parentNode instanceof Param) { - $this->renameParam($lastName, $parentNode, $usedName); - } - - if ($parentNode instanceof New_) { - $this->renameNew($lastName, $parentNode, $usedName); - } - - if ($parentNode instanceof ClassMethod) { - $this->renameClassMethod($lastName, $parentNode, $usedName); - } - - if ($parentNode instanceof Interface_) { - $this->renameInterface($lastName, $parentNode, $usedName); - } - - if ($parentNode instanceof StaticCall) { - $this->renameStaticCall($lastName, $parentNode); - } - - if ($parentNode instanceof UnionType) { - $this->renameUnionType($lastName, $parentNode, $usedName); - } - } - } - - private function renameTraitUse(string $lastName, TraitUse $traitUse, Name | Identifier $usedNameNode): void - { - foreach ($traitUse->traits as $key => $traitName) { - if (! $this->nodeNameResolver->areNamesEqual($traitName, $usedNameNode)) { - continue; - } - - $traitUse->traits[$key] = new Name($lastName); - } - } - - private function renameClass(string $lastName, Class_ $class, Name | Identifier $usedNameNode): void - { - if ($class->name !== null && $this->nodeNameResolver->areNamesEqual($class->name, $usedNameNode)) { - $class->name = new Identifier($lastName); - } - - if ($class->extends !== null && $this->nodeNameResolver->areNamesEqual($class->extends, $usedNameNode)) { - $class->extends = new Name($lastName); - } - - foreach ($class->implements as $key => $implementNode) { - if ($this->nodeNameResolver->areNamesEqual($implementNode, $usedNameNode)) { - $class->implements[$key] = new Name($lastName); - } - } - } - - private function renameParam(string $lastName, Param $param, Name | Identifier $usedNameNode): void - { - if ($param->type === null) { - return; - } - - if (! $this->nodeNameResolver->areNamesEqual($param->type, $usedNameNode)) { - return; - } - - $param->type = new Name($lastName); - } - - private function renameUnionType(string $lastName, UnionType $unionType, Node $usedNameNode): void - { - foreach ($unionType->types as $key => $unionedType) { - if (! $this->nodeNameResolver->areNamesEqual($unionedType, $usedNameNode)) { - continue; - } - - if (! $unionedType instanceof Name) { - continue; - } - - $unionType->types[$key] = new Name($lastName); - } - } - - private function renameNew(string $lastName, New_ $new, Name | Identifier $usedNameNode): void - { - if ($this->nodeNameResolver->areNamesEqual($new->class, $usedNameNode)) { - $new->class = new Name($lastName); - } - } - - private function renameClassMethod( - string $lastName, - ClassMethod $classMethod, - Name | Identifier $usedNameNode - ): void { - if ($classMethod->returnType === null) { - return; - } - - if (! $this->nodeNameResolver->areNamesEqual($classMethod->returnType, $usedNameNode)) { - return; - } - - $classMethod->returnType = new Name($lastName); - } - - private function renameInterface(string $lastName, Interface_ $interface, Name | Identifier $usedNameNode): void - { - foreach ($interface->extends as $key => $extendInterfaceName) { - if (! $this->nodeNameResolver->areNamesEqual($extendInterfaceName, $usedNameNode)) { - continue; - } - - $interface->extends[$key] = new Name($lastName); - } - } - - private function renameStaticCall(string $lastName, StaticCall $staticCall): void - { - $staticCall->class = new Name($lastName); - } -} diff --git a/rules/CodingStyle/Node/ConcatJoiner.php b/rules/CodingStyle/Node/ConcatJoiner.php deleted file mode 100644 index 8653782cbcf..00000000000 --- a/rules/CodingStyle/Node/ConcatJoiner.php +++ /dev/null @@ -1,58 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Concat) { - $this->reset(); - } - - $this->processConcatSide($concat->left); - $this->processConcatSide($concat->right); - - return new ConcatStringAndPlaceholders($this->content, $this->placeholderNodes); - } - - private function reset(): void - { - $this->content = ''; - $this->placeholderNodes = []; - } - - private function processConcatSide(Expr $expr): void - { - if ($expr instanceof String_) { - $this->content .= $expr->value; - } elseif ($expr instanceof Concat) { - $this->joinToStringAndPlaceholderNodes($expr); - } else { - $objectHash = '____' . spl_object_hash($expr) . '____'; - $this->placeholderNodes[$objectHash] = $expr; - - $this->content .= $objectHash; - } - } -} diff --git a/rules/CodingStyle/Node/ConcatManipulator.php b/rules/CodingStyle/Node/ConcatManipulator.php deleted file mode 100644 index 42499b68970..00000000000 --- a/rules/CodingStyle/Node/ConcatManipulator.php +++ /dev/null @@ -1,57 +0,0 @@ -left instanceof Concat) { - $concat = $concat->left; - } - - return $concat->left; - } - - public function removeFirstItemFromConcat(Concat $concat): Expr - { - // just 2 items, return right one - if (! $concat->left instanceof Concat) { - return $concat->right; - } - - $newConcat = clone $concat; - $firstConcatItem = $this->getFirstConcatItem($concat); - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($newConcat, function (Node $node) use ( - $firstConcatItem - ): ?Expr { - if (! $node instanceof Concat) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($node->left, $firstConcatItem)) { - return null; - } - - return $node->right; - }); - - return $newConcat; - } -} diff --git a/rules/CodingStyle/Node/DocAliasResolver.php b/rules/CodingStyle/Node/DocAliasResolver.php deleted file mode 100644 index e4a3295e9d8..00000000000 --- a/rules/CodingStyle/Node/DocAliasResolver.php +++ /dev/null @@ -1,93 +0,0 @@ -\w+)(\\\\)?#s'; - - public function __construct( - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private PhpDocInfoFactory $phpDocInfoFactory - ) { - } - - /** - * @return string[] - */ - public function resolve(Node $node): array - { - $possibleDocAliases = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($node, function (Node $node) use ( - &$possibleDocAliases - ): void { - $docComment = $node->getDocComment(); - if (! $docComment instanceof Doc) { - return; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $possibleDocAliases = $this->collectVarType($phpDocInfo, $possibleDocAliases); - - // e.g. "use Dotrine\ORM\Mapping as ORM" etc. - $matches = Strings::matchAll($docComment->getText(), self::DOC_ALIAS_REGEX); - foreach ($matches as $match) { - $possibleDocAliases[] = $match['possible_alias']; - } - }); - - return array_unique($possibleDocAliases); - } - - /** - * @param string[] $possibleDocAliases - * @return string[] - */ - private function collectVarType(PhpDocInfo $phpDocInfo, array $possibleDocAliases): array - { - $possibleDocAliases = $this->appendPossibleAliases($phpDocInfo->getVarType(), $possibleDocAliases); - $possibleDocAliases = $this->appendPossibleAliases($phpDocInfo->getReturnType(), $possibleDocAliases); - - foreach ($phpDocInfo->getParamTypesByName() as $paramType) { - $possibleDocAliases = $this->appendPossibleAliases($paramType, $possibleDocAliases); - } - - return $possibleDocAliases; - } - - /** - * @param string[] $possibleDocAliases - * @return string[] - */ - private function appendPossibleAliases(Type $varType, array $possibleDocAliases): array - { - if ($varType instanceof AliasedObjectType) { - $possibleDocAliases[] = $varType->getClassName(); - } - - if ($varType instanceof UnionType) { - foreach ($varType->getTypes() as $type) { - $possibleDocAliases = $this->appendPossibleAliases($type, $possibleDocAliases); - } - } - - return $possibleDocAliases; - } -} diff --git a/rules/CodingStyle/Node/NameImporter.php b/rules/CodingStyle/Node/NameImporter.php index 27250a5761a..404bec35ef3 100644 --- a/rules/CodingStyle/Node/NameImporter.php +++ b/rules/CodingStyle/Node/NameImporter.php @@ -4,108 +4,97 @@ namespace Rector\CodingStyle\Node; -use PhpParser\Node; -use PhpParser\Node\Expr\ConstFetch; -use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Identifier; use PhpParser\Node\Name; -use PhpParser\Node\Stmt\Namespace_; +use PhpParser\Node\Name\FullyQualified; +use PhpParser\Node\Stmt\GroupUse; use PhpParser\Node\Stmt\Use_; -use PhpParser\Node\Stmt\UseUse; -use PHPStan\Reflection\ReflectionProvider; -use Rector\CodingStyle\ClassNameImport\AliasUsesResolver; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper; -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\Application\File; -use Rector\NodeNameResolver\NodeNameResolver; +use Rector\Naming\Naming\AliasNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\PostRector\Collector\UseNodesToAddCollector; -use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\StaticTypeMapper\PhpParser\FullyQualifiedNodeMapper; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; -use Symplify\PackageBuilder\Parameter\ParameterProvider; +use Rector\ValueObject\Application\File; -final class NameImporter +final readonly class NameImporter { - /** - * @var string[] - */ - private array $aliasedUses = []; - public function __construct( - private AliasUsesResolver $aliasUsesResolver, private ClassNameImportSkipper $classNameImportSkipper, - private NodeNameResolver $nodeNameResolver, - private ParameterProvider $parameterProvider, - private StaticTypeMapper $staticTypeMapper, + private FullyQualifiedNodeMapper $fullyQualifiedNodeMapper, private UseNodesToAddCollector $useNodesToAddCollector, - private ReflectionProvider $reflectionProvider + private AliasNameResolver $aliasNameResolver ) { } /** - * @param Use_[] $uses + * @param array $currentUses */ - public function importName(Name $name, File $file, array $uses): ?Name + public function importName(FullyQualified $fullyQualified, File $file, array $currentUses): ?Name { - if ($this->shouldSkipName($name)) { - return null; - } - if ($this->classNameImportSkipper->isShortNameInUseStatement($name, $uses)) { + if ($this->classNameImportSkipper->shouldSkipName($fullyQualified, $currentUses)) { return null; } - $staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($name); + $staticType = $this->fullyQualifiedNodeMapper->mapToPHPStan($fullyQualified); if (! $staticType instanceof FullyQualifiedObjectType) { return null; } - $this->aliasedUses = $this->aliasUsesResolver->resolveForNode($name); - - return $this->importNameAndCollectNewUseStatement($file, $name, $staticType); + return $this->importNameAndCollectNewUseStatement($file, $fullyQualified, $staticType, $currentUses); } - private function shouldSkipName(Name $name): bool + /** + * @param array $currentUses + */ + private function resolveNameInUse(FullyQualified $fullyQualified, array $currentUses): ?Name { - $virtualNode = $name->getAttribute(AttributeKey::VIRTUAL_NODE); - if ($virtualNode) { - return true; + $aliasName = $this->aliasNameResolver->resolveByName($fullyQualified, $currentUses); + if (is_string($aliasName)) { + return new Name($aliasName); } - // is scalar name? - if (in_array($name->toLowerString(), ['true', 'false', 'bool'], true)) { - return true; - } - - // namespace - // use ; - if ($this->isNamespaceOrUseImportName($name)) { - return true; + if (substr_count($fullyQualified->toCodeString(), '\\') === 1) { + return null; } - if ($this->isFunctionOrConstantImportWithSingleName($name)) { - return true; - } + $lastName = $fullyQualified->getLast(); + foreach ($currentUses as $currentUse) { + foreach ($currentUse->uses as $useUse) { + if ($useUse->name->getLast() !== $lastName) { + continue; + } - // Importing root namespace classes (like \DateTime) is optional - if (! $this->parameterProvider->provideBoolParameter(Option::IMPORT_SHORT_CLASSES)) { - $name = $this->nodeNameResolver->getName($name); - if ($name !== null && substr_count($name, '\\') === 0) { - return true; + if ($useUse->alias instanceof Identifier && $useUse->alias->toString() !== $lastName) { + return new Name($lastName); + } } } - return false; + return null; } + /** + * @param array $currentUses + */ private function importNameAndCollectNewUseStatement( File $file, - Name $name, - FullyQualifiedObjectType $fullyQualifiedObjectType + FullyQualified $fullyQualified, + FullyQualifiedObjectType $fullyQualifiedObjectType, + array $currentUses ): ?Name { + // make use of existing use import + $nameInUse = $this->resolveNameInUse($fullyQualified, $currentUses); + if ($nameInUse instanceof Name) { + $nameInUse->setAttribute(AttributeKey::NAMESPACED_NAME, $fullyQualified->toString()); + return $nameInUse; + } + // the same end is already imported → skip if ($this->classNameImportSkipper->shouldSkipNameForFullyQualifiedObjectType( $file, - $name, + $fullyQualified, $fullyQualifiedObjectType )) { return null; @@ -119,67 +108,45 @@ private function importNameAndCollectNewUseStatement( return null; } - $this->addUseImport($file, $name, $fullyQualifiedObjectType); - - // possibly aliased - foreach ($this->aliasedUses as $aliasedUse) { - if ($fullyQualifiedObjectType->getClassName() === $aliasedUse) { - return null; - } - } + $this->addUseImport($file, $fullyQualified, $fullyQualifiedObjectType); + $name = $fullyQualifiedObjectType->getShortNameNode(); - return $fullyQualifiedObjectType->getShortNameNode(); - } + $oldTokens = $file->getOldTokens(); + $startTokenPos = $fullyQualified->getStartTokenPos(); - /** - * Skip: - * - namespace name - * - use import name - */ - private function isNamespaceOrUseImportName(Name $name): bool - { - $parentNode = $name->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Namespace_) { - return true; + if (! isset($oldTokens[$startTokenPos])) { + return $name; } - return $parentNode instanceof UseUse; - } - - private function isFunctionOrConstantImportWithSingleName(Name $name): bool - { - $parentNode = $name->getAttribute(AttributeKey::PARENT_NODE); - - $fullName = $name->toString(); - - $autoImportNames = $this->parameterProvider->provideParameter(Option::AUTO_IMPORT_NAMES); - if ($autoImportNames && ! $parentNode instanceof Node && ! \str_contains( - $fullName, - '\\' - ) && $this->reflectionProvider->hasFunction(new Name($fullName), null)) { - return true; + $tokenShortName = $oldTokens[$startTokenPos]; + if (str_starts_with($tokenShortName->text, '\\')) { + return $name; } - if ($parentNode instanceof ConstFetch) { - return count($name->parts) === 1; + if (str_contains($tokenShortName->text, '\\')) { + return $name; } - if ($parentNode instanceof FuncCall) { - return count($name->parts) === 1; + if ($name->toString() !== $tokenShortName->text) { + return $name; } - return false; + return null; } - private function addUseImport(File $file, Name $name, FullyQualifiedObjectType $fullyQualifiedObjectType): void - { - if ($this->useNodesToAddCollector->hasImport($file, $name, $fullyQualifiedObjectType)) { + private function addUseImport( + File $file, + FullyQualified $fullyQualified, + FullyQualifiedObjectType $fullyQualifiedObjectType + ): void { + if ($this->useNodesToAddCollector->hasImport($file, $fullyQualifiedObjectType)) { return; } - $parentNode = $name->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof FuncCall) { - $this->useNodesToAddCollector->addFunctionUseImport($name, $fullyQualifiedObjectType); + if ($fullyQualified->getAttribute(AttributeKey::IS_FUNCCALL_NAME) === true) { + $this->useNodesToAddCollector->addFunctionUseImport($fullyQualifiedObjectType); + } elseif ($fullyQualified->getAttribute(AttributeKey::IS_CONSTFETCH_NAME) === true) { + $this->useNodesToAddCollector->addConstantUseImport($fullyQualifiedObjectType); } else { $this->useNodesToAddCollector->addUseImport($fullyQualifiedObjectType); } diff --git a/rules/CodingStyle/Node/UseManipulator.php b/rules/CodingStyle/Node/UseManipulator.php deleted file mode 100644 index 99dee3ecf60..00000000000 --- a/rules/CodingStyle/Node/UseManipulator.php +++ /dev/null @@ -1,100 +0,0 @@ -resolvedNodeNames = []; - - $this->resolveUsedNames($node); - $this->resolveUsedClassNames($node); - $this->resolveTraitUseNames($node); - - return $this->resolvedNodeNames; - } - - private function resolveUsedNames(Node $node): void - { - /** @var Name[] $namedNodes */ - $namedNodes = $this->betterNodeFinder->findInstanceOf($node, Name::class); - - foreach ($namedNodes as $namedNode) { - /** node name before becoming FQN - attribute from @see NameResolver */ - $originalName = $namedNode->getAttribute(AttributeKey::ORIGINAL_NAME); - if (! $originalName instanceof Name) { - continue; - } - - $parentNode = $namedNode->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - throw new ShouldNotHappenException(); - } - - $this->resolvedNodeNames[$originalName->toString()][] = new NameAndParent($namedNode, $parentNode); - } - } - - private function resolveUsedClassNames(Node $searchNode): void - { - /** @var ClassLike[] $classLikes */ - $classLikes = $this->betterNodeFinder->findClassLikes([$searchNode]); - - foreach ($classLikes as $classLike) { - $classLikeName = $classLike->name; - if (! $classLikeName instanceof Identifier) { - continue; - } - - $name = $this->nodeNameResolver->getName($classLikeName); - if ($name === null) { - continue; - } - - $this->resolvedNodeNames[$name][] = new NameAndParent($classLikeName, $classLike); - } - } - - private function resolveTraitUseNames(Node $searchNode): void - { - /** @var Identifier[] $identifiers */ - $identifiers = $this->betterNodeFinder->findInstanceOf($searchNode, Identifier::class); - - foreach ($identifiers as $identifier) { - $parentNode = $identifier->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof UseUse) { - continue; - } - - $this->resolvedNodeNames[$identifier->name][] = new NameAndParent($identifier, $parentNode); - } - } -} diff --git a/rules/CodingStyle/Node/UseNameAliasToNameResolver.php b/rules/CodingStyle/Node/UseNameAliasToNameResolver.php deleted file mode 100644 index b4ba4c49e10..00000000000 --- a/rules/CodingStyle/Node/UseNameAliasToNameResolver.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ - public function resolve(File $file, Use_ $use): array - { - $useNamesAliasToName = []; - - $shortNames = $this->shortNameResolver->resolveForNode($file); - foreach ($shortNames as $alias => $useImport) { - if (! is_string($alias)) { - continue; - } - - $shortName = $this->classNaming->getShortName($useImport); - if ($shortName === $alias) { - continue; - } - - $useNamesAliasToName[$shortName][] = $alias; - } - - return $useNamesAliasToName; - } -} diff --git a/rules/CodingStyle/NodeAnalyzer/ImplodeAnalyzer.php b/rules/CodingStyle/NodeAnalyzer/ImplodeAnalyzer.php deleted file mode 100644 index 6a824158b70..00000000000 --- a/rules/CodingStyle/NodeAnalyzer/ImplodeAnalyzer.php +++ /dev/null @@ -1,42 +0,0 @@ -nodeNameResolver->isName($expr, 'implode')) { - return false; - } - - if (! isset($expr->args[1])) { - return false; - } - - $firstArgumentValue = $expr->args[0]->value; - if (! $firstArgumentValue instanceof String_) { - return true; - } - return $firstArgumentValue->value === '","'; - } -} diff --git a/rules/CodingStyle/NodeAnalyzer/SpreadVariablesCollector.php b/rules/CodingStyle/NodeAnalyzer/SpreadVariablesCollector.php deleted file mode 100644 index f34b40870bd..00000000000 --- a/rules/CodingStyle/NodeAnalyzer/SpreadVariablesCollector.php +++ /dev/null @@ -1,59 +0,0 @@ - - */ - public function resolveFromMethodReflection(MethodReflection | FunctionReflection $functionLikeReflection): array - { - $spreadParameterReflections = []; - - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($functionLikeReflection->getVariants()); - foreach ($parametersAcceptor->getParameters() as $key => $parameterReflection) { - if (! $parameterReflection->isVariadic()) { - continue; - } - - $spreadParameterReflections[$key] = $parameterReflection; - } - - return $spreadParameterReflections; - } - - /** - * @return array - */ - public function resolveFromClassMethod(ClassMethod $classMethod): array - { - $spreadParams = []; - - foreach ($classMethod->params as $key => $param) { - // prevent race-condition removal on class method - $originalParam = $param->getAttribute(AttributeKey::ORIGINAL_NODE); - if (! $originalParam instanceof Param) { - continue; - } - - if (! $originalParam->variadic) { - continue; - } - - $spreadParams[$key] = $param; - } - - return $spreadParams; - } -} diff --git a/rules/CodingStyle/NodeAnalyzer/UseImportNameMatcher.php b/rules/CodingStyle/NodeAnalyzer/UseImportNameMatcher.php index 3681b43acd1..f1a53de2cc7 100644 --- a/rules/CodingStyle/NodeAnalyzer/UseImportNameMatcher.php +++ b/rules/CodingStyle/NodeAnalyzer/UseImportNameMatcher.php @@ -5,23 +5,28 @@ namespace Rector\CodingStyle\NodeAnalyzer; use Nette\Utils\Strings; +use PhpParser\Node\Identifier; use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\GroupUse; use PhpParser\Node\Stmt\Use_; -use PhpParser\Node\Stmt\UseUse; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use PhpParser\Node\UseItem; +use Rector\Exception\ShouldNotHappenException; +use Rector\Naming\Naming\UseImportsResolver; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Util\StringUtils; -final class UseImportNameMatcher +final readonly class UseImportNameMatcher { /** - * @var string - * * @see https://regex101.com/r/ZxFSlc/1 for last name, eg: Entity and UniqueEntity * @see https://regex101.com/r/OLO0Un/1 for inside namespace, eg: ORM for ORM\Id or ORM\Column */ - private const SHORT_NAME_REGEX = '#^%s(\\\\[\w]+)?$#i'; + private const string SHORT_NAME_REGEX = '#^%s(\\\\[\w]+)?$#i'; public function __construct( - private BetterNodeFinder $betterNodeFinder + private BetterNodeFinder $betterNodeFinder, + private UseImportsResolver $useImportsResolver ) { } @@ -36,43 +41,65 @@ public function matchNameWithStmts(string $tag, array $stmts): ?string } /** - * @param Use_[] $uses + * @param array $uses + * @return non-empty-string|null */ public function matchNameWithUses(string $tag, array $uses): ?string { foreach ($uses as $use) { + $prefix = $this->useImportsResolver->resolvePrefix($use); foreach ($use->uses as $useUse) { if (! $this->isUseMatchingName($tag, $useUse)) { continue; } - return $this->resolveName($tag, $useUse); + return $this->resolveName($prefix, $tag, $useUse); } } return null; } - private function isUseMatchingName(string $tag, UseUse $useUse): bool + /** + * @return non-empty-string + */ + private function resolveName(string $prefix, string $tag, UseItem $useItem): string { - $shortName = $useUse->alias !== null ? $useUse->alias->name : $useUse->name->getLast(); - $shortNamePattern = preg_quote($shortName, '#'); - $pattern = sprintf(self::SHORT_NAME_REGEX, $shortNamePattern); + // UseItem can be renamed on the fly, so just in case, use the original one + $originalUseUseNode = $useItem->getAttribute(AttributeKey::ORIGINAL_NODE); + if (! $originalUseUseNode instanceof UseItem) { + throw new ShouldNotHappenException(); + } - return (bool) Strings::match($tag, $pattern); - } + if (! $originalUseUseNode->alias instanceof Identifier) { + $lastName = $originalUseUseNode->name->getLast(); + if (str_starts_with($tag, $lastName . '\\')) { + $tagName = Strings::after($tag, '\\'); + return $prefix . $originalUseUseNode->name->toString() . '\\' . $tagName; + } - private function resolveName(string $tag, UseUse $useUse): string - { - if ($useUse->alias === null) { - return $useUse->name->toString(); + return $prefix . $originalUseUseNode->name->toString(); } - $unaliasedShortClass = Strings::substring($tag, Strings::length($useUse->alias->toString())); + $unaliasedShortClass = Strings::substring($tag, strlen($originalUseUseNode->alias->toString())); if (\str_starts_with($unaliasedShortClass, '\\')) { - return $useUse->name . $unaliasedShortClass; + return $prefix . $originalUseUseNode->name . $unaliasedShortClass; } - return $useUse->name . '\\' . $unaliasedShortClass; + return $prefix . $originalUseUseNode->name . '\\' . $unaliasedShortClass; + } + + private function isUseMatchingName(string $tag, UseItem $useItem): bool + { + // UseItem can be renamed on the fly, so just in case, use the original one + $originalUseUseNode = $useItem->getAttribute(AttributeKey::ORIGINAL_NODE); + if (! $originalUseUseNode instanceof UseItem) { + return false; + } + + $shortName = $originalUseUseNode->alias instanceof Identifier ? $originalUseUseNode->alias->name : $originalUseUseNode->name->getLast(); + $shortNamePattern = preg_quote($shortName, '#'); + $pattern = sprintf(self::SHORT_NAME_REGEX, $shortNamePattern); + return StringUtils::isMatch($tag, $pattern); } } diff --git a/rules/CodingStyle/NodeFactory/ArrayCallableToMethodCallFactory.php b/rules/CodingStyle/NodeFactory/ArrayCallableToMethodCallFactory.php index 1b0564d9493..9ccc920a25d 100644 --- a/rules/CodingStyle/NodeFactory/ArrayCallableToMethodCallFactory.php +++ b/rules/CodingStyle/NodeFactory/ArrayCallableToMethodCallFactory.php @@ -4,16 +4,16 @@ namespace Rector\CodingStyle\NodeFactory; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Scalar\String_; -use PHPStan\Type\TypeWithClassName; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; -final class ArrayCallableToMethodCallFactory +final readonly class ArrayCallableToMethodCallFactory { public function __construct( private NodeTypeResolver $nodeTypeResolver @@ -45,8 +45,10 @@ public function create(Array_ $array): ?MethodCall return null; } - $firstItemType = $this->nodeTypeResolver->resolve($firstItem->value); - if (! $firstItemType instanceof TypeWithClassName) { + $firstItemType = $this->nodeTypeResolver->getType($firstItem->value); + $className = ClassNameFromObjectTypeResolver::resolve($firstItemType); + + if ($className === null) { return null; } diff --git a/rules/CodingStyle/NodeFactory/JsonArrayFactory.php b/rules/CodingStyle/NodeFactory/JsonArrayFactory.php deleted file mode 100644 index fd955986e6b..00000000000 --- a/rules/CodingStyle/NodeFactory/JsonArrayFactory.php +++ /dev/null @@ -1,83 +0,0 @@ -nodeFactory->createArray($array); - } - - /** - * @param Expr[] $placeholderNodes - */ - public function createFromJsonStringAndPlaceholders(string $jsonString, array $placeholderNodes): Array_ - { - $jsonArray = $this->createFromJsonString($jsonString); - $this->replaceNodeObjectHashPlaceholdersWithNodes($jsonArray, $placeholderNodes); - - return $jsonArray; - } - - /** - * @param Expr[] $placeholderNodes - */ - private function replaceNodeObjectHashPlaceholdersWithNodes(Array_ $array, array $placeholderNodes): void - { - // traverse and replace placeholder by original nodes - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($array, function (Node $node) use ( - $placeholderNodes - ): ?Expr { - if ($node instanceof Array_ && count($node->items) === 1) { - $onlyItem = $node->items[0]; - if (! $onlyItem instanceof ArrayItem) { - throw new ShouldNotHappenException(); - } - - $placeholderNode = $this->matchPlaceholderNode($onlyItem->value, $placeholderNodes); - - if ($placeholderNode && $this->implodeAnalyzer->isImplodeToJson($placeholderNode)) { - /** @var FuncCall $placeholderNode */ - return $placeholderNode->args[1]->value; - } - } - - return $this->matchPlaceholderNode($node, $placeholderNodes); - }); - } - - /** - * @param Expr[] $placeholderNodes - */ - private function matchPlaceholderNode(Node $node, array $placeholderNodes): ?Expr - { - if (! $node instanceof String_) { - return null; - } - - return $placeholderNodes[$node->value] ?? null; - } -} diff --git a/rules/CodingStyle/NodeFactory/JsonEncodeStaticCallFactory.php b/rules/CodingStyle/NodeFactory/JsonEncodeStaticCallFactory.php deleted file mode 100644 index 588ddb9544e..00000000000 --- a/rules/CodingStyle/NodeFactory/JsonEncodeStaticCallFactory.php +++ /dev/null @@ -1,41 +0,0 @@ -expr = $this->nodeFactory->createStaticCall('Nette\Utils\Json', 'encode', [$jsonDataVariable]); - - return $jsonDataAssign; - } -} diff --git a/rules/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector.php b/rules/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector.php new file mode 100644 index 00000000000..68dac2a5001 --- /dev/null +++ b/rules/CodingStyle/Rector/ArrowFunction/ArrowFunctionDelegatingCallToFirstClassCallableRector.php @@ -0,0 +1,89 @@ + Call::to($parameter); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +Call::to(...); +CODE_SAMPLE + , + )] + ); + } + + public function getNodeTypes(): array + { + return [ArrowFunction::class]; + } + + /** + * @param ArrowFunction $node + */ + public function refactor(Node $node): null|CallLike + { + if (! $node->expr instanceof FuncCall && ! $node->expr instanceof MethodCall && ! $node->expr instanceof StaticCall) { + return null; + } + + $callLike = $node->expr; + + // dynamic name? skip + if ($callLike->name instanceof Expr) { + return null; + } + + if ($this->arrowFunctionAndCLosureFirstClassCallableGuard->shouldSkip( + $node, + $callLike, + ScopeFetcher::fetch($node) + )) { + return null; + } + + // turn into first class callable + $callLike->args = [new VariadicPlaceholder()]; + + return $callLike; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::FIRST_CLASS_CALLABLE_SYNTAX; + } +} diff --git a/rules/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector.php b/rules/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector.php new file mode 100644 index 00000000000..a1ea91399b7 --- /dev/null +++ b/rules/CodingStyle/Rector/ArrowFunction/StaticArrowFunctionRector.php @@ -0,0 +1,62 @@ + 'test'; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +static fn (): string => 'test'; +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ArrowFunction::class]; + } + + /** + * @param ArrowFunction $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->staticGuard->isLegal($node)) { + return null; + } + + $node->static = true; + return $node; + } +} diff --git a/rules/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector.php b/rules/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector.php deleted file mode 100644 index 455a73d28f4..00000000000 --- a/rules/CodingStyle/Rector/Assign/ManualJsonStringToJsonEncodeArrayRector.php +++ /dev/null @@ -1,318 +0,0 @@ -[^\"])(?____\w+____)#'; - - /** - * @var string - * @see https://regex101.com/r/jdJ6n9/1 - */ - private const JSON_STRING_REGEX = '#{(.*?\:.*?)}#s'; - - /** - * @var string - */ - private const JSON_DATA = 'jsonData'; - - public function __construct( - private ConcatJoiner $concatJoiner, - private ConcatManipulator $concatManipulator, - private JsonEncodeStaticCallFactory $jsonEncodeStaticCallFactory, - private JsonArrayFactory $jsonArrayFactory, - private ReflectionProvider $reflectionProvider, - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Convert manual JSON string to JSON::encode array', [ - new CodeSample( - <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run() - { - $someJsonAsString = '{"role_name":"admin","numberz":{"id":"10"}}'; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -use Nette\Utils\Json; - -final class SomeClass -{ - public function run() - { - $data = [ - 'role_name' => 'admin', - 'numberz' => ['id' => 10] - ]; - $someJsonAsString = Json::encode($data); - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Assign::class]; - } - - /** - * @param Assign $node - */ - public function refactor(Node $node): ?Node - { - if ($node->expr instanceof String_) { - $stringValue = $node->expr->value; - - // A. full json string - $isJsonString = $this->isJsonString($stringValue); - if ($isJsonString) { - $jsonArray = $this->jsonArrayFactory->createFromJsonString($stringValue); - $jsonEncodeAssign = $this->createJsonEncodeAssign($node->var, $jsonArray); - - $jsonDataVariable = new Variable(self::JSON_DATA); - $jsonDataAssign = new Assign($jsonDataVariable, $jsonArray); - - $this->addNodeBeforeNode($jsonDataAssign, $node); - - return $jsonEncodeAssign; - } - - // B. just start of a json? join with all the strings that concat so same variable - $concatExpressionJoinData = $this->collectContentAndPlaceholderNodesFromNextExpressions($node); - - $stringValue .= $concatExpressionJoinData->getString(); - - return $this->removeNodesAndCreateJsonEncodeFromStringValue( - $concatExpressionJoinData->getNodesToRemove(), - $stringValue, - $concatExpressionJoinData->getPlaceholdersToNodes(), - $node - ); - } - - if ($node->expr instanceof Concat) { - // process only first concat - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Concat) { - return null; - } - - $concatStringAndPlaceholders = $this->concatJoiner->joinToStringAndPlaceholderNodes($node->expr); - - // B. just start of a json? join with all the strings that concat so same variable - $concatExpressionJoinData = $this->collectContentAndPlaceholderNodesFromNextExpressions($node); - - $placeholderNodes = array_merge( - $concatStringAndPlaceholders->getPlaceholderNodes(), - $concatExpressionJoinData->getPlaceholdersToNodes() - ); - - $stringValue = $concatStringAndPlaceholders->getContent(); - $stringValue .= $concatExpressionJoinData->getString(); - - return $this->removeNodesAndCreateJsonEncodeFromStringValue( - $concatExpressionJoinData->getNodesToRemove(), - $stringValue, - $placeholderNodes, - $node - ); - } - - return null; - } - - private function isJsonString(string $stringValue): bool - { - if (! (bool) Strings::match($stringValue, self::JSON_STRING_REGEX)) { - return false; - } - - try { - return (bool) Json::decode($stringValue, Json::FORCE_ARRAY); - } catch (JsonException) { - return false; - } - } - - private function collectContentAndPlaceholderNodesFromNextExpressions(Assign $assign): ConcatExpressionJoinData - { - $concatExpressionJoinData = new ConcatExpressionJoinData(); - - $currentNode = $assign; - - while ($nextExprAndConcatItem = $this->matchNextExprAssignConcatToSameVariable($assign->var, $currentNode)) { - $concatItemNode = $nextExprAndConcatItem->getConcatItemNode(); - - if ($concatItemNode instanceof String_) { - $concatExpressionJoinData->addString($concatItemNode->value); - } elseif ($concatItemNode instanceof Concat) { - $joinToStringAndPlaceholderNodes = $this->concatJoiner->joinToStringAndPlaceholderNodes( - $concatItemNode - ); - - $content = $joinToStringAndPlaceholderNodes->getContent(); - $concatExpressionJoinData->addString($content); - - foreach ($joinToStringAndPlaceholderNodes->getPlaceholderNodes() as $placeholder => $expr) { - /** @var string $placeholder */ - $concatExpressionJoinData->addPlaceholderToNode($placeholder, $expr); - } - } elseif ($concatItemNode instanceof Expr) { - $objectHash = '____' . spl_object_hash($concatItemNode) . '____'; - - $concatExpressionJoinData->addString($objectHash); - $concatExpressionJoinData->addPlaceholderToNode($objectHash, $concatItemNode); - } - - $concatExpressionJoinData->addNodeToRemove($nextExprAndConcatItem->getRemovedExpr()); - - // jump to next one - $currentNode = $this->getNextExpression($currentNode); - if (! $currentNode instanceof Node) { - return $concatExpressionJoinData; - } - } - - return $concatExpressionJoinData; - } - - /** - * @param Node[] $nodesToRemove - * @param Expr[] $placeholderNodes - */ - private function removeNodesAndCreateJsonEncodeFromStringValue( - array $nodesToRemove, - string $stringValue, - array $placeholderNodes, - Assign $assign - ): ?Assign { - $stringValue = Strings::replace($stringValue, self::UNQUOTED_OBJECT_HASH_REGEX, '$1"$2"'); - if (! $this->isJsonString($stringValue)) { - return null; - } - - $this->removeNodes($nodesToRemove); - - $jsonArray = $this->jsonArrayFactory->createFromJsonStringAndPlaceholders($stringValue, $placeholderNodes); - $jsonDataVariable = new Variable(self::JSON_DATA); - $jsonDataAssign = new Assign($jsonDataVariable, $jsonArray); - - $this->addNodeBeforeNode($jsonDataAssign, $assign); - - return $this->createJsonEncodeAssign($assign->var, $jsonArray); - } - - private function matchNextExprAssignConcatToSameVariable( - Expr $expr, - Assign | ConcatAssign | Expression | Node $currentNode - ): ?NodeToRemoveAndConcatItem { - $nextExpression = $this->getNextExpression($currentNode); - if (! $nextExpression instanceof Expression) { - return null; - } - - $nextExpressionNode = $nextExpression->expr; - if ($nextExpressionNode instanceof ConcatAssign) { - // is assign to same variable? - if (! $this->nodeComparator->areNodesEqual($expr, $nextExpressionNode->var)) { - return null; - } - - return new NodeToRemoveAndConcatItem($nextExpressionNode, $nextExpressionNode->expr); - } - - // $value = $value . '...'; - if ($nextExpressionNode instanceof Assign) { - if (! $nextExpressionNode->expr instanceof Concat) { - return null; - } - - // is assign to same variable? - if (! $this->nodeComparator->areNodesEqual($expr, $nextExpressionNode->var)) { - return null; - } - - $firstConcatItem = $this->concatManipulator->getFirstConcatItem($nextExpressionNode->expr); - - // is the first concat the same variable - if (! $this->nodeComparator->areNodesEqual($expr, $firstConcatItem)) { - return null; - } - - // return all but first node - $allButFirstConcatItem = $this->concatManipulator->removeFirstItemFromConcat($nextExpressionNode->expr); - - return new NodeToRemoveAndConcatItem($nextExpressionNode, $allButFirstConcatItem); - } - - return null; - } - - private function getNextExpression(Node $node): ?Node - { - $currentExpression = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - if (! $currentExpression instanceof Expression) { - return null; - } - - return $currentExpression->getAttribute(AttributeKey::NEXT_NODE); - } - - private function createJsonEncodeAssign(Expr $assignExpr, Array_ $jsonArray): Assign - { - if ($this->reflectionProvider->hasClass('Nette\Utils\Json')) { - return $this->jsonEncodeStaticCallFactory->createFromArray($assignExpr, $jsonArray); - } - - $jsonDataAssign = new Assign($assignExpr, $jsonArray); - $jsonDataVariable = new Variable(self::JSON_DATA); - $jsonDataAssign->expr = $this->nodeFactory->createFuncCall('json_encode', [$jsonDataVariable]); - - return $jsonDataAssign; - } -} diff --git a/rules/CodingStyle/Rector/Assign/NestedTernaryToMatchRector.php b/rules/CodingStyle/Rector/Assign/NestedTernaryToMatchRector.php new file mode 100644 index 00000000000..dc73567fbd7 --- /dev/null +++ b/rules/CodingStyle/Rector/Assign/NestedTernaryToMatchRector.php @@ -0,0 +1,161 @@ + 100 ? 'more than 100' : ($input > 5 ? 'more than 5' : 'less'); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function getValue($input) + { + return match (true) { + $input > 100 => 'more than 100', + $input > 5 => 'more than 5', + default => 'less', + }; + } +} +CODE_SAMPLE + ), + + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Assign::class]; + } + + /** + * @param Assign $node + */ + public function refactor(Node $node): ?Assign + { + if (! $node->expr instanceof Ternary) { + return null; + } + + $ternary = $node->expr; + + // traverse nested ternaries to collect them all + $currentTernary = $ternary; + + /** @var ConditionAndResult[] $conditionsAndResults */ + $conditionsAndResults = []; + $defaultExpr = null; + + while ($currentTernary instanceof Ternary) { + if (! $currentTernary->if instanceof Expr) { + // short ternary, skip + return null; + } + + $conditionsAndResults[] = new ConditionAndResult($currentTernary->cond, $currentTernary->if); + + $currentTernary = $currentTernary->else; + + if (! $currentTernary instanceof Ternary) { + $defaultExpr = $currentTernary; + } + } + + // nothing long enough + if (count($conditionsAndResults) < 2 || ! $defaultExpr instanceof Expr) { + return null; + } + + $match = $this->createMatch($conditionsAndResults, $defaultExpr); + $node->expr = $match; + + return $node; + } + + /** + * @param ConditionAndResult[] $conditionsAndResults + */ + private function createMatch(array $conditionsAndResults, Expr $defaultExpr): Match_ + { + $singleVariableName = $this->matchAlwaysIdenticalVariableName($conditionsAndResults); + if (is_string($singleVariableName)) { + $isVariableIdentical = true; + $match = new Match_(new Variable($singleVariableName)); + } else { + $isVariableIdentical = false; + $match = new Match_($this->nodeFactory->createTrue()); + } + + foreach ($conditionsAndResults as $conditionAndResult) { + $match->arms[] = new MatchArm([ + $isVariableIdentical ? $conditionAndResult->getIdenticalExpr() : $conditionAndResult->getConditionExpr(), + ], $conditionAndResult->getResultExpr()); + } + + $match->arms[] = new MatchArm(null, $defaultExpr); + + return $match; + } + + /** + * @param ConditionAndResult[] $conditionsAndResults + */ + private function matchAlwaysIdenticalVariableName(array $conditionsAndResults): mixed + { + $identicalVariableNames = []; + foreach ($conditionsAndResults as $conditionAndResult) { + if (! $conditionAndResult->isIdenticalCompare()) { + return null; + } + + $variableName = $conditionAndResult->getIdenticalVariableName(); + if (! is_string($variableName)) { + return null; + } + + $identicalVariableNames[] = $variableName; + } + + $uniqueIdenticalVariableNames = array_unique($identicalVariableNames); + $uniqueIdenticalVariableNames = array_values($uniqueIdenticalVariableNames); + + if (count($uniqueIdenticalVariableNames) === 1) { + return $uniqueIdenticalVariableNames[0]; + } + + return null; + } +} diff --git a/rules/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector.php b/rules/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector.php deleted file mode 100644 index 1449cec36a2..00000000000 --- a/rules/CodingStyle/Rector/Assign/PHPStormVarAnnotationRector.php +++ /dev/null @@ -1,170 +0,0 @@ -\$\w+)(?\s+)(?[\\\\\w]+)#'; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change various @var annotation formats to one PHPStorm understands', - [ - new CodeSample( - <<<'CODE_SAMPLE' -$config = 5; -/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */ -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */ -$config = 5; -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Assign::class]; - } - - /** - * @param Assign $node - */ - public function refactor(Node $node): ?Node - { - $expression = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - - // unable to analyze - if (! $expression instanceof Expression) { - return null; - } - - $nextNode = $expression->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Node) { - return null; - } - - $docContent = $this->getDocContent($nextNode); - if ($docContent === '') { - return null; - } - - if (! \str_contains($docContent, '@var')) { - return null; - } - - if (! $node->var instanceof Variable) { - return null; - } - - $varName = '$' . $this->getName($node->var); - $varPattern = '# ' . preg_quote($varName, '#') . ' #'; - if (! Strings::match($docContent, $varPattern)) { - return null; - } - - // switch docs - $expression->setDocComment($this->createDocComment($nextNode)); - - $expressionPhpDocInfo = $this->phpDocInfoFactory->createFromNode($expression); - $expression->setAttribute(AttributeKey::PHP_DOC_INFO, $expressionPhpDocInfo); - - // invoke override - $expression->setAttribute(AttributeKey::ORIGINAL_NODE, null); - - // remove otherwise empty node - if ($nextNode instanceof Nop) { - $this->removeNode($nextNode); - return null; - } - - // remove commnets - $nextNode->setAttribute(AttributeKey::PHP_DOC_INFO, null); - $nextNode->setAttribute(AttributeKey::COMMENTS, null); - - return $node; - } - - private function getDocContent(Node $node): string - { - $docComment = $node->getDocComment(); - if ($docComment !== null) { - return $docComment->getText(); - } - - if ($node->getComments() !== []) { - $docContent = ''; - foreach ($node->getComments() as $comment) { - $docContent .= $comment->getText(); - } - - return $docContent; - } - - return ''; - } - - private function createDocComment(Node $node): Doc - { - if ($node->getDocComment() !== null) { - return $node->getDocComment(); - } - - $docContent = $this->getDocContent($node); - - // normalize content - - // starts with "/*", instead of "/**" - if (\str_starts_with($docContent, '/* ')) { - $docContent = Strings::replace($docContent, self::SINGLE_ASTERISK_COMMENT_START_REGEX, '/** '); - } - - // $value is first, instead of type is first - if (Strings::match($docContent, self::VAR_ANNOTATION_REGEX)) { - $docContent = Strings::replace($docContent, self::VARIABLE_NAME_AND_TYPE_MATCH_REGEX, '$3$2$1'); - } - - return new Doc($docContent); - } -} diff --git a/rules/CodingStyle/Rector/Assign/SplitDoubleAssignRector.php b/rules/CodingStyle/Rector/Assign/SplitDoubleAssignRector.php index 6e3406fdce5..977d7ccdccb 100644 --- a/rules/CodingStyle/Rector/Assign/SplitDoubleAssignRector.php +++ b/rules/CodingStyle/Rector/Assign/SplitDoubleAssignRector.php @@ -6,14 +6,11 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\New_; -use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -58,50 +55,67 @@ public function run() */ public function getNodeTypes(): array { - return [Assign::class]; + return [Expression::class]; } /** - * @param Assign $node + * @param Expression $node + * @return Expression[]|null */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Expression) { + if (! $node->expr instanceof Assign) { return null; } - if (! $node->expr instanceof Assign) { + $firstAssign = $node->expr; + if (! $firstAssign->expr instanceof Assign) { return null; } - $newAssign = new Assign($node->var, $node->expr->expr); + $expr = $this->resolveLastAssignExpr($firstAssign); + $collectExpressions = $this->collectExpressions($firstAssign, $expr); - if (! $this->isExprCallOrNew($node->expr->expr)) { - $this->addNodeAfterNode($node->expr, $node); - return $newAssign; + if ($collectExpressions === []) { + return null; } - $varAssign = new Assign($node->expr->var, $node->var); - $this->addNodeBeforeNode(new Expression($newAssign), $node); - - return $varAssign; + return $collectExpressions; } - private function isExprCallOrNew(Expr $expr): bool + /** + * @return Expression[] + */ + private function collectExpressions(Assign $assign, Expr $expr): array { - if ($expr instanceof MethodCall) { - return true; - } + /** @var Expression[] $expressions */ + $expressions = []; - if ($expr instanceof StaticCall) { - return true; + while ($assign instanceof Assign) { + if ($assign->var instanceof ArrayDimFetch) { + return []; + } + + $expressions[] = new Expression(new Assign($assign->var, $expr)); + + // CallLike check need to be after first fill Expression + // so use existing variable defined to avoid repetitive call + if ($expr instanceof CallLike) { + $expr = $assign->var; + } + + $assign = $assign->expr; } - if ($expr instanceof FuncCall) { - return true; + return $expressions; + } + + private function resolveLastAssignExpr(Assign $assign): Expr + { + if (! $assign->expr instanceof Assign) { + return $assign->expr; } - return $expr instanceof New_; + return $this->resolveLastAssignExpr($assign->expr); } } diff --git a/rules/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector.php b/rules/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector.php index bfd66adfdf6..7864ddde1ca 100644 --- a/rules/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector.php +++ b/rules/CodingStyle/Rector/Catch_/CatchExceptionNameMatchingTypeRector.php @@ -7,12 +7,22 @@ use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\FunctionLike; +use PhpParser\Node\Name; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Catch_; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\TryCatch; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; +use PHPStan\Analyser\Scope; +use PHPStan\Type\ObjectType; +use Rector\Naming\Naming\PropertyNaming; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\FileNode; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,10 +32,14 @@ final class CatchExceptionNameMatchingTypeRector extends AbstractRector { /** - * @var string * @see https://regex101.com/r/xmfMAX/1 */ - private const STARTS_WITH_ABBREVIATION_REGEX = '#^([A-Za-z]+?)([A-Z]{1}[a-z]{1})([A-Za-z]*)#'; + private const string STARTS_WITH_ABBREVIATION_REGEX = '#^([A-Za-z]+?)([A-Z]{1}[a-z]{1})([A-Za-z]*)#'; + + public function __construct( + private readonly PropertyNaming $propertyNaming, + ) { + } public function getRuleDefinition(): RuleDefinition { @@ -34,30 +48,18 @@ public function getRuleDefinition(): RuleDefinition [ new CodeSample( <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - try { - // ... - } catch (SomeException $typoException) { - $typoException->getMessage(); - } - } +try { + // ... +} catch (SomeException $typoException) { + $typoException->getMessage(); } CODE_SAMPLE , <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - try { - // ... - } catch (SomeException $someException) { - $someException->getMessage(); - } - } +try { + // ... +} catch (SomeException $someException) { + $someException->getMessage(); } CODE_SAMPLE ), @@ -70,146 +72,228 @@ public function run() */ public function getNodeTypes(): array { - return [Catch_::class]; + return [ClassMethod::class, Function_::class, Closure::class, FileNode::class, Namespace_::class]; } /** - * @param Catch_ $node + * @param ClassMethod|Function_|Closure|FileNode|Namespace_ $node */ public function refactor(Node $node): ?Node { - if (count($node->types) !== 1) { + if ($node->stmts === null) { return null; } - if ($node->var === null) { + if ($node instanceof FileNode && $node->isNamespaced()) { + // handled in Namespace_ node return null; } - $oldVariableName = $this->getName($node->var); - if (! $oldVariableName) { - return null; + $hasChanged = false; + + foreach ($node->stmts as $key => $stmt) { + if ($this->shouldSkip($stmt)) { + continue; + } + + // variable defined first only resolvable by Scope pulled from Stmt + $scope = $stmt->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + continue; + } + + /** @var TryCatch $stmt */ + $catch = $stmt->catches[0]; + + /** @var Variable $catchVar */ + $catchVar = $catch->var; + + $oldVariableName = (string) $this->getName($catchVar); + + $typeShortName = $this->resolveVariableName($catch->types[0]); + $newVariableName = $this->resolveNewVariableName($typeShortName); + + $objectType = new ObjectType($newVariableName); + $newVariableName = $this->propertyNaming->fqnToVariableName($objectType); + + if ($oldVariableName === $newVariableName) { + continue; + } + + $isFoundInPrevious = $scope->hasVariableType($newVariableName) + ->yes(); + if ($isFoundInPrevious) { + return null; + } + + $catch->var = new Variable($newVariableName); + + $this->renameVariableInStmts( + $catch, + $oldVariableName, + $newVariableName, + $key, + $node->stmts, + $node->stmts[$key + 1] ?? null + ); + + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function shouldSkipFollowingCatch(Catch_ $catch, string $newVariableName, string $oldVariableName): bool + { + if ($catch->var instanceof Variable) { + $nextCatchVariableName = $this->getName($catch->var); + + return in_array($nextCatchVariableName, [$newVariableName, $oldVariableName], true); } - $type = $node->types[0]; - $typeShortName = $this->nodeNameResolver->getShortName($type); + if (count($catch->types) === 1) { + $soleType = $catch->types[0]->toString(); - $newVariableName = Strings::replace( + return lcfirst($soleType) === $newVariableName; + } + + return false; + } + + private function resolveNewVariableName(string $typeShortName): string + { + return Strings::replace( lcfirst($typeShortName), self::STARTS_WITH_ABBREVIATION_REGEX, - function (array $matches): string { - $output = ''; - - $output .= isset($matches[1]) ? strtolower($matches[1]) : ''; + static function (array $matches): string { + $output = isset($matches[1]) ? strtolower((string) $matches[1]) : ''; $output .= $matches[2] ?? ''; - $output .= $matches[3] ?? ''; - return $output; + return $output . ($matches[3] ?? ''); } ); + } - if ($oldVariableName === $newVariableName) { - return null; + private function shouldSkip(Stmt $stmt): bool + { + if (! $stmt instanceof TryCatch) { + return true; } - $newVariable = new Variable($newVariableName); - $isFoundInPrevious = (bool) $this->betterNodeFinder->findFirstPrevious( - $node, - fn (Node $n): bool => $this->nodeComparator->areNodesEqual($n, $newVariable) - ); - - if ($isFoundInPrevious) { - return null; + if (count($stmt->catches) !== 1) { + return true; } - $node->var->name = $newVariableName; - $this->renameVariableInStmts($node, $oldVariableName, $newVariableName); + if (count($stmt->catches[0]->types) !== 1) { + return true; + } - return $node; + $catch = $stmt->catches[0]; + return ! $catch->var instanceof Variable; } - private function renameVariableInStmts(Catch_ $catch, string $oldVariableName, string $newVariableName): void - { + /** + * @param Stmt[] $stmts + */ + private function renameVariableInStmts( + Catch_ $catch, + string $oldVariableName, + string $newVariableName, + int $key, + array $stmts, + ?Stmt $stmt + ): void { $this->traverseNodesWithCallable($catch->stmts, function (Node $node) use ( $oldVariableName, $newVariableName - ): void { + ): null { if (! $node instanceof Variable) { - return; + return null; } - if (! $this->nodeNameResolver->isName($node, $oldVariableName)) { - return; + if (! $this->isName($node, $oldVariableName)) { + return null; } $node->name = $newVariableName; + return null; }); - /** @var TryCatch $tryCatch */ - $tryCatch = $catch->getAttribute(AttributeKey::PARENT_NODE); - $next = $tryCatch->getAttribute(AttributeKey::NEXT_NODE); - - $this->replaceNextUsageVariable($tryCatch, $next, $oldVariableName, $newVariableName); + $this->replaceNextUsageVariable($oldVariableName, $newVariableName, $key, $stmts, $stmt); } + /** + * @param Stmt[] $stmts + */ private function replaceNextUsageVariable( - Node $currentNode, - ?Node $nextNode, string $oldVariableName, - string $newVariableName + string $newVariableName, + int $key, + array $stmts, + ?Node $nextNode, ): void { if (! $nextNode instanceof Node) { - $parent = $currentNode->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { - return; - } - if ($parent instanceof FunctionLike) { - return; - } - $nextNode = $parent->getAttribute(AttributeKey::NEXT_NODE); - $this->replaceNextUsageVariable($parent, $nextNode, $oldVariableName, $newVariableName); - return; } - /** @var Variable[] $variables */ - $variables = $this->betterNodeFinder->find($nextNode, function (Node $node) use ($oldVariableName): bool { + $nonAssignedVariables = []; + + $this->traverseNodesWithCallable($nextNode, function (Node $node) use ( + $oldVariableName, + $newVariableName, + &$nonAssignedVariables + ): ?int { + if ($node instanceof Assign && $node->var instanceof Variable) { + return NodeVisitor::STOP_TRAVERSAL; + } + + if ($node instanceof Catch_ && $this->shouldSkipFollowingCatch($node, $newVariableName, $oldVariableName)) { + return NodeVisitor::STOP_TRAVERSAL; + } + if (! $node instanceof Variable) { - return false; + return null; + } + + if (! $this->isName($node, $oldVariableName)) { + return null; } - return $this->nodeNameResolver->isName($node, $oldVariableName); + $nonAssignedVariables[] = $node; + return null; }); - $processRenameVariables = $this->processRenameVariable($variables, $oldVariableName, $newVariableName); - if (! $processRenameVariables) { + foreach ($nonAssignedVariables as $nonAssignedVariable) { + $nonAssignedVariable->name = $newVariableName; + } + + if (! isset($stmts[$key + 1])) { + return; + } + + if (! isset($stmts[$key + 2])) { return; } - $currentNode = $nextNode; - $nextNode = $nextNode->getAttribute(AttributeKey::NEXT_NODE); - $this->replaceNextUsageVariable($currentNode, $nextNode, $oldVariableName, $newVariableName); + $nextNode = $stmts[$key + 2]; + $key += 2; + + $this->replaceNextUsageVariable($oldVariableName, $newVariableName, $key, $stmts, $nextNode); } - /** - * @param Variable[] $variables - */ - private function processRenameVariable(array $variables, string $oldVariableName, string $newVariableName): bool + private function resolveVariableName(Name $name): string { - foreach ($variables as $variable) { - $parent = $variable->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Assign && $this->nodeComparator->areNodesEqual( - $parent->var, - $variable - ) && $this->nodeNameResolver->isName($parent->var, $oldVariableName) - && ! $this->nodeComparator->areNodesEqual($parent->expr, $variable) - ) { - return false; - } + $originalName = $name->getAttribute(AttributeKey::ORIGINAL_NAME); - $variable->name = $newVariableName; + // this allows to respect the name alias, if used + if ($originalName instanceof Name) { + return $originalName->toString(); } - return true; + return $name->toString(); } } diff --git a/rules/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector.php b/rules/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector.php new file mode 100644 index 00000000000..b9438c586d1 --- /dev/null +++ b/rules/CodingStyle/Rector/ClassConst/RemoveFinalFromConstRector.php @@ -0,0 +1,86 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->isFinal()) { + return null; + } + + $hasChanged = false; + foreach ($node->getConstants() as $classConst) { + if (! $classConst->isFinal()) { + continue; + } + + $this->visibilityManipulator->removeFinal($classConst); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::FINAL_CLASS_CONSTANTS; + } +} diff --git a/rules/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector.php b/rules/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector.php new file mode 100644 index 00000000000..7c87ebc0dfc --- /dev/null +++ b/rules/CodingStyle/Rector/ClassConst/SplitGroupedClassConstantsRector.php @@ -0,0 +1,88 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassConst::class]; + } + + /** + * @param ClassConst $node + * @return ClassConst[]|null + */ + public function refactor(Node $node): ?array + { + if (count($node->consts) < 2) { + return null; + } + + /** @var Const_[] $allConsts */ + $allConsts = $node->consts; + + /** @var Const_ $firstConst */ + $firstConst = array_shift($allConsts); + $node->consts = [$firstConst]; + + $nextClassConsts = $this->createNextClassConsts($allConsts, $node); + + return [$node, ...$nextClassConsts]; + } + + /** + * @param Const_[] $consts + * @return ClassConst[] + */ + private function createNextClassConsts(array $consts, ClassConst $classConst): array + { + $decoratedConsts = []; + + foreach ($consts as $const) { + $decoratedConsts[] = new ClassConst([$const], $classConst->flags, $classConst->getAttributes()); + } + + return $decoratedConsts; + } +} diff --git a/rules/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector.php b/rules/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector.php deleted file mode 100644 index c5c8e40f3e3..00000000000 --- a/rules/CodingStyle/Rector/ClassConst/SplitGroupedConstantsAndPropertiesRector.php +++ /dev/null @@ -1,123 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassConst::class, Property::class]; - } - - /** - * @param ClassConst|Property $node - * @return Node[]|null - */ - public function refactor(Node $node): ?array - { - if ($node instanceof ClassConst) { - if (count($node->consts) < 2) { - return null; - } - - /** @var Const_[] $allConsts */ - $allConsts = $node->consts; - - /** @var Const_ $firstConst */ - $firstConst = array_shift($allConsts); - $node->consts = [$firstConst]; - - $nextClassConsts = $this->createNextClassConsts($allConsts, $node); - - return array_merge([$node], $nextClassConsts); - } - - if (count($node->props) < 2) { - return null; - } - - $allProperties = $node->props; - /** @var PropertyProperty $firstPropertyProperty */ - $firstPropertyProperty = array_shift($allProperties); - $node->props = [$firstPropertyProperty]; - - $nextProperties = []; - foreach ($allProperties as $allProperty) { - $nextProperties[] = new Property($node->flags, [$allProperty], $node->getAttributes()); - } - - return [...[$node], ...$nextProperties]; - } - - /** - * @param Const_[] $consts - * @return ClassConst[] - */ - private function createNextClassConsts(array $consts, ClassConst $classConst): array - { - $decoratedConsts = []; - - foreach ($consts as $const) { - $decoratedConsts[] = new ClassConst([$const], $classConst->flags, $classConst->getAttributes()); - } - - return $decoratedConsts; - } -} diff --git a/rules/CodingStyle/Rector/ClassConst/VarConstantCommentRector.php b/rules/CodingStyle/Rector/ClassConst/VarConstantCommentRector.php deleted file mode 100644 index 059050c1d94..00000000000 --- a/rules/CodingStyle/Rector/ClassConst/VarConstantCommentRector.php +++ /dev/null @@ -1,158 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassConst::class]; - } - - /** - * @param ClassConst $node - */ - public function refactor(Node $node): ?Node - { - if (count($node->consts) > 1) { - return null; - } - - $constType = $this->getStaticType($node->consts[0]->value); - if ($constType instanceof MixedType) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - if ($this->shouldSkipConstantArrayType($constType, $phpDocInfo)) { - return null; - } - - if ($this->typeComparator->isSubtype($constType, $phpDocInfo->getVarType())) { - return null; - } - - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $constType); - - return $node; - } - - private function hasTwoAndMoreGenericClassStringTypes(ConstantArrayType $constantArrayType): bool - { - $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( - $constantArrayType, - TypeKind::RETURN() - ); - if (! $typeNode instanceof ArrayTypeNode) { - return false; - } - - if (! $typeNode->type instanceof UnionTypeNode) { - return false; - } - - $genericTypeNodeCount = 0; - foreach ($typeNode->type->types as $unionedTypeNode) { - if ($unionedTypeNode instanceof GenericTypeNode) { - ++$genericTypeNodeCount; - } - } - - return $genericTypeNodeCount > 1; - } - - /** - * Skip big arrays and mixed[] constants - */ - private function shouldSkipConstantArrayType(Type $constType, PhpDocInfo $phpDocInfo): bool - { - if (! $constType instanceof ConstantArrayType) { - return false; - } - - $currentVarType = $phpDocInfo->getVarType(); - if ($currentVarType instanceof ArrayType && $currentVarType->getItemType() instanceof MixedType) { - return true; - } - - if ($this->hasTwoAndMoreGenericClassStringTypes($constType)) { - return true; - } - - return $this->isHugeNestedConstantArrayTyp($constType); - } - - private function isHugeNestedConstantArrayTyp(ConstantArrayType $constantArrayType): bool - { - if (count($constantArrayType->getValueTypes()) <= 3) { - return false; - } - - foreach ($constantArrayType->getValueTypes() as $constValueType) { - if ($constValueType instanceof ConstantArrayType) { - return true; - } - } - - return false; - } -} diff --git a/rules/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector.php b/rules/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector.php new file mode 100644 index 00000000000..b6fa3e55087 --- /dev/null +++ b/rules/CodingStyle/Rector/ClassLike/NewlineBetweenClassLikeStmtsRector.php @@ -0,0 +1,127 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassLike::class]; + } + + /** + * @param ClassLike $node + */ + public function refactor(Node $node): ?ClassLike + { + return $this->processAddNewLine($node, false); + } + + private function processAddNewLine(ClassLike $classLike, bool $hasChanged, int $jumpToKey = 0): null|ClassLike + { + $totalKeys = array_key_last($classLike->stmts); + + for ($key = $jumpToKey; $key < $totalKeys; ++$key) { + if (! isset($classLike->stmts[$key], $classLike->stmts[$key + 1])) { + break; + } + + $stmt = $classLike->stmts[$key]; + $nextStmt = $classLike->stmts[$key + 1]; + + if ($stmt instanceof TraitUse && $nextStmt instanceof TraitUse) { + continue; + } + + if ($stmt instanceof EnumCase && $nextStmt instanceof EnumCase) { + continue; + } + + $endLine = $stmt->getEndLine(); + $rangeLine = $nextStmt->getStartLine() - $endLine; + + if ($rangeLine > 1) { + $rangeLine = $this->commentResolver->resolveRangeLineFromComment($rangeLine, $endLine, $nextStmt); + } + + // skip same line or < 0 that cause infinite loop or crash + if ($rangeLine !== 1) { + continue; + } + + array_splice($classLike->stmts, $key + 1, 0, [new Nop()]); + + $hasChanged = true; + + // iterate next + return $this->processAddNewLine($classLike, $hasChanged, $key + 2); + } + + if ($hasChanged) { + return $classLike; + } + + return null; + } +} diff --git a/rules/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector.php b/rules/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector.php new file mode 100644 index 00000000000..8ab71fb74a6 --- /dev/null +++ b/rules/CodingStyle/Rector/ClassMethod/BinaryOpStandaloneAssignsToDirectRector.php @@ -0,0 +1,176 @@ + 200; +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +function run() +{ + return 100 <=> 200; +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class, Closure::class]; + } + + /** + * @param ClassMethod|Function_|Closure $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + if (count($node->stmts) !== 3) { + return null; + } + + $firstStmt = $node->stmts[0]; + $secondStmt = $node->stmts[1]; + $thirdStmt = $node->stmts[2]; + + if (! $thirdStmt instanceof Return_) { + return null; + } + + $firstVariableAndExprAssign = $this->matchToVariableAssignExpr($firstStmt); + if (! $firstVariableAndExprAssign instanceof VariableAndExprAssign) { + return null; + } + + $secondVariableAndExprAssign = $this->matchToVariableAssignExpr($secondStmt); + if (! $secondVariableAndExprAssign instanceof VariableAndExprAssign) { + return null; + } + + if (! $thirdStmt->expr instanceof BinaryOp) { + return null; + } + + $binaryOp = $thirdStmt->expr; + + if (! $this->nodeComparator->areNodesEqual($binaryOp->left, $firstVariableAndExprAssign->getVariable())) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($binaryOp->right, $secondVariableAndExprAssign->getVariable())) { + return null; + } + + $resolveParamByRefVariables = $this->resolveParamByRefVariables($node); + if ($this->isNames($binaryOp->left, $resolveParamByRefVariables)) { + return null; + } + + if ($this->isNames($binaryOp->right, $resolveParamByRefVariables)) { + return null; + } + + $binaryOp->left = $firstVariableAndExprAssign->getExpr(); + $binaryOp->right = $secondVariableAndExprAssign->getExpr(); + + $node->stmts = [$thirdStmt]; + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::VARIADIC_PARAM; + } + + /** + * @return string[] + */ + private function resolveParamByRefVariables(ClassMethod|Function_|Closure $node): array + { + $paramByRefVariables = []; + foreach ($node->params as $param) { + if (! $param->var instanceof Variable) { + continue; + } + + if (! $param->byRef) { + continue; + } + + $paramByRefVariables[] = $this->getName($param); + } + + return $paramByRefVariables; + } + + private function matchToVariableAssignExpr(Stmt $stmt): ?VariableAndExprAssign + { + if (! $stmt instanceof Expression) { + return null; + } + + if (! $stmt->expr instanceof Assign) { + return null; + } + + $assign = $stmt->expr; + if (! $assign->var instanceof Variable) { + return null; + } + + // skip complex cases + if ($assign->expr instanceof CallLike && ! $assign->expr->isFirstClassCallable() && $assign->expr->getArgs() !== []) { + return null; + } + + if ($assign->expr instanceof BinaryOp) { + return null; + } + + return new VariableAndExprAssign($assign->var, $assign->expr); + } +} diff --git a/rules/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector.php b/rules/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector.php index 9f1b4589e41..d470bfb0fa9 100644 --- a/rules/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector.php +++ b/rules/CodingStyle/Rector/ClassMethod/FuncGetArgsToVariadicParamRector.php @@ -10,29 +10,30 @@ use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\FunctionLike; use PhpParser\Node\Param; -use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Function_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://3v4l.org/d4tBd - * * @see \Rector\Tests\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector\FuncGetArgsToVariadicParamRectorTest */ final class FuncGetArgsToVariadicParamRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly BetterNodeFinder $betterNodeFinder + ) { + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Refactor func_get_args() in to a variadic param', [ + return new RuleDefinition('Refactor `func_get_args()` in to a variadic param', [ new CodeSample( <<<'CODE_SAMPLE' function run() @@ -64,27 +65,43 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($node->params !== []) { + if ($node->params !== [] || $node->stmts === null) { return null; } - $assign = $this->matchFuncGetArgsVariableAssign($node); - if (! $assign instanceof Assign) { + /** @var Expression|null $expression */ + $expression = $this->matchFuncGetArgsVariableAssign($node); + if (! $expression instanceof Expression) { return null; } - if ($assign->var instanceof Variable) { - $variableName = $this->getName($assign->var); - if ($variableName === null) { - return null; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof Assign) { + continue; + } + + $assign = $stmt->expr; + if (! $this->isFuncGetArgsFuncCall($assign->expr)) { + continue; + } + + if ($assign->var instanceof Variable) { + /** @var string $variableName */ + $variableName = $this->getName($assign->var); + unset($node->stmts[$key]); + + return $this->applyVariadicParams($node, $variableName); } - return $this->removeOrChangeAssignToVariable($node, $assign, $variableName); + $assign->expr = new Variable('args'); + return $this->applyVariadicParams($node, 'args'); } - $variableName = 'args'; - $assign->expr = new Variable('args'); - return $this->applyVariadicParams($node, $assign, $variableName); + return null; } public function provideMinPhpVersion(): int @@ -94,12 +111,11 @@ public function provideMinPhpVersion(): int private function applyVariadicParams( ClassMethod | Function_ | Closure $node, - Assign $assign, string $variableName - ): ?Node { + ): ClassMethod | Function_ | Closure | null { $param = $this->createVariadicParam($variableName); - $variableParam = $param->var; - if ($variableParam instanceof Variable && $this->hasFunctionOrClosureInside($node, $variableParam)) { + + if ($param->var instanceof Variable && $this->hasFunctionOrClosureInside($node, $param->var)) { return null; } @@ -107,34 +123,6 @@ private function applyVariadicParams( return $node; } - private function removeOrChangeAssignToVariable( - ClassMethod | Function_ | Closure $node, - Assign $assign, - string $variableName - ): ?Node { - $parent = $assign->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Expression) { - $this->removeNode($assign); - return $this->applyVariadicParams($node, $assign, $variableName); - } - - $variable = $assign->var; - /** @var ClassMethod|Function_|Closure $functionLike */ - $functionLike = $this->betterNodeFinder->findParentType($parent, FunctionLike::class); - /** @var Stmt[] $stmts */ - $stmts = $functionLike->getStmts(); - $this->traverseNodesWithCallable($stmts, function (Node $node) use ($assign, $variable): ?Expr { - if (! $this->nodeComparator->areNodesEqual($node, $assign)) { - return null; - } - - return $variable; - }); - - $this->applyVariadicParams($functionLike, $assign, $variableName); - return $node; - } - private function hasFunctionOrClosureInside( ClassMethod | Function_ | Closure $functionLike, Variable $variable @@ -154,21 +142,31 @@ private function hasFunctionOrClosureInside( return false; } - $assign = $this->matchFuncGetArgsVariableAssign($node); - if (! $assign instanceof Assign) { + $expression = $this->matchFuncGetArgsVariableAssign($node); + if (! $expression instanceof Expression) { return false; } + /** @var Assign $assign */ + $assign = $expression->expr; return $this->nodeComparator->areNodesEqual($assign->var, $variable); }); } - private function matchFuncGetArgsVariableAssign(ClassMethod | Function_ | Closure $functionLike): ?Assign + /** + * @return Expression|null + */ + private function matchFuncGetArgsVariableAssign(ClassMethod | Function_ | Closure $functionLike): ?Expression { - /** @var Assign[] $assigns */ - $assigns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Assign::class); + /** @var Expression[] $expressions */ + $expressions = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, Expression::class); - foreach ($assigns as $assign) { + foreach ($expressions as $expression) { + if (! $expression->expr instanceof Assign) { + continue; + } + + $assign = $expression->expr; if (! $assign->expr instanceof FuncCall) { continue; } @@ -177,7 +175,7 @@ private function matchFuncGetArgsVariableAssign(ClassMethod | Function_ | Closur continue; } - return $assign; + return $expression; } return null; @@ -188,4 +186,13 @@ private function createVariadicParam(string $variableName): Param $variable = new Variable($variableName); return new Param($variable, null, null, false, true); } + + private function isFuncGetArgsFuncCall(Expr $expr): bool + { + if (! $expr instanceof FuncCall) { + return false; + } + + return $this->isName($expr, 'func_get_args'); + } } diff --git a/rules/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php b/rules/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php index da0be3fe7fd..3e95086f781 100644 --- a/rules/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php +++ b/rules/CodingStyle/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php @@ -5,27 +5,27 @@ namespace Rector\CodingStyle\Rector\ClassMethod; use PhpParser\Node; -use PhpParser\Node\Expr\New_; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Return_; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; use ReflectionMethod; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/RFYmn - * * @see \Rector\Tests\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector\MakeInheritedMethodVisibilitySameAsParentRectorTest */ final class MakeInheritedMethodVisibilitySameAsParentRector extends AbstractRector { + public function __construct( + private readonly VisibilityManipulator $visibilityManipulator, + private readonly ReflectionResolver $reflectionResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Make method visibility same as parent one', [ @@ -70,51 +70,68 @@ protected function run() */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($node->isMagic()) { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { return null; } - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - // possibly trait + if ($classReflection->isAnonymous()) { return null; } - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { + $parentClassReflections = $classReflection->getParents(); + + if ($parentClassReflections === []) { return null; } - /** @var string $methodName */ - $methodName = $this->getName($node->name); - - foreach ($classReflection->getParents() as $parentClassReflection) { - $nativeClassReflection = $parentClassReflection->getNativeReflection(); + $hasChanged = false; + $interfaces = $classReflection->getInterfaces(); - // the class reflection aboves takes also @method annotations into an account - if (! $nativeClassReflection->hasMethod($methodName)) { + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->isMagic()) { continue; } - $parentReflectionMethod = $nativeClassReflection->getMethod($methodName); - if ($this->isClassMethodCompatibleWithParentReflectionMethod($node, $parentReflectionMethod)) { - return null; - } + /** @var string $methodName */ + $methodName = $this->getName($classMethod->name); - if ($this->isConstructorWithStaticFactory($node, $methodName)) { - return null; + if ($classMethod->isPublic()) { + foreach ($interfaces as $interface) { + if ($interface->hasNativeMethod($methodName)) { + continue 2; + } + } } - $this->changeClassMethodVisibilityBasedOnReflectionMethod($node, $parentReflectionMethod); + foreach ($parentClassReflections as $parentClassReflection) { + $nativeClassReflection = $parentClassReflection->getNativeReflection(); + // the class reflection above takes also @method annotations into an account + if (! $nativeClassReflection->hasMethod($methodName)) { + continue; + } + + /** @var ReflectionMethod $parentReflectionMethod */ + $parentReflectionMethod = $nativeClassReflection->getMethod($methodName); + if ($this->isClassMethodCompatibleWithParentReflectionMethod($classMethod, $parentReflectionMethod)) { + continue 2; + } + + $this->changeClassMethodVisibilityBasedOnReflectionMethod($classMethod, $parentReflectionMethod); + $hasChanged = true; + } + } + + if ($hasChanged) { return $node; } @@ -132,50 +149,12 @@ private function isClassMethodCompatibleWithParentReflectionMethod( if ($reflectionMethod->isProtected() && $classMethod->isProtected()) { return true; } - if (! $reflectionMethod->isPrivate()) { - return false; - } - return $classMethod->isPrivate(); - } - - /** - * Parent constructor visibility override is allowed only since PHP 7.2+ - * @see https://3v4l.org/RFYmn - */ - private function isConstructorWithStaticFactory(ClassMethod $classMethod, string $methodName): bool - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PARENT_VISIBILITY_OVERRIDE)) { - return false; - } - if ($methodName !== MethodName::CONSTRUCT) { - return false; - } - - $classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { + if (! $reflectionMethod->isPrivate()) { return false; } - foreach ($classLike->getMethods() as $iteratedClassMethod) { - if (! $iteratedClassMethod->isPublic()) { - continue; - } - - if (! $iteratedClassMethod->isStatic()) { - continue; - } - - $isStaticSelfFactory = $this->isStaticNamedConstructor($iteratedClassMethod); - - if (! $isStaticSelfFactory) { - continue; - } - - return true; - } - - return false; + return $classMethod->isPrivate(); } private function changeClassMethodVisibilityBasedOnReflectionMethod( @@ -196,33 +175,4 @@ private function changeClassMethodVisibilityBasedOnReflectionMethod( $this->visibilityManipulator->makePrivate($classMethod); } } - - /** - * Looks for: - * public static someMethod() { return new self(); } - * or - * public static someMethod() { return new static(); } - */ - private function isStaticNamedConstructor(ClassMethod $classMethod): bool - { - if (! $classMethod->isPublic()) { - return false; - } - - if (! $classMethod->isStatic()) { - return false; - } - - return (bool) $this->betterNodeFinder->findFirst($classMethod, function (Node $node): bool { - if (! $node instanceof Return_) { - return false; - } - - if (! $node->expr instanceof New_) { - return false; - } - - return $this->isNames($node->expr->class, ['self', 'static']); - }); - } } diff --git a/rules/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php b/rules/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php index 7bfb8029990..186d791fce4 100644 --- a/rules/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php +++ b/rules/CodingStyle/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php @@ -8,20 +8,23 @@ use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Nop; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\HTMLAverseRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector\NewlineBeforeNewAssignSetRectorTest */ -final class NewlineBeforeNewAssignSetRector extends AbstractRector +final class NewlineBeforeNewAssignSetRector extends AbstractRector implements HTMLAverseRectorInterface { private ?string $previousStmtVariableName = null; @@ -40,6 +43,10 @@ public function run() $value->setValue(5); $value2 = new Value; $value2->setValue(1); + $foo = new Value; + $foo->bar = 5; + $bar = new Value; + $bar->foo = 1; } } CODE_SAMPLE @@ -54,6 +61,12 @@ public function run() $value2 = new Value; $value2->setValue(1); + + $foo = new Value; + $foo->bar = 5; + + $bar = new Value; + $bar->foo = 1; } } CODE_SAMPLE @@ -92,6 +105,7 @@ public function refactor(Node $node): ?Node // insert newline before stmt $newStmts[] = new Nop(); } + $newStmts[] = $stmt; $this->previousPreviousStmtVariableName = $this->previousStmtVariableName; @@ -111,15 +125,32 @@ private function reset(): void private function resolveCurrentStmtVariableName(Stmt $stmt): ?string { - $stmt = $this->unwrapExpression($stmt); + if (! $stmt instanceof Expression) { + return null; + } - if ($stmt instanceof Assign || $stmt instanceof MethodCall) { - if ($this->shouldSkipLeftVariable($stmt)) { + $stmtExpr = $stmt->expr; + + if ($stmtExpr instanceof Assign || $stmtExpr instanceof MethodCall) { + if ($this->shouldSkipLeftVariable($stmtExpr)) { return null; } - if (! $stmt->var instanceof MethodCall && ! $stmt->var instanceof StaticCall) { - return $this->getName($stmt->var); + if (! $stmtExpr->var instanceof MethodCall && ! $stmtExpr->var instanceof StaticCall) { + $nodeVar = $stmtExpr->var; + + if ($nodeVar instanceof PropertyFetch) { + do { + $previous = $nodeVar; + $nodeVar = $nodeVar->var; + } while ($nodeVar instanceof PropertyFetch); + + if ($this->getName($nodeVar) === 'this') { + $nodeVar = $previous; + } + } + + return $this->getName($nodeVar); } } @@ -146,7 +177,7 @@ private function shouldSkipLeftVariable(Assign | MethodCall $node): bool } // local method call - return $this->nodeNameResolver->isName($node->var, 'this'); + return $this->isName($node->var, 'this'); } private function isNewVariableThanBefore(?string $currentStmtVariableName): bool @@ -179,6 +210,6 @@ private function isPrecededByEmptyLine(ClassMethod | Function_ | Closure $node, $previousNode = $node->stmts[$key - 1]; $currentNode = $node->stmts[$key]; - return abs($currentNode->getLine() - $previousNode->getLine()) >= 2; + return abs($currentNode->getStartLine() - $previousNode->getStartLine()) >= 2; } } diff --git a/rules/CodingStyle/Rector/ClassMethod/OrderAttributesRector.php b/rules/CodingStyle/Rector/ClassMethod/OrderAttributesRector.php deleted file mode 100644 index 3b11ca669cc..00000000000 --- a/rules/CodingStyle/Rector/ClassMethod/OrderAttributesRector.php +++ /dev/null @@ -1,128 +0,0 @@ - - */ - private array $attributesOrderByName = []; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Order attributes by desired names', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -#[Second] -#[First] -class Someclass -{ -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -#[First] -#[Second] -class Someclass -{ -} -CODE_SAMPLE -, - [ - self::ATTRIBUTES_ORDER => ['First', 'Second'], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ - Class_::class, - Property::class, - Param::class, - ClassMethod::class, - Function_::class, - Closure::class, - ArrowFunction::class, - ]; - } - - /** - * @param ClassMethod|Property|Function_|Closure|Param|Class_|ArrowFunction $node - */ - public function refactor(Node $node): ?Node - { - if ($node->attrGroups === []) { - return null; - } - - $originalAttrGroups = $node->attrGroups; - $currentAttrGroups = $originalAttrGroups; - - usort($currentAttrGroups, function ( - AttributeGroup $firstAttributeGroup, - AttributeGroup $secondAttributeGroup, - ): int { - $firstAttributePosition = $this->resolveAttributeGroupPosition($firstAttributeGroup); - $secondAttributePosition = $this->resolveAttributeGroupPosition($secondAttributeGroup); - - return $firstAttributePosition <=> $secondAttributePosition; - }); - - if ($currentAttrGroups === $originalAttrGroups) { - return null; - } - - $node->attrGroups = $currentAttrGroups; - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $attributesOrder = $configuration[self::ATTRIBUTES_ORDER] ?? []; - Assert::allString($attributesOrder); - - $this->attributesOrderByName = array_flip($attributesOrder); - } - - private function resolveAttributeGroupPosition(AttributeGroup $attributeGroup): int - { - $attrName = $this->getName($attributeGroup->attrs[0]->name); - - // 1000 makes the attribute last, as positioned attributes have a higher priority - return $this->attributesOrderByName[$attrName] ?? 1000; - } -} diff --git a/rules/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector.php b/rules/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector.php deleted file mode 100644 index f81058a04eb..00000000000 --- a/rules/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector.php +++ /dev/null @@ -1,95 +0,0 @@ -__getSurname(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function getName($anotherObject) - { - $anotherObject->getSurname(); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, MethodCall::class, StaticCall::class]; - } - - /** - * @param ClassMethod|MethodCall|StaticCall $node - */ - public function refactor(Node $node): ?Node - { - $methodName = $this->getName($node->name); - if ($methodName === null) { - return null; - } - - if (in_array($methodName, ObjectMagicMethods::METHOD_NAMES, true)) { - return null; - } - - if (! Strings::match($methodName, self::DOUBLE_UNDERSCORE_START_REGEX)) { - return null; - } - - $newName = Strings::substring($methodName, 2); - - if (is_numeric($newName[0])) { - return null; - } - - $node->name = new Identifier($newName); - - return $node; - } -} diff --git a/rules/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector.php b/rules/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector.php deleted file mode 100644 index 908ad7b4f06..00000000000 --- a/rules/CodingStyle/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector.php +++ /dev/null @@ -1,187 +0,0 @@ - [ - new ReturnArrayClassMethodToYield('PHPUnit\Framework\TestCase', '*provide*'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $hasChanged = false; - foreach ($this->methodsToYields as $methodToYield) { - if (! $this->isObjectType($node, $methodToYield->getObjectType())) { - continue; - } - - if (! $this->isName($node, $methodToYield->getMethod())) { - continue; - } - - $arrayNode = $this->collectReturnArrayNodesFromClassMethod($node); - if (! $arrayNode instanceof Array_) { - continue; - } - - $this->transformArrayToYieldsOnMethodNode($node, $arrayNode); - - $this->commentsMerger->keepParent($node, $arrayNode); - $hasChanged = true; - } - - if (! $hasChanged) { - return null; - } - - return $node; - } - - /** - * @param mixed[] $configuration - */ - public function configure(array $configuration): void - { - $methodsToYields = $configuration[self::METHODS_TO_YIELDS] ?? []; - Assert::allIsInstanceOf($methodsToYields, ReturnArrayClassMethodToYield::class); - $this->methodsToYields = $methodsToYields; - } - - private function collectReturnArrayNodesFromClassMethod(ClassMethod $classMethod): ?Array_ - { - if ($classMethod->stmts === null) { - return null; - } - - foreach ($classMethod->stmts as $statement) { - if ($statement instanceof Return_) { - $returnedExpr = $statement->expr; - if (! $returnedExpr instanceof Array_) { - continue; - } - - return $returnedExpr; - } - } - - return null; - } - - private function transformArrayToYieldsOnMethodNode(ClassMethod $classMethod, Array_ $array): void - { - $yieldNodes = $this->nodeTransformer->transformArrayToYields($array); - - // remove whole return node - $parentNode = $array->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - throw new ShouldNotHappenException(); - } - - $this->removeReturnTag($classMethod); - - // change return typehint - $classMethod->returnType = new FullyQualified('Iterator'); - - foreach ((array) $classMethod->stmts as $key => $classMethodStmt) { - if (! $classMethodStmt instanceof Return_) { - continue; - } - - unset($classMethod->stmts[$key]); - } - - $classMethod->stmts = array_merge((array) $classMethod->stmts, $yieldNodes); - } - - private function removeReturnTag(ClassMethod $classMethod): void - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - $phpDocInfo->removeByType(ReturnTagValueNode::class); - } -} diff --git a/rules/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector.php b/rules/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector.php deleted file mode 100644 index 65fc930df8c..00000000000 --- a/rules/CodingStyle/Rector/ClassMethod/UnSpreadOperatorRector.php +++ /dev/null @@ -1,207 +0,0 @@ -run(...$data); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(array $array) - { - } - - public function execute(array $data) - { - $this->run($data); - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, MethodCall::class]; - } - - /** - * @param ClassMethod|MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof ClassMethod) { - return $this->processUnspreadOperatorClassMethodParams($node); - } - - return $this->processUnspreadOperatorMethodCallArgs($node); - } - - private function processUnspreadOperatorClassMethodParams(ClassMethod $classMethod): ?ClassMethod - { - $spreadParams = $this->spreadVariablesCollector->resolveFromClassMethod($classMethod); - if ($spreadParams === []) { - return null; - } - - foreach ($spreadParams as $spreadParam) { - $spreadParam->variadic = false; - $spreadParam->type = new Identifier('array'); - $spreadParam->default = $this->nodeFactory->createArray([]); - } - - return $classMethod; - } - - private function processUnspreadOperatorMethodCallArgs(MethodCall $methodCall): ?MethodCall - { - $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromMethodCall($methodCall); - if (! $methodReflection instanceof MethodReflection) { - return null; - } - - // skip those in vendor - if ($this->vendorLocationDetector->detectFunctionLikeReflection($methodReflection)) { - return null; - } - - $spreadParameterReflections = $this->spreadVariablesCollector->resolveFromMethodReflection( - $methodReflection - ); - - if ($spreadParameterReflections === []) { - return null; - } - - $firstSpreadParamPosition = array_key_first($spreadParameterReflections); - $variadicArgs = $this->resolveVariadicArgsByVariadicParams($methodCall, $firstSpreadParamPosition); - - if ($this->hasUnpackedArgs($variadicArgs)) { - $this->changeArgToPacked($variadicArgs, $methodCall); - return $methodCall; - } - - if ($variadicArgs !== []) { - $array = $this->nodeFactory->createArray($variadicArgs); - - $spreadArg = $methodCall->args[$firstSpreadParamPosition] ?? null; - - // already set value - if ($spreadArg instanceof Arg && $spreadArg->value instanceof Array_) { - return null; - } - - if (count($variadicArgs) === 1) { - return null; - } - - $methodCall->args[$firstSpreadParamPosition] = new Arg($array); - $this->removeLaterArguments($methodCall, $firstSpreadParamPosition); - - return $methodCall; - } - - return null; - } - - /** - * @return Arg[] - */ - private function resolveVariadicArgsByVariadicParams(MethodCall $methodCall, int $firstSpreadParamPosition): array - { - $variadicArgs = []; - - foreach ($methodCall->args as $position => $arg) { - if ($position < $firstSpreadParamPosition) { - continue; - } - - $variadicArgs[] = $arg; - } - - return $variadicArgs; - } - - private function removeLaterArguments(MethodCall $methodCall, int $argumentPosition): void - { - $argCount = count($methodCall->args); - for ($i = $argumentPosition + 1; $i < $argCount; ++$i) { - unset($methodCall->args[$i]); - } - } - - /** - * @param Arg[] $variadicArgs - */ - private function changeArgToPacked(array $variadicArgs, MethodCall $methodCall): void - { - foreach ($variadicArgs as $position => $variadicArg) { - if ($variadicArg->unpack) { - $variadicArg->unpack = false; - $methodCall->args[$position] = $variadicArg; - } - } - } - - /** - * @param Arg[] $args - */ - private function hasUnpackedArgs(array $args): bool - { - foreach ($args as $arg) { - if ($arg->unpack) { - return true; - } - } - - return false; - } -} diff --git a/rules/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector.php b/rules/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector.php deleted file mode 100644 index 0dc50777f19..00000000000 --- a/rules/CodingStyle/Rector/Class_/AddArrayDefaultToArrayPropertyRector.php +++ /dev/null @@ -1,261 +0,0 @@ -values === null; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var int[] - */ - private $values = []; - - public function isEmpty() - { - return $this->values === []; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - $changedProperties = $this->collectPropertyNamesWithMissingDefaultArray($node); - if ($changedProperties === []) { - return null; - } - - $this->completeDefaultArrayToPropertyNames($node, $changedProperties); - - // $this->variable !== null && count($this->variable) > 0 → count($this->variable) > 0 - $this->clearNotNullBeforeCount($node, $changedProperties); - - // $this->variable === null → $this->variable === [] - $this->replaceNullComparisonOfArrayPropertiesWithArrayComparison($node, $changedProperties); - - return $node; - } - - /** - * @return string[] - */ - private function collectPropertyNamesWithMissingDefaultArray(Class_ $class): array - { - $propertyNames = []; - $this->traverseNodesWithCallable($class, function (Node $node) use (&$propertyNames) { - if (! $node instanceof PropertyProperty) { - return null; - } - - if ($node->default !== null) { - return null; - } - - $varType = $this->resolveVarType($node); - if (! $this->iterableTypeAnalyzer->detect($varType)) { - return null; - } - - $propertyNames[] = $this->getName($node); - - return null; - }); - - return $propertyNames; - } - - /** - * @param string[] $propertyNames - */ - private function completeDefaultArrayToPropertyNames(Class_ $class, array $propertyNames): void - { - $this->traverseNodesWithCallable($class, function (Node $class) use ($propertyNames): ?PropertyProperty { - if (! $class instanceof PropertyProperty) { - return null; - } - - if (! $this->isNames($class, $propertyNames)) { - return null; - } - - $class->default = new Array_(); - - return $class; - }); - } - - /** - * @param string[] $propertyNames - */ - private function clearNotNullBeforeCount(Class_ $class, array $propertyNames): void - { - $this->traverseNodesWithCallable($class, function (Node $node) use ($propertyNames): ?Expr { - if (! $node instanceof BooleanAnd) { - return null; - } - if (! $this->isLocalPropertyOfNamesNotIdenticalToNull($node->left, $propertyNames)) { - return null; - } - - $isNextNodeCountingProperty = (bool) $this->betterNodeFinder->findFirst($node->right, function (Node $node) use ( - $propertyNames - ): ?bool { - if (! $node instanceof FuncCall) { - return null; - } - - if (! $this->isName($node, 'count')) { - return null; - } - - if (! isset($node->args[0])) { - return null; - } - - $countedArgument = $node->args[0]->value; - if (! $countedArgument instanceof PropertyFetch) { - return null; - } - - return $this->isNames($countedArgument, $propertyNames); - }); - - if (! $isNextNodeCountingProperty) { - return null; - } - - return $node->right; - }); - } - - /** - * @param string[] $propertyNames - */ - private function replaceNullComparisonOfArrayPropertiesWithArrayComparison( - Class_ $class, - array $propertyNames - ): void { - // replace comparison to "null" with "[]" - $this->traverseNodesWithCallable($class, function (Node $node) use ($propertyNames): ?BinaryOp { - if (! $node instanceof BinaryOp) { - return null; - } - - if ($this->propertyFetchAnalyzer->isLocalPropertyOfNames( - $node->left, - $propertyNames - ) && $this->valueResolver->isNull($node->right)) { - $node->right = new Array_(); - } - - if ($this->propertyFetchAnalyzer->isLocalPropertyOfNames( - $node->right, - $propertyNames - ) && $this->valueResolver->isNull($node->left)) { - $node->left = new Array_(); - } - - return $node; - }); - } - - private function resolveVarType(PropertyProperty $propertyProperty): Type - { - /** @var Property $property */ - $property = $propertyProperty->getAttribute(AttributeKey::PARENT_NODE); - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - return $phpDocInfo->getVarType(); - } - - /** - * @param string[] $propertyNames - */ - private function isLocalPropertyOfNamesNotIdenticalToNull(Expr $expr, array $propertyNames): bool - { - if (! $expr instanceof NotIdentical) { - return false; - } - - if ($this->propertyFetchAnalyzer->isLocalPropertyOfNames( - $expr->left, - $propertyNames - ) && $this->valueResolver->isNull($expr->right)) { - return true; - } - - if (! $this->propertyFetchAnalyzer->isLocalPropertyOfNames($expr->right, $propertyNames)) { - return false; - } - - return $this->valueResolver->isNull($expr->left); - } -} diff --git a/rules/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector.php b/rules/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector.php new file mode 100644 index 00000000000..78efe5155e2 --- /dev/null +++ b/rules/CodingStyle/Rector/Closure/ClosureDelegatingCallToFirstClassCallableRector.php @@ -0,0 +1,97 @@ +stmts) !== 1 || ! $node->stmts[0] instanceof Return_) { + return null; + } + + $callLike = $node->stmts[0]->expr; + if (! $callLike instanceof FuncCall + && ! $callLike instanceof MethodCall + && ! $callLike instanceof StaticCall + ) { + return null; + } + + // dynamic name? skip + if ($callLike->name instanceof Expr) { + return null; + } + + if ($this->arrowFunctionAndClosureFirstClassCallableGuard->shouldSkip( + $node, + $callLike, + ScopeFetcher::fetch($node) + )) { + return null; + } + + $callLike->args = [new VariadicPlaceholder()]; + + return $callLike; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::FIRST_CLASS_CALLABLE_SYNTAX; + } +} diff --git a/rules/CodingStyle/Rector/Closure/StaticClosureRector.php b/rules/CodingStyle/Rector/Closure/StaticClosureRector.php new file mode 100644 index 00000000000..8d50fe1fec2 --- /dev/null +++ b/rules/CodingStyle/Rector/Closure/StaticClosureRector.php @@ -0,0 +1,79 @@ +> + */ + public function getNodeTypes(): array + { + return [Closure::class]; + } + + /** + * @param Closure $node + */ + public function refactor(Node $node): ?Node + { + if ($node->hasAttribute(AttributeKey::IS_CLOSURE_USES_THIS)) { + return null; + } + + if (! $this->staticGuard->isLegal($node)) { + return null; + } + + $node->static = true; + return $node; + } +} diff --git a/rules/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector.php b/rules/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector.php index dfe2b297688..dd176f40d0c 100644 --- a/rules/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector.php +++ b/rules/CodingStyle/Rector/Encapsed/EncapsedStringsToSprintfRector.php @@ -5,7 +5,6 @@ namespace Rector\CodingStyle\Rector\Encapsed; use Nette\Utils\Strings; -use const PHP_EOL; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr; @@ -13,25 +12,31 @@ use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\InterpolatedStringPart; use PhpParser\Node\Name; -use PhpParser\Node\Scalar\Encapsed; -use PhpParser\Node\Scalar\EncapsedStringPart; +use PhpParser\Node\Scalar\InterpolatedString; use PhpParser\Node\Scalar\String_; use PHPStan\Type\Type; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeTypeResolver\Node\AttributeKey; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector\EncapsedStringsToSprintfRectorTest */ -final class EncapsedStringsToSprintfRector extends AbstractRector +final class EncapsedStringsToSprintfRector extends AbstractRector implements ConfigurableRectorInterface { + /** + * @api + */ + public const string ALWAYS = 'always'; + /** * @var array>> */ - private const FORMAT_SPECIFIERS = [ + private const array FORMAT_SPECIFIERS = [ '%s' => ['PHPStan\Type\StringType'], '%d' => [ 'PHPStan\Type\Constant\ConstantIntegerType', @@ -40,6 +45,8 @@ final class EncapsedStringsToSprintfRector extends AbstractRector ], ]; + private bool $always = false; + private string $sprintfFormat = ''; /** @@ -47,31 +54,53 @@ final class EncapsedStringsToSprintfRector extends AbstractRector */ private array $argumentVariables = []; + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + $this->always = $configuration[self::ALWAYS] ?? false; + + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Convert enscaped {$string} to more readable sprintf', + 'Convert enscaped {$string} to more readable sprintf or concat, if no mask is used', [ - new CodeSample( + new ConfiguredCodeSample( <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run(string $format) - { - return "Unsupported format {$format}"; - } -} +echo "Unsupported format {$format} - use another"; + +echo "Try {$allowed}"; CODE_SAMPLE , <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run(string $format) - { - return sprintf('Unsupported format %s', $format); - } -} +echo sprintf('Unsupported format %s - use another', $format); + +echo 'Try ' . $allowed; +CODE_SAMPLE + , + [ + self::ALWAYS => false, + ] + ), + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +echo "Unsupported format {$format} - use another"; + +echo "Try {$allowed}"; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +echo sprintf('Unsupported format %s - use another', $format); + +echo sprintf('Try %s', $allowed); CODE_SAMPLE + , + [ + self::ALWAYS => true, + ] ), ] ); @@ -82,21 +111,25 @@ public function run(string $format) */ public function getNodeTypes(): array { - return [Encapsed::class]; + return [InterpolatedString::class]; } /** - * @param Encapsed $node + * @param InterpolatedString $node */ public function refactor(Node $node): ?Node { + if ($this->shouldSkip($node)) { + return null; + } + $this->sprintfFormat = ''; $this->argumentVariables = []; foreach ($node->parts as $part) { - if ($part instanceof EncapsedStringPart) { + if ($part instanceof InterpolatedStringPart) { $this->collectEncapsedStringPart($part); - } elseif ($part instanceof Expr) { + } else { $this->collectExpr($part); } } @@ -104,9 +137,28 @@ public function refactor(Node $node): ?Node return $this->createSprintfFuncCallOrConcat($this->sprintfFormat, $this->argumentVariables); } - private function collectEncapsedStringPart(EncapsedStringPart $encapsedStringPart): void + private function shouldSkip(InterpolatedString $interpolatedString): bool + { + if ($interpolatedString->hasAttribute(AttributeKey::DOC_LABEL)) { + return true; + } + + foreach ($interpolatedString->parts as $part) { + if (! $part instanceof InterpolatedStringPart) { + continue; + } + + if ($this->containsControlASCIIChar($part->value)) { + return true; + } + } + + return false; + } + + private function collectEncapsedStringPart(InterpolatedStringPart $interpolatedStringPart): void { - $stringValue = $encapsedStringPart->value; + $stringValue = $interpolatedStringPart->value; if ($stringValue === "\n") { $this->argumentVariables[] = new ConstFetch(new Name('PHP_EOL')); $this->sprintfFormat .= '%s'; @@ -118,7 +170,7 @@ private function collectEncapsedStringPart(EncapsedStringPart $encapsedStringPar private function collectExpr(Expr $expr): void { - $type = $this->nodeTypeResolver->resolve($expr); + $type = $this->nodeTypeResolver->getType($expr); $found = false; foreach (self::FORMAT_SPECIFIERS as $key => $types) { @@ -143,21 +195,34 @@ private function collectExpr(Expr $expr): void /** * @param Expr[] $argumentVariables - * @return Concat|FuncCall|null */ - private function createSprintfFuncCallOrConcat(string $string, array $argumentVariables): ?Node + private function createSprintfFuncCallOrConcat(string $mask, array $argumentVariables): Concat|FuncCall|Expr|null { - // special case for variable with PHP_EOL - if ($string === '%s%s' && count($argumentVariables) === 2 && $this->hasEndOfLine($argumentVariables)) { - return new Concat($argumentVariables[0], $argumentVariables[1]); + $bareMask = str_repeat('%s', count($argumentVariables)); + + if ($mask === $bareMask) { + if (count($argumentVariables) === 1) { + return $argumentVariables[0]; + } + + return $this->nodeFactory->createConcat($argumentVariables); + } + + if (! $this->always) { + $singleValueConcat = $this->createSingleValueEdgeConcat($argumentVariables, $mask); + if ($singleValueConcat instanceof Concat) { + return $singleValueConcat; + } } // checks for windows or linux line ending. \n is contained in both. - if (\str_contains($string, "\n")) { + if (\str_contains($mask, "\n")) { return null; } - $arguments = [new Arg(new String_($string))]; + $string = $this->createString($mask); + + $arguments = [new Arg($string)]; foreach ($argumentVariables as $argumentVariable) { $arguments[] = new Arg($argumentVariable); } @@ -168,18 +233,42 @@ private function createSprintfFuncCallOrConcat(string $string, array $argumentVa /** * @param Expr[] $argumentVariables */ - private function hasEndOfLine(array $argumentVariables): bool + private function createSingleValueEdgeConcat(array $argumentVariables, string $mask): ?Concat { - foreach ($argumentVariables as $argumentVariable) { - if (! $argumentVariable instanceof ConstFetch) { - continue; - } + if (count($argumentVariables) !== 1) { + return null; + } - if ($this->isName($argumentVariable, 'PHP_EOL')) { - return true; - } + if (substr_count($mask, '%s') !== 1 && substr_count($mask, '%d') !== 1) { + return null; } - return false; + $cleanMask = Strings::replace($mask, '#\%\%#', '%'); + + if (str_ends_with($mask, '%s') || str_ends_with($mask, '%d')) { + $bareString = new String_(substr($cleanMask, 0, -2)); + return new Concat($bareString, $argumentVariables[0]); + } + + if (str_starts_with($mask, '%s') || str_starts_with($mask, '%d')) { + $bareString = new String_(substr($cleanMask, 2)); + return new Concat($argumentVariables[0], $bareString); + } + + return null; + } + + private function createString(string $value): String_ + { + $kind = str_contains($value, "'") ? String_::KIND_DOUBLE_QUOTED : String_::KIND_SINGLE_QUOTED; + + return new String_($value, [ + 'kind' => $kind, + ]); + } + + private function containsControlASCIIChar(string $content): bool + { + return (bool) Strings::match($content, '#[\x00-\x08\x0B\x0C\x0E-\x1F]#'); } } diff --git a/rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php b/rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php index f5a4ac1d9a0..3d039823d08 100644 --- a/rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php +++ b/rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php @@ -6,9 +6,9 @@ use PhpParser\Node; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Scalar\Encapsed; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\Node\Scalar\InterpolatedString; +use PhpParser\Token; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -43,26 +43,29 @@ function run($world) */ public function getNodeTypes(): array { - return [Encapsed::class]; + return [InterpolatedString::class]; } /** - * @param Encapsed $node + * @param InterpolatedString $node */ public function refactor(Node $node): ?Node { - $startTokenPos = $node->getStartTokenPos(); $hasVariableBeenWrapped = false; + $oldTokens = $this->getFile() + ->getOldTokens(); foreach ($node->parts as $index => $nodePart) { - if ($nodePart instanceof Variable) { - $previousNode = $nodePart->getAttribute(AttributeKey::PREVIOUS_NODE); - $previousNodeEndTokenPosition = $previousNode instanceof Node ? $previousNode->getEndTokenPos() : $startTokenPos; + if ($nodePart instanceof Variable && $nodePart->getStartTokenPos() >= 0) { + $start = $oldTokens[$nodePart->getStartTokenPos() - 1] ?? null; + $end = $oldTokens[$nodePart->getEndTokenPos() + 1] ?? null; - if ($previousNodeEndTokenPosition + 1 === $nodePart->getStartTokenPos()) { - $hasVariableBeenWrapped = true; - $node->parts[$index] = new Variable($nodePart->name); + if ($start instanceof Token && $end instanceof Token && $start->text === '{' && $end->text === '}') { + continue; } + + $hasVariableBeenWrapped = true; + $node->parts[$index] = new Variable($nodePart->name); } } diff --git a/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php b/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php new file mode 100644 index 00000000000..e3b48e80ded --- /dev/null +++ b/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php @@ -0,0 +1,218 @@ +refactorEnum($node); + } + + if ($node instanceof ClassConstFetch) { + return $this->refactorClassConstFetch($node); + } + + return null; + } + + public function refactorEnum(Enum_ $enum): Enum_|null + { + $enumName = $this->getName($enum); + if ($enumName === null) { + return null; + } + + $hasChanged = false; + + foreach ($enum->stmts as $stmt) { + if (! $stmt instanceof EnumCase) { + continue; + } + + $currentName = $stmt->name->toString(); + $pascalCaseName = $this->convertToPascalCase($currentName); + + if ($currentName === $pascalCaseName) { + continue; + } + + $stmt->name = new Identifier($pascalCaseName); + $hasChanged = true; + } + + return $hasChanged ? $enum : null; + } + + private function refactorClassConstFetch(ClassConstFetch $classConstFetch): ?Node + { + if (! $classConstFetch->class instanceof Name) { + return null; + } + + if (! $classConstFetch->name instanceof Identifier) { + return null; + } + + $constName = $classConstFetch->name->toString(); + $pascalCaseName = $this->convertToPascalCase($constName); + + // short circuit if already in pascal case + if ($constName === $pascalCaseName) { + return null; + } + + $classReflection = $this->nodeTypeResolver->getType($classConstFetch->class) + ->getObjectClassReflections()[0] ?? null; + + if ($classReflection === null || ! $classReflection->isEnum()) { + return null; + } + + if (! $this->isEnumCase($classReflection, $constName, $pascalCaseName)) { + return null; + } + + if ($this->isUsedOutsideOfProject($classConstFetch->class)) { + return null; + } + + $classConstFetch->name = new Identifier($pascalCaseName); + return $classConstFetch; + } + + private function isUsedOutsideOfProject(Name $name): bool + { + if (in_array($name->toString(), ['self', 'static'], true)) { + return false; + } + + $sourceLocator = $this->dynamicSourceLocatorProvider->provide(); + $defaultReflector = new DefaultReflector($sourceLocator); + + try { + $classIdentifier = $defaultReflector->reflectClass($name->toString()); + } catch (IdentifierNotFound) { + // source is outside the paths defined in withPaths(), eg: vendor + return true; + } + + $fileTarget = $classIdentifier->getFileName(); + + // possibly native + if ($fileTarget === null) { + return true; + } + + $autoloadPaths = SimpleParameterProvider::provideArrayParameter(Option::AUTOLOAD_PATHS); + $normalizedFileTarget = PathNormalizer::normalize((string) realpath($fileTarget)); + + foreach ($autoloadPaths as $autoloadPath) { + $normalizedAutoloadPath = PathNormalizer::normalize($autoloadPath); + + if ($autoloadPath === $fileTarget) { + return true; + } + + if (str_starts_with($normalizedFileTarget, $normalizedAutoloadPath . '/')) { + return true; + } + } + + return false; + } + + private function isEnumCase(ClassReflection $classReflection, string $name, string $pascalName): bool + { + // the enum case might have already been renamed, need to check both + if ($classReflection->hasEnumCase($name)) { + return true; + } + + return $classReflection->hasEnumCase($pascalName); + } + + private function convertToPascalCase(string $name): string + { + $parts = explode('_', $name); + return implode( + '', + array_map( + fn ($part): string => + // If part is all uppercase, convert to ucfirst(strtolower()) + // If part is already mixed or PascalCase, keep as is except ucfirst + ctype_upper($part) + ? ucfirst(strtolower($part)) + : ucfirst($part), + $parts + ) + ); + } +} diff --git a/rules/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector.php b/rules/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector.php new file mode 100644 index 00000000000..cdcf424659f --- /dev/null +++ b/rules/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector.php @@ -0,0 +1,203 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($this->isName($node, 'array_merge')) { + return $this->refactorArray($node); + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_SPREAD; + } + + private function refactorArray(FuncCall $funcCall): ?Array_ + { + if ($funcCall->isFirstClassCallable()) { + return null; + } + + $array = new Array_(); + + foreach ($funcCall->args as $arg) { + if (! $arg instanceof Arg) { + continue; + } + + // cannot handle unpacked arguments + if ($arg->unpack) { + return null; + } + + $value = $arg->value; + if ($this->shouldSkipArrayForInvalidKeys($value)) { + return null; + } + + if ($value instanceof Array_) { + $array->items = [...$array->items, ...$value->items]; + + continue; + } + + $value = $this->resolveValue($value); + $array->items[] = $this->createUnpackedArrayItem($value); + } + + return $array; + } + + private function shouldSkipArrayForInvalidKeys(Expr $expr): bool + { + $type = $this->getType($expr); + + if ($type->getIterableKeyType()->isInteger()->yes()) { + // when on PHP 8.0+, pass non-array values already error on the first place + // this check avoid unpack non-array values that cause error on php 7.4 as well, + // @see https://3v4l.org/DuYHu#v7.4.33 + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::ARRAY_ON_ARRAY_MERGE)) { + $nativeType = $this->nodeTypeResolver->getNativeType($expr); + return ! $nativeType->isArray() + ->yes(); + } + + return false; + } + + // php 8.1+ allow mixed key: int, string, and null + return ! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::ARRAY_SPREAD_STRING_KEYS); + } + + private function resolveValue(Expr $expr): Expr + { + if ($expr instanceof FuncCall && $this->isIteratorToArrayFuncCall($expr)) { + /** @var Arg $arg */ + $arg = $expr->args[0]; + /** @var FuncCall $expr */ + $expr = $arg->value; + } + + if (! $expr instanceof Ternary) { + return $expr; + } + + if (! $expr->cond instanceof FuncCall) { + return $expr; + } + + if (! $this->isName($expr->cond, 'is_array')) { + return $expr; + } + + if ($expr->if instanceof Variable && $this->isIteratorToArrayFuncCall($expr->else)) { + return $expr->if; + } + + return $expr; + } + + private function createUnpackedArrayItem(Expr $expr): ArrayItem + { + return new ArrayItem($expr, null, false, [], true); + } + + private function isIteratorToArrayFuncCall(Expr $expr): bool + { + if (! $expr instanceof FuncCall) { + return false; + } + + if (! $this->isName($expr, 'iterator_to_array')) { + return false; + } + + if ($expr->isFirstClassCallable()) { + return false; + } + + return isset($expr->getArgs()[0]); + } +} diff --git a/rules/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector.php b/rules/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector.php index 8a6c93b7e11..74774df94aa 100644 --- a/rules/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector.php +++ b/rules/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector.php @@ -12,28 +12,27 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Scalar\String_; use Rector\CodingStyle\NodeFactory\ArrayCallableToMethodCallFactory; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://www.php.net/manual/en/function.call-user-func-array.php#117655 - * @changelog https://3v4l.org/CBWt9 - * * @see \Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\CallUserFuncArrayToVariadicRectorTest */ final class CallUserFuncArrayToVariadicRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private ArrayCallableToMethodCallFactory $arrayCallableToMethodCallFactory + private readonly ArrayCallableToMethodCallFactory $arrayCallableToMethodCallFactory, + private readonly ValueResolver $valueResolver, ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Replace call_user_func_array() with variadic', [ + return new RuleDefinition('Replace `call_user_func_array()` with variadic', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -75,8 +74,14 @@ public function refactor(Node $node): ?Node return null; } - $firstArgValue = $node->args[0]->value; - $secondArgValue = $node->args[1]->value; + if ($node->isFirstClassCallable()) { + return null; + } + + $firstArgValue = $node->getArgs()[0] + ->value; + $secondArgValue = $node->getArgs()[1] + ->value; if ($firstArgValue instanceof String_) { $functionName = $this->valueResolver->getValue($firstArgValue); @@ -88,6 +93,12 @@ public function refactor(Node $node): ?Node return $this->createMethodCall($firstArgValue, $secondArgValue); } + if ($firstArgValue instanceof MethodCall && $firstArgValue->isFirstClassCallable()) { + $firstArgValue->args = [$this->createUnpackedArg($secondArgValue)]; + + return $firstArgValue; + } + return null; } @@ -98,8 +109,7 @@ public function provideMinPhpVersion(): int private function createFuncCall(Expr $expr, string $functionName): FuncCall { - $args = []; - $args[] = $this->createUnpackedArg($expr); + $args = [$this->createUnpackedArg($expr)]; return $this->nodeFactory->createFuncCall($functionName, $args); } diff --git a/rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php b/rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php index 0549366d9fd..a4d7bf6d4b9 100644 --- a/rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php +++ b/rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php @@ -9,25 +9,23 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use Rector\CodingStyle\NodeFactory\ArrayCallableToMethodCallFactory; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/a/1596250/1348344 - * * @see \Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector\CallUserFuncToMethodCallRectorTest */ final class CallUserFuncToMethodCallRector extends AbstractRector { public function __construct( - private ArrayCallableToMethodCallFactory $arrayCallableToMethodCallFactory + private readonly ArrayCallableToMethodCallFactory $arrayCallableToMethodCallFactory ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Refactor call_user_func() on known class method to a method call', [ + return new RuleDefinition('Refactor `call_user_func()` on known class method to a method call', [ new CodeSample( <<<'CODE_SAMPLE' final class SomeClass @@ -70,19 +68,29 @@ public function refactor(Node $node): ?Node return null; } - $firstArgValue = $node->args[0]->value; + if ($node->isFirstClassCallable()) { + return null; + } + + if (! isset($node->getArgs()[0])) { + return null; + } + + $firstArgValue = $node->getArgs()[0] + ->value; if (! $firstArgValue instanceof Array_) { return null; } + // remove first arg + $originalArgs = $node->getArgs(); + array_shift($originalArgs); + $methodCall = $this->arrayCallableToMethodCallFactory->create($firstArgValue); if (! $methodCall instanceof MethodCall) { return null; } - $originalArgs = $node->args; - unset($originalArgs[0]); - $methodCall->args = $originalArgs; return $methodCall; } diff --git a/rules/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php b/rules/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php new file mode 100644 index 00000000000..81300fb5181 --- /dev/null +++ b/rules/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php @@ -0,0 +1,152 @@ +method(...);'), + new CodeSample("Closure::fromCallable('trim');", 'trim(...);'), + new CodeSample( + "Closure::fromCallable(['SomeClass', 'staticMethod']);", + 'SomeClass::staticMethod(...);' + ), + ] + ); + + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [StaticCall::class]; + } + + /** + * @param StaticCall $node + */ + public function refactor(Node $node): ?Node + { + if ($this->shouldSkip($node)) { + return null; + } + + $arg = $node->args[0]; + if (! $arg instanceof Arg) { + return null; + } + + if ($arg->value instanceof String_) { + return new FuncCall($this->toFullyQualified($arg->value->value), [new VariadicPlaceholder()]); + } + + if ($arg->value instanceof Array_) { + $array = $arg->value; + + if ( + ! array_key_exists(0, $array->items) + || ! array_key_exists(1, $array->items) + || ! $array->items[1]->value instanceof String_ + ) { + return null; + } + + if ($array->items[0]->value instanceof Variable) { + return new MethodCall( + $array->items[0]->value, + $array->items[1]->value->value, + [new VariadicPlaceholder()], + ); + } + + if ($array->items[0]->value instanceof String_) { + $classNode = new FullyQualified($array->items[0]->value->value); + } elseif ($array->items[0]->value instanceof ClassConstFetch) { + if ($array->items[0]->value->class instanceof Expr) { + return null; + } + + if ($array->items[0]->value->class instanceof FullyQualified) { + $classNode = new FullyQualified($array->items[0]->value->class->name); + } else { + $classNode = new Name($array->items[0]->value->class->name); + } + + } elseif ($array->items[0]->value instanceof FullyQualified) { + $classNode = new FullyQualified($array->items[0]->value->name); + } else { + return null; + } + + return new StaticCall($classNode, $array->items[1]->value->value, [new VariadicPlaceholder()]); + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::FIRST_CLASS_CALLABLE_SYNTAX; + } + + public function shouldSkip(StaticCall $staticCall): bool + { + if (! $staticCall->class instanceof Name) { + return true; + } + + if (! $this->isName($staticCall->class, 'Closure')) { + return true; + } + + if (! $staticCall->name instanceof Identifier || $staticCall->name->name !== 'fromCallable') { + return true; + } + + if ($staticCall->isFirstClassCallable()) { + return true; + } + + $args = $staticCall->getArgs(); + return count($args) !== 1; + } + + public function toFullyQualified(string $functionName): FullyQualified + { + // in case there's already a \ prefix, remove it + $functionName = ltrim($functionName, '\\'); + + return new FullyQualified($functionName); + } +} diff --git a/rules/CodingStyle/Rector/FuncCall/ConsistentImplodeRector.php b/rules/CodingStyle/Rector/FuncCall/ConsistentImplodeRector.php index 45609c027e0..2140ec4dde2 100644 --- a/rules/CodingStyle/Rector/FuncCall/ConsistentImplodeRector.php +++ b/rules/CodingStyle/Rector/FuncCall/ConsistentImplodeRector.php @@ -8,20 +8,18 @@ use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; use Rector\NodeTypeResolver\TypeAnalyzer\StringTypeAnalyzer; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog http://php.net/manual/en/function.implode.php#refsect1-function.implode-description - * @see https://3v4l.org/iYTgh * @see \Rector\Tests\CodingStyle\Rector\FuncCall\ConsistentImplodeRector\ConsistentImplodeRectorTest */ final class ConsistentImplodeRector extends AbstractRector { public function __construct( - private StringTypeAnalyzer $stringTypeAnalyzer + private readonly StringTypeAnalyzer $stringTypeAnalyzer, ) { } @@ -38,8 +36,6 @@ public function run(array $items) { $itemsAsStrings = implode($items); $itemsAsStrings = implode($items, '|'); - - $itemsAsStrings = implode('|', $items); } } CODE_SAMPLE @@ -51,8 +47,6 @@ public function run(array $items) { $itemsAsStrings = implode('', $items); $itemsAsStrings = implode('|', $items); - - $itemsAsStrings = implode('|', $items); } } CODE_SAMPLE @@ -74,29 +68,41 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if (! $this->isName($node, 'implode')) { + if (! $this->isNames($node, ['implode', 'join'])) { + return null; + } + + if ($node->isFirstClassCallable()) { return null; } - if (count($node->args) === 1) { + if (count($node->getArgs()) === 1) { // complete default value '' - $node->args[1] = $node->args[0]; + $node->args[1] = $node->getArgs()[0]; $node->args[0] = new Arg(new String_('')); return $node; } - $firstArgumentValue = $node->args[0]->value; - if ($firstArgumentValue instanceof String_) { + $firstArg = $node->getArgs()[0]; + $firstArgumentValue = $firstArg->value; + + $firstArgumentType = $this->getType($firstArgumentValue); + if ($firstArgumentType->isString()->yes()) { + return null; + } + + if (count($node->getArgs()) !== 2) { return null; } - if (count($node->args) === 2 && $this->stringTypeAnalyzer->isStringOrUnionStringOnlyType( - $node->args[1]->value - )) { - $node->args = array_reverse($node->args); + $secondArg = $node->getArgs()[1]; + + if ($this->stringTypeAnalyzer->isStringOrUnionStringOnlyType($secondArg->value)) { + $node->args = array_reverse($node->getArgs()); + return $node; } - return $node; + return null; } } diff --git a/rules/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector.php b/rules/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector.php deleted file mode 100644 index c6c3d88c753..00000000000 --- a/rules/CodingStyle/Rector/FuncCall/ConsistentPregDelimiterRector.php +++ /dev/null @@ -1,183 +0,0 @@ -.*?)(?[imsxeADSUXJu]*)$#s'; - - /** - * All with pattern as 1st argument - * @var array - */ - private const FUNCTIONS_WITH_REGEX_PATTERN = [ - 'preg_match' => 0, - 'preg_replace_callback_array' => 0, - 'preg_replace_callback' => 0, - 'preg_replace' => 0, - 'preg_match_all' => 0, - 'preg_split' => 0, - 'preg_grep' => 0, - ]; - - /** - * All with pattern as 2st argument - * @var array> - */ - private const STATIC_METHODS_WITH_REGEX_PATTERN = [ - 'Nette\Utils\Strings' => [ - 'match' => 1, - 'matchAll' => 1, - 'replace' => 1, - 'split' => 1, - ], - ]; - - private string $delimiter = '#'; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Replace PREG delimiter with configured one', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - preg_match('~value~', $value); - preg_match_all('~value~im', $value); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - preg_match('#value#', $value); - preg_match_all('#value#im', $value); - } -} -CODE_SAMPLE -, - [ - self::DELIMITER => '#', - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [FuncCall::class, StaticCall::class]; - } - - /** - * @param FuncCall|StaticCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof FuncCall) { - return $this->refactorFuncCall($node); - } - - foreach (self::STATIC_METHODS_WITH_REGEX_PATTERN as $type => $methodsToPositions) { - if (! $this->isObjectType($node->class, new ObjectType($type))) { - continue; - } - - foreach ($methodsToPositions as $method => $position) { - if (! $this->isName($node->name, $method)) { - continue; - } - - $this->refactorArgument($node->args[$position]); - - return $node; - } - } - - return null; - } - - public function configure(array $configuration): void - { - $this->delimiter = $configuration[self::DELIMITER] ?? '#'; - } - - private function refactorFuncCall(FuncCall $funcCall): ?FuncCall - { - foreach (self::FUNCTIONS_WITH_REGEX_PATTERN as $function => $position) { - if (! $this->isName($funcCall, $function)) { - continue; - } - - $this->refactorArgument($funcCall->args[$position]); - - return $funcCall; - } - - return null; - } - - private function refactorArgument(Arg $arg): void - { - if (! $arg->value instanceof String_) { - return; - } - - /** @var String_ $string */ - $string = $arg->value; - $value = $string->value; - - $string->value = Strings::replace($value, self::INNER_REGEX, function (array $match): string { - $innerPattern = $match['content']; - $positionDelimiter = strpos($innerPattern, $this->delimiter); - - if ($positionDelimiter > 0) { - $innerPattern = str_replace($this->delimiter, '\\' . $this->delimiter, $innerPattern); - } - - // change delimiter - if (strlen($innerPattern) > 2 && $innerPattern[0] === $innerPattern[strlen($innerPattern) - 1]) { - $innerPattern[0] = $this->delimiter; - $innerPattern[strlen($innerPattern) - 1] = $this->delimiter; - } - - return $innerPattern . $match['close']; - }); - } -} diff --git a/rules/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector.php b/rules/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector.php index 0c7dd7e4f97..29f3e1a5b37 100644 --- a/rules/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector.php +++ b/rules/CodingStyle/Rector/FuncCall/CountArrayToEmptyArrayComparisonRector.php @@ -13,13 +13,10 @@ use PhpParser\Node\Expr\BinaryOp\Smaller; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Stmt\ElseIf_; use PhpParser\Node\Stmt\If_; -use PHPStan\Analyser\Scope; -use PHPStan\Type\ArrayType; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -39,7 +36,7 @@ public function getRuleDefinition(): RuleDefinition count($array) > 0; ! count($array); CODE_SAMPLE -, + , <<<'CODE_SAMPLE' $array === []; $array !== []; @@ -55,62 +52,61 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [FuncCall::class, BooleanNot::class]; + return [ + Identical::class, + NotIdentical::class, + BooleanNot::class, + Greater::class, + Smaller::class, + If_::class, + ElseIf_::class, + ]; } /** - * @param FuncCall|BooleanNot $node + * @param Identical|NotIdentical|BooleanNot|Greater|Smaller|If_|ElseIf_ $node */ public function refactor(Node $node): ?Node { if ($node instanceof BooleanNot) { - return $this->processMarkTruthyNegation($node); + return $this->refactorBooleanNot($node); } - if (! $this->isName($node, 'count')) { - return null; - } + if ($node instanceof Identical || $node instanceof NotIdentical) { + if ($node->left instanceof FuncCall) { + $expr = $this->matchCountFuncCallArgExpr($node->left); + } elseif ($node->right instanceof FuncCall) { + $expr = $this->matchCountFuncCallArgExpr($node->right); + } else { + return null; + } - /** @var Expr $expr */ - $expr = $node->args[0]->value; + if (! $expr instanceof Expr) { + return null; + } - // not pass array type, skip - if (! $this->isArray($expr)) { - return null; - } + // not pass array type, skip + if (! $this->isArray($expr)) { + return null; + } - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - - if (! $parent instanceof Node) { - return null; + return $this->refactorIdenticalOrNotIdentical($node, $expr); } - $processIdentical = $this->processIdenticalOrNotIdentical($parent, $node, $expr); - if ($processIdentical !== null) { - return $processIdentical; + if ($node instanceof Smaller || $node instanceof Greater) { + return $this->refactorGreaterOrSmaller($node); } - $processGreaterOrSmaller = $this->processGreaterOrSmaller($parent, $node, $expr); - if ($processGreaterOrSmaller !== null) { - return $processGreaterOrSmaller; - } - - return $this->processMarkTruthy($parent, $node, $expr); + return $this->refactorIfElseIf($node); } - private function processMarkTruthyNegation(BooleanNot $booleanNot): ?Identical + private function refactorBooleanNot(BooleanNot $booleanNot): ?Identical { - if (! $booleanNot->expr instanceof FuncCall) { - return null; - } - - if (! $this->isName($booleanNot->expr, 'count')) { + $expr = $this->matchCountFuncCallArgExpr($booleanNot->expr); + if (! $expr instanceof Expr) { return null; } - /** @var Expr $expr */ - $expr = $booleanNot->expr->args[0]->value; - // not pass array type, skip if (! $this->isArray($expr)) { return null; @@ -121,65 +117,100 @@ private function processMarkTruthyNegation(BooleanNot $booleanNot): ?Identical private function isArray(Expr $expr): bool { - /** @var Scope|null $scope */ - $scope = $expr->getAttribute(AttributeKey::SCOPE); + return $this->nodeTypeResolver->getNativeType($expr) + ->isArray() + ->yes(); + } - if (! $scope instanceof Scope) { - return false; + private function refactorIdenticalOrNotIdentical( + Identical|NotIdentical $binaryOp, + Expr $expr + ): Identical|NotIdentical|null { + if ($this->isZeroLNumber($binaryOp->right)) { + $binaryOp->left = $expr; + $binaryOp->right = new Array_([]); + + return $binaryOp; } - return $scope->getType($expr) instanceof ArrayType; + if ($this->isZeroLNumber($binaryOp->left)) { + $binaryOp->left = new Array_([]); + $binaryOp->right = $expr; + + return $binaryOp; + } + + return null; } - private function processIdenticalOrNotIdentical(Node $node, FuncCall $funcCall, Expr $expr): ?Expr + private function refactorGreaterOrSmaller(Greater | Smaller $binaryOp): NotIdentical | null { - if (($node instanceof Identical || $node instanceof NotIdentical) && $node->right instanceof LNumber && $node->right->value === 0) { - $this->removeNode($funcCall); - $node->right = new Array_([]); + if ($binaryOp instanceof Greater) { + $leftExpr = $this->matchCountFuncCallArgExpr($binaryOp->left); + if (! $leftExpr instanceof Expr) { + return null; + } + + if (! $this->isZeroLNumber($binaryOp->right)) { + return null; + } - return $expr; + return new NotIdentical($leftExpr, new Array_([])); } - if (($node instanceof Identical || $node instanceof NotIdentical) && $node->left instanceof LNumber && $node->left->value === 0) { - $this->removeNode($funcCall); - $node->left = new Array_([]); + $rightExpr = $this->matchCountFuncCallArgExpr($binaryOp->right); + if (! $rightExpr instanceof Expr) { + return null; + } - return $expr; + if (! $this->isZeroLNumber($binaryOp->left)) { + return null; } - return null; + return new NotIdentical(new Array_([]), $rightExpr); } - private function processGreaterOrSmaller(Node $node, FuncCall $funcCall, Expr $expr): ?NotIdentical + private function refactorIfElseIf(If_ | ElseIf_ $ifElseIf): If_ | ElseIf_ | null { - if ($node instanceof Greater && $node->right instanceof LNumber && $node->right->value === 0) { - $this->removeNode($funcCall); - $this->removeNode($node->right); + $expr = $this->matchCountFuncCallArgExpr($ifElseIf->cond); + if (! $expr instanceof Expr) { + return null; + } - return new NotIdentical($expr, new Array_([])); + $ifElseIf->cond = new NotIdentical($expr, new Array_([])); + + return $ifElseIf; + } + + private function matchCountFuncCallArgExpr(Expr $expr): ?Expr + { + if (! $expr instanceof FuncCall) { + return null; } - if ($node instanceof Smaller && $node->left instanceof LNumber && $node->left->value === 0) { - $this->removeNode($funcCall); - $this->removeNode($node->left); + if (! $this->isName($expr, 'count')) { + return null; + } - return new NotIdentical(new Array_([]), $expr); + if ($expr->isFirstClassCallable()) { + return null; } - return null; - } + $firstArg = $expr->getArgs()[0]; - private function processMarkTruthy(Node $node, FuncCall $funcCall, Expr $expr): ?Expr - { - if (! $node instanceof If_ && ! $node instanceof ElseIf_) { + if (! $this->isArray($firstArg->value)) { return null; } - if ($node->cond === $funcCall) { - $node->cond = new NotIdentical($expr, new Array_([])); - return $node->cond; + return $firstArg->value; + } + + private function isZeroLNumber(Expr $expr): bool + { + if (! $expr instanceof Int_) { + return false; } - return null; + return $expr->value === 0; } } diff --git a/rules/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector.php b/rules/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector.php new file mode 100644 index 00000000000..5a610bc3e15 --- /dev/null +++ b/rules/CodingStyle/Rector/FuncCall/FunctionFirstClassCallableRector.php @@ -0,0 +1,117 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?FuncCall + { + if (! $node->name instanceof Name) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $functionName = (string) $this->getName($node); + + try { + $reflectionFunction = new ReflectionFunction($functionName); + } catch (ReflectionException) { + return null; + } + + $callableArgs = []; + + foreach ($reflectionFunction->getParameters() as $reflectionParameter) { + if ($reflectionParameter->getType() instanceof ReflectionNamedType && $reflectionParameter->getType()->getName() === 'callable') { + $callableArgs[] = $reflectionParameter->getPosition(); + } + } + + $hasChanged = false; + foreach ($node->getArgs() as $key => $arg) { + if (! in_array($key, $callableArgs, true)) { + continue; + } + + if (! $arg->value instanceof String_) { + continue; + } + + $node->args[$key] = new Arg( + new FuncCall(new Name($arg->value->value), [new VariadicPlaceholder()]), + name: $arg->name + ); + $hasChanged = true; + } + + return $hasChanged ? $node : null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_81; + } +} diff --git a/rules/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector.php b/rules/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector.php deleted file mode 100644 index 7d85be7699c..00000000000 --- a/rules/CodingStyle/Rector/FuncCall/PreslashSimpleFunctionRector.php +++ /dev/null @@ -1,95 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node->name instanceof FullyQualified) { - return null; - } - - $functionName = $this->getName($node); - if ($functionName === null) { - return null; - } - - if (\str_contains($functionName, '\\')) { - return null; - } - - $isDefinedPreviousCall = (bool) $this->betterNodeFinder->findFirstPreviousOfNode($node, function (Node $node) use ( - $functionName - ): bool { - if (! $node instanceof Function_) { - return false; - } - - return $this->isName($node->name, $functionName); - }); - if ($isDefinedPreviousCall) { - return null; - } - - $node->name = new FullyQualified($functionName); - - return $node; - } -} diff --git a/rules/CodingStyle/Rector/FuncCall/StrictArraySearchRector.php b/rules/CodingStyle/Rector/FuncCall/StrictArraySearchRector.php index 8209ed40766..9c20d1123a9 100644 --- a/rules/CodingStyle/Rector/FuncCall/StrictArraySearchRector.php +++ b/rules/CodingStyle/Rector/FuncCall/StrictArraySearchRector.php @@ -6,7 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -42,8 +42,9 @@ public function refactor(Node $node): ?Node if (count($node->args) === 2) { $node->args[2] = $this->nodeFactory->createArg($this->nodeFactory->createTrue()); + return $node; } - return $node; + return null; } } diff --git a/rules/CodingStyle/Rector/FuncCall/StrictInArrayRector.php b/rules/CodingStyle/Rector/FuncCall/StrictInArrayRector.php new file mode 100644 index 00000000000..f0ac1007161 --- /dev/null +++ b/rules/CodingStyle/Rector/FuncCall/StrictInArrayRector.php @@ -0,0 +1,95 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node, 'in_array')) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + if (count($args) !== 2) { + return null; + } + + $firstArgType = $this->nodeTypeResolver->getNativeType($args[0]->value); + $secondArgType = $this->nodeTypeResolver->getNativeType($args[1]->value); + + if (! $secondArgType->isArray()->yes()) { + return null; + } + + if ($this->typeComparator->isSubtype($secondArgType->getIterableValueType(), $firstArgType)) { + $node->args[] = new Arg($this->nodeFactory->createTrue()); + return $node; + } + + return null; + } +} diff --git a/rules/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php b/rules/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php index d4e43112850..4e2afeff160 100644 --- a/rules/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php +++ b/rules/CodingStyle/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php @@ -16,10 +16,10 @@ use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Util\PhpVersionFactory; +use Rector\Rector\AbstractRector; +use Rector\Util\PhpVersionFactory; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -31,7 +31,7 @@ final class VersionCompareFuncCallToConstantRector extends AbstractRector /** * @var array> */ - private const OPERATOR_TO_COMPARISON = [ + private const array OPERATOR_TO_COMPARISON = [ '=' => Identical::class, '==' => Identical::class, 'eq' => Identical::class, @@ -48,11 +48,6 @@ final class VersionCompareFuncCallToConstantRector extends AbstractRector 'le' => SmallerOrEqual::class, ]; - public function __construct( - private PhpVersionFactory $phpVersionFactory - ) { - } - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -100,27 +95,32 @@ public function refactor(Node $node): ?Node return null; } - if (count($node->args) !== 3) { + if ($node->isFirstClassCallable()) { + return null; + } + + if (count($node->getArgs()) !== 3) { return null; } - if (! $this->isPhpVersionConstant($node->args[0]->value) && ! $this->isPhpVersionConstant( - $node->args[1]->value - )) { + $args = $node->getArgs(); + if (! $this->isPhpVersionConstant($args[0]->value) && ! $this->isPhpVersionConstant($args[1]->value)) { return null; } - $left = $this->getNewNodeForArg($node->args[0]->value); - $right = $this->getNewNodeForArg($node->args[1]->value); - if ($left === null) { + $left = $this->getNewNodeForArg($args[0]->value); + $right = $this->getNewNodeForArg($args[1]->value); + + if (! $left instanceof Expr) { return null; } - if ($right === null) { + + if (! $right instanceof Expr) { return null; } /** @var String_ $operator */ - $operator = $node->args[2]->value; + $operator = $args[2]->value; $comparisonClass = self::OPERATOR_TO_COMPARISON[$operator->value]; return new $comparisonClass($left, $right); @@ -131,10 +131,11 @@ private function isPhpVersionConstant(Expr $expr): bool if (! $expr instanceof ConstFetch) { return false; } + return $expr->name->toString() === 'PHP_VERSION'; } - private function getNewNodeForArg(Expr $expr): ConstFetch | LNumber | null + private function getNewNodeForArg(Expr $expr): ConstFetch | Int_ | null { if ($this->isPhpVersionConstant($expr)) { return new ConstFetch(new Name('PHP_VERSION_ID')); @@ -143,13 +144,13 @@ private function getNewNodeForArg(Expr $expr): ConstFetch | LNumber | null return $this->getVersionNumberFormVersionString($expr); } - private function getVersionNumberFormVersionString(Expr $expr): ?LNumber + private function getVersionNumberFormVersionString(Expr $expr): ?Int_ { if (! $expr instanceof String_) { return null; } - $value = $this->phpVersionFactory->createIntVersion($expr->value); - return new LNumber($value); + $value = PhpVersionFactory::createIntVersion($expr->value); + return new Int_($value); } } diff --git a/rules/CodingStyle/Rector/FunctionLike/FunctionLikeToFirstClassCallableRector.php b/rules/CodingStyle/Rector/FunctionLike/FunctionLikeToFirstClassCallableRector.php new file mode 100644 index 00000000000..b54925dbbfd --- /dev/null +++ b/rules/CodingStyle/Rector/FunctionLike/FunctionLikeToFirstClassCallableRector.php @@ -0,0 +1,69 @@ +getStaticType($node); - if ($staticType instanceof MixedType) { - return false; - } + $nativeType = $this->nodeTypeResolver->getNativeType($expr); - if (! $staticType instanceof UnionType) { + // is non-nullable? + if (! TypeCombinator::containsNull($nativeType)) { return false; } - // is non-nullable? - if ($staticType->isSuperTypeOf(new NullType())->no()) { + if (! $nativeType instanceof UnionType) { return false; } // is array? - foreach ($staticType->getTypes() as $subType) { - if ($subType instanceof ArrayType) { + foreach ($nativeType->getTypes() as $subType) { + if ($subType->isArray()->yes()) { return false; } } - // is string? - if ($staticType->isSuperTypeOf(new StringType())->yes()) { - return false; - } - - // is number? - if ($staticType->isSuperTypeOf(new IntegerType())->yes()) { - return false; - } - - // is bool? - if ($staticType->isSuperTypeOf(new BooleanType())->yes()) { - return false; - } - - return ! $staticType->isSuperTypeOf(new FloatType()) + $nativeType = TypeCombinator::removeNull($nativeType); + return ! $nativeType->isScalar() ->yes(); } } diff --git a/rules/CodingStyle/Rector/Include_/FollowRequireByDirRector.php b/rules/CodingStyle/Rector/Include_/FollowRequireByDirRector.php deleted file mode 100644 index a370dacfba8..00000000000 --- a/rules/CodingStyle/Rector/Include_/FollowRequireByDirRector.php +++ /dev/null @@ -1,115 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Include_::class]; - } - - /** - * @param Include_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node->expr instanceof Concat && $node->expr->left instanceof String_ && $this->isRefactorableStringPath( - $node->expr->left - )) { - $node->expr->left = $this->prefixWithDir($node->expr->left); - - return $node; - } - - if ($node->expr instanceof String_ && $this->isRefactorableStringPath($node->expr)) { - $node->expr = $this->prefixWithDir($node->expr); - - return $node; - } - // nothing we can do - return null; - } - - private function isRefactorableStringPath(String_ $string): bool - { - return ! \str_starts_with($string->value, 'phar://'); - } - - private function prefixWithDir(String_ $string): Concat - { - $this->removeExtraDotSlash($string); - $this->prependSlashIfMissing($string); - - return new Concat(new Dir(), $string); - } - - /** - * Remove "./" which would break the path - */ - private function removeExtraDotSlash(String_ $string): void - { - if (! \str_starts_with($string->value, './')) { - return; - } - - $string->value = Strings::replace($string->value, '#^\.\/#', '/'); - } - - private function prependSlashIfMissing(String_ $string): void - { - if (\str_starts_with($string->value, '/')) { - return; - } - - $string->value = '/' . $string->value; - } -} diff --git a/rules/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector.php b/rules/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector.php deleted file mode 100644 index 6e88014f8f4..00000000000 --- a/rules/CodingStyle/Rector/MethodCall/PreferThisOrSelfMethodCallRector.php +++ /dev/null @@ -1,158 +0,0 @@ - - */ - private array $typeToPreference = []; - - public function __construct(private AstResolver $astResolver) - { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Changes $this->... and static:: to self:: or vise versa for given types', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -class SomeClass extends \PHPUnit\Framework\TestCase -{ - public function run() - { - $this->assertEquals('a', 'a'); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass extends \PHPUnit\Framework\TestCase -{ - public function run() - { - self::assertEquals('a', 'a'); - } -} -CODE_SAMPLE - , - [ - self::TYPE_TO_PREFERENCE => [ - 'PHPUnit\Framework\TestCase' => PreferenceSelfThis::PREFER_SELF(), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class, StaticCall::class]; - } - - /** - * @param MethodCall|StaticCall $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->typeToPreference as $type => $preference) { - if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType($node, new ObjectType($type))) { - continue; - } - - /** @var PreferenceSelfThis $preference */ - if ($preference->equals(PreferenceSelfThis::PREFER_SELF())) { - return $this->processToSelf($node); - } - - return $this->processToThis($node); - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $typeToPreference = $configuration[self::TYPE_TO_PREFERENCE] ?? []; - Assert::allIsAOf($typeToPreference, PreferenceSelfThis::class); - - $this->typeToPreference = $typeToPreference; - } - - private function processToSelf(MethodCall | StaticCall $node): ?StaticCall - { - if ($node instanceof StaticCall && ! $this->isNames($node->class, [self::SELF, 'static'])) { - return null; - } - - if ($node instanceof MethodCall && ! $this->isName($node->var, 'this')) { - return null; - } - - $classMethod = $this->astResolver->resolveClassMethodFromCall($node); - if ($classMethod instanceof ClassMethod && ! $classMethod->isStatic()) { - return null; - } - - $name = $this->getName($node->name); - if ($name === null) { - return null; - } - - return $this->nodeFactory->createStaticCall(self::SELF, $name, $node->args); - } - - private function processToThis(MethodCall | StaticCall $node): ?MethodCall - { - if ($node instanceof MethodCall) { - return null; - } - - if (! $this->isNames($node->class, [self::SELF, 'static'])) { - return null; - } - - $name = $this->getName($node->name); - if ($name === null) { - return null; - } - - return $this->nodeFactory->createMethodCall('this', $name, $node->args); - } -} diff --git a/rules/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector.php b/rules/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector.php deleted file mode 100644 index 8ca3ca43fd3..00000000000 --- a/rules/CodingStyle/Rector/MethodCall/UseMessageVariableForSprintfInSymfonyStyleRector.php +++ /dev/null @@ -1,95 +0,0 @@ -symfonyStyle->method()', - [ - new CodeSample( - <<<'CODE_SAMPLE' -use Symfony\Component\Console\Style\SymfonyStyle; - -final class SomeClass -{ - public function run(SymfonyStyle $symfonyStyle) - { - $symfonyStyle->info(sprintf('Hi %s', 'Tom')); - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -use Symfony\Component\Console\Style\SymfonyStyle; - -final class SomeClass -{ - public function run(SymfonyStyle $symfonyStyle) - { - $message = sprintf('Hi %s', 'Tom'); - $symfonyStyle->info($message); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isObjectType($node->var, new ObjectType('Symfony\Component\Console\Style\SymfonyStyle'))) { - return null; - } - - if (! isset($node->args[0])) { - return null; - } - - $argValue = $node->args[0]->value; - if (! $argValue instanceof FuncCall) { - return null; - } - - if (! $this->nodeNameResolver->isName($argValue, 'sprintf')) { - return null; - } - - $messageVariable = new Variable('message'); - $assign = new Assign($messageVariable, $argValue); - $this->addNodeBeforeNode($assign, $node); - - $node->args[0]->value = $messageVariable; - - return $node; - } -} diff --git a/rules/CodingStyle/Rector/Plus/UseIncrementAssignRector.php b/rules/CodingStyle/Rector/Plus/UseIncrementAssignRector.php deleted file mode 100644 index e854e606ba5..00000000000 --- a/rules/CodingStyle/Rector/Plus/UseIncrementAssignRector.php +++ /dev/null @@ -1,79 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Plus::class, Minus::class]; - } - - /** - * @param Plus|Minus $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->expr instanceof LNumber) { - return null; - } - - if ($node->expr->value !== 1) { - return null; - } - - if ($node instanceof Plus) { - return new PreInc($node->var); - } - - return new PreDec($node->var); - } -} diff --git a/rules/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector.php b/rules/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector.php index 9fe3d25d3c6..2d1bffd8546 100644 --- a/rules/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector.php +++ b/rules/CodingStyle/Rector/PostInc/PostIncDecToPreIncDecRector.php @@ -5,16 +5,13 @@ namespace Rector\CodingStyle\Rector\PostInc; use PhpParser\Node; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\PostDec; use PhpParser\Node\Expr\PostInc; use PhpParser\Node\Expr\PreDec; use PhpParser\Node\Expr\PreInc; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\For_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -60,40 +57,35 @@ public function run($value = 1) */ public function getNodeTypes(): array { - return [PostInc::class, PostDec::class]; + return [For_::class, Expression::class]; } /** - * @param PostInc|PostDec $node + * @param For_|Expression $node */ public function refactor(Node $node): ?Node { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($this->isAnExpression($parentNode)) { - return $this->processPrePost($node); + if ($node instanceof Expression) { + return $this->refactorExpression($node); } - if ($parentNode instanceof ArrayDimFetch && $this->nodeComparator->areNodesEqual($parentNode->dim, $node)) { - return $this->processPreArray($node, $parentNode); - } - if (! $parentNode instanceof For_) { - return null; - } - if (count($parentNode->loop) !== 1) { + return $this->refactorFor($node); + } + + private function refactorFor(For_ $for): For_|null + { + if (count($for->loop) !== 1) { return null; } - if (! $this->nodeComparator->areNodesEqual($parentNode->loop[0], $node)) { + + $singleLoopExpr = $for->loop[0]; + if (! $singleLoopExpr instanceof PostInc && ! $singleLoopExpr instanceof PostDec) { return null; } - return $this->processPreFor($node, $parentNode); - } - private function isAnExpression(?Node $node = null): bool - { - if (! $node instanceof Node) { - return false; - } - return $node instanceof Expression; + $for->loop = [$this->processPrePost($singleLoopExpr)]; + + return $for; } private function processPrePost(PostInc | PostDec $node): PreInc | PreDec @@ -105,22 +97,13 @@ private function processPrePost(PostInc | PostDec $node): PreInc | PreDec return new PreDec($node->var); } - private function processPreArray(PostInc | PostDec $node, ArrayDimFetch $arrayDimFetch): ?Expr + private function refactorExpression(Expression $expression): ?Expression { - $parentOfArrayDimFetch = $arrayDimFetch->getAttribute(AttributeKey::PARENT_NODE); - if (! $this->isAnExpression($parentOfArrayDimFetch)) { - return null; + if ($expression->expr instanceof PostInc || $expression->expr instanceof PostDec) { + $expression->expr = $this->processPrePost($expression->expr); + return $expression; } - $arrayDimFetch->dim = $node->var; - $this->addNodeAfterNode($this->processPrePost($node), $arrayDimFetch); - - return $arrayDimFetch->dim; - } - - private function processPreFor(PostInc | PostDec $node, For_ $for): PreDec | PreInc - { - $for->loop = [$this->processPrePost($node)]; - return $for->loop[0]; + return null; } } diff --git a/rules/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector.php b/rules/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector.php deleted file mode 100644 index 6be58af64db..00000000000 --- a/rules/CodingStyle/Rector/Property/AddFalseDefaultToBoolPropertyRector.php +++ /dev/null @@ -1,85 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Property::class]; - } - - /** - * @param Property $node - */ - public function refactor(Node $node): ?Node - { - if (count($node->props) !== 1) { - return null; - } - - $onlyProperty = $node->props[0]; - if ($onlyProperty->default !== null) { - return null; - } - - if (! $this->isBoolDocType($node)) { - return null; - } - - $onlyProperty->default = $this->nodeFactory->createFalse(); - - return $node; - } - - private function isBoolDocType(Property $property): bool - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - return $phpDocInfo->getVarType() instanceof BooleanType; - } -} diff --git a/rules/CodingStyle/Rector/Property/SplitGroupedPropertiesRector.php b/rules/CodingStyle/Rector/Property/SplitGroupedPropertiesRector.php new file mode 100644 index 00000000000..39640209b7e --- /dev/null +++ b/rules/CodingStyle/Rector/Property/SplitGroupedPropertiesRector.php @@ -0,0 +1,85 @@ +> + */ + public function getNodeTypes(): array + { + return [Property::class]; + } + + /** + * @param Property $node + * @return Property[]|null + */ + public function refactor(Node $node): ?array + { + $allProperties = $node->props; + if (count($allProperties) === 1) { + return null; + } + + /** @var PropertyItem $firstPropertyProperty */ + $firstPropertyProperty = array_shift($allProperties); + $node->props = [$firstPropertyProperty]; + + $nextProperties = []; + + foreach ($allProperties as $allProperty) { + $nextProperties[] = new Property($node->flags, [$allProperty], $node->getAttributes()); + } + + return [$node, ...$nextProperties]; + } +} diff --git a/rules/CodingStyle/Rector/Stmt/NewlineAfterStatementRector.php b/rules/CodingStyle/Rector/Stmt/NewlineAfterStatementRector.php new file mode 100644 index 00000000000..e2c82948222 --- /dev/null +++ b/rules/CodingStyle/Rector/Stmt/NewlineAfterStatementRector.php @@ -0,0 +1,134 @@ +> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + * @return StmtsAware|null + */ + public function refactor(Node $node): null|Node + { + return $this->processAddNewLine($node, false); + } + + /** + * @param StmtsAware $node + * @return StmtsAware|null + */ + private function processAddNewLine(Node $node, bool $hasChanged, int $jumpToKey = 0): null|Node + { + if ($node->stmts === null) { + return null; + } + + $totalKeys = array_key_last($node->stmts); + + for ($key = $jumpToKey; $key < $totalKeys; ++$key) { + if (! isset($node->stmts[$key], $node->stmts[$key + 1])) { + break; + } + + $stmt = $node->stmts[$key]; + $nextStmt = $node->stmts[$key + 1]; + + if ($this->shouldSkip($stmt)) { + continue; + } + + /** @var Stmt $stmt */ + $endLine = $stmt->getEndLine(); + $rangeLine = $nextStmt->getStartLine() - $endLine; + + if ($rangeLine > 1) { + $rangeLine = $this->commentResolver->resolveRangeLineFromComment($rangeLine, $endLine, $nextStmt); + } + + // skip same line or < 0 that cause infinite loop or crash + if ($rangeLine !== 1) { + continue; + } + + array_splice($node->stmts, $key + 1, 0, [new Nop()]); + + $hasChanged = true; + return $this->processAddNewLine($node, $hasChanged, $key + 2); + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function shouldSkip(Stmt $stmt): bool + { + return ! in_array($stmt::class, NodeGroup::STMTS_TO_HAVE_NEXT_NEWLINE, true); + } +} diff --git a/rules/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector.php b/rules/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector.php new file mode 100644 index 00000000000..fbd2df030d4 --- /dev/null +++ b/rules/CodingStyle/Rector/Stmt/RemoveUselessAliasInUseStatementRector.php @@ -0,0 +1,95 @@ +> + */ + public function getNodeTypes(): array + { + return [FileNode::class, Namespace_::class]; + } + + /** + * @param Namespace_|FileNode $node + */ + public function refactor(Node $node): null|FileNode|Namespace_ + { + if ($node instanceof FileNode && $node->isNamespaced()) { + // handle in Namespace_ node + return null; + } + + $hasChanged = false; + + foreach ($node->stmts as $stmt) { + if (! $stmt instanceof Use_) { + continue; + } + + if (count($stmt->uses) !== 1) { + continue; + } + + if (! isset($stmt->uses[0])) { + continue; + } + + $aliasName = $stmt->uses[0]->alias instanceof Identifier + ? $stmt->uses[0]->alias->toString() + : null; + + if ($aliasName === null) { + continue; + } + + $useName = $stmt->uses[0]->name->toString(); + $lastName = Strings::after($useName, '\\', -1) ?? $useName; + if ($lastName === $aliasName) { + $stmt->uses[0]->alias = null; + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } +} diff --git a/rules/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector.php b/rules/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector.php new file mode 100644 index 00000000000..db117848c10 --- /dev/null +++ b/rules/CodingStyle/Rector/String_/SimplifyQuoteEscapeRector.php @@ -0,0 +1,140 @@ +> + */ + public function getNodeTypes(): array + { + return [String_::class]; + } + + /** + * @param String_ $node + */ + public function refactor(Node $node): ?String_ + { + $this->hasChanged = false; + + if (StringUtils::isMatch($node->value, self::HAS_NON_PRINTABLE_CHARS)) { + return null; + } + + $doubleQuoteCount = substr_count($node->value, '"'); + $singleQuoteCount = substr_count($node->value, "'"); + $kind = $node->getAttribute(AttributeKey::KIND); + + if ($kind === String_::KIND_SINGLE_QUOTED) { + $this->processSingleQuoted($node, $doubleQuoteCount, $singleQuoteCount); + } + + $quoteKind = $node->getAttribute(AttributeKey::KIND); + if ($quoteKind === String_::KIND_DOUBLE_QUOTED) { + $this->processDoubleQuoted($node, $singleQuoteCount, $doubleQuoteCount); + } + + if (! $this->hasChanged) { + return null; + } + + return $node; + } + + private function processSingleQuoted(String_ $string, int $doubleQuoteCount, int $singleQuoteCount): void + { + if ($doubleQuoteCount === 0 && $singleQuoteCount > 0) { + // contains chars that will be newly escaped + if ($this->isMatchEscapedChars($string->value)) { + return; + } + + $string->setAttribute(AttributeKey::KIND, String_::KIND_DOUBLE_QUOTED); + // invoke override + $string->setAttribute(AttributeKey::ORIGINAL_NODE, null); + + $this->hasChanged = true; + } + } + + private function processDoubleQuoted(String_ $string, int $singleQuoteCount, int $doubleQuoteCount): void + { + if ($singleQuoteCount === 0 && $doubleQuoteCount > 0) { + // contains chars that will be newly escaped + if ($this->isMatchEscapedChars($string->value)) { + return; + } + + $string->setAttribute(AttributeKey::KIND, String_::KIND_SINGLE_QUOTED); + // invoke override + $string->setAttribute(AttributeKey::ORIGINAL_NODE, null); + + $this->hasChanged = true; + } + } + + private function isMatchEscapedChars(string $string): bool + { + return StringUtils::isMatch($string, self::ESCAPED_CHAR_REGEX); + } +} diff --git a/rules/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector.php b/rules/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector.php deleted file mode 100644 index 3af80cb55b1..00000000000 --- a/rules/CodingStyle/Rector/String_/SplitStringClassConstantToClassConstFetchRector.php +++ /dev/null @@ -1,95 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [String_::class]; - } - - /** - * @param String_ $node - */ - public function refactor(Node $node): ?Node - { - if (substr_count($node->value, '::') !== 1) { - return null; - } - - // a possible constant reference - [$possibleClass, $secondPart] = explode('::', $node->value); - - if (! $this->reflectionProvider->hasClass($possibleClass)) { - return null; - } - - $classConstFetch = new ClassConstFetch(new FullyQualified(ltrim($possibleClass, '\\')), 'class'); - - return new Concat($classConstFetch, new String_('::' . $secondPart)); - } -} diff --git a/rules/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector.php b/rules/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector.php index 73a82e72d4e..36a7c021ae4 100644 --- a/rules/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector.php +++ b/rules/CodingStyle/Rector/String_/SymplifyQuoteEscapeRector.php @@ -4,25 +4,19 @@ namespace Rector\CodingStyle\Rector\String_; -use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Configuration\Deprecation\Contract\DeprecatedInterface; +use Rector\Exception\ShouldNotHappenException; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -/** - * @see \Rector\Tests\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector\SymplifyQuoteEscapeRectorTest +/*** + * @deprecated Renamed to \Rector\CodingStyle\Rector\String_\SimplifyQuoteEscapeRector */ -final class SymplifyQuoteEscapeRector extends AbstractRector +final class SymplifyQuoteEscapeRector extends AbstractRector implements DeprecatedInterface { - /** - * @var string - * @see https://regex101.com/r/qEkCe9/2 - */ - private const ESCAPED_CHAR_REGEX = '#\\\\|\$|\\n|\\t#sim'; - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -66,54 +60,12 @@ public function getNodeTypes(): array /** * @param String_ $node */ - public function refactor(Node $node): String_ - { - $doubleQuoteCount = substr_count($node->value, '"'); - $singleQuoteCount = substr_count($node->value, "'"); - $kind = $node->getAttribute(AttributeKey::KIND); - - if ($kind === String_::KIND_SINGLE_QUOTED) { - $this->processSingleQuoted($node, $doubleQuoteCount, $singleQuoteCount); - } - - $quoteKind = $node->getAttribute(AttributeKey::KIND); - if ($quoteKind === String_::KIND_DOUBLE_QUOTED) { - $this->processDoubleQuoted($node, $singleQuoteCount, $doubleQuoteCount); - } - - return $node; - } - - private function processSingleQuoted(String_ $string, int $doubleQuoteCount, int $singleQuoteCount): void - { - if ($doubleQuoteCount === 0 && $singleQuoteCount > 0) { - // contains chars that will be newly escaped - if ($this->isMatchEscapedChars($string->value)) { - return; - } - - $string->setAttribute(AttributeKey::KIND, String_::KIND_DOUBLE_QUOTED); - // invoke override - $string->setAttribute(AttributeKey::ORIGINAL_NODE, null); - } - } - - private function processDoubleQuoted(String_ $string, int $singleQuoteCount, int $doubleQuoteCount): void - { - if ($singleQuoteCount === 0 && $doubleQuoteCount > 0) { - // contains chars that will be newly escaped - if ($this->isMatchEscapedChars($string->value)) { - return; - } - - $string->setAttribute(AttributeKey::KIND, String_::KIND_SINGLE_QUOTED); - // invoke override - $string->setAttribute(AttributeKey::ORIGINAL_NODE, null); - } - } - - private function isMatchEscapedChars(string $string): bool + public function refactor(Node $node): ?String_ { - return (bool) Strings::match($string, self::ESCAPED_CHAR_REGEX); + throw new ShouldNotHappenException(sprintf( + '%s is deprecated and renamed to "%s". Use it instead.', + self::class, + SimplifyQuoteEscapeRector::class + )); } } diff --git a/rules/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector.php b/rules/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector.php index d13ab8edd5f..a26405b154e 100644 --- a/rules/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector.php +++ b/rules/CodingStyle/Rector/String_/UseClassKeywordForClassNameResolutionRector.php @@ -10,7 +10,8 @@ use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Scalar\String_; use PHPStan\Reflection\ReflectionProvider; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,13 +21,12 @@ final class UseClassKeywordForClassNameResolutionRector extends AbstractRector { /** - * @var string * @see https://regex101.com/r/Vv41Qr/1/ */ - private const CLASS_BEFORE_STATIC_ACCESS_REGEX = '#(?[\\\\a-zA-Z0-9_\\x80-\\xff]*)::#'; + private const string CLASS_BEFORE_STATIC_ACCESS_REGEX = '#(?[\\\\a-zA-Z0-9_\\x80-\\xff]*)::#'; public function __construct( - private ReflectionProvider $reflectionProvider + private readonly ReflectionProvider $reflectionProvider ) { } @@ -41,7 +41,7 @@ public function getRuleDefinition(): RuleDefinition CODE_SAMPLE , <<<'CODE_SAMPLE' -$value = \App\SomeClass . '::someMethod()'; +$value = \App\SomeClass::class . '::someMethod()'; CODE_SAMPLE ), ] @@ -61,9 +61,16 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { + $stringKind = $node->getAttribute(AttributeKey::KIND); + if (in_array($stringKind, [String_::KIND_HEREDOC, String_::KIND_NOWDOC], true)) { + return null; + } + $classNames = $this->getExistingClasses($node); + $classNames = $this->filterOurShortClasses($classNames); + if ($classNames === []) { - return $node; + return null; } $parts = $this->getParts($node, $classNames); @@ -72,13 +79,28 @@ public function refactor(Node $node): ?Node } $exprsToConcat = $this->createExpressionsToConcat($parts); + return $this->nodeFactory->createConcat($exprsToConcat); } + /** + * @param string[] $classNames + * @return mixed[] + */ + private function getParts(String_ $string, array $classNames): array + { + $quotedClassNames = array_map(preg_quote(...), $classNames); + + // @see https://regex101.com/r/8nGS0F/1 + $parts = Strings::split($string->value, '#(' . implode('|', $quotedClassNames) . ')#'); + + return array_filter($parts, static fn (string $className): bool => $className !== ''); + } + /** * @return string[] */ - public function getExistingClasses(String_ $string): array + private function getExistingClasses(String_ $string): array { /** @var mixed[] $matches */ $matches = Strings::matchAll($string->value, self::CLASS_BEFORE_STATIC_ACCESS_REGEX, PREG_PATTERN_ORDER); @@ -99,20 +121,6 @@ public function getExistingClasses(String_ $string): array return $classNames; } - /** - * @param string[] $classNames - * @return mixed[] - */ - public function getParts(String_ $string, array $classNames): array - { - $quotedClassNames = array_map('preg_quote', $classNames); - - // @see https://regex101.com/r/8nGS0F/1 - $parts = Strings::split($string->value, '#(' . implode('|', $quotedClassNames) . ')#'); - - return array_filter($parts, fn (string $className): bool => $className !== ''); - } - /** * @param string[] $parts * @return ClassConstFetch[]|String_[] @@ -127,6 +135,16 @@ private function createExpressionsToConcat(array $parts): array $exprsToConcat[] = new String_($part); } } + return $exprsToConcat; } + + /** + * @param string[] $classNames + * @return string[] + */ + private function filterOurShortClasses(array $classNames): array + { + return array_filter($classNames, static fn (string $className): bool => str_contains($className, '\\')); + } } diff --git a/rules/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector.php b/rules/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector.php deleted file mode 100644 index d41f836c068..00000000000 --- a/rules/CodingStyle/Rector/Switch_/BinarySwitchToIfElseRector.php +++ /dev/null @@ -1,111 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Switch_::class]; - } - - /** - * @param Switch_ $node - */ - public function refactor(Node $node): ?Node - { - if (count($node->cases) > 2) { - return null; - } - - /** @var Case_ $firstCase */ - $firstCase = array_shift($node->cases); - if ($firstCase->cond === null) { - return null; - } - - $secondCase = array_shift($node->cases); - - // special case with empty first case → || - $isFirstCaseEmpty = $firstCase->stmts === []; - if ($isFirstCaseEmpty && $secondCase !== null && $secondCase->cond !== null) { - $else = new BooleanOr(new Equal($node->cond, $firstCase->cond), new Equal($node->cond, $secondCase->cond)); - - $ifNode = new If_($else); - $ifNode->stmts = $this->switchManipulator->removeBreakNodes($secondCase->stmts); - - return $ifNode; - } - - $ifNode = new If_(new Equal($node->cond, $firstCase->cond)); - $ifNode->stmts = $this->switchManipulator->removeBreakNodes($firstCase->stmts); - - // just one condition - if (! $secondCase instanceof Case_) { - return $ifNode; - } - - if ($secondCase->cond !== null) { - // has condition - $equal = new Equal($node->cond, $secondCase->cond); - $ifNode->elseifs[] = new ElseIf_($equal, $this->switchManipulator->removeBreakNodes($secondCase->stmts)); - } else { - // defaults - $ifNode->else = new Else_($this->switchManipulator->removeBreakNodes($secondCase->stmts)); - } - - return $ifNode; - } -} diff --git a/rules/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector.php b/rules/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector.php index 553f955fbb8..ff02c065acd 100644 --- a/rules/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector.php +++ b/rules/CodingStyle/Rector/Ternary/TernaryConditionVariableAssignmentRector.php @@ -8,8 +8,8 @@ use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Variable; -use Rector\Core\Rector\AbstractRector; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -60,6 +60,7 @@ public function refactor(Node $node): ?Node if (! $nodeIf instanceof Assign) { return null; } + if (! $nodeElse instanceof Assign) { return null; } @@ -69,6 +70,7 @@ public function refactor(Node $node): ?Node if (! $nodeIfVar instanceof Variable) { return null; } + if (! $nodeElseVar instanceof Variable) { return null; } @@ -77,8 +79,8 @@ public function refactor(Node $node): ?Node return null; } - $previousNode = $node->getAttribute(AttributeKey::PREVIOUS_NODE); - if ($previousNode !== null) { + $assignedTo = $node->getAttribute(AttributeKey::IS_ASSIGNED_TO); + if ($assignedTo === true) { return null; } diff --git a/rules/CodingStyle/Rector/Use_/RemoveUnusedAliasRector.php b/rules/CodingStyle/Rector/Use_/RemoveUnusedAliasRector.php deleted file mode 100644 index 201840f29fd..00000000000 --- a/rules/CodingStyle/Rector/Use_/RemoveUnusedAliasRector.php +++ /dev/null @@ -1,246 +0,0 @@ - - */ - private array $useNamesAliasToName = []; - - /** - * @var string[] - */ - private array $resolvedDocPossibleAliases = []; - - public function __construct( - private DocAliasResolver $docAliasResolver, - private UseManipulator $useManipulator, - private UseNameAliasToNameResolver $useNameAliasToNameResolver, - private NameRenamer $nameRenamer, - private ClassNameImportSkipper $classNameImportSkipper - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Removes unused use aliases. Keep annotation aliases like "Doctrine\ORM\Mapping as ORM" to keep convention format', - [ - new CodeSample( - <<<'CODE_SAMPLE' -use Symfony\Kernel as BaseKernel; - -class SomeClass extends BaseKernel -{ -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -use Symfony\Kernel; - -class SomeClass extends Kernel -{ -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Use_::class]; - } - - /** - * @param Use_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkipUse($node)) { - return null; - } - - $searchNode = $this->resolveSearchNode($node); - if (! $searchNode instanceof Node) { - return null; - } - - $this->resolvedNodeNames = $this->useManipulator->resolveUsedNameNodes($searchNode); - $this->resolvedDocPossibleAliases = $this->docAliasResolver->resolve($searchNode); - - $this->useNamesAliasToName = $this->useNameAliasToNameResolver->resolve($this->file, $node); - - // lowercase - $this->resolvedDocPossibleAliases = $this->lowercaseArray($this->resolvedDocPossibleAliases); - - $this->resolvedNodeNames = array_change_key_case($this->resolvedNodeNames, CASE_LOWER); - $this->useNamesAliasToName = array_change_key_case($this->useNamesAliasToName, CASE_LOWER); - - foreach ($node->uses as $use) { - if ($use->alias === null) { - continue; - } - - $lastName = $use->name->getLast(); - $lowercasedLastName = strtolower($lastName); - - /** @var string $aliasName */ - $aliasName = $this->getName($use->alias); - if ($this->shouldSkip($node, $use->name, $lastName, $aliasName)) { - continue; - } - - // only last name is used → no need for alias - if (isset($this->resolvedNodeNames[$lowercasedLastName])) { - $use->alias = null; - continue; - } - - $this->refactorAliasName($node, $aliasName, $lastName, $use); - } - - return $node; - } - - private function shouldSkipUse(Use_ $use): bool - { - // skip cases without namespace, problematic to analyse - $namespace = $this->betterNodeFinder->findParentType($use, Namespace_::class); - if (! $namespace instanceof Node) { - return true; - } - - return ! $this->hasUseAlias($use); - } - - private function resolveSearchNode(Use_ $use): ?Node - { - $searchNode = $use->getAttribute(AttributeKey::PARENT_NODE); - if ($searchNode !== null) { - return $searchNode; - } - - return $use->getAttribute(AttributeKey::NEXT_NODE); - } - - /** - * @param string[] $values - * @return string[] - */ - private function lowercaseArray(array $values): array - { - return array_map('strtolower', $values); - } - - private function shouldSkip(Use_ $use, Name $name, string $lastName, string $aliasName): bool - { - // PHP is case insensitive - $loweredLastName = strtolower($lastName); - $loweredAliasName = strtolower($aliasName); - - // both are used → nothing to remove - if (isset($this->resolvedNodeNames[$loweredLastName], $this->resolvedNodeNames[$loweredAliasName])) { - return true; - } - - // part of some @Doc annotation - if (in_array($loweredAliasName, $this->resolvedDocPossibleAliases, true)) { - return true; - } - - return (bool) $this->betterNodeFinder->findFirstNext($use, function (Node $node) use ( - $name, - $loweredAliasName - ): bool { - if ($node instanceof FullyQualified) { - $originalName = $node->getAttribute(AttributeKey::ORIGINAL_NAME); - if ($originalName instanceof Name) { - $loweredOriginalName = strtolower($originalName->toString()); - $loweredOriginalNameNamespace = Strings::before($loweredOriginalName, '\\'); - return $loweredAliasName === $loweredOriginalNameNamespace; - } - } - - if (! $node instanceof ClassConstFetch) { - return false; - } - - if (! $node->class instanceof Name) { - return false; - } - - return $node->class->toString() === $name->toString(); - }); - } - - private function refactorAliasName(Use_ $use, string $aliasName, string $lastName, UseUse $useUse): void - { - $parentUse = $use->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentUse instanceof Node) { - return; - } - - /** @var Use_[] $uses */ - $uses = $this->betterNodeFinder->find($parentUse, function (Node $node) use ($use): bool { - if ($node === $use) { - return false; - } - - return $node instanceof Use_; - }); - - if ($this->classNameImportSkipper->isShortNameInUseStatement(new Name($lastName), $uses)) { - return; - } - - $lowerAliasName = strtolower($aliasName); - $this->nameRenamer->renameNameNode($this->resolvedNodeNames[$lowerAliasName], $lastName); - $useUse->alias = null; - } - - private function hasUseAlias(Use_ $use): bool - { - foreach ($use->uses as $useUse) { - if ($useUse->alias !== null) { - return true; - } - } - - return false; - } -} diff --git a/rules/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector.php b/rules/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector.php index 8c7e8048aef..06b5d7d3cac 100644 --- a/rules/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector.php +++ b/rules/CodingStyle/Rector/Use_/SeparateMultiUseImportsRector.php @@ -5,9 +5,14 @@ namespace Rector\CodingStyle\Rector\Use_; use PhpParser\Node; +use PhpParser\Node\Name; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\TraitUse; +use PhpParser\Node\Stmt\TraitUseAdaptation\Alias; use PhpParser\Node\Stmt\Use_; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\FileNode; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -30,7 +35,7 @@ class SomeClass use SomeTrait, AnotherTrait; } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' use A; use B; @@ -51,20 +56,49 @@ class SomeClass */ public function getNodeTypes(): array { - return [Use_::class, TraitUse::class]; + return [FileNode::class, Namespace_::class, Class_::class]; } /** - * @param Use_|TraitUse $node - * @return Use_[]|TraitUse[]|null + * @param FileNode|Namespace_|Class_ $node */ - public function refactor(Node $node): ?array + public function refactor(Node $node): FileNode|Namespace_|Class_|null { - if ($node instanceof Use_) { - return $this->refactorUseImport($node); + if ($node instanceof FileNode && $node->isNamespaced()) { + // handled in Namespace_ + return null; + } + + $hasChanged = false; + foreach ($node->stmts as $key => $stmt) { + if ($stmt instanceof Use_) { + $refactorUseImport = $this->refactorUseImport($stmt); + if ($refactorUseImport !== null) { + unset($node->stmts[$key]); + array_splice($node->stmts, $key, 0, $refactorUseImport); + + $hasChanged = true; + } + + continue; + } + + if ($stmt instanceof TraitUse) { + $refactorTraitUse = $this->refactorTraitUse($stmt); + if ($refactorTraitUse !== null) { + unset($node->stmts[$key]); + array_splice($node->stmts, $key, 0, $refactorTraitUse); + + $hasChanged = true; + } + } + } + + if (! $hasChanged) { + return null; } - return $this->refactorTraitUse($node); + return $node; } /** @@ -95,7 +129,17 @@ private function refactorTraitUse(TraitUse $traitUse): ?array $traitUses = []; foreach ($traitUse->traits as $singleTraitUse) { - $traitUses[] = new TraitUse([$singleTraitUse]); + $adaptation = []; + + foreach ($traitUse->adaptations as $traitAdaptation) { + if ($traitAdaptation instanceof Alias + && $traitAdaptation->trait instanceof Name + && $traitAdaptation->trait->toString() === $singleTraitUse->toString()) { + $adaptation[] = $traitAdaptation; + } + } + + $traitUses[] = new TraitUse([$singleTraitUse], $adaptation); } return $traitUses; diff --git a/rules/CodingStyle/Reflection/VendorLocationDetector.php b/rules/CodingStyle/Reflection/VendorLocationDetector.php deleted file mode 100644 index 2de96a07471..00000000000 --- a/rules/CodingStyle/Reflection/VendorLocationDetector.php +++ /dev/null @@ -1,52 +0,0 @@ -resolveReflectionFileName($reflection); - - // probably internal - if ($fileName === false) { - return false; - } - - $normalizedFileName = $this->pathNormalizer->normalizePath($fileName, '/'); - return str_contains($normalizedFileName, '/vendor/'); - } - - private function resolveReflectionFileName( - MethodReflection | ReflectionWithFilename | FunctionReflection $reflection - ): string | false { - if ($reflection instanceof ReflectionWithFilename) { - return $reflection->getFileName(); - } - - if ($reflection instanceof PhpFunctionReflection) { - return $reflection->getFileName(); - } - - if ($reflection instanceof MethodReflection) { - $declaringClassReflection = $reflection->getDeclaringClass(); - return $declaringClassReflection->getFileName(); - } - - return false; - } -} diff --git a/rules/CodingStyle/TypeAnalyzer/IterableTypeAnalyzer.php b/rules/CodingStyle/TypeAnalyzer/IterableTypeAnalyzer.php deleted file mode 100644 index b1540565249..00000000000 --- a/rules/CodingStyle/TypeAnalyzer/IterableTypeAnalyzer.php +++ /dev/null @@ -1,36 +0,0 @@ -getTypes() as $unionedType) { - if (! $this->detect($unionedType)) { - return false; - } - } - - return true; - } - - return false; - } -} diff --git a/rules/CodingStyle/ValueObject/ConcatExpressionJoinData.php b/rules/CodingStyle/ValueObject/ConcatExpressionJoinData.php deleted file mode 100644 index 22e6fdbcefb..00000000000 --- a/rules/CodingStyle/ValueObject/ConcatExpressionJoinData.php +++ /dev/null @@ -1,62 +0,0 @@ -values[] = $value; - } - - public function addNodeToRemove(Node $node): void - { - $this->nodesToRemove[] = $node; - } - - public function getString(): string - { - return implode('', $this->values); - } - - /** - * @return Node[] - */ - public function getNodesToRemove(): array - { - return $this->nodesToRemove; - } - - public function addPlaceholderToNode(string $objectHash, Expr $expr): void - { - $this->placeholdersToNodes[$objectHash] = $expr; - } - - /** - * @return Expr[] - */ - public function getPlaceholdersToNodes(): array - { - return $this->placeholdersToNodes; - } -} diff --git a/rules/CodingStyle/ValueObject/ConcatStringAndPlaceholders.php b/rules/CodingStyle/ValueObject/ConcatStringAndPlaceholders.php deleted file mode 100644 index 58e0e3828d9..00000000000 --- a/rules/CodingStyle/ValueObject/ConcatStringAndPlaceholders.php +++ /dev/null @@ -1,32 +0,0 @@ -content; - } - - /** - * @return Expr[] - */ - public function getPlaceholderNodes(): array - { - return $this->placeholderNodes; - } -} diff --git a/rules/CodingStyle/ValueObject/ConditionAndResult.php b/rules/CodingStyle/ValueObject/ConditionAndResult.php new file mode 100644 index 00000000000..d15655e6c19 --- /dev/null +++ b/rules/CodingStyle/ValueObject/ConditionAndResult.php @@ -0,0 +1,64 @@ +conditionExpr; + } + + public function isIdenticalCompare(): bool + { + return $this->conditionExpr instanceof Identical; + } + + public function getIdenticalVariableName(): ?string + { + $identical = $this->getConditionIdentical(); + if (! $identical->left instanceof Variable) { + return null; + } + + $variable = $identical->left; + if ($variable->name instanceof Expr) { + return null; + } + + return $variable->name; + } + + public function getResultExpr(): Expr + { + return $this->resultExpr; + } + + public function getIdenticalExpr(): Expr + { + /** @var Identical $identical */ + $identical = $this->conditionExpr; + + return $identical->right; + } + + private function getConditionIdentical(): Identical + { + Assert::isInstanceOf($this->conditionExpr, Identical::class); + + return $this->conditionExpr; + } +} diff --git a/rules/CodingStyle/ValueObject/NameAndParent.php b/rules/CodingStyle/ValueObject/NameAndParent.php deleted file mode 100644 index 77f49773944..00000000000 --- a/rules/CodingStyle/ValueObject/NameAndParent.php +++ /dev/null @@ -1,31 +0,0 @@ -nameNode; - } - - public function getParentNode(): Node - { - return $this->parentNode; - } -} diff --git a/rules/CodingStyle/ValueObject/NodeToRemoveAndConcatItem.php b/rules/CodingStyle/ValueObject/NodeToRemoveAndConcatItem.php deleted file mode 100644 index 84d0b4f88ee..00000000000 --- a/rules/CodingStyle/ValueObject/NodeToRemoveAndConcatItem.php +++ /dev/null @@ -1,27 +0,0 @@ -removedExpr; - } - - public function getConcatItemNode(): Node - { - return $this->concatItemNode; - } -} diff --git a/rules/CodingStyle/ValueObject/ObjectMagicMethods.php b/rules/CodingStyle/ValueObject/ObjectMagicMethods.php index a84b6613f23..b4b9a9cfa48 100644 --- a/rules/CodingStyle/ValueObject/ObjectMagicMethods.php +++ b/rules/CodingStyle/ValueObject/ObjectMagicMethods.php @@ -4,22 +4,22 @@ namespace Rector\CodingStyle\ValueObject; -use Rector\Core\ValueObject\MethodName; +use Rector\ValueObject\MethodName; final class ObjectMagicMethods { /** * @var string[] */ - public const METHOD_NAMES = [ + public const array METHOD_NAMES = [ '__call', '__callStatic', MethodName::CLONE, MethodName::CONSTRUCT, '__debugInfo', - MethodName::DESCTRUCT, + MethodName::DESTRUCT, '__get', - '__invoke', + MethodName::INVOKE, '__isset', '__serialize', '__set', diff --git a/rules/CodingStyle/ValueObject/ReturnArrayClassMethodToYield.php b/rules/CodingStyle/ValueObject/ReturnArrayClassMethodToYield.php deleted file mode 100644 index ea4c77da0e5..00000000000 --- a/rules/CodingStyle/ValueObject/ReturnArrayClassMethodToYield.php +++ /dev/null @@ -1,26 +0,0 @@ -type); - } - - public function getMethod(): string - { - return $this->method; - } -} diff --git a/rules/CodingStyle/ValueObject/VariableAndExprAssign.php b/rules/CodingStyle/ValueObject/VariableAndExprAssign.php new file mode 100644 index 00000000000..dc5bb7295e9 --- /dev/null +++ b/rules/CodingStyle/ValueObject/VariableAndExprAssign.php @@ -0,0 +1,27 @@ +variable; + } + + public function getExpr(): Expr + { + return $this->expr; + } +} diff --git a/rules/Composer/Application/FileProcessor/ComposerFileProcessor.php b/rules/Composer/Application/FileProcessor/ComposerFileProcessor.php deleted file mode 100644 index 1566021e6a3..00000000000 --- a/rules/Composer/Application/FileProcessor/ComposerFileProcessor.php +++ /dev/null @@ -1,79 +0,0 @@ -composerRectors === []) { - return; - } - - // to avoid modification of file - $smartFileInfo = $file->getSmartFileInfo(); - $composerJson = $this->composerJsonFactory->createFromFileInfo($smartFileInfo); - - $oldComposerJson = clone $composerJson; - foreach ($this->composerRectors as $composerRector) { - $composerRector->refactor($composerJson); - } - - // nothing has changed - if ($oldComposerJson->getJsonArray() === $composerJson->getJsonArray()) { - return; - } - - $changeFileContent = $this->composerJsonPrinter->printToString($composerJson); - $file->changeFileContent($changeFileContent); - } - - public function supports(File $file, Configuration $configuration): bool - { - $smartFileInfo = $file->getSmartFileInfo(); - - if ($this->isJsonInTests($smartFileInfo)) { - return true; - } - - return $smartFileInfo->getBasename() === 'composer.json'; - } - - /** - * @return string[] - */ - public function getSupportedFileExtensions(): array - { - return ['json']; - } - - private function isJsonInTests(SmartFileInfo $fileInfo): bool - { - if (! StaticPHPUnitEnvironment::isPHPUnitRun()) { - return false; - } - - return $fileInfo->hasSuffixes(['json']); - } -} diff --git a/rules/Composer/Contract/Rector/ComposerRectorInterface.php b/rules/Composer/Contract/Rector/ComposerRectorInterface.php deleted file mode 100644 index bf559b713c8..00000000000 --- a/rules/Composer/Contract/Rector/ComposerRectorInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -versionParser->parseConstraints($versionAware->getVersion()); - } - } -} diff --git a/rules/Composer/Rector/AddPackageToRequireComposerRector.php b/rules/Composer/Rector/AddPackageToRequireComposerRector.php deleted file mode 100644 index 6603a2a09c0..00000000000 --- a/rules/Composer/Rector/AddPackageToRequireComposerRector.php +++ /dev/null @@ -1,76 +0,0 @@ -packagesAndVersions as $packageAndVersion) { - $composerJson->addRequiredPackage($packageAndVersion->getPackageName(), $packageAndVersion->getVersion()); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Add package to "require" in `composer.json`', [new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -{ -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -{ - "require": { - "symfony/console": "^3.4" - } -} -CODE_SAMPLE - , - [ - self::PACKAGES_AND_VERSIONS => [new PackageAndVersion('symfony/console', '^3.4')], - ] - ), - ]); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $packagesAndVersions = $configuration[self::PACKAGES_AND_VERSIONS] ?? []; - Assert::allIsInstanceOf($packagesAndVersions, PackageAndVersion::class); - - $this->versionGuard->validate($packagesAndVersions); - $this->packagesAndVersions = $packagesAndVersions; - } -} diff --git a/rules/Composer/Rector/AddPackageToRequireDevComposerRector.php b/rules/Composer/Rector/AddPackageToRequireDevComposerRector.php deleted file mode 100644 index e50b5ea21bb..00000000000 --- a/rules/Composer/Rector/AddPackageToRequireDevComposerRector.php +++ /dev/null @@ -1,79 +0,0 @@ -packageAndVersions as $packageAndVersion) { - $composerJson->addRequiredDevPackage( - $packageAndVersion->getPackageName(), - $packageAndVersion->getVersion() - ); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Add package to "require-dev" in `composer.json`', [new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -{ -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -{ - "require-dev": { - "symfony/console": "^3.4" - } -} -CODE_SAMPLE - , - [ - self::PACKAGES_AND_VERSIONS => [new PackageAndVersion('symfony/console', '^3.4')], - ] - ), - ]); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $packagesAndVersions = $configuration[self::PACKAGES_AND_VERSIONS] ?? []; - Assert::allIsInstanceOf($packagesAndVersions, PackageAndVersion::class); - - $this->versionGuard->validate($packagesAndVersions); - $this->packageAndVersions = $packagesAndVersions; - } -} diff --git a/rules/Composer/Rector/ChangePackageVersionComposerRector.php b/rules/Composer/Rector/ChangePackageVersionComposerRector.php deleted file mode 100644 index ef1c12a5662..00000000000 --- a/rules/Composer/Rector/ChangePackageVersionComposerRector.php +++ /dev/null @@ -1,82 +0,0 @@ -packagesAndVersions as $packageAndVersion) { - $composerJson->changePackageVersion( - $packageAndVersion->getPackageName(), - $packageAndVersion->getVersion() - ); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Change package version `composer.json`', [new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -{ - "require-dev": { - "symfony/console": "^3.4" - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -{ - "require": { - "symfony/console": "^4.4" - } -} -CODE_SAMPLE - , - [ - self::PACKAGES_AND_VERSIONS => [new PackageAndVersion('symfony/console', '^4.4')], - ] - ), - ]); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $packagesAndVersions = $configuration[self::PACKAGES_AND_VERSIONS] ?? []; - Assert::allIsInstanceOf($packagesAndVersions, PackageAndVersion::class); - - $this->versionGuard->validate($packagesAndVersions); - $this->packagesAndVersions = $packagesAndVersions; - } -} diff --git a/rules/Composer/Rector/RemovePackageComposerRector.php b/rules/Composer/Rector/RemovePackageComposerRector.php deleted file mode 100644 index add23a6620a..00000000000 --- a/rules/Composer/Rector/RemovePackageComposerRector.php +++ /dev/null @@ -1,64 +0,0 @@ -packageNames as $packageName) { - $composerJson->removePackage($packageName); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Remove package from "require" and "require-dev" in `composer.json`', [new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -{ - "require": { - "symfony/console": "^3.4" - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -{ -} -CODE_SAMPLE - , - [ - self::PACKAGE_NAMES => ['symfony/console'], - ] - ), - ]); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $this->packageNames = $configuration[self::PACKAGE_NAMES] ?? []; - } -} diff --git a/rules/Composer/Rector/RenamePackageComposerRector.php b/rules/Composer/Rector/RenamePackageComposerRector.php deleted file mode 100644 index 156f2c7d73d..00000000000 --- a/rules/Composer/Rector/RenamePackageComposerRector.php +++ /dev/null @@ -1,86 +0,0 @@ -renamePackages as $renamePackage) { - if ($composerJson->hasRequiredPackage($renamePackage->getOldPackageName())) { - $version = $composerJson->getRequire()[$renamePackage->getOldPackageName()]; - $composerJson->replacePackage( - $renamePackage->getOldPackageName(), - $renamePackage->getNewPackageName(), - $version - ); - } - if ($composerJson->hasRequiredDevPackage($renamePackage->getOldPackageName())) { - $version = $composerJson->getRequireDev()[$renamePackage->getOldPackageName()]; - $composerJson->replacePackage( - $renamePackage->getOldPackageName(), - $renamePackage->getNewPackageName(), - $version - ); - } - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Change package name in `composer.json`', [new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -{ - "require": { - "rector/rector": "dev-main" - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -{ - "require": { - "rector/rector-src": "dev-main" - } -} -CODE_SAMPLE - , - [ - self::RENAME_PACKAGES => [new RenamePackage('rector/rector', 'rector/rector-src')], - ] - ), - ]); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $renamePackages = $configuration[self::RENAME_PACKAGES] ?? []; - Assert::allIsInstanceOf($renamePackages, RenamePackage::class); - $this->renamePackages = $renamePackages; - } -} diff --git a/rules/Composer/Rector/ReplacePackageAndVersionComposerRector.php b/rules/Composer/Rector/ReplacePackageAndVersionComposerRector.php deleted file mode 100644 index e0b562d920a..00000000000 --- a/rules/Composer/Rector/ReplacePackageAndVersionComposerRector.php +++ /dev/null @@ -1,87 +0,0 @@ -replacePackagesAndVersions as $replacePackageAndVersion) { - $composerJson->replacePackage( - $replacePackageAndVersion->getOldPackageName(), - $replacePackageAndVersion->getNewPackageName(), - $replacePackageAndVersion->getVersion() - ); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Change package name and version `composer.json`', [new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -{ - "require-dev": { - "symfony/console": "^3.4" - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -{ - "require-dev": { - "symfony/http-kernel": "^4.4" - } -} -CODE_SAMPLE - , - [ - self::REPLACE_PACKAGES_AND_VERSIONS => [new ReplacePackageAndVersion( - 'symfony/console', - 'symfony/http-kernel', - '^4.4' - )], - ] - ), - ]); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $replacePackagesAndVersions = $configuration[self::REPLACE_PACKAGES_AND_VERSIONS] ?? []; - Assert::allIsInstanceOf($replacePackagesAndVersions, ReplacePackageAndVersion::class); - - $this->versionGuard->validate($replacePackagesAndVersions); - $this->replacePackagesAndVersions = $replacePackagesAndVersions; - } -} diff --git a/rules/Composer/ValueObject/PackageAndVersion.php b/rules/Composer/ValueObject/PackageAndVersion.php deleted file mode 100644 index cde69d71792..00000000000 --- a/rules/Composer/ValueObject/PackageAndVersion.php +++ /dev/null @@ -1,26 +0,0 @@ -packageName; - } - - public function getVersion(): string - { - return $this->version; - } -} diff --git a/rules/Composer/ValueObject/RenamePackage.php b/rules/Composer/ValueObject/RenamePackage.php deleted file mode 100644 index 805db757edd..00000000000 --- a/rules/Composer/ValueObject/RenamePackage.php +++ /dev/null @@ -1,24 +0,0 @@ -oldPackageName; - } - - public function getNewPackageName(): string - { - return $this->newPackageName; - } -} diff --git a/rules/Composer/ValueObject/ReplacePackageAndVersion.php b/rules/Composer/ValueObject/ReplacePackageAndVersion.php deleted file mode 100644 index c95db145ab9..00000000000 --- a/rules/Composer/ValueObject/ReplacePackageAndVersion.php +++ /dev/null @@ -1,46 +0,0 @@ -oldPackageName = $oldPackageName; - $this->newPackageName = $newPackageName; - } - - public function getOldPackageName(): string - { - return $this->oldPackageName; - } - - public function getNewPackageName(): string - { - return $this->newPackageName; - } - - public function getVersion(): string - { - return $this->version; - } -} diff --git a/rules/DeadCode/Comparator/CurrentAndParentClassMethodComparator.php b/rules/DeadCode/Comparator/CurrentAndParentClassMethodComparator.php deleted file mode 100644 index ea690b076ee..00000000000 --- a/rules/DeadCode/Comparator/CurrentAndParentClassMethodComparator.php +++ /dev/null @@ -1,180 +0,0 @@ -isSameMethodParentCall($classMethod, $staticCall)) { - return false; - } - - if (! $this->areArgsAndParamsEqual($staticCall->args, $classMethod->params)) { - return false; - } - - if (! $this->parameterTypeComparator->isClassMethodIdenticalToParentStaticCall($classMethod, $staticCall)) { - return false; - } - - return ! $this->isParentClassMethodVisibilityOrDefaultOverride($classMethod, $staticCall); - } - - private function isSameMethodParentCall(ClassMethod $classMethod, StaticCall $staticCall): bool - { - if (! $this->nodeNameResolver->areNamesEqual($staticCall->name, $classMethod->name)) { - return false; - } - - return $this->nodeNameResolver->isName($staticCall->class, 'parent'); - } - - /** - * @param Arg[] $parentStaticCallArgs - * @param Param[] $currentClassMethodParams - */ - private function areArgsAndParamsEqual(array $parentStaticCallArgs, array $currentClassMethodParams): bool - { - if (count($parentStaticCallArgs) !== count($currentClassMethodParams)) { - return false; - } - - if ($parentStaticCallArgs === []) { - return true; - } - - foreach ($parentStaticCallArgs as $key => $arg) { - if (! isset($currentClassMethodParams[$key])) { - return false; - } - - // this only compares variable name, but those can be differnt, so its kinda useless - $param = $currentClassMethodParams[$key]; - if (! $this->nodeComparator->areNodesEqual($param->var, $arg->value)) { - return false; - } - } - - return true; - } - - private function isParentClassMethodVisibilityOrDefaultOverride( - ClassMethod $classMethod, - StaticCall $staticCall - ): bool { - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - $methodName = $this->nodeNameResolver->getName($staticCall->name); - if ($methodName === null) { - return false; - } - - foreach ($classReflection->getParents() as $parentClassReflection) { - if (! $parentClassReflection->hasMethod($methodName)) { - continue; - } - - $nativeParentClassReflection = $parentClassReflection->getNativeReflection(); - $nativeParentClassMethodReflection = $nativeParentClassReflection->getMethod($methodName); - - if (! $nativeParentClassMethodReflection->isProtected()) { - return $this->isOverridingParentParameters($classMethod, $parentClassReflection, $methodName); - } - - if (! $nativeParentClassMethodReflection->isPublic()) { - return $this->isOverridingParentParameters($classMethod, $parentClassReflection, $methodName); - } - - return true; - } - - return false; - } - - private function isOverridingParentParameters( - ClassMethod $classMethod, - ClassReflection $classReflection, - string $methodName - ): bool { - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - throw new ShouldNotHappenException(); - } - - $parentMethodReflection = $classReflection->getMethod($methodName, $scope); - - // 3rd party code - if (! $parentMethodReflection->isPrivate() && ! $parentMethodReflection->isPublic() && $classMethod->isPublic()) { - return true; - } - - if ($parentMethodReflection->isInternal()->yes()) { - // we can't know for certain so we assume its a override with purpose - return true; - } - - return $this->areParameterDefaultsDifferent($classMethod, $parentMethodReflection); - } - - private function areParameterDefaultsDifferent( - ClassMethod $classMethod, - MethodReflection $methodReflection - ): bool { - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); - - foreach ($parametersAcceptor->getParameters() as $key => $parameterReflection) { - if (! isset($classMethod->params[$key])) { - if ($parameterReflection->getDefaultValue() !== null) { - continue; - } - - return true; - } - - $methodParam = $classMethod->params[$key]; - - if ($this->parameterDefaultsComparator->areDefaultValuesDifferent( - $parameterReflection, - $methodParam - )) { - return true; - } - } - - return false; - } -} diff --git a/rules/DeadCode/Comparator/Parameter/ParameterDefaultsComparator.php b/rules/DeadCode/Comparator/Parameter/ParameterDefaultsComparator.php deleted file mode 100644 index dfe1590beec..00000000000 --- a/rules/DeadCode/Comparator/Parameter/ParameterDefaultsComparator.php +++ /dev/null @@ -1,53 +0,0 @@ -getDefaultValue() === null && $param->default === null) { - return false; - } - - if ($this->isMutuallyExclusiveNull($parameterReflection, $param)) { - return true; - } - - /** @var Expr $paramDefault */ - $paramDefault = $param->default; - - $firstParameterValue = $this->defaultParameterValueResolver->resolveFromParameterReflection( - $parameterReflection - ); - - return ! $this->nodeComparator->areNodesEqual($paramDefault, $firstParameterValue); - } - - private function isMutuallyExclusiveNull(ParameterReflection $parameterReflection, Param $param): bool - { - if ($parameterReflection->getDefaultValue() === null && $param->default !== null) { - return true; - } - - if ($parameterReflection->getDefaultValue() === null) { - return false; - } - - return $param->default === null; - } -} diff --git a/rules/DeadCode/Comparator/Parameter/ParameterTypeComparator.php b/rules/DeadCode/Comparator/Parameter/ParameterTypeComparator.php deleted file mode 100644 index 73d60f34f81..00000000000 --- a/rules/DeadCode/Comparator/Parameter/ParameterTypeComparator.php +++ /dev/null @@ -1,38 +0,0 @@ -methodParameterTypeResolver->provideParameterTypesByClassMethod($classMethod); - $parentParameterTypes = $this->methodParameterTypeResolver->provideParameterTypesByStaticCall($staticCall); - - foreach ($currentParameterTypes as $key => $currentParameterType) { - if (! isset($parentParameterTypes[$key])) { - continue; - } - - $parentParameterType = $parentParameterTypes[$key]; - if (! $currentParameterType->equals($parentParameterType)) { - return false; - } - } - - return true; - } -} diff --git a/rules/DeadCode/ConditionEvaluator.php b/rules/DeadCode/ConditionEvaluator.php index bef7d01dca2..49366651d3b 100644 --- a/rules/DeadCode/ConditionEvaluator.php +++ b/rules/DeadCode/ConditionEvaluator.php @@ -8,30 +8,27 @@ use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\Php\PhpVersionProvider; use Rector\DeadCode\Contract\ConditionInterface; use Rector\DeadCode\ValueObject\BinaryToVersionCompareCondition; use Rector\DeadCode\ValueObject\VersionCompareCondition; +use Rector\Exception\ShouldNotHappenException; +use Rector\Php\PhpVersionProvider; -final class ConditionEvaluator +final readonly class ConditionEvaluator { public function __construct( private PhpVersionProvider $phpVersionProvider ) { } - /** - * @return bool|int|null - */ - public function evaluate(ConditionInterface $condition) + public function evaluate(ConditionInterface $condition): bool|int|null { if ($condition instanceof VersionCompareCondition) { return $this->evaluateVersionCompareCondition($condition); } if ($condition instanceof BinaryToVersionCompareCondition) { - return $this->isEvaluedAsTrue($condition); + return $this->isEvaluatedAsTrue($condition); } return null; @@ -59,7 +56,7 @@ private function evaluateVersionCompareCondition( ); } - private function isEvaluedAsTrue(BinaryToVersionCompareCondition $binaryToVersionCompareCondition): bool + private function isEvaluatedAsTrue(BinaryToVersionCompareCondition $binaryToVersionCompareCondition): bool { $versionCompareResult = $this->evaluateVersionCompareCondition( $binaryToVersionCompareCondition->getVersionCompareCondition() diff --git a/rules/DeadCode/ConditionResolver.php b/rules/DeadCode/ConditionResolver.php index 723c6e46d6a..84bcb12967a 100644 --- a/rules/DeadCode/ConditionResolver.php +++ b/rules/DeadCode/ConditionResolver.php @@ -4,28 +4,27 @@ namespace Rector\DeadCode; -use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Php\PhpVersionProvider; -use Rector\Core\PhpParser\Node\Value\ValueResolver; -use Rector\Core\Util\PhpVersionFactory; use Rector\DeadCode\Contract\ConditionInterface; use Rector\DeadCode\ValueObject\BinaryToVersionCompareCondition; use Rector\DeadCode\ValueObject\VersionCompareCondition; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\Php\PhpVersionProvider; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Util\PhpVersionFactory; -final class ConditionResolver +final readonly class ConditionResolver { public function __construct( private NodeNameResolver $nodeNameResolver, private PhpVersionProvider $phpVersionProvider, private ValueResolver $valueResolver, - private PhpVersionFactory $phpVersionFactory ) { } @@ -65,13 +64,13 @@ public function resolveFromExpr(Expr $expr): ?ConditionInterface return null; } - private function isVersionCompareFuncCall(Node $node): bool + private function isVersionCompareFuncCall(Expr $expr): bool { - if (! $node instanceof FuncCall) { + if (! $expr instanceof FuncCall) { return false; } - return $this->nodeNameResolver->isName($node, 'version_compare'); + return $this->nodeNameResolver->isName($expr, 'version_compare'); } private function resolveVersionCompareConditionForFuncCall(FuncCall $funcCall): ?VersionCompareCondition @@ -88,7 +87,7 @@ private function resolveVersionCompareConditionForFuncCall(FuncCall $funcCall): // includes compare sign as 3rd argument $versionCompareSign = null; - if (isset($funcCall->args[2])) { + if (isset($funcCall->args[2]) && $funcCall->args[2] instanceof Arg) { $versionCompareSign = $this->valueResolver->getValue($funcCall->args[2]->value); } @@ -112,6 +111,14 @@ private function resolveFuncCall( private function resolveArgumentValue(FuncCall $funcCall, int $argumentPosition): ?int { + if (! isset($funcCall->args[$argumentPosition])) { + return null; + } + + if (! $funcCall->args[$argumentPosition] instanceof Arg) { + return null; + } + $firstArgValue = $funcCall->args[$argumentPosition]->value; /** @var mixed|null $version */ @@ -121,7 +128,7 @@ private function resolveArgumentValue(FuncCall $funcCall, int $argumentPosition) } if (is_string($version)) { - return $this->phpVersionFactory->createIntVersion($version); + return PhpVersionFactory::createIntVersion($version); } return $version; diff --git a/rules/DeadCode/FeatureSupport/FunctionSupportResolver.php b/rules/DeadCode/FeatureSupport/FunctionSupportResolver.php deleted file mode 100644 index 9e79a261ff9..00000000000 --- a/rules/DeadCode/FeatureSupport/FunctionSupportResolver.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ - private const FUNCTIONS_BY_VERSION = [ - PhpVersion::PHP_56 => ['session_abort', 'hash_equals', 'ldap_escape'], - PhpVersion::PHP_70 => [ - 'random_int', - 'random_bytes', - 'intdiv', - 'preg_replace_callback_array', - 'error_clear_last', - ], - PhpVersion::PHP_71 => ['is_iterable'], - PhpVersion::PHP_72 => ['spl_object_id', 'stream_isatty'], - PhpVersion::PHP_73 => ['array_key_first', 'array_key_last', 'hrtime', 'is_countable'], - PhpVersion::PHP_74 => ['get_mangled_object_vars', 'mb_str_split', 'password_algos'], - ]; - - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - public function isFunctionSupported(string $desiredFunction): bool - { - foreach (self::FUNCTIONS_BY_VERSION as $version => $functions) { - if (! in_array($desiredFunction, $functions, true)) { - continue; - } - - if (! $this->phpVersionProvider->isAtLeastPhpVersion($version)) { - continue; - } - - return true; - } - - return false; - } -} diff --git a/rules/DeadCode/NodeAnalyzer/CallCollectionAnalyzer.php b/rules/DeadCode/NodeAnalyzer/CallCollectionAnalyzer.php index 16610c72d16..967e04b74c8 100644 --- a/rules/DeadCode/NodeAnalyzer/CallCollectionAnalyzer.php +++ b/rules/DeadCode/NodeAnalyzer/CallCollectionAnalyzer.php @@ -5,12 +5,18 @@ namespace Rector\DeadCode\NodeAnalyzer; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\StaticCall; -use PHPStan\Type\TypeWithClassName; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; +use PhpParser\Node\Name; +use PHPStan\Type\MixedType; +use Rector\Enum\ObjectReference; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; -final class CallCollectionAnalyzer +final readonly class CallCollectionAnalyzer { public function __construct( private NodeTypeResolver $nodeTypeResolver, @@ -19,28 +25,76 @@ public function __construct( } /** - * @param StaticCall[]|MethodCall[] $calls + * @param StaticCall[]|MethodCall[]|NullsafeMethodCall[] $calls */ - public function isExists(array $calls, string $classMethodName, ?string $className): bool + public function isExists(array $calls, string $classMethodName, string $className): bool { foreach ($calls as $call) { $callerRoot = $call instanceof StaticCall ? $call->class : $call->var; - $callerType = $this->nodeTypeResolver->resolve($callerRoot); + $callerType = $this->nodeTypeResolver->getType($callerRoot); + + $callerTypeClassName = ClassNameFromObjectTypeResolver::resolve($callerType); + if ($callerTypeClassName === null) { + // handle fluent by $this->bar()->baz()->qux() + // that methods don't have return type + if ($callerType instanceof MixedType && ! $callerType->isExplicitMixed()) { + $cloneCallerRoot = clone $callerRoot; + $isFluent = false; + // init + $methodCallNames = []; + + // first append + $methodCallNames[] = (string) $this->nodeNameResolver->getName($call->name); + while ($cloneCallerRoot instanceof MethodCall) { + $methodCallNames[] = (string) $this->nodeNameResolver->getName($cloneCallerRoot->name); + if ($cloneCallerRoot->var instanceof Variable && $cloneCallerRoot->var->name === 'this') { + $isFluent = true; + break; + } + + $cloneCallerRoot = $cloneCallerRoot->var; + } + + if ($isFluent && in_array($classMethodName, $methodCallNames, true)) { + return true; + } + } - if (! $callerType instanceof TypeWithClassName) { continue; } - if ($callerType->getClassName() !== $className) { + if ($this->isSelfStatic($call) && $this->shouldSkip($call, $classMethodName)) { + return true; + } + + if ($callerTypeClassName !== $className) { continue; } - // the method is used - if ($this->nodeNameResolver->isName($call->name, $classMethodName)) { + if ($this->shouldSkip($call, $classMethodName)) { return true; } } return false; } + + private function isSelfStatic(MethodCall|StaticCall|NullsafeMethodCall $call): bool + { + return $call instanceof StaticCall && $call->class instanceof Name && in_array( + $call->class->toString(), + [ObjectReference::SELF, ObjectReference::STATIC], + true + ); + } + + private function shouldSkip(StaticCall|MethodCall|NullsafeMethodCall $call, string $classMethodName): bool + { + if (! $call->name instanceof Identifier) { + return true; + } + + // the method is used + return $this->nodeNameResolver->isName($call->name, $classMethodName); + } } diff --git a/rules/DeadCode/NodeAnalyzer/CallLikeParamDefaultResolver.php b/rules/DeadCode/NodeAnalyzer/CallLikeParamDefaultResolver.php new file mode 100644 index 00000000000..6358ab26ad9 --- /dev/null +++ b/rules/DeadCode/NodeAnalyzer/CallLikeParamDefaultResolver.php @@ -0,0 +1,73 @@ +reflectionResolver->resolveFunctionLikeReflectionFromCall($callLike); + if (! $reflection instanceof MethodReflection && ! $reflection instanceof FunctionReflection) { + return []; + } + + if ($reflection instanceof MethodReflection && $reflection->getName() === 'get') { + $classReflection = $reflection->getDeclaringClass(); + if ($classReflection->getName() === 'Ds\Map') { + return []; + } + } + + $nullPositions = []; + + $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors($reflection->getVariants()); + foreach ($extendedParametersAcceptor->getParameters() as $position => $extendedParameterReflection) { + if (! $extendedParameterReflection->getDefaultValue() instanceof NullType) { + continue; + } + + $nullPositions[] = $position; + } + + return $nullPositions; + } + + public function resolvePositionParameterByName( + MethodCall|StaticCall|New_|FuncCall $callLike, + string $parameterName + ): ?int { + $reflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($callLike); + if (! $reflection instanceof MethodReflection && ! $reflection instanceof FunctionReflection) { + return null; + } + + $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors($reflection->getVariants()); + foreach ($extendedParametersAcceptor->getParameters() as $position => $extendedParameterReflection) { + if ($extendedParameterReflection->getName() === $parameterName) { + return $position; + } + } + + return null; + } +} diff --git a/rules/DeadCode/NodeAnalyzer/ExprUsedInNextNodeAnalyzer.php b/rules/DeadCode/NodeAnalyzer/ExprUsedInNextNodeAnalyzer.php deleted file mode 100644 index db3d703c7f9..00000000000 --- a/rules/DeadCode/NodeAnalyzer/ExprUsedInNextNodeAnalyzer.php +++ /dev/null @@ -1,26 +0,0 @@ -betterNodeFinder->findFirstNext( - $expr, - fn (Node $node): bool => $this->exprUsedInNodeAnalyzer->isUsed($node, $expr) - ); - } -} diff --git a/rules/DeadCode/NodeAnalyzer/ExprUsedInNodeAnalyzer.php b/rules/DeadCode/NodeAnalyzer/ExprUsedInNodeAnalyzer.php index ca3ede01843..aa509c38ea6 100644 --- a/rules/DeadCode/NodeAnalyzer/ExprUsedInNodeAnalyzer.php +++ b/rules/DeadCode/NodeAnalyzer/ExprUsedInNodeAnalyzer.php @@ -9,42 +9,31 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Include_; use PhpParser\Node\Expr\Variable; -use Rector\Core\NodeAnalyzer\CompactFuncCallAnalyzer; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Printer\BetterStandardPrinter; +use Rector\NodeAnalyzer\CompactFuncCallAnalyzer; -final class ExprUsedInNodeAnalyzer +final readonly class ExprUsedInNodeAnalyzer { public function __construct( - private NodeComparator $nodeComparator, private UsedVariableNameAnalyzer $usedVariableNameAnalyzer, - private CompactFuncCallAnalyzer $compactFuncCallAnalyzer, - private BetterStandardPrinter $betterStandardPrinter + private CompactFuncCallAnalyzer $compactFuncCallAnalyzer ) { } - public function isUsed(Node $node, Expr $expr): bool + public function isUsed(Node $node, Variable $variable): bool { if ($node instanceof Include_) { return true; } // variable as variable variable need mark as used - if ($node instanceof Variable && $expr instanceof Variable) { - $print = $this->betterStandardPrinter->print($node); - if (\str_starts_with($print, '${$')) { - return true; - } - } - - if ($node instanceof FuncCall && $expr instanceof Variable) { - return $this->compactFuncCallAnalyzer->isInCompact($node, $expr); + if ($node instanceof Variable && $node->name instanceof Expr) { + return true; } - if ($expr instanceof Variable) { - return $this->usedVariableNameAnalyzer->isVariableNamed($node, $expr); + if ($node instanceof FuncCall) { + return $this->compactFuncCallAnalyzer->isInCompact($node, $variable); } - return $this->nodeComparator->areNodesEqual($node, $expr); + return $this->usedVariableNameAnalyzer->isVariableNamed($node, $variable); } } diff --git a/rules/DeadCode/NodeAnalyzer/InstanceOfUniqueKeyResolver.php b/rules/DeadCode/NodeAnalyzer/InstanceOfUniqueKeyResolver.php deleted file mode 100644 index f1bcb11fda1..00000000000 --- a/rules/DeadCode/NodeAnalyzer/InstanceOfUniqueKeyResolver.php +++ /dev/null @@ -1,35 +0,0 @@ -expr instanceof Variable) { - return null; - } - $variableName = $this->nodeNameResolver->getName($instanceof->expr); - if ($variableName === null) { - return null; - } - - $className = $this->nodeNameResolver->getName($instanceof->class); - if ($className === null) { - return null; - } - - return $variableName . '_' . $className; - } -} diff --git a/rules/DeadCode/NodeAnalyzer/IsClassMethodUsedAnalyzer.php b/rules/DeadCode/NodeAnalyzer/IsClassMethodUsedAnalyzer.php index c0695b9bf84..f1eca7b9ef7 100644 --- a/rules/DeadCode/NodeAnalyzer/IsClassMethodUsedAnalyzer.php +++ b/rules/DeadCode/NodeAnalyzer/IsClassMethodUsedAnalyzer.php @@ -4,24 +4,33 @@ namespace Rector\DeadCode\NodeAnalyzer; +use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; -use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Trait_; +use PhpParser\NodeVisitor; use PHPStan\Analyser\Scope; +use PHPStan\Parser\ArrayMapArgVisitor; use PHPStan\Reflection\ClassReflection; -use Rector\Core\PhpParser\AstResolver; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\Node\Value\ValueResolver; use Rector\NodeCollector\NodeAnalyzer\ArrayCallableMethodMatcher; use Rector\NodeCollector\ValueObject\ArrayCallable; +use Rector\NodeCollector\ValueObject\ArrayCallableDynamicMethod; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\AstResolver; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Reflection\ReflectionResolver; -final class IsClassMethodUsedAnalyzer +final readonly class IsClassMethodUsedAnalyzer { public function __construct( private NodeNameResolver $nodeNameResolver, @@ -29,17 +38,14 @@ public function __construct( private BetterNodeFinder $betterNodeFinder, private ValueResolver $valueResolver, private ArrayCallableMethodMatcher $arrayCallableMethodMatcher, - private CallCollectionAnalyzer $callCollectionAnalyzer + private CallCollectionAnalyzer $callCollectionAnalyzer, + private ReflectionResolver $reflectionResolver, + private SimpleCallableNodeTraverser $simpleCallableNodeTraverser ) { } - public function isClassMethodUsed(ClassMethod $classMethod): bool + public function isClassMethodUsed(Class_ $class, ClassMethod $classMethod, Scope $scope): bool { - $class = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { - return true; - } - $classMethodName = $this->nodeNameResolver->getName($classMethod); // 1. direct normal calls @@ -47,13 +53,18 @@ public function isClassMethodUsed(ClassMethod $classMethod): bool return true; } - // 2. direct static calls + // 2. direct null-safe calls + if ($this->isClassMethodCalledInLocalNullsafeMethodCall($class, $classMethodName)) { + return true; + } + + // 3. direct static calls if ($this->isClassMethodUsedInLocalStaticCall($class, $classMethodName)) { return true; } - // 3. magic array calls! - if ($this->isClassMethodCalledInLocalArrayCall($class, $classMethod)) { + // 4. magic array calls! + if ($this->isClassMethodCalledInLocalArrayCall($class, $classMethod, $scope)) { return true; } @@ -63,7 +74,7 @@ public function isClassMethodUsed(ClassMethod $classMethod): bool private function isClassMethodUsedInLocalStaticCall(Class_ $class, string $classMethodName): bool { - $className = $this->nodeNameResolver->getName($class); + $className = (string) $this->nodeNameResolver->getName($class); /** @var StaticCall[] $staticCalls */ $staticCalls = $this->betterNodeFinder->findInstanceOf($class, StaticCall::class); @@ -72,29 +83,29 @@ private function isClassMethodUsedInLocalStaticCall(Class_ $class, string $class private function isClassMethodCalledInLocalMethodCall(Class_ $class, string $classMethodName): bool { - $className = $this->nodeNameResolver->getName($class); + $className = (string) $this->nodeNameResolver->getName($class); /** @var MethodCall[] $methodCalls */ $methodCalls = $this->betterNodeFinder->findInstanceOf($class, MethodCall::class); return $this->callCollectionAnalyzer->isExists($methodCalls, $classMethodName, $className); } - private function isInArrayMap(Class_ $class, Array_ $array): bool + private function isClassMethodCalledInLocalNullsafeMethodCall(Class_ $class, string $classMethodName): bool { - $parentFuncCall = $this->betterNodeFinder->findParentType($array, FuncCall::class); - if (! $parentFuncCall instanceof FuncCall) { - return false; - } + $className = (string) $this->nodeNameResolver->getName($class); - if (! $this->nodeNameResolver->isName($parentFuncCall->name, 'array_map')) { - return false; - } + /** @var NullsafeMethodCall[] $methodCalls */ + $methodCalls = $this->betterNodeFinder->findInstanceOf($class, NullsafeMethodCall::class); + return $this->callCollectionAnalyzer->isExists($methodCalls, $classMethodName, $className); + } - if (count($array->items) !== 2) { + private function isInArrayMap(Class_ $class, Array_ $array): bool + { + if (! $array->getAttribute(ArrayMapArgVisitor::ATTRIBUTE_NAME) instanceof Arg) { return false; } - if (! $array->items[1] instanceof ArrayItem) { + if (count($array->items) !== 2) { return false; } @@ -107,17 +118,22 @@ private function isInArrayMap(Class_ $class, Array_ $array): bool return $class->getMethod($value) instanceof ClassMethod; } - private function isClassMethodCalledInLocalArrayCall(Class_ $class, ClassMethod $classMethod): bool + private function isClassMethodCalledInLocalArrayCall(Class_ $class, ClassMethod $classMethod, Scope $scope): bool { /** @var Array_[] $arrays */ $arrays = $this->betterNodeFinder->findInstanceOf($class, Array_::class); + $classMethodName = $this->nodeNameResolver->getName($classMethod); foreach ($arrays as $array) { if ($this->isInArrayMap($class, $array)) { return true; } - $arrayCallable = $this->arrayCallableMethodMatcher->match($array); + $arrayCallable = $this->arrayCallableMethodMatcher->match($array, $scope, $classMethodName); + if ($arrayCallable instanceof ArrayCallableDynamicMethod) { + return true; + } + if ($this->shouldSkipArrayCallable($class, $arrayCallable)) { continue; } @@ -132,7 +148,7 @@ private function isClassMethodCalledInLocalArrayCall(Class_ $class, ClassMethod return false; } - private function shouldSkipArrayCallable(Class_ $class, ?ArrayCallable $arrayCallable): bool + private function shouldSkipArrayCallable(Class_ $class, null | ArrayCallable $arrayCallable): bool { if (! $arrayCallable instanceof ArrayCallable) { return true; @@ -142,31 +158,79 @@ private function shouldSkipArrayCallable(Class_ $class, ?ArrayCallable $arrayCal return ! $this->nodeNameResolver->isName($class, $arrayCallable->getClass()); } - private function doesMethodExistInTrait(ClassMethod $classMethod, ?string $classMethodName): bool + private function doesMethodExistInTrait(ClassMethod $classMethod, string $classMethodName): bool { - if ($classMethodName === null) { - return false; - } - - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); if (! $classReflection instanceof ClassReflection) { return false; } $traits = $this->astResolver->parseClassReflectionTraits($classReflection); + $className = $classReflection->getName(); + foreach ($traits as $trait) { - $method = $trait->getMethod($classMethodName); - if (! $method instanceof ClassMethod) { - continue; + if ($this->isUsedByTrait($trait, $classMethodName, $className)) { + return true; } - return true; } return false; } + + private function isUsedByTrait(Trait_ $trait, string $classMethodName, string $className): bool + { + foreach ($trait->getMethods() as $classMethod) { + if ($classMethod->name->toString() === $classMethodName) { + return true; + } + + /** + * Trait can't detect class type, so it rely on "this" or "self" or "static" or "ClassName::methodName()" usage... + */ + + $callMethod = null; + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + (array) $classMethod->stmts, + function (Node $subNode) use ($className, $classMethodName, &$callMethod): ?int { + if ($subNode instanceof Class_ || $subNode instanceof Function_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($subNode instanceof MethodCall + && $this->nodeNameResolver->isName($subNode->var, 'this') + && $this->nodeNameResolver->isName($subNode->name, $classMethodName)) { + $callMethod = $subNode; + return NodeVisitor::STOP_TRAVERSAL; + } + + if ($this->isStaticCallMatch($subNode, $className, $classMethodName)) { + $callMethod = $subNode; + return NodeVisitor::STOP_TRAVERSAL; + } + + return null; + } + ); + + if ($callMethod instanceof CallLike) { + return true; + } + } + + return false; + } + + private function isStaticCallMatch(Node $subNode, string $className, string $classMethodName): bool + { + if (! $subNode instanceof StaticCall) { + return false; + } + + if (! $subNode->class instanceof Name) { + return false; + } + + return ($subNode->class->isSpecialClassName() || $subNode->class->toString() === $className) + && $this->nodeNameResolver->isName($subNode->name, $classMethodName); + } } diff --git a/rules/DeadCode/NodeAnalyzer/ParentClassAnalyzer.php b/rules/DeadCode/NodeAnalyzer/ParentClassAnalyzer.php new file mode 100644 index 00000000000..88a5f4b3ccd --- /dev/null +++ b/rules/DeadCode/NodeAnalyzer/ParentClassAnalyzer.php @@ -0,0 +1,53 @@ +isAbstract()) { + return false; + } + + if ($classMethod->isPrivate()) { + return false; + } + + $classMethodName = $classMethod->name->name; + + foreach ((array) $classMethod->stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + $expr = $stmt->expr; + if (! $expr instanceof StaticCall) { + continue; + } + + if (! $this->nodeNameResolver->isName($expr->class, 'parent')) { + continue; + } + + if ($this->nodeNameResolver->isName($expr->name, $classMethodName)) { + return true; + } + } + + return false; + } +} diff --git a/rules/DeadCode/NodeAnalyzer/PropertyWriteonlyAnalyzer.php b/rules/DeadCode/NodeAnalyzer/PropertyWriteonlyAnalyzer.php new file mode 100644 index 00000000000..2cc7995700e --- /dev/null +++ b/rules/DeadCode/NodeAnalyzer/PropertyWriteonlyAnalyzer.php @@ -0,0 +1,85 @@ +nodeTypeResolver->isObjectType( + $class, + new ObjectType('JsonSerializable') + ); + + return (bool) $this->betterNodeFinder->findFirst($class, function (Node $node) use ( + $isImplementsJsonSerializable + ): bool { + if ($isImplementsJsonSerializable && $node instanceof FuncCall && $this->nodeNameResolver->isName( + $node, + 'get_object_vars' + ) && ! $node->isFirstClassCallable()) { + $firstArg = $node->getArgs()[0] ?? null; + if ($firstArg instanceof Arg && $firstArg->value instanceof Variable && $firstArg->value->name === 'this') { + return true; + } + } + + if (! $node instanceof PropertyFetch && ! $node instanceof NullsafePropertyFetch) { + return false; + } + + // has dynamic name - could be anything + return $node->name instanceof Expr; + }); + } + + /** + * The property fetches are always only assigned to, nothing else + * + * @param array $propertyFetches + */ + public function arePropertyFetchesExclusivelyBeingAssignedTo(array $propertyFetches): bool + { + foreach ($propertyFetches as $propertyFetch) { + if ((bool) $propertyFetch->getAttribute(AttributeKey::IS_MULTI_ASSIGN, false)) { + return false; + } + + if ((bool) $propertyFetch->getAttribute(AttributeKey::IS_ASSIGNED_TO, false)) { + return false; + } + + if ((bool) $propertyFetch->getAttribute(AttributeKey::IS_BEING_ASSIGNED, false)) { + continue; + } + + return false; + } + + return true; + } +} diff --git a/rules/DeadCode/NodeAnalyzer/SafeLeftTypeBooleanAndOrAnalyzer.php b/rules/DeadCode/NodeAnalyzer/SafeLeftTypeBooleanAndOrAnalyzer.php new file mode 100644 index 00000000000..f52b8d045bf --- /dev/null +++ b/rules/DeadCode/NodeAnalyzer/SafeLeftTypeBooleanAndOrAnalyzer.php @@ -0,0 +1,72 @@ +betterNodeFinder->findFirst( + $booleanAnd->left, + fn (Node $node): bool => $node instanceof Variable && $this->exprAnalyzer->isNonTypedFromParam($node) + ); + + if ($hasNonTypedFromParam) { + return false; + } + + $hasPropertyFetchOrArrayDimFetch = (bool) $this->betterNodeFinder->findFirst( + $booleanAnd->left, + static fn (Node $node): bool => $node instanceof PropertyFetch || $node instanceof StaticPropertyFetch || $node instanceof ArrayDimFetch + ); + + // get type from Property and ArrayDimFetch is unreliable + if ($hasPropertyFetchOrArrayDimFetch) { + return false; + } + + // skip trait this + $classReflection = $this->reflectionResolver->resolveClassReflection($booleanAnd); + if ($classReflection instanceof ClassReflection && $classReflection->isTrait()) { + return ! $booleanAnd->left instanceof Instanceof_; + } + + return ! (bool) $this->betterNodeFinder->findFirst( + $booleanAnd->left, + function (Node $node): bool { + if (! $node instanceof CallLike) { + return false; + } + + $nativeType = $this->nodeTypeResolver->getNativeType($node); + return $nativeType instanceof MixedType && ! $nativeType->isExplicitMixed(); + } + ); + } +} diff --git a/rules/DeadCode/NodeAnalyzer/UsedVariableNameAnalyzer.php b/rules/DeadCode/NodeAnalyzer/UsedVariableNameAnalyzer.php index ec4ba5c84e4..cfe25d01563 100644 --- a/rules/DeadCode/NodeAnalyzer/UsedVariableNameAnalyzer.php +++ b/rules/DeadCode/NodeAnalyzer/UsedVariableNameAnalyzer.php @@ -10,7 +10,7 @@ use PhpParser\Node\Expr\Variable; use Rector\NodeNameResolver\NodeNameResolver; -final class UsedVariableNameAnalyzer +final readonly class UsedVariableNameAnalyzer { public function __construct( private NodeNameResolver $nodeNameResolver diff --git a/rules/DeadCode/NodeCollector/ModifiedVariableNamesCollector.php b/rules/DeadCode/NodeCollector/ModifiedVariableNamesCollector.php deleted file mode 100644 index 7e1c46b8e1a..00000000000 --- a/rules/DeadCode/NodeCollector/ModifiedVariableNamesCollector.php +++ /dev/null @@ -1,103 +0,0 @@ -collectFromArgs($stmt); - $assignNames = $this->collectFromAssigns($stmt); - - return array_merge($argNames, $assignNames); - } - - /** - * @return string[] - */ - private function collectFromArgs(Stmt $stmt): array - { - $variableNames = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmt, function (Node $node) use ( - &$variableNames - ) { - if (! $node instanceof Arg) { - return null; - } - - if (! $this->isVariableChangedInReference($node)) { - return null; - } - - $variableName = $this->nodeNameResolver->getName($node->value); - if ($variableName === null) { - return null; - } - - $variableNames[] = $variableName; - }); - - return $variableNames; - } - - /** - * @return string[] - */ - private function collectFromAssigns(Stmt $stmt): array - { - $modifiedVariableNames = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmt, function (Node $node) use ( - &$modifiedVariableNames - ) { - if (! $node instanceof Assign) { - return null; - } - - if (! $node->var instanceof Variable) { - return null; - } - - $variableName = $this->nodeNameResolver->getName($node->var); - if ($variableName === null) { - return null; - } - - $modifiedVariableNames[] = $variableName; - }); - - return $modifiedVariableNames; - } - - private function isVariableChangedInReference(Arg $arg): bool - { - $parentNode = $arg->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof FuncCall) { - return false; - } - - return $this->nodeNameResolver->isNames($parentNode, ['array_shift', 'array_pop']); - } -} diff --git a/rules/DeadCode/NodeCollector/NodeByTypeAndPositionCollector.php b/rules/DeadCode/NodeCollector/NodeByTypeAndPositionCollector.php deleted file mode 100644 index 200dc766219..00000000000 --- a/rules/DeadCode/NodeCollector/NodeByTypeAndPositionCollector.php +++ /dev/null @@ -1,94 +0,0 @@ -getAttribute(AttributeKey::START_TOKEN_POSITION); - - if ($startTokenPos === null) { - continue; - } - - // not in different scope, than previous one - e.g. if/while/else... - // get nesting level to $classMethodNode - /** @var Assign $assign */ - $assign = $assignedVariable->getAttribute(AttributeKey::PARENT_NODE); - $nestingHash = $this->flowOfControlLocator->resolveNestingHashFromFunctionLike($functionLike, $assign); - - /** @var string $variableName */ - $variableName = $this->nodeNameResolver->getName($assignedVariable); - - $nodesByTypeAndPosition[] = new VariableNodeUse( - $startTokenPos, - $variableName, - VariableNodeUse::TYPE_ASSIGN, - $assignedVariable, - $nestingHash - ); - } - - foreach ($assignedVariablesUse as $assignedVariableUse) { - /** @var int|null $startTokenPos */ - $startTokenPos = $assignedVariableUse->getAttribute(AttributeKey::START_TOKEN_POSITION); - - if ($startTokenPos === null) { - continue; - } - - /** @var string $variableName */ - $variableName = $this->nodeNameResolver->getName($assignedVariableUse); - - $nodesByTypeAndPosition[] = new VariableNodeUse( - $startTokenPos, - $variableName, - VariableNodeUse::TYPE_USE, - $assignedVariableUse - ); - } - - return $this->sortByStart($nodesByTypeAndPosition); - } - - /** - * @param VariableNodeUse[] $nodesByTypeAndPosition - * @return VariableNodeUse[] - */ - private function sortByStart(array $nodesByTypeAndPosition): array - { - usort( - $nodesByTypeAndPosition, - fn (VariableNodeUse $firstVariableNodeUse, VariableNodeUse $secondVariableNodeUse): int => $firstVariableNodeUse->getStartTokenPosition() <=> $secondVariableNodeUse->getStartTokenPosition() - ); - return $nodesByTypeAndPosition; - } -} diff --git a/rules/DeadCode/NodeCollector/UnusedParameterResolver.php b/rules/DeadCode/NodeCollector/UnusedParameterResolver.php index 9078bae0fda..8d17be38d1a 100644 --- a/rules/DeadCode/NodeCollector/UnusedParameterResolver.php +++ b/rules/DeadCode/NodeCollector/UnusedParameterResolver.php @@ -6,38 +6,25 @@ use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\NodeManipulator\ClassMethodManipulator; -use Rector\Core\ValueObject\MethodName; -use Rector\NodeNameResolver\NodeNameResolver; +use Rector\NodeAnalyzer\ParamAnalyzer; -final class UnusedParameterResolver +final readonly class UnusedParameterResolver { public function __construct( - private ClassMethodManipulator $classMethodManipulator, - private NodeNameResolver $nodeNameResolver + private ParamAnalyzer $paramAnalyzer ) { } /** - * @return Param[] + * @return array */ public function resolve(ClassMethod $classMethod): array { + /** @var array $unusedParameters */ $unusedParameters = []; foreach ($classMethod->params as $i => $param) { - // skip property promotion - /** @var Param $param */ - if ($param->flags !== 0) { - continue; - } - - if ($this->classMethodManipulator->isParameterUsedInClassMethod($param, $classMethod)) { - // reset to keep order of removed arguments, if not construtctor - probably autowired - if (! $this->nodeNameResolver->isName($classMethod, MethodName::CONSTRUCT)) { - $unusedParameters = []; - } - + if ($this->paramAnalyzer->isParamUsedInClassMethod($classMethod, $param)) { continue; } diff --git a/rules/DeadCode/NodeFinder/NextVariableUsageNodeFinder.php b/rules/DeadCode/NodeFinder/NextVariableUsageNodeFinder.php deleted file mode 100644 index a9965f6690a..00000000000 --- a/rules/DeadCode/NodeFinder/NextVariableUsageNodeFinder.php +++ /dev/null @@ -1,78 +0,0 @@ -parentScopeFinder->find($assign); - if ($scopeNode === null) { - return null; - } - - /** @var Variable $expr */ - $expr = $assign->var; - $this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $scopeNode->stmts, function ( - Node $currentNode - ) use ($expr, &$nextUsageOfVariable): ?int { - // used above the assign - if ($currentNode->getStartTokenPos() < $expr->getStartTokenPos()) { - return null; - } - - // skip self - if ($this->nodeComparator->areSameNode($currentNode, $expr)) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($currentNode, $expr)) { - return null; - } - - $currentNodeParent = $currentNode->getAttribute(AttributeKey::PARENT_NODE); - - if ($currentNodeParent instanceof Assign && ! $this->hasInParentExpression($currentNodeParent, $expr)) { - return NodeTraverser::STOP_TRAVERSAL; - } - - $nextUsageOfVariable = $currentNode; - - return NodeTraverser::STOP_TRAVERSAL; - }); - - return $nextUsageOfVariable; - } - - private function hasInParentExpression(Assign $assign, Variable $variable): bool - { - $name = $this->nodeNameResolver->getName($variable); - if ($name === null) { - return false; - } - - return $this->betterNodeFinder->hasVariableOfName($assign->expr, $name); - } -} diff --git a/rules/DeadCode/NodeFinder/PreviousVariableAssignNodeFinder.php b/rules/DeadCode/NodeFinder/PreviousVariableAssignNodeFinder.php deleted file mode 100644 index feee37f302b..00000000000 --- a/rules/DeadCode/NodeFinder/PreviousVariableAssignNodeFinder.php +++ /dev/null @@ -1,47 +0,0 @@ -nodeNameResolver->getName($assign->var); - if ($variableName === null) { - return null; - } - - return $this->betterNodeFinder->findFirstPrevious($assign, function (Node $node) use ( - $variableName, - $currentAssign - ): bool { - if (! $node instanceof Assign) { - return false; - } - - // skip self - if ($this->nodeComparator->areSameNode($node, $currentAssign)) { - return false; - } - - return $this->nodeNameResolver->isName($node->var, $variableName); - }); - } -} diff --git a/rules/DeadCode/NodeFinder/VariableUseFinder.php b/rules/DeadCode/NodeFinder/VariableUseFinder.php deleted file mode 100644 index 3c3743a05ea..00000000000 --- a/rules/DeadCode/NodeFinder/VariableUseFinder.php +++ /dev/null @@ -1,50 +0,0 @@ -betterNodeFinder->find($node, function (Node $node) use ($assignedVariables): bool { - if (! $node instanceof Variable) { - return false; - } - - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - // is the left assign - not use of one - if ($parentNode instanceof Assign && ($parentNode->var instanceof Variable && $parentNode->var === $node)) { - return false; - } - $nodeNameResolverGetName = $this->nodeNameResolver->getName($node); - - // simple variable only - if ($nodeNameResolverGetName === null) { - return false; - } - - return $this->nodeComparator->isNodeEqual($node, $assignedVariables); - }); - } -} diff --git a/rules/DeadCode/NodeManipulator/ClassMethodParamRemover.php b/rules/DeadCode/NodeManipulator/ClassMethodParamRemover.php new file mode 100644 index 00000000000..0a0f25368b5 --- /dev/null +++ b/rules/DeadCode/NodeManipulator/ClassMethodParamRemover.php @@ -0,0 +1,41 @@ +params as $key => $param) { + if ($this->paramAnalyzer->isParamUsedInClassMethod($classMethod, $param)) { + continue; + } + + $paramKeysToBeRemoved[] = $key; + } + + if ($paramKeysToBeRemoved === []) { + return null; + } + + $removedParamKeys = $this->complexNodeRemover->processRemoveParamWithKeys($classMethod, $paramKeysToBeRemoved); + if ($removedParamKeys !== []) { + return $classMethod; + } + + return null; + } +} diff --git a/rules/DeadCode/NodeManipulator/ControllerClassMethodManipulator.php b/rules/DeadCode/NodeManipulator/ControllerClassMethodManipulator.php index 89454497a45..d08cfac84d7 100644 --- a/rules/DeadCode/NodeManipulator/ControllerClassMethodManipulator.php +++ b/rules/DeadCode/NodeManipulator/ControllerClassMethodManipulator.php @@ -4,51 +4,47 @@ namespace Rector\DeadCode\NodeManipulator; +use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; +use Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -final class ControllerClassMethodManipulator +final readonly class ControllerClassMethodManipulator { public function __construct( private NodeNameResolver $nodeNameResolver, - private PhpDocInfoFactory $phpDocInfoFactory + private PhpDocInfoFactory $phpDocInfoFactory, ) { } - public function isControllerClassMethodWithBehaviorAnnotation(ClassMethod $classMethod): bool - { - if (! $this->isControllerClassMethod($classMethod)) { - return false; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - return $phpDocInfo->hasByType(GenericTagValueNode::class); - } - - private function isControllerClassMethod(ClassMethod $classMethod): bool + public function isControllerClassMethod(Class_ $class, ClassMethod $classMethod): bool { if (! $classMethod->isPublic()) { return false; } - $classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { + if (! $this->hasParentClassController($class)) { return false; } - return $this->hasParentClassController($classLike); + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + return $phpDocInfo->hasByTypes([GenericTagValueNode::class, SpacelessPhpDocTagNode::class]); } private function hasParentClassController(Class_ $class): bool { - if ($class->extends === null) { + if (! $class->extends instanceof Name) { return false; } - return $this->nodeNameResolver->isName($class->extends, '#(Controller|Presenter)$#'); + $parentClassName = $this->nodeNameResolver->getName($class->extends); + if (str_ends_with($parentClassName, 'Controller')) { + return true; + } + + return str_ends_with($parentClassName, 'Presenter'); } } diff --git a/rules/DeadCode/NodeManipulator/CountManipulator.php b/rules/DeadCode/NodeManipulator/CountManipulator.php index 7785aec5b78..c6974e1758d 100644 --- a/rules/DeadCode/NodeManipulator/CountManipulator.php +++ b/rules/DeadCode/NodeManipulator/CountManipulator.php @@ -4,45 +4,47 @@ namespace Rector\DeadCode\NodeManipulator; -use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Greater; use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual; use PhpParser\Node\Expr\BinaryOp\Smaller; use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual; use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Scalar\LNumber; -use Rector\Core\PhpParser\Comparing\NodeComparator; +use PhpParser\Node\Scalar\Int_; +use PHPStan\Type\NeverType; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Comparing\NodeComparator; -final class CountManipulator +final readonly class CountManipulator { public function __construct( private NodeNameResolver $nodeNameResolver, - private NodeComparator $nodeComparator + private NodeComparator $nodeComparator, + private NodeTypeResolver $nodeTypeResolver ) { } - public function isCounterHigherThanOne(Node $node, Expr $expr): bool + public function isCounterHigherThanOne(Expr $firstExpr, Expr $secondExpr): bool { // e.g. count($values) > 0 - if ($node instanceof Greater) { - return $this->isGreater($node, $expr); + if ($firstExpr instanceof Greater) { + return $this->isGreater($firstExpr, $secondExpr); } // e.g. count($values) >= 1 - if ($node instanceof GreaterOrEqual) { - return $this->isGreaterOrEqual($node, $expr); + if ($firstExpr instanceof GreaterOrEqual) { + return $this->isGreaterOrEqual($firstExpr, $secondExpr); } // e.g. 0 < count($values) - if ($node instanceof Smaller) { - return $this->isSmaller($node, $expr); + if ($firstExpr instanceof Smaller) { + return $this->isSmaller($firstExpr, $secondExpr); } // e.g. 1 <= count($values) - if ($node instanceof SmallerOrEqual) { - return $this->isSmallerOrEqual($node, $expr); + if ($firstExpr instanceof SmallerOrEqual) { + return $this->isSmallerOrEqual($firstExpr, $secondExpr); } return false; @@ -84,16 +86,16 @@ private function isSmallerOrEqual(SmallerOrEqual $smallerOrEqual, Expr $expr): b return $this->isCountWithExpression($smallerOrEqual->right, $expr); } - private function isNumber(Node $node, int $value): bool + private function isNumber(Expr $expr, int $value): bool { - if (! $node instanceof LNumber) { + if (! $expr instanceof Int_) { return false; } - return $node->value === $value; + return $expr->value === $value; } - private function isCountWithExpression(Node $node, Expr $expr): bool + private function isCountWithExpression(Expr $node, Expr $expr): bool { if (! $node instanceof FuncCall) { return false; @@ -103,8 +105,26 @@ private function isCountWithExpression(Node $node, Expr $expr): bool return false; } - $countedExpr = $node->args[0]->value; + if ($node->isFirstClassCallable()) { + return false; + } + + if (! isset($node->getArgs()[0])) { + return false; + } - return $this->nodeComparator->areNodesEqual($countedExpr, $expr); + $countedExpr = $node->getArgs()[0] + ->value; + + if ($this->nodeComparator->areNodesEqual($countedExpr, $expr)) { + $exprType = $this->nodeTypeResolver->getNativeType($expr); + if (! $exprType->isArray()->yes()) { + return $exprType instanceof NeverType; + } + + return true; + } + + return false; } } diff --git a/rules/DeadCode/NodeManipulator/LivingCodeManipulator.php b/rules/DeadCode/NodeManipulator/LivingCodeManipulator.php index 28199660973..2b1313799c3 100644 --- a/rules/DeadCode/NodeManipulator/LivingCodeManipulator.php +++ b/rules/DeadCode/NodeManipulator/LivingCodeManipulator.php @@ -13,6 +13,7 @@ use PhpParser\Node\Expr\BinaryOp\Coalesce; use PhpParser\Node\Expr\BinaryOp\LogicalAnd; use PhpParser\Node\Expr\BinaryOp\LogicalOr; +use PhpParser\Node\Expr\BinaryOp\Pipe; use PhpParser\Node\Expr\BitwiseNot; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Cast; @@ -29,27 +30,16 @@ use PhpParser\Node\Expr\UnaryPlus; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Scalar; -use PhpParser\Node\Stmt\Expression; use PHPStan\Type\ObjectType; use Rector\NodeTypeResolver\NodeTypeResolver; -use Rector\PostRector\Collector\NodesToAddCollector; -final class LivingCodeManipulator +final readonly class LivingCodeManipulator { public function __construct( - private NodesToAddCollector $nodesToAddCollector, private NodeTypeResolver $nodeTypeResolver ) { } - public function addLivingCodeBeforeNode(Expr $expr, Node $addBeforeThisNode): void - { - $livinExprs = $this->keepLivingCodeFromExpr($expr); - foreach ($livinExprs as $livinExpr) { - $this->nodesToAddCollector->addNodeBeforeNode(new Expression($livinExpr), $addBeforeThisNode); - } - } - /** * @return Expr[]|mixed[] */ @@ -72,14 +62,11 @@ public function keepLivingCodeFromExpr(Node | int | string | null $expr): array } if ($expr instanceof PropertyFetch) { - return array_merge( - $this->keepLivingCodeFromExpr($expr->var), - $this->keepLivingCodeFromExpr($expr->name) - ); + return [...$this->keepLivingCodeFromExpr($expr->var), ...$this->keepLivingCodeFromExpr($expr->name)]; } if ($expr instanceof ArrayDimFetch) { - $type = $this->nodeTypeResolver->resolve($expr->var); + $type = $this->nodeTypeResolver->getType($expr->var); if ($type instanceof ObjectType) { $objectType = new ObjectType('ArrayAccess'); @@ -88,17 +75,11 @@ public function keepLivingCodeFromExpr(Node | int | string | null $expr): array } } - return array_merge( - $this->keepLivingCodeFromExpr($expr->var), - $this->keepLivingCodeFromExpr($expr->dim) - ); + return [...$this->keepLivingCodeFromExpr($expr->var), ...$this->keepLivingCodeFromExpr($expr->dim)]; } if ($expr instanceof ClassConstFetch || $expr instanceof StaticPropertyFetch) { - return array_merge( - $this->keepLivingCodeFromExpr($expr->class), - $this->keepLivingCodeFromExpr($expr->name) - ); + return [...$this->keepLivingCodeFromExpr($expr->class), ...$this->keepLivingCodeFromExpr($expr->name)]; } if ($this->isBinaryOpWithoutChange($expr)) { @@ -108,10 +89,7 @@ public function keepLivingCodeFromExpr(Node | int | string | null $expr): array } if ($expr instanceof Instanceof_) { - return array_merge( - $this->keepLivingCodeFromExpr($expr->expr), - $this->keepLivingCodeFromExpr($expr->class) - ); + return [...$this->keepLivingCodeFromExpr($expr->expr), ...$this->keepLivingCodeFromExpr($expr->class)]; } if ($expr instanceof Isset_) { @@ -143,7 +121,8 @@ private function isBinaryOpWithoutChange(Expr $expr): bool $expr instanceof BooleanAnd || $expr instanceof LogicalOr || $expr instanceof BooleanOr || - $expr instanceof Coalesce + $expr instanceof Coalesce || + $expr instanceof Pipe ); } @@ -152,10 +131,7 @@ private function isBinaryOpWithoutChange(Expr $expr): bool */ private function processBinary(BinaryOp $binaryOp): array { - return array_merge( - $this->keepLivingCodeFromExpr($binaryOp->left), - $this->keepLivingCodeFromExpr($binaryOp->right) - ); + return [...$this->keepLivingCodeFromExpr($binaryOp->left), ...$this->keepLivingCodeFromExpr($binaryOp->right)]; } /** @@ -165,7 +141,7 @@ private function processIsset(Isset_ $isset): array { $livingExprs = []; foreach ($isset->vars as $expr) { - $livingExprs = array_merge($livingExprs, $this->keepLivingCodeFromExpr($expr)); + $livingExprs = [...$livingExprs, ...$this->keepLivingCodeFromExpr($expr)]; } return $livingExprs; diff --git a/rules/DeadCode/NodeManipulator/VariadicFunctionLikeDetector.php b/rules/DeadCode/NodeManipulator/VariadicFunctionLikeDetector.php deleted file mode 100644 index 761d05d9296..00000000000 --- a/rules/DeadCode/NodeManipulator/VariadicFunctionLikeDetector.php +++ /dev/null @@ -1,50 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $functionLike->getStmts(), - function (Node $node) use (&$isVariadic): ?int { - if (! $node instanceof FuncCall) { - return null; - } - - if (! $this->nodeNameResolver->isNames($node, self::VARIADIC_FUNCTION_NAMES)) { - return null; - } - - $isVariadic = true; - - return NodeTraverser::STOP_TRAVERSAL; - } - ); - - return $isVariadic; - } -} diff --git a/rules/DeadCode/PhpDoc/DeadParamTagValueNodeAnalyzer.php b/rules/DeadCode/PhpDoc/DeadParamTagValueNodeAnalyzer.php index 31340b36eb9..187cb79ed7f 100644 --- a/rules/DeadCode/PhpDoc/DeadParamTagValueNodeAnalyzer.php +++ b/rules/DeadCode/PhpDoc/DeadParamTagValueNodeAnalyzer.php @@ -4,35 +4,70 @@ namespace Rector\DeadCode\PhpDoc; +use PhpParser\Node; use PhpParser\Node\FunctionLike; +use PhpParser\Node\Name; use PhpParser\Node\Param; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode; -use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareCallableTypeNode; +use Rector\DeadCode\PhpDoc\Guard\StandaloneTypeRemovalGuard; +use Rector\DeadCode\PhpDoc\Guard\TemplateTypeRemovalGuard; +use Rector\DeadCode\TypeNodeAnalyzer\GenericTypeNodeAnalyzer; +use Rector\DeadCode\TypeNodeAnalyzer\MixedArrayTypeNodeAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\TypeDeclaration\NodeAnalyzer\ParamAnalyzer; -final class DeadParamTagValueNodeAnalyzer +final readonly class DeadParamTagValueNodeAnalyzer { public function __construct( private NodeNameResolver $nodeNameResolver, - private TypeComparator $typeComparator + private TypeComparator $typeComparator, + private GenericTypeNodeAnalyzer $genericTypeNodeAnalyzer, + private MixedArrayTypeNodeAnalyzer $mixedArrayTypeNodeAnalyzer, + private ParamAnalyzer $paramAnalyzer, + private PhpDocTypeChanger $phpDocTypeChanger, + private StandaloneTypeRemovalGuard $standaloneTypeRemovalGuard, + private StaticTypeMapper $staticTypeMapper, + private TemplateTypeRemovalGuard $templateTypeRemovalGuard ) { } public function isDead(ParamTagValueNode $paramTagValueNode, FunctionLike $functionLike): bool { - $param = $this->matchParamByName($paramTagValueNode->parameterName, $functionLike); + $param = $this->paramAnalyzer->getParamByName($paramTagValueNode->parameterName, $functionLike); if (! $param instanceof Param) { return false; } - if ($param->type === null) { + if (! $param->type instanceof Node) { return false; } + if ($paramTagValueNode->description !== '') { + return false; + } + + if ($paramTagValueNode->type instanceof GenericTypeNode) { + return false; + } + + $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( + $paramTagValueNode->type, + $functionLike + ); + if (! $this->templateTypeRemovalGuard->isLegal($docType)) { + return false; + } + + if ($param->type instanceof Name && $this->nodeNameResolver->isName($param->type, 'object')) { + return $paramTagValueNode->type instanceof IdentifierTypeNode && (string) $paramTagValueNode->type === 'object'; + } + if (! $this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual( $param->type, $paramTagValueNode->type, @@ -41,49 +76,23 @@ public function isDead(ParamTagValueNode $paramTagValueNode, FunctionLike $funct return false; } - if (in_array($paramTagValueNode->type::class, [ - GenericTypeNode::class, - SpacingAwareCallableTypeNode::class, - ], true)) { + if ($this->phpDocTypeChanger->isAllowed($paramTagValueNode->type)) { return false; } - if (! $paramTagValueNode->type instanceof BracketsAwareUnionTypeNode) { - return $paramTagValueNode->description === ''; - } - if (! $this->hasGenericType($paramTagValueNode->type)) { - return $paramTagValueNode->description === ''; - } - return false; - } - private function hasGenericType(BracketsAwareUnionTypeNode $bracketsAwareUnionTypeNode): bool - { - $types = $bracketsAwareUnionTypeNode->types; - - foreach ($types as $type) { - if ($type instanceof GenericTypeNode) { - if ($type->type instanceof IdentifierTypeNode && $type->type->name === 'array') { - continue; - } - - return true; - } + if (! $paramTagValueNode->type instanceof BracketsAwareUnionTypeNode) { + return $this->standaloneTypeRemovalGuard->isLegal($paramTagValueNode->type, $param->type); } - return false; + return $this->isAllowedBracketAwareUnion($paramTagValueNode->type); } - private function matchParamByName(string $desiredParamName, FunctionLike $functionLike): ?Param + private function isAllowedBracketAwareUnion(BracketsAwareUnionTypeNode $bracketsAwareUnionTypeNode): bool { - foreach ($functionLike->getParams() as $param) { - $paramName = $this->nodeNameResolver->getName($param); - if ('$' . $paramName !== $desiredParamName) { - continue; - } - - return $param; + if ($this->mixedArrayTypeNodeAnalyzer->hasMixedArrayType($bracketsAwareUnionTypeNode)) { + return false; } - return null; + return ! $this->genericTypeNodeAnalyzer->hasGenericType($bracketsAwareUnionTypeNode); } } diff --git a/rules/DeadCode/PhpDoc/DeadReturnTagValueNodeAnalyzer.php b/rules/DeadCode/PhpDoc/DeadReturnTagValueNodeAnalyzer.php index 54c2c178b16..e55e247c9e1 100644 --- a/rules/DeadCode/PhpDoc/DeadReturnTagValueNodeAnalyzer.php +++ b/rules/DeadCode/PhpDoc/DeadReturnTagValueNodeAnalyzer.php @@ -4,76 +4,166 @@ namespace Rector\DeadCode\PhpDoc; -use PhpParser\Node\FunctionLike; -use PhpParser\Node\Stmt\Trait_; +use PhpParser\Node; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; +use PHPStan\Analyser\Scope; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode; +use PHPStan\Type\TypeCombinator; +use PHPStan\Type\UnionType; +use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode; -use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareCallableTypeNode; +use Rector\DeadCode\PhpDoc\Guard\StandaloneTypeRemovalGuard; +use Rector\DeadCode\PhpDoc\Guard\TemplateTypeRemovalGuard; +use Rector\DeadCode\TypeNodeAnalyzer\GenericTypeNodeAnalyzer; +use Rector\DeadCode\TypeNodeAnalyzer\MixedArrayTypeNodeAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; +use Rector\StaticTypeMapper\StaticTypeMapper; -final class DeadReturnTagValueNodeAnalyzer +final readonly class DeadReturnTagValueNodeAnalyzer { public function __construct( - private TypeComparator $typeComparator + private TypeComparator $typeComparator, + private GenericTypeNodeAnalyzer $genericTypeNodeAnalyzer, + private MixedArrayTypeNodeAnalyzer $mixedArrayTypeNodeAnalyzer, + private StandaloneTypeRemovalGuard $standaloneTypeRemovalGuard, + private PhpDocTypeChanger $phpDocTypeChanger, + private StaticTypeMapper $staticTypeMapper, + private TemplateTypeRemovalGuard $templateTypeRemovalGuard, ) { } - public function isDead(ReturnTagValueNode $returnTagValueNode, FunctionLike $functionLike): bool + public function isDead(ReturnTagValueNode $returnTagValueNode, ClassMethod|Function_ $functionLike): bool { $returnType = $functionLike->getReturnType(); + if ($returnType === null) { return false; } - $classLike = $functionLike->getAttribute(AttributeKey::CLASS_NODE); - if ($classLike instanceof Trait_ && $returnTagValueNode->type instanceof ThisTypeNode) { + if ($returnTagValueNode->description !== '') { + return false; + } + + if ($returnTagValueNode->type instanceof GenericTypeNode) { + return false; + } + + $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( + $returnTagValueNode->type, + $functionLike + ); + if (! $this->templateTypeRemovalGuard->isLegal($docType)) { + return false; + } + + $scope = $functionLike->getAttribute(AttributeKey::SCOPE); + if ($scope instanceof Scope && $scope->isInTrait() && $returnTagValueNode->type instanceof ThisTypeNode) { return false; } if (! $this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual( $returnType, $returnTagValueNode->type, - $functionLike + $functionLike, )) { - return false; + return $this->isDeadNotEqual($returnTagValueNode, $returnType, $functionLike); } - if (in_array($returnTagValueNode->type::class, [ - GenericTypeNode::class, - SpacingAwareCallableTypeNode::class, - ], true)) { + if ($this->phpDocTypeChanger->isAllowed($returnTagValueNode->type)) { return false; } if (! $returnTagValueNode->type instanceof BracketsAwareUnionTypeNode) { - return $returnTagValueNode->description === ''; + return $this->standaloneTypeRemovalGuard->isLegal($returnTagValueNode->type, $returnType); } - if (! $this->hasGenericType($returnTagValueNode->type)) { - return $returnTagValueNode->description === ''; + if ($this->genericTypeNodeAnalyzer->hasGenericType($returnTagValueNode->type)) { + return false; } - return false; + if ($this->mixedArrayTypeNodeAnalyzer->hasMixedArrayType($returnTagValueNode->type)) { + return false; + } + + return ! $this->hasTrueFalsePseudoType($returnTagValueNode->type); + } + + private function isVoidReturnType(Node $node): bool + { + return $node instanceof Identifier && $node->toString() === 'void'; + } + + private function isNeverReturnType(Node $node): bool + { + return $node instanceof Identifier && $node->toString() === 'never'; + } + + private function isDeadNotEqual( + ReturnTagValueNode $returnTagValueNode, + Node $node, + ClassMethod|Function_ $functionLike + ): bool { + if ($returnTagValueNode->type instanceof IdentifierTypeNode && (string) $returnTagValueNode->type === 'void') { + return true; + } + + if (! $this->hasUsefulPhpdocType($returnTagValueNode, $node)) { + return true; + } + + $nodeType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($node); + $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( + $returnTagValueNode->type, + $functionLike + ); + + return $docType instanceof UnionType && $this->typeComparator->areTypesEqual( + TypeCombinator::removeNull($docType), + $nodeType + ); } - private function hasGenericType(BracketsAwareUnionTypeNode $bracketsAwareUnionTypeNode): bool + private function hasTrueFalsePseudoType(BracketsAwareUnionTypeNode $bracketsAwareUnionTypeNode): bool { - $types = $bracketsAwareUnionTypeNode->types; + $unionTypes = $bracketsAwareUnionTypeNode->types; - foreach ($types as $type) { - if ($type instanceof GenericTypeNode) { - if ($type->type instanceof IdentifierTypeNode && $type->type->name === 'array') { - continue; - } + foreach ($unionTypes as $unionType) { + if (! $unionType instanceof IdentifierTypeNode) { + continue; + } + $name = strtolower((string) $unionType); + if (in_array($name, ['true', 'false'], true)) { return true; } } return false; } + + /** + * exact different between @return and node return type + */ + private function hasUsefulPhpdocType(ReturnTagValueNode $returnTagValueNode, mixed $returnType): bool + { + if ($returnTagValueNode->type instanceof IdentifierTypeNode && $returnTagValueNode->type->name === 'mixed') { + return false; + } + + if (! $this->isVoidReturnType($returnType)) { + return ! $this->isNeverReturnType($returnType); + } + + if (! $returnTagValueNode->type instanceof IdentifierTypeNode || (string) $returnTagValueNode->type !== 'never') { + return false; + } + + return ! $this->isNeverReturnType($returnType); + } } diff --git a/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php b/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php index 387d72cd203..6cdfd81d3a8 100644 --- a/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php +++ b/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php @@ -4,31 +4,102 @@ namespace Rector\DeadCode\PhpDoc; +use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Stmt\ClassConst; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; +use PHPStan\Type\IntersectionType; +use PHPStan\Type\ObjectType; +use PHPStan\Type\TypeCombinator; +use PHPStan\Type\UnionType; +use Rector\DeadCode\PhpDoc\Guard\TemplateTypeRemovalGuard; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; +use Rector\PHPStan\ScopeFetcher; +use Rector\StaticTypeMapper\StaticTypeMapper; -final class DeadVarTagValueNodeAnalyzer +final readonly class DeadVarTagValueNodeAnalyzer { public function __construct( - private TypeComparator $typeComparator + private TypeComparator $typeComparator, + private StaticTypeMapper $staticTypeMapper, + private TemplateTypeRemovalGuard $templateTypeRemovalGuard, ) { } - public function isDead(VarTagValueNode $varTagValueNode, Property $property): bool + public function isDead(VarTagValueNode $varTagValueNode, Property|ClassConst|Expression $node): bool { - if ($property->type === null) { + if (! $node instanceof Expression && ! $node->type instanceof Node) { return false; } - if (! $this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual( - $property->type, + if ($varTagValueNode->description !== '') { + return false; + } + + $targetNode = null; + + if ($node instanceof Expression && $node->expr instanceof Assign) { + $targetNode = $node->expr->expr; + } elseif ($node instanceof Property || $node instanceof ClassConst) { + $targetNode = $node->type; + } + + // allow Identifier, ComplexType, and Name on Property and ClassConst + if (! $targetNode instanceof Node) { + return false; + } + + if ($varTagValueNode->type instanceof GenericTypeNode) { + return false; + } + + // is strict type superior to doc type? keep strict type only + $propertyType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($targetNode); + $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($varTagValueNode->type, $node); + + if ($node instanceof Expression) { + $scope = ScopeFetcher::fetch($node); + + // only allow Expr on assign expr + if (! $targetNode instanceof Expr) { + return false; + } + + $nativeType = $scope->getNativeType($targetNode); + if (! $docType->equals($nativeType)) { + return false; + } + } + + if (! $this->templateTypeRemovalGuard->isLegal($docType)) { + return false; + } + + if ($propertyType instanceof UnionType && ! $docType instanceof UnionType) { + return ! $docType instanceof IntersectionType; + } + + if ($propertyType instanceof ObjectType && $docType instanceof ObjectType) { + // more specific type is already in the property + return $docType->isSuperTypeOf($propertyType) + ->yes(); + } + + if ($this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual( + $targetNode, $varTagValueNode->type, - $property + $node )) { - return false; + return true; } - return $varTagValueNode->description === ''; + return $docType instanceof UnionType && $this->typeComparator->areTypesEqual( + TypeCombinator::removeNull($docType), + $propertyType + ); } } diff --git a/rules/DeadCode/PhpDoc/Guard/StandaloneTypeRemovalGuard.php b/rules/DeadCode/PhpDoc/Guard/StandaloneTypeRemovalGuard.php new file mode 100644 index 00000000000..3d73c1fec7f --- /dev/null +++ b/rules/DeadCode/PhpDoc/Guard/StandaloneTypeRemovalGuard.php @@ -0,0 +1,35 @@ +toString() !== 'bool') { + return true; + } + + return ! in_array($typeNode->name, self::ALLOWED_TYPES, true); + } +} diff --git a/rules/DeadCode/PhpDoc/Guard/TemplateTypeRemovalGuard.php b/rules/DeadCode/PhpDoc/Guard/TemplateTypeRemovalGuard.php new file mode 100644 index 00000000000..8ed117cd107 --- /dev/null +++ b/rules/DeadCode/PhpDoc/Guard/TemplateTypeRemovalGuard.php @@ -0,0 +1,33 @@ +getTypes() + : [$docType]; + + foreach ($types as $type) { + if ($type instanceof TemplateType) { + return false; + } + } + + return true; + } +} diff --git a/rules/DeadCode/PhpDoc/TagRemover/ParamTagRemover.php b/rules/DeadCode/PhpDoc/TagRemover/ParamTagRemover.php index 21261dcb8e0..e410f430b38 100644 --- a/rules/DeadCode/PhpDoc/TagRemover/ParamTagRemover.php +++ b/rules/DeadCode/PhpDoc/TagRemover/ParamTagRemover.php @@ -8,22 +8,32 @@ use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; +use PHPStan\Type\Type; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\DeadCode\PhpDoc\DeadParamTagValueNodeAnalyzer; -use Symplify\SimplePhpDocParser\PhpDocNodeTraverser; +use Rector\PhpDocParser\PhpDocParser\PhpDocNodeTraverser; -final class ParamTagRemover +final readonly class ParamTagRemover { public function __construct( - private DeadParamTagValueNodeAnalyzer $deadParamTagValueNodeAnalyzer + private DeadParamTagValueNodeAnalyzer $deadParamTagValueNodeAnalyzer, + private DocBlockUpdater $docBlockUpdater, ) { } - public function removeParamTagsIfUseless(PhpDocInfo $phpDocInfo, FunctionLike $functionLike): void - { + public function removeParamTagsIfUseless( + PhpDocInfo $phpDocInfo, + FunctionLike $functionLike, + ?Type $type = null + ): bool { + $hasChanged = false; + $phpDocNodeTraverser = new PhpDocNodeTraverser(); $phpDocNodeTraverser->traverseWithCallable($phpDocInfo->getPhpDocNode(), '', function (Node $docNode) use ( $functionLike, + &$hasChanged, + $type, $phpDocInfo ): ?int { if (! $docNode instanceof PhpDocTagNode) { @@ -39,12 +49,25 @@ public function removeParamTagsIfUseless(PhpDocInfo $phpDocInfo, FunctionLike $f return null; } + if ($type instanceof Type) { + $paramType = $phpDocInfo->getParamType($docNode->value->parameterName); + if (! $type->equals($paramType)) { + return null; + } + } + if (! $this->deadParamTagValueNodeAnalyzer->isDead($docNode->value, $functionLike)) { return null; } - $phpDocInfo->markAsChanged(); + $hasChanged = true; return PhpDocNodeTraverser::NODE_REMOVE; }); + + if ($hasChanged) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($functionLike); + } + + return $hasChanged; } } diff --git a/rules/DeadCode/PhpDoc/TagRemover/ReturnTagRemover.php b/rules/DeadCode/PhpDoc/TagRemover/ReturnTagRemover.php index 52be9834c08..b33dea2f6d0 100644 --- a/rules/DeadCode/PhpDoc/TagRemover/ReturnTagRemover.php +++ b/rules/DeadCode/PhpDoc/TagRemover/ReturnTagRemover.php @@ -4,32 +4,33 @@ namespace Rector\DeadCode\PhpDoc\TagRemover; -use PhpParser\Node\FunctionLike; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; -use PHPStan\Type\Type; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\DeadCode\PhpDoc\DeadReturnTagValueNodeAnalyzer; -final class ReturnTagRemover +final readonly class ReturnTagRemover { public function __construct( private DeadReturnTagValueNodeAnalyzer $deadReturnTagValueNodeAnalyzer ) { } - public function removeReturnTagIfUseless(PhpDocInfo $phpDocInfo, FunctionLike $functionLike): void + public function removeReturnTagIfUseless(PhpDocInfo $phpDocInfo, ClassMethod|Function_ $functionLike): bool { // remove existing type $returnTagValueNode = $phpDocInfo->getReturnTagValue(); if (! $returnTagValueNode instanceof ReturnTagValueNode) { - return; + return false; } $isReturnTagValueDead = $this->deadReturnTagValueNodeAnalyzer->isDead($returnTagValueNode, $functionLike); if (! $isReturnTagValueDead) { - return; + return false; } $phpDocInfo->removeByType(ReturnTagValueNode::class); + return true; } } diff --git a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php index 07552243e07..f8e7be2e4d7 100644 --- a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php +++ b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php @@ -6,51 +6,78 @@ use PhpParser\Node; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; -use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; -use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; -use PHPStan\Type\ArrayType; -use PHPStan\Type\ObjectType; +use PHPStan\Type\Generic\TemplateObjectWithoutClassType; use PHPStan\Type\Type; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode; -use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareArrayTypeNode; +use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\DeadCode\PhpDoc\DeadVarTagValueNodeAnalyzer; +use Rector\NodeTypeResolver\TypeComparator\TypeComparator; use Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer; -use Rector\StaticTypeMapper\StaticTypeMapper; -use Symplify\PackageBuilder\Reflection\ClassLikeExistenceChecker; -final class VarTagRemover +final readonly class VarTagRemover { public function __construct( private DoctrineTypeAnalyzer $doctrineTypeAnalyzer, - private StaticTypeMapper $staticTypeMapper, private PhpDocInfoFactory $phpDocInfoFactory, - private ClassLikeExistenceChecker $classLikeExistenceChecker, - private DeadVarTagValueNodeAnalyzer $deadVarTagValueNodeAnalyzer + private DeadVarTagValueNodeAnalyzer $deadVarTagValueNodeAnalyzer, + private PhpDocTypeChanger $phpDocTypeChanger, + private DocBlockUpdater $docBlockUpdater, + private TypeComparator $typeComparator, ) { } - public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property $property): void + public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property|ClassConst|Expression $node): bool { $varTagValueNode = $phpDocInfo->getVarTagValueNode(); if (! $varTagValueNode instanceof VarTagValueNode) { - return; + return false; } - $isVarTagValueDead = $this->deadVarTagValueNodeAnalyzer->isDead($varTagValueNode, $property); + $isVarTagValueDead = $this->deadVarTagValueNodeAnalyzer->isDead($varTagValueNode, $node); if (! $isVarTagValueDead) { - return; + return false; + } + + if ($this->phpDocTypeChanger->isAllowed($varTagValueNode->type)) { + return false; + } + + $phpDocInfo->removeByType(VarTagValueNode::class, $varTagValueNode->variableName); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return true; + } + + /** + * @api generic + */ + public function removeVarTag(Node $node): bool + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + if (! $varTagValueNode instanceof VarTagValueNode) { + return false; } $phpDocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return true; } public function removeVarPhpTagValueNodeIfNotComment(Expression | Property | Param $node, Type $type): void { + if ($type instanceof TemplateObjectWithoutClassType) { + return; + } + // keep doctrine collection narrow type if ($this->doctrineTypeAnalyzer->isDoctrineCollectionWithIterableUnionType($type)) { return; @@ -68,61 +95,23 @@ public function removeVarPhpTagValueNodeIfNotComment(Expression | Property | Par return; } - // keep generic types - if ($varTagValueNode->type instanceof GenericTypeNode) { + // keep string[] etc. + if ($this->phpDocTypeChanger->isAllowed($varTagValueNode->type)) { return; } - // keep string[] etc. - if ($this->isNonBasicArrayType($node, $varTagValueNode)) { + // keep subtypes like positive-int + if ($this->shouldKeepSubtypes($type, $phpDocInfo->getVarType())) { return; } $phpDocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); } - private function isNonBasicArrayType(Expression | Param | Property $node, VarTagValueNode $varTagValueNode): bool - { - if ($varTagValueNode->type instanceof BracketsAwareUnionTypeNode) { - foreach ($varTagValueNode->type->types as $type) { - if ($type instanceof SpacingAwareArrayTypeNode && $this->isArrayOfExistingClassNode($node, $type)) { - return true; - } - } - } - - if (! $this->isArrayTypeNode($varTagValueNode)) { - return false; - } - - return (string) $varTagValueNode->type !== 'array'; - } - - private function isArrayTypeNode(VarTagValueNode $varTagValueNode): bool + private function shouldKeepSubtypes(Type $type, Type $varType): bool { - return $varTagValueNode->type instanceof ArrayTypeNode; - } - - private function isArrayOfExistingClassNode( - Node $node, - SpacingAwareArrayTypeNode $spacingAwareArrayTypeNode - ): bool { - $staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( - $spacingAwareArrayTypeNode, - $node - ); - - if (! $staticType instanceof ArrayType) { - return false; - } - - $itemType = $staticType->getItemType(); - if (! $itemType instanceof ObjectType) { - return false; - } - - $className = $itemType->getClassName(); - - return $this->classLikeExistenceChecker->doesClassLikeExist($className); + return ! $this->typeComparator->areTypesEqual($type, $varType) + && $this->typeComparator->isSubtype($varType, $type); } } diff --git a/rules/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector.php b/rules/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector.php index 5f3b90aae39..1fc41db12c0 100644 --- a/rules/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector.php +++ b/rules/DeadCode/Rector/Array_/RemoveDuplicatedArrayKeyRector.php @@ -5,21 +5,32 @@ namespace Rector\DeadCode\Rector\Array_; use PhpParser\Node; +use PhpParser\Node\ArrayItem; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Expr\PreDec; +use PhpParser\Node\Expr\PreInc; +use PhpParser\Node\Expr\Variable; +use Rector\NodeAnalyzer\ExprAnalyzer; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/SG0Wu * @see \Rector\Tests\DeadCode\Rector\Array_\RemoveDuplicatedArrayKeyRector\RemoveDuplicatedArrayKeyRectorTest */ final class RemoveDuplicatedArrayKeyRector extends AbstractRector { + public function __construct( + private readonly BetterStandardPrinter $betterStandardPrinter, + private readonly ExprAnalyzer $exprAnalyzer + ) { + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Remove duplicated key in defined arrays.', [ + return new RuleDefinition('Remove duplicated key in defined arrays', [ new CodeSample( <<<'CODE_SAMPLE' $item = [ @@ -50,24 +61,30 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $arrayItemsWithDuplicatedKey = $this->getArrayItemsWithDuplicatedKey($node); - if ($arrayItemsWithDuplicatedKey === []) { + $duplicatedKeysArrayItems = $this->resolveDuplicateKeysArrayItems($node); + if ($duplicatedKeysArrayItems === []) { return null; } - foreach ($arrayItemsWithDuplicatedKey as $arrayItemWithDuplicatedKey) { - // keep last item - array_pop($arrayItemWithDuplicatedKey); - $this->removeNodes($arrayItemWithDuplicatedKey); + foreach ($node->items as $key => $arrayItem) { + if (! $arrayItem instanceof ArrayItem) { + continue; + } + + if (! $this->isArrayItemDuplicated($duplicatedKeysArrayItems, $arrayItem)) { + continue; + } + + unset($node->items[$key]); } return $node; } /** - * @return ArrayItem[][] + * @return ArrayItem[] */ - private function getArrayItemsWithDuplicatedKey(Array_ $array): array + private function resolveDuplicateKeysArrayItems(Array_ $array): array { $arrayItemsByKeys = []; @@ -76,11 +93,16 @@ private function getArrayItemsWithDuplicatedKey(Array_ $array): array continue; } - if ($arrayItem->key === null) { + if (! $arrayItem->key instanceof Expr) { + continue; + } + + // local variable is mostly fine, other dynamic, just skip + if (! $arrayItem->key instanceof Variable && $this->exprAnalyzer->isDynamicExpr($arrayItem->key)) { continue; } - $keyValue = $this->print($arrayItem->key); + $keyValue = $this->betterStandardPrinter->print($arrayItem->key); $arrayItemsByKeys[$keyValue][] = $arrayItem; } @@ -88,14 +110,44 @@ private function getArrayItemsWithDuplicatedKey(Array_ $array): array } /** - * @param ArrayItem[][] $arrayItemsByKeys - * @return ArrayItem[][] + * @param array $arrayItemsByKeys + * @return array */ private function filterItemsWithSameKey(array $arrayItemsByKeys): array { - /** @var ArrayItem[][] $arrayItemsByKeys */ - $arrayItemsByKeys = array_filter($arrayItemsByKeys, fn (array $arrayItems): bool => count($arrayItems) > 1); + $duplicatedArrayItems = []; + + foreach ($arrayItemsByKeys as $arrayItems) { + if (count($arrayItems) <= 1) { + continue; + } - return array_filter($arrayItemsByKeys, fn (array $arrayItems): bool => count($arrayItems) > 1); + $currentArrayItem = current($arrayItems); + + /** @var Expr $currentArrayItemKey */ + $currentArrayItemKey = $currentArrayItem->key; + if ($currentArrayItemKey instanceof PreInc) { + continue; + } + + if ($currentArrayItemKey instanceof PreDec) { + continue; + } + + // keep last one + array_pop($arrayItems); + + $duplicatedArrayItems = array_merge($duplicatedArrayItems, $arrayItems); + } + + return $duplicatedArrayItems; + } + + /** + * @param ArrayItem[] $duplicatedKeysArrayItems + */ + private function isArrayItemDuplicated(array $duplicatedKeysArrayItems, ArrayItem $arrayItem): bool + { + return in_array($arrayItem, $duplicatedKeysArrayItems, true); } } diff --git a/rules/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector.php b/rules/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector.php deleted file mode 100644 index 462bd8af8e6..00000000000 --- a/rules/DeadCode/Rector/Assign/RemoveAssignOfVoidReturnFunctionRector.php +++ /dev/null @@ -1,99 +0,0 @@ -getOne(); - } - - private function getOne(): void - { - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $this->getOne(); - } - - private function getOne(): void - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Assign::class]; - } - - /** - * @param Assign $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->expr instanceof FuncCall && ! $node->expr instanceof MethodCall && ! $node->expr instanceof StaticCall) { - return null; - } - - if (! $node->var instanceof Variable) { - return null; - } - - $exprType = $this->nodeTypeResolver->resolve($node->expr); - if (! $exprType instanceof VoidType) { - return null; - } - - if ($this->exprUsedInNextNodeAnalyzer->isUsed($node->var)) { - return null; - } - - return $node->expr; - } -} diff --git a/rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php b/rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php index 47b63cd85a5..a9181c0417f 100644 --- a/rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php +++ b/rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php @@ -10,10 +10,11 @@ use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Stmt\TryCatch; use Rector\DeadCode\SideEffect\SideEffectNodeDetector; -use Rector\NodeNestingScope\ScopeNestingComparator; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,8 +24,8 @@ final class RemoveDoubleAssignRector extends AbstractRector { public function __construct( - private ScopeNestingComparator $scopeNestingComparator, - private SideEffectNodeDetector $sideEffectNodeDetector + private readonly SideEffectNodeDetector $sideEffectNodeDetector, + private readonly BetterNodeFinder $betterNodeFinder, ) { } @@ -47,61 +48,90 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Assign::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Assign $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if (! $node->var instanceof Variable && ! $node->var instanceof PropertyFetch && ! $node->var instanceof StaticPropertyFetch) { + if ($node->stmts === null) { return null; } - $previousStatement = $node->getAttribute(AttributeKey::PREVIOUS_STATEMENT); - if (! $previousStatement instanceof Expression) { - return null; - } - - if (! $previousStatement->expr instanceof Assign) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($previousStatement->expr->var, $node->var)) { - return null; - } - - // early check self referencing, ensure that variable not re-used - if ($this->isSelfReferencing($node)) { - return null; - } - - // detect call expression has side effect - if ($this->sideEffectNodeDetector->detectCallExpr($previousStatement->expr->expr)) { - return null; + $hasChanged = false; + + foreach ($node->stmts as $key => $stmt) { + if (! isset($node->stmts[$key + 1])) { + continue; + } + + if (! $stmt instanceof Expression) { + continue; + } + + $nextStmt = $node->stmts[$key + 1]; + if (! $nextStmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof Assign) { + continue; + } + + if (! $nextStmt->expr instanceof Assign) { + continue; + } + + $nextAssign = $nextStmt->expr; + if (! $this->nodeComparator->areNodesEqual($nextAssign->var, $stmt->expr->var)) { + continue; + } + + // early check self referencing, ensure that variable not re-used + if ($this->isSelfReferencing($nextAssign)) { + continue; + } + + // detect call expression has side effect + // no calls on right, could hide e.g. array_pop()|array_shift() + if ($this->sideEffectNodeDetector->detectCallExpr($stmt->expr->expr)) { + continue; + } + + // next stmts can have side effect as well + if (($nextAssign->var instanceof PropertyFetch || $nextAssign->var instanceof StaticPropertyFetch) && $this->sideEffectNodeDetector->detectCallExpr( + $nextAssign->expr + )) { + continue; + } + + if (! $stmt->expr->var instanceof Variable && ! $stmt->expr->var instanceof PropertyFetch && ! $stmt->expr->var instanceof StaticPropertyFetch) { + continue; + } + + // side effect may throw exception, and may be handled by catch block + if ($node instanceof TryCatch && $this->sideEffectNodeDetector->detectCallExpr($nextAssign->expr)) { + continue; + } + + // remove current Stmt if will be overridden in next stmt + unset($node->stmts[$key]); + + $hasChanged = true; } - // check scoping variable - if ($this->shouldSkipForDifferentScope($node, $previousStatement)) { + if (! $hasChanged) { return null; } - // no calls on right, could hide e.g. array_pop()|array_shift() - $this->removeNode($previousStatement); + // update array keys to fit printer + $node->stmts = array_values($node->stmts); return $node; } - private function shouldSkipForDifferentScope(Assign $assign, Expression $expression): bool - { - if (! $this->areInSameClassMethod($assign, $expression)) { - return true; - } - - return ! $this->scopeNestingComparator->areScopeNestingEqual($assign, $expression); - } - private function isSelfReferencing(Assign $assign): bool { return (bool) $this->betterNodeFinder->findFirst( @@ -109,12 +139,4 @@ private function isSelfReferencing(Assign $assign): bool fn (Node $subNode): bool => $this->nodeComparator->areNodesEqual($assign->var, $subNode) ); } - - private function areInSameClassMethod(Assign $assign, Expression $previousExpression): bool - { - return $this->nodeComparator->areNodesEqual( - $assign->getAttribute(AttributeKey::METHOD_NODE), - $previousExpression->getAttribute(AttributeKey::METHOD_NODE) - ); - } } diff --git a/rules/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector.php b/rules/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector.php deleted file mode 100644 index 48c254ddca7..00000000000 --- a/rules/DeadCode/Rector/Assign/RemoveUnusedAssignVariableRector.php +++ /dev/null @@ -1,172 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Assign::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Remove assigned unused variable', [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $value = $this->process(); - } - - public function process() - { - // something going on - return 5; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $this->process(); - } - - public function process() - { - // something going on - return 5; - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @param Assign $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkipAssign($node)) { - return null; - } - - if (! $this->isPreviousVariablePartOfOverridingAssign($node) && ($this->isVariableTypeInScope( - $node - ) || $this->exprUsedInNextNodeAnalyzer->isUsed($node->var))) { - return null; - } - - // is scalar assign? remove whole - if (! $this->sideEffectNodeDetector->detect($node->expr)) { - $this->removeNode($node); - return null; - } - - return $node->expr; - } - - private function shouldSkipAssign(Assign $assign): bool - { - $variable = $assign->var; - if (! $variable instanceof Variable) { - return true; - } - - // unable to resolve name - $variableName = $this->getName($variable); - if ($variableName === null) { - return true; - } - - if ($this->isNestedAssign($assign)) { - return true; - } - - $parentIf = $this->betterNodeFinder->findParentType($assign, If_::class); - if (! $parentIf instanceof If_) { - return (bool) $this->nextVariableUsageNodeFinder->find($assign); - } - if (! $parentIf->else instanceof Else_) { - return (bool) $this->nextVariableUsageNodeFinder->find($assign); - } - - return (bool) $this->betterNodeFinder->findFirstNext( - $parentIf, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $variable) - ); - } - - private function isVariableTypeInScope(Assign $assign): bool - { - $scope = $assign->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - /** @var string $variableName */ - $variableName = $this->getName($assign->var); - - return ! $scope->hasVariableType($variableName) - ->no(); - } - - private function isPreviousVariablePartOfOverridingAssign(Assign $assign): bool - { - // is previous variable node as part of assign? - $previousVariableAssign = $this->previousVariableAssignNodeFinder->find($assign); - if (! $previousVariableAssign instanceof Node) { - return false; - } - - return $this->scopeNestingComparator->areScopeNestingEqual($assign, $previousVariableAssign); - } - - /** - * Nested assign, e.g "$oldValues = <$values> = 5;" - */ - private function isNestedAssign(Assign $assign): bool - { - $parent = $assign->getAttribute(AttributeKey::PARENT_NODE); - return $parent instanceof Assign; - } -} diff --git a/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php b/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php index 17c809caf37..286c6af2969 100644 --- a/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php +++ b/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php @@ -5,23 +5,37 @@ namespace Rector\DeadCode\Rector\Assign; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\AssignRef; +use PhpParser\Node\Expr\Cast; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\Include_; use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\FunctionLike; +use PhpParser\Node\Name; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\If_; -use Rector\Core\Php\ReservedKeywordAnalyzer; -use Rector\Core\PhpParser\Comparing\ConditionSearcher; -use Rector\Core\Rector\AbstractRector; -use Rector\DeadCode\NodeAnalyzer\ExprUsedInNextNodeAnalyzer; -use Rector\DeadCode\NodeAnalyzer\UsedVariableNameAnalyzer; +use PhpParser\Node\Stmt\Function_; +use PhpParser\NodeVisitor; +use PHPStan\Reflection\AttributeReflection; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Type\ObjectType; use Rector\DeadCode\SideEffect\SideEffectNodeDetector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeAnalyzer\VariableAnalyzer; +use Rector\NodeManipulator\StmtsManipulator; +use Rector\Php\ReservedKeywordAnalyzer; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -31,11 +45,12 @@ final class RemoveUnusedVariableAssignRector extends AbstractRector { public function __construct( - private ReservedKeywordAnalyzer $reservedKeywordAnalyzer, - private ConditionSearcher $conditionSearcher, - private UsedVariableNameAnalyzer $usedVariableNameAnalyzer, - private SideEffectNodeDetector $sideEffectNodeDetector, - private ExprUsedInNextNodeAnalyzer $exprUsedInNextNodeAnalyzer + private readonly ReservedKeywordAnalyzer $reservedKeywordAnalyzer, + private readonly SideEffectNodeDetector $sideEffectNodeDetector, + private readonly VariableAnalyzer $variableAnalyzer, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly StmtsManipulator $stmtsManipulator, + private readonly ReflectionProvider $reflectionProvider, ) { } @@ -52,7 +67,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -70,131 +85,286 @@ public function run() */ public function getNodeTypes(): array { - return [Assign::class]; + return [ClassMethod::class, Function_::class]; } /** - * @param Assign $node + * @param ClassMethod|Function_ $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): null|ClassMethod|Function_ { - if ($this->shouldSkip($node)) { + $stmts = $node->stmts; + if ($stmts === null || $stmts === []) { return null; } - $variable = $node->var; - if (! $variable instanceof Variable) { + // we cannot be sure here + if ($this->shouldSkip($stmts)) { return null; } - $variableName = $this->getName($variable); - if ($variableName !== null && $this->reservedKeywordAnalyzer->isNativeVariable($variableName)) { - return null; + $assignedVariableNamesByStmtPosition = $this->resolvedAssignedVariablesByStmtPosition($stmts); + + $hasChanged = false; + + foreach ($assignedVariableNamesByStmtPosition as $stmtPosition => $variableName) { + if ($this->stmtsManipulator->isVariableUsedInNextStmt($node, $stmtPosition + 1, $variableName)) { + continue; + } + + /** @var Expression $currentStmt */ + $currentStmt = $stmts[$stmtPosition]; + + /** @var Assign $assign */ + $assign = $currentStmt->expr; + + if ($this->isObjectWithDestructMethod($assign->expr)) { + continue; + } + + if ($this->hasCallLikeInAssignExpr($assign)) { + // clean safely + $cleanAssignedExpr = $this->cleanCastedExpr($assign->expr); + $newExpression = new Expression($cleanAssignedExpr); + $this->mirrorComments($newExpression, $currentStmt); + $node->stmts[$stmtPosition] = $newExpression; + } else { + unset($node->stmts[$stmtPosition]); + } + + $hasChanged = true; } - // variable is used - if ($this->isUsed($node, $variable)) { - return $this->refactorUsedVariable($node); + if ($hasChanged) { + return $node; } - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Expression) { - return null; + return null; + } + + private function isObjectWithDestructMethod(Expr $expr): bool + { + $exprType = $this->getType($expr); + if (! $exprType instanceof ObjectType) { + return false; } - if ($this->sideEffectNodeDetector->detectCallExpr($node->expr)) { - // keep the expr, can have side effect - return $node->expr; + $classReflection = $exprType->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; } - $this->removeNode($node); - return $node; + return $classReflection->hasNativeMethod(MethodName::DESTRUCT); } - private function shouldSkip(Assign $assign): bool + private function cleanCastedExpr(Expr $expr): Expr { - $classMethod = $assign->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof FunctionLike) { - return true; + if (! $expr instanceof Cast) { + return $expr; } - $variable = $assign->var; - if (! $variable instanceof Variable) { - return true; + return $this->cleanCastedExpr($expr->expr); + } + + private function hasCallLikeInAssignExpr(Expr $expr): bool + { + return (bool) $this->betterNodeFinder->findFirst($expr, $this->sideEffectNodeDetector->detectCallExpr(...)); + } + + /** + * @param Stmt[] $stmts + */ + private function shouldSkip(array $stmts): bool + { + return (bool) $this->betterNodeFinder->findFirst($stmts, function (Node $node): bool { + if ($node instanceof Include_) { + return true; + } + + if (! $node instanceof FuncCall) { + return false; + } + + return $this->isNames($node, ['compact', 'get_defined_vars']); + }); + } + + /** + * @param string[] $refVariableNames + */ + private function collectAssignRefVariableNames(Stmt $stmt, array &$refVariableNames): void + { + if (! NodeGroup::isStmtAwareNode($stmt)) { + return; } - return $variable->name instanceof Variable && $this->betterNodeFinder->findFirstNext( - $assign, - fn (Node $node): bool => $node instanceof Variable - ); + $this->traverseNodesWithCallable($stmt, function (Node $subNode) use (&$refVariableNames): Node { + if ($subNode instanceof AssignRef && $subNode->var instanceof Variable) { + $refVariableNames[] = (string) $this->getName($subNode->var); + } + + return $subNode; + }); } - private function isUsed(Assign $assign, Variable $variable): bool + /** + * @param array $stmts + * @return array + */ + private function resolvedAssignedVariablesByStmtPosition(array $stmts): array { - $isUsedPrev = (bool) $this->betterNodeFinder->findFirstPreviousOfNode( - $variable, - fn (Node $node): bool => $this->usedVariableNameAnalyzer->isVariableNamed($node, $variable) - ); + $assignedVariableNamesByStmtPosition = []; + $refVariableNames = []; + + foreach ($stmts as $key => $stmt) { + if (! $stmt instanceof Expression) { + $this->collectAssignRefVariableNames($stmt, $refVariableNames); + continue; + } + + if ($stmt->expr instanceof AssignRef && $stmt->expr->var instanceof Variable) { + $refVariableNames[] = (string) $this->getName($stmt->expr->var); + } + + if (! $stmt->expr instanceof Assign) { + continue; + } + + $this->traverseNodesWithCallable( + $stmt->expr->expr, + function (Node $subNode) use (&$refVariableNames) { + if ($subNode instanceof Class_ || $subNode instanceof Function_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $subNode instanceof Closure) { + return null; + } + + foreach ($subNode->uses as $closureUse) { + if (! $closureUse->var instanceof Variable) { + continue; + } - if ($isUsedPrev) { + if (! $closureUse->byRef) { + continue; + } + + $refVariableNames[] = (string) $this->getName($closureUse->var); + } + } + ); + + $assign = $stmt->expr; + if (! $assign->var instanceof Variable) { + continue; + } + + $variableName = $this->getName($assign->var); + if (! is_string($variableName)) { + continue; + } + + if ($this->reservedKeywordAnalyzer->isNativeVariable($variableName)) { + continue; + } + + if ($this->shouldSkipVariable($assign->var, $variableName, $refVariableNames)) { + continue; + } + + if ($this->isNoDiscardCall($assign->expr)) { + continue; + } + + $assignedVariableNamesByStmtPosition[$key] = $variableName; + } + + return $assignedVariableNamesByStmtPosition; + } + + /** + * @param string[] $refVariableNames + */ + private function shouldSkipVariable(Variable $variable, string $variableName, array $refVariableNames): bool + { + if ($this->variableAnalyzer->isStaticOrGlobal($variable)) { return true; } - if ($this->exprUsedInNextNodeAnalyzer->isUsed($variable)) { + if ($this->variableAnalyzer->isUsedByReference($variable)) { return true; } - /** @var FuncCall|MethodCall|New_|NullsafeMethodCall|StaticCall $expr */ - $expr = $assign->expr; - if (! $this->sideEffectNodeDetector->detectCallExpr($expr)) { + return in_array($variableName, $refVariableNames, true); + } + + private function isNoDiscardCall(Expr $expr): bool + { + if ($expr instanceof FuncCall) { + $name = $this->getName($expr); + + if ($name === null) { + return false; + } + + $scope = ScopeFetcher::fetch($expr); + + if (! $this->reflectionProvider->hasFunction(new Name($name), $scope)) { + return false; + } + + return $this->hasNoDiscardAttribute( + $this->reflectionProvider->getFunction(new Name($name), $scope) + ->getAttributes() + ); + } + + if ($expr instanceof StaticCall) { + $classNames = $this->getType($expr->class) + ->getObjectClassNames(); + $methodName = $this->getName($expr->name); + } elseif ($expr instanceof MethodCall || $expr instanceof NullsafeMethodCall) { + $classNames = $this->getType($expr->var) + ->getObjectClassNames(); + $methodName = $this->getName($expr->name); + } else { return false; } - return $this->isUsedInAssignExpr($expr, $assign); - } + if ($classNames === [] || $methodName === null) { + return false; + } - private function isUsedInAssignExpr( - FuncCall | MethodCall | New_ | NullsafeMethodCall | StaticCall $expr, - Assign $assign - ): bool { - foreach ($expr->args as $arg) { - $variable = $arg->value; - if (! $variable instanceof Variable) { + foreach ($classNames as $className) { + if (! $this->reflectionProvider->hasClass($className)) { continue; } - $previousAssign = $this->betterNodeFinder->findFirstPreviousOfNode( - $assign, - fn (Node $node): bool => $node instanceof Assign && $this->usedVariableNameAnalyzer->isVariableNamed( - $node->var, - $variable - ) - ); + $classReflection = $this->reflectionProvider->getClass($className); + if (! $classReflection->hasNativeMethod($methodName)) { + continue; + } - if ($previousAssign instanceof Assign) { - return $this->isUsed($assign, $variable); + if ($this->hasNoDiscardAttribute($classReflection->getNativeMethod($methodName)->getAttributes())) { + return true; } } return false; } - private function refactorUsedVariable(Assign $assign): ?Assign + /** + * @param AttributeReflection[] $attributes + */ + private function hasNoDiscardAttribute(array $attributes): bool { - $parentNode = $assign->getAttribute(AttributeKey::PARENT_NODE); - - $if = $parentNode->getAttribute(AttributeKey::NEXT_NODE); - - // check if next node is if - if (! $if instanceof If_) { - return null; - } - - if ($this->conditionSearcher->hasIfAndElseForVariableRedeclaration($assign, $if)) { - $this->removeNode($assign); - return $assign; + foreach ($attributes as $attribute) { + if ($attribute->getName() === 'NoDiscard') { + return true; + } } - return null; + return false; } } diff --git a/rules/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector.php b/rules/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector.php deleted file mode 100644 index 72324c797d1..00000000000 --- a/rules/DeadCode/Rector/BinaryOp/RemoveDuplicatedInstanceOfRector.php +++ /dev/null @@ -1,104 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [BinaryOp::class]; - } - - /** - * @param BinaryOp $node - */ - public function refactor(Node $node): ?Node - { - $duplicatedInstanceOfs = $this->resolveDuplicatedInstancesOf($node); - if ($duplicatedInstanceOfs === []) { - return null; - } - - $this->removeNodes($duplicatedInstanceOfs); - - return $node; - } - - /** - * @return Instanceof_[] - */ - private function resolveDuplicatedInstancesOf(BinaryOp $binaryOp): array - { - $duplicatedInstanceOfs = []; - - /** @var Instanceof_[] $instanceOfs */ - $instanceOfs = $this->betterNodeFinder->findInstanceOf($binaryOp, Instanceof_::class); - - $uniqueInstanceOfKeys = []; - foreach ($instanceOfs as $instanceOf) { - $uniqueKey = $this->instanceOfUniqueKeyResolver->resolve($instanceOf); - if ($uniqueKey === null) { - continue; - } - - // already present before → duplicated - if (in_array($uniqueKey, $uniqueInstanceOfKeys, true)) { - $duplicatedInstanceOfs[] = $instanceOf; - } - - $uniqueInstanceOfKeys[] = $uniqueKey; - } - - return $duplicatedInstanceOfs; - } -} diff --git a/rules/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector.php b/rules/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector.php new file mode 100644 index 00000000000..d57a8a2f2f0 --- /dev/null +++ b/rules/DeadCode/Rector/Block/ReplaceBlockToItsStmtsRector.php @@ -0,0 +1,65 @@ +> + */ + public function getNodeTypes(): array + { + return [Block::class]; + } + + /** + * @param Block $node + * @return int|Stmt[] + */ + public function refactor(Node $node): int|array + { + if ($node->stmts === []) { + return NodeVisitor::REMOVE_NODE; + } + + return $node->stmts; + } +} diff --git a/rules/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector.php b/rules/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector.php index 825d5c4b075..5659d07b139 100644 --- a/rules/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector.php +++ b/rules/DeadCode/Rector/BooleanAnd/RemoveAndTrueRector.php @@ -5,8 +5,10 @@ namespace Rector\DeadCode\Rector\BooleanAnd; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -15,9 +17,14 @@ */ final class RemoveAndTrueRector extends AbstractRector { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Remove and true that has no added value', [ + return new RuleDefinition('Remove `and true` that has no added value', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -66,20 +73,20 @@ public function refactor(Node $node): ?Node return null; } - private function isTrueOrBooleanAndTrues(Node $node): bool + private function isTrueOrBooleanAndTrues(Expr $expr): bool { - if ($this->valueResolver->isTrue($node)) { + if ($this->valueResolver->isTrue($expr)) { return true; } - if (! $node instanceof BooleanAnd) { + if (! $expr instanceof BooleanAnd) { return false; } - if (! $this->isTrueOrBooleanAndTrues($node->left)) { + if (! $this->isTrueOrBooleanAndTrues($expr->left)) { return false; } - return $this->isTrueOrBooleanAndTrues($node->right); + return $this->isTrueOrBooleanAndTrues($expr->right); } } diff --git a/rules/DeadCode/Rector/Cast/RecastingRemovalRector.php b/rules/DeadCode/Rector/Cast/RecastingRemovalRector.php index e5c819be36d..1d8f6184cc5 100644 --- a/rules/DeadCode/Rector/Cast/RecastingRemovalRector.php +++ b/rules/DeadCode/Rector/Cast/RecastingRemovalRector.php @@ -13,21 +13,23 @@ use PhpParser\Node\Expr\Cast\Int_; use PhpParser\Node\Expr\Cast\Object_; use PhpParser\Node\Expr\Cast\String_; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\StaticPropertyFetch; -use PhpParser\Node\FunctionLike; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\StaticCall; use PHPStan\Reflection\Php\PhpPropertyReflection; use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; +use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\FloatType; use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\StringType; use PHPStan\Type\Type; -use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Reflection\ReflectionResolver; +use PHPStan\Type\UnionType; +use Rector\NodeAnalyzer\ExprAnalyzer; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -39,7 +41,7 @@ final class RecastingRemovalRector extends AbstractRector /** * @var array, class-string> */ - private const CAST_CLASS_TO_NODE_TYPE = [ + private const array CAST_CLASS_TO_NODE_TYPE = [ String_::class => StringType::class, Bool_::class => BooleanType::class, Array_::class => ArrayType::class, @@ -49,14 +51,15 @@ final class RecastingRemovalRector extends AbstractRector ]; public function __construct( - private PropertyFetchAnalyzer $propertyFetchAnalyzer, - private ReflectionResolver $reflectionResolver + private readonly PropertyFetchAnalyzer $propertyFetchAnalyzer, + private readonly ReflectionResolver $reflectionResolver, + private readonly ExprAnalyzer $exprAnalyzer, ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Removes recasting of the same type', [ + return new RuleDefinition('Remove recasting of the same type', [ new CodeSample( <<<'CODE_SAMPLE' $string = ''; @@ -95,13 +98,25 @@ public function refactor(Node $node): ?Node return null; } - $nodeType = $this->getStaticType($node->expr); + $nodeType = $this->nodeTypeResolver->getNativeType($node->expr); if ($nodeType instanceof MixedType) { return null; } + if ($nodeType instanceof ConstantArrayType && $nodeClass === Array_::class) { + if ($this->shouldSkip($node->expr)) { + return null; + } + + if ($this->shouldSkipCall($node->expr)) { + return null; + } + + return $node->expr; + } + $sameNodeType = self::CAST_CLASS_TO_NODE_TYPE[$nodeClass]; - if (! is_a($nodeType, $sameNodeType, true)) { + if (! $nodeType instanceof $sameNodeType) { return null; } @@ -109,41 +124,45 @@ public function refactor(Node $node): ?Node return null; } + if ($this->shouldSkipCall($node->expr)) { + return null; + } + return $node->expr; } - private function shouldSkip(Expr $expr): bool + private function shouldSkipCall(Expr $expr): bool { - if (! $this->propertyFetchAnalyzer->isPropertyFetch($expr)) { - return $this->isNonTypedFromParam($expr); - } - - /** @var PropertyFetch|StaticPropertyFetch $expr */ - $phpPropertyReflection = $this->reflectionResolver->resolvePropertyReflectionFromPropertyFetch($expr); - if (! $phpPropertyReflection instanceof PhpPropertyReflection) { + if (! $expr instanceof MethodCall && ! $expr instanceof StaticCall) { return false; } - $nativeType = $phpPropertyReflection->getNativeType(); - return $nativeType instanceof MixedType; + $type = $this->nodeTypeResolver->getNativeType($expr); + return $type instanceof MixedType && ! $type->isExplicitMixed(); } - private function isNonTypedFromParam(Expr $expr): bool + private function shouldSkip(Expr $expr): bool { - $functionLike = $this->betterNodeFinder->findParentType($expr, FunctionLike::class); - if (! $functionLike instanceof FunctionLike) { - return false; - } + $type = $this->getType($expr); - $params = $functionLike->getParams(); - foreach ($params as $param) { - if (! $this->nodeComparator->areNodesEqual($param->var, $expr)) { - continue; + if ($type instanceof UnionType) { + foreach ($type->getTypes() as $unionedType) { + if ($unionedType instanceof MixedType) { + return true; + } } + } - return $param->type === null; + if (! $this->propertyFetchAnalyzer->isPropertyFetch($expr)) { + return $this->exprAnalyzer->isNonTypedFromParam($expr); } - return false; + $phpPropertyReflection = $this->reflectionResolver->resolvePropertyReflectionFromPropertyFetch($expr); + if (! $phpPropertyReflection instanceof PhpPropertyReflection) { + return true; + } + + $nativeType = $phpPropertyReflection->getNativeType(); + return $nativeType instanceof MixedType; } } diff --git a/rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php b/rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php index 9d9dc1c1809..8a5ea5448f9 100644 --- a/rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php +++ b/rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php @@ -6,12 +6,12 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassConst; -use PHPStan\Analyser\Scope; +use PhpParser\NodeVisitor; use PHPStan\Reflection\ClassReflection; -use Rector\Core\NodeAnalyzer\EnumAnalyzer; -use Rector\Core\NodeManipulator\ClassConstManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeManipulator\ClassConstManipulator; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,8 +21,8 @@ final class RemoveUnusedPrivateClassConstantRector extends AbstractRector { public function __construct( - private ClassConstManipulator $classConstManipulator, - private EnumAnalyzer $enumAnalyzer, + private readonly ClassConstManipulator $classConstManipulator, + private readonly ReflectionResolver $reflectionResolver ) { } @@ -40,7 +40,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -64,18 +64,13 @@ public function getNodeTypes(): array /** * @param ClassConst $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?int { if ($this->shouldSkipClassConst($node)) { return null; } - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - $classReflection = $scope->getClassReflection(); + $classReflection = $this->reflectionResolver->resolveClassReflection($node); if (! $classReflection instanceof ClassReflection) { return null; } @@ -84,9 +79,7 @@ public function refactor(Node $node): ?Node return null; } - $this->removeNode($node); - - return null; + return NodeVisitor::REMOVE_NODE; } private function shouldSkipClassConst(ClassConst $classConst): bool @@ -99,6 +92,24 @@ private function shouldSkipClassConst(ClassConst $classConst): bool return true; } - return $this->enumAnalyzer->isEnumClassConst($classConst); + $scope = ScopeFetcher::fetch($classConst); + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + return $this->hasParentClassOfEnumSuffix($classReflection); + } + + private function hasParentClassOfEnumSuffix(ClassReflection $classReflection): bool + { + foreach ($classReflection->getParentClassesNames() as $parentClassesName) { + if (str_ends_with($parentClassesName, 'Enum')) { + return true; + } + } + + return false; } } diff --git a/rules/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php b/rules/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php index f843cdbd850..977d28c894c 100644 --- a/rules/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php +++ b/rules/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php @@ -10,10 +10,12 @@ use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; @@ -23,18 +25,15 @@ */ final class RemoveAnnotationRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const ANNOTATIONS_TO_REMOVE = 'annotations_to_remove'; - /** * @var string[] */ private array $annotationsToRemove = []; public function __construct( - private PhpDocTagRemover $phpDocTagRemover + private readonly PhpDocTagRemover $phpDocTagRemover, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly PhpDocInfoFactory $phpDocInfoFactory, ) { } @@ -50,16 +49,14 @@ final class SomeClass { } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' final class SomeClass { } CODE_SAMPLE -, - [ - self::ANNOTATIONS_TO_REMOVE => ['method'], - ] + , + ['method'], ), ]); } @@ -77,23 +74,34 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($this->annotationsToRemove === []) { + Assert::notEmpty($this->annotationsToRemove); + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $phpDocInfo instanceof PhpDocInfo) { return null; } - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $hasChanged = false; foreach ($this->annotationsToRemove as $annotationToRemove) { - $this->phpDocTagRemover->removeByName($phpDocInfo, $annotationToRemove); + $namedHasChanged = $this->phpDocTagRemover->removeByName($phpDocInfo, $annotationToRemove); + if ($namedHasChanged) { + $hasChanged = true; + } + if (! is_a($annotationToRemove, PhpDocTagValueNode::class, true)) { continue; } - $phpDocInfo->removeByType($annotationToRemove); + $typedHasChanged = $phpDocInfo->removeByType($annotationToRemove); + if ($typedHasChanged) { + $hasChanged = true; + } } - if ($phpDocInfo->hasChanged()) { - $node->setAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED, true); + if ($hasChanged) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + return $node; } @@ -101,13 +109,12 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $annotationsToRemove = $configuration[self::ANNOTATIONS_TO_REMOVE] ?? []; - Assert::allString($annotationsToRemove); + Assert::allString($configuration); - $this->annotationsToRemove = $annotationsToRemove; + $this->annotationsToRemove = $configuration; } } diff --git a/rules/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector.php b/rules/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector.php new file mode 100644 index 00000000000..d9659ee557c --- /dev/null +++ b/rules/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector.php @@ -0,0 +1,153 @@ +isObjectType($node, new ObjectType(ClassName::TEST_CASE_CLASS))) { + return null; + } + + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + // not yet typed + if (! $property->type instanceof Node) { + continue; + } + + if (count($property->props) !== 1) { + continue; + } + + if (! $property->type instanceof FullyQualified) { + continue; + } + + if ($this->isObjectType($property->type, new ObjectType(ClassName::MOCK_OBJECT))) { + continue; + } + + $propertyDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + if (! $this->isVarTagUnionTypeMockObject($propertyDocInfo, $property)) { + continue; + } + + // clear var docblock + if ($this->varTagRemover->removeVarTag($property)) { + $hasChanged = true; + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_PROPERTIES; + } + + private function isVarTagUnionTypeMockObject(PhpDocInfo $phpDocInfo, Property $property): bool + { + $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + if (! $varTagValueNode instanceof VarTagValueNode) { + return false; + } + + if (! $varTagValueNode->type instanceof UnionTypeNode) { + return false; + } + + $varTagType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($varTagValueNode->type, $property); + if (! $varTagType instanceof UnionType) { + return false; + } + + foreach ($varTagType->getTypes() as $unionedType) { + if ($unionedType->isSuperTypeOf(new ObjectType(ClassName::MOCK_OBJECT))->yes()) { + return true; + } + } + + return false; + } +} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector.php new file mode 100644 index 00000000000..f58411325e4 --- /dev/null +++ b/rules/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector.php @@ -0,0 +1,207 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + if (! $node->extends instanceof FullyQualified) { + return null; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $ancestors = array_filter( + $classReflection->getAncestors(), + fn (ClassReflection $ancestorClassReflection): bool => $classReflection->isClass() && $ancestorClassReflection->getName() !== $classReflection->getName() + ); + + $hasChanged = false; + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->isPrivate()) { + continue; + } + + if ($classMethod->isAbstract()) { + continue; + } + + foreach ((array) $classMethod->stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof StaticCall) { + continue; + } + + if (! $this->isName($stmt->expr->class, 'parent')) { + continue; + } + + if ($stmt->expr->isFirstClassCallable()) { + continue; + } + + $args = $stmt->expr->getArgs(); + + if ($args === []) { + continue; + } + + if ($this->argsAnalyzer->hasNamedArg($args)) { + continue; + } + + $methodName = $this->getName($stmt->expr->name); + if ($methodName === null) { + continue; + } + + foreach ($ancestors as $ancestor) { + $nativeClassReflection = $ancestor->getNativeReflection(); + if (! $nativeClassReflection->hasMethod($methodName)) { + continue; + } + + $method = $nativeClassReflection->getMethod($methodName); + + $parameters = $method->getParameters(); + $totalParameters = count($parameters); + $justChanged = false; + + for ($index = $totalParameters - 1; $index >= 0; --$index) { + if (! $parameters[$index]->isDefaultValueAvailable()) { + break; + } + + // already passed + if (! isset($args[$index])) { + break; + } + + // only literal values + if ($this->exprAnalyzer->isDynamicExpr($args[$index]->value)) { + break; + } + + // on decrement loop, when next arg is not removed, then current can't be removed + if (isset($args[$index + 1])) { + break; + } + + $defaultValue = $parameters[$index]->getDefaultValue(); + if ($defaultValue === $this->valueResolver->getValue($args[$index]->value)) { + unset($args[$index]); + $hasChanged = true; + $justChanged = true; + } + } + + if ($justChanged) { + $stmt->expr->args = array_values($args); + } + + break; + } + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector.php deleted file mode 100644 index d79632f727a..00000000000 --- a/rules/DeadCode/Rector/ClassMethod/RemoveDeadConstructorRector.php +++ /dev/null @@ -1,99 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } - - if ($this->shouldSkipPropertyPromotion($node)) { - return null; - } - - if ($classLike->extends instanceof FullyQualified) { - return null; - } - - $this->removeNode($node); - - return null; - } - - private function shouldSkipPropertyPromotion(ClassMethod $classMethod): bool - { - if (! $this->isName($classMethod, MethodName::CONSTRUCT)) { - return true; - } - if ($classMethod->stmts === null) { - return true; - } - if ($classMethod->stmts !== []) { - return true; - } - - if ($this->classMethodManipulator->isPropertyPromotion($classMethod)) { - return true; - } - - return $this->classMethodManipulator->isNamedConstructor($classMethod); - } -} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector.php deleted file mode 100644 index c2c2364a4a1..00000000000 --- a/rules/DeadCode/Rector/ClassMethod/RemoveDelegatingParentCallRector.php +++ /dev/null @@ -1,160 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if ($this->shouldSkipClass($classLike)) { - return null; - } - - $onlyStmt = $this->matchClassMethodOnlyStmt($node); - if ($onlyStmt === null) { - return null; - } - - // are both return? - if ($this->isMethodReturnType($node, 'void') && ! $onlyStmt instanceof Return_) { - return null; - } - - $staticCall = $this->matchStaticCall($onlyStmt); - if (! $staticCall instanceof StaticCall) { - return null; - } - - if (! $this->currentAndParentClassMethodComparator->isParentCallMatching($node, $staticCall)) { - return null; - } - - if ($this->hasRequiredAnnotation($node)) { - return null; - } - - // the method is just delegation, nothing extra - $this->removeNode($node); - - return null; - } - - private function shouldSkipClass(?ClassLike $classLike): bool - { - if (! $classLike instanceof Class_) { - return true; - } - return $classLike->extends === null; - } - - private function isMethodReturnType(ClassMethod $classMethod, string $type): bool - { - if ($classMethod->returnType === null) { - return false; - } - - return $this->isName($classMethod->returnType, $type); - } - - private function matchStaticCall(Node $node): ?StaticCall - { - // must be static call - if ($node instanceof Return_) { - if ($node->expr instanceof StaticCall) { - return $node->expr; - } - - return null; - } - - if ($node instanceof StaticCall) { - return $node; - } - - return null; - } - - private function hasRequiredAnnotation(ClassMethod $classMethod): bool - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - return $phpDocInfo->hasByName('required'); - } - - private function matchClassMethodOnlyStmt(ClassMethod $classMethod): null | Stmt | Expr - { - if ($classMethod->stmts === null) { - return null; - } - - if (count($classMethod->stmts) !== 1) { - return null; - } - - // recount empty notes - $stmtsValues = array_values($classMethod->stmts); - - return $this->unwrapExpression($stmtsValues[0]); - } -} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector.php index fcb7b112e62..6557fd579c4 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector.php @@ -4,15 +4,22 @@ namespace Rector\DeadCode\Rector\ClassMethod; +use PhpParser\Comment\Doc; use PhpParser\Node; -use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\NodeManipulator\ClassMethodManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; +use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode; +use PHPStan\Reflection\ClassReflection; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Configuration\Parameter\FeatureFlags; +use Rector\DeadCode\NodeAnalyzer\IsClassMethodUsedAnalyzer; use Rector\DeadCode\NodeManipulator\ControllerClassMethodManipulator; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeAnalyzer\ParamAnalyzer; +use Rector\NodeManipulator\ClassMethodManipulator; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,8 +29,11 @@ final class RemoveEmptyClassMethodRector extends AbstractRector { public function __construct( - private ClassMethodManipulator $classMethodManipulator, - private ControllerClassMethodManipulator $controllerClassMethodManipulator + private readonly ClassMethodManipulator $classMethodManipulator, + private readonly ControllerClassMethodManipulator $controllerClassMethodManipulator, + private readonly ParamAnalyzer $paramAnalyzer, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly IsClassMethodUsedAnalyzer $isClassMethodUsedAnalyzer ) { } @@ -57,47 +67,55 @@ class OrphanClass */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?Class_ { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } + $hasChanged = false; - if ($node->stmts !== null && $node->stmts !== []) { - return null; - } + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof ClassMethod) { + continue; + } - if ($node->isAbstract()) { - return null; - } + if ($stmt->stmts !== null && $stmt->stmts !== []) { + continue; + } - if ($node->isFinal() && ! $classLike->isFinal()) { - return null; - } + if ($stmt->isAbstract()) { + continue; + } - if ($this->shouldSkipNonFinalNonPrivateClassMethod($classLike, $node)) { - return null; - } + if ($stmt->isFinal() && ! $node->isFinal() && FeatureFlags::treatClassesAsFinal($node) === false) { + continue; + } - if ($this->shouldSkipClassMethod($node)) { - return null; + if ($this->shouldSkipNonFinalNonPrivateClassMethod($node, $stmt)) { + continue; + } + + if ($this->shouldSkipClassMethod($node, $stmt)) { + continue; + } + + unset($node->stmts[$key]); + $hasChanged = true; } - $this->removeNode($node); + if ($hasChanged) { + return $node; + } - return $node; + return null; } private function shouldSkipNonFinalNonPrivateClassMethod(Class_ $class, ClassMethod $classMethod): bool { - if ($class->isFinal()) { + if ($class->isFinal() || FeatureFlags::treatClassesAsFinal($class)) { return false; } @@ -112,29 +130,73 @@ private function shouldSkipNonFinalNonPrivateClassMethod(Class_ $class, ClassMet return $classMethod->isPublic(); } - private function shouldSkipClassMethod(ClassMethod $classMethod): bool + private function shouldSkipClassMethod(Class_ $class, ClassMethod $classMethod): bool { + // is method called somewhere else in the class? + $scope = ScopeFetcher::fetch($class); + if ($this->isClassMethodUsedAnalyzer->isClassMethodUsed($class, $classMethod, $scope)) { + return true; + } + if ($this->classMethodManipulator->isNamedConstructor($classMethod)) { return true; } - if ($this->classMethodManipulator->hasParentMethodOrInterfaceMethod($classMethod)) { + if ($this->classMethodManipulator->hasParentMethodOrInterfaceMethod($class, $classMethod->name->toString())) { return true; } - if ($this->classMethodManipulator->isPropertyPromotion($classMethod)) { + if ($this->paramAnalyzer->hasPropertyPromotion($classMethod->params)) { return true; } - if ($this->controllerClassMethodManipulator->isControllerClassMethodWithBehaviorAnnotation($classMethod)) { + if ($this->hasDeprecatedAnnotation($classMethod)) { + return true; + } + + if ($this->controllerClassMethodManipulator->isControllerClassMethod($class, $classMethod)) { + return true; + } + + if ($this->isName($classMethod, MethodName::CLONE)) { + return ! $classMethod->isPublic(); + } + + if ($this->isName($classMethod, MethodName::INVOKE)) { return true; } - if ($this->nodeNameResolver->isName($classMethod, MethodName::CONSTRUCT)) { - $class = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - return $class instanceof Class_ && $class->extends instanceof FullyQualified; + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + return $this->isAttributeMarkerConstructor($classMethod, $classReflection); + } + + private function hasDeprecatedAnnotation(ClassMethod $classMethod): bool + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); + if (! $phpDocInfo instanceof PhpDocInfo) { + return false; + } + + return $phpDocInfo->hasByType(DeprecatedTagValueNode::class); + } + + /** + * Skip constructor in attributes as might be a marker parameter + */ + private function isAttributeMarkerConstructor(ClassMethod $classMethod, ClassReflection $classReflection): bool + { + if (! $this->isName($classMethod, MethodName::CONSTRUCT)) { + return false; + } + + if (! $classReflection->isAttributeClass()) { + return false; } - return $this->nodeNameResolver->isName($classMethod, MethodName::INVOKE); + return $classMethod->getDocComment() instanceof Doc; } } diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveLastReturnRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveLastReturnRector.php deleted file mode 100644 index 99d0a4997fd..00000000000 --- a/rules/DeadCode/Rector/ClassMethod/RemoveLastReturnRector.php +++ /dev/null @@ -1,96 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, Function_::class]; - } - - /** - * @param ClassMethod|Function_ $node - */ - public function refactor(Node $node): ?Node - { - // last node and last return - $lastNode = $this->betterNodeFinder->findLastInstanceOf((array) $node->stmts, Node::class); - $lastReturn = $this->betterNodeFinder->findLastInstanceOf((array) $node->stmts, Return_::class); - - if (! $lastReturn instanceof Return_) { - return null; - } - - if (! $lastNode instanceof Node) { - return null; - } - - if ($lastNode !== $lastReturn) { - return null; - } - - if ($this->contextAnalyzer->isInLoop($lastReturn)) { - return null; - } - - $this->removeNode($lastReturn); - - return null; - } -} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php new file mode 100644 index 00000000000..3a3669ef9af --- /dev/null +++ b/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php @@ -0,0 +1,170 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class, Expression::class, Property::class]; + } + + /** + * @param ClassMethod|Function_|Expression|Property $node + */ + public function refactor(Node $node): ?Node + { + if ($node instanceof Expression || $node instanceof Property) { + return $this->processVarTagNull($node); + } + + $phpdocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $removedParamNames = []; + + foreach ($node->params as $param) { + $paramName = $this->getName($param); + $paramTagValueNode = $phpdocInfo->getParamTagValueByName($paramName); + + if ($paramTagValueNode instanceof ParamTagValueNode && $this->isNull($paramTagValueNode)) { + $removedParamNames[] = $paramTagValueNode->parameterName; + } + } + + $hasRemoved = false; + if ($removedParamNames !== []) { + $this->removeParamNullTag($phpdocInfo, $removedParamNames); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + $hasRemoved = true; + } + + $returnTagValueNode = $phpdocInfo->getReturnTagValue(); + if ($returnTagValueNode instanceof ReturnTagValueNode && $this->isNull($returnTagValueNode)) { + $phpdocInfo->removeByType(ReturnTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + $hasRemoved = true; + } + + if (! $hasRemoved) { + return null; + } + + return $node; + } + + private function isNull(VarTagValueNode|ParamTagValueNode|ReturnTagValueNode $tag): bool + { + return $tag->type instanceof IdentifierTypeNode + && $tag->type->__toString() === 'null' + && $tag->description === ''; + } + + /** + * @param string[] $paramNames + */ + private function removeParamNullTag(PhpDocInfo $phpDocInfo, array $paramNames): void + { + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + $phpDocNodeTraverser->traverseWithCallable( + $phpDocInfo->getPhpDocNode(), + '', + static function (AstNode $astNode) use ($paramNames): ?int { + if (! $astNode instanceof PhpDocTagNode) { + return null; + } + + if (! $astNode->value instanceof ParamTagValueNode) { + return null; + } + + if (in_array($astNode->value->parameterName, $paramNames, true)) { + return PhpDocNodeTraverser::NODE_REMOVE; + } + + return null; + } + ); + } + + private function processVarTagNull(Expression|Property $node): ?Node + { + $phpdocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $varTagValueNode = $phpdocInfo->getVarTagValueNode(); + + if ($varTagValueNode instanceof VarTagValueNode && $this->isNull($varTagValueNode)) { + $phpdocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; + } + + return null; + } +} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector.php new file mode 100644 index 00000000000..a8ce2bd02f6 --- /dev/null +++ b/rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector.php @@ -0,0 +1,320 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?int + { + if (! $this->isName($node, MethodName::CONSTRUCT)) { + return null; + } + + if ($node->stmts === null || count($node->stmts) !== 1) { + return null; + } + + if ($node->isFinal()) { + return null; + } + + if (! $node->isPublic()) { + return null; + } + + $parentMethodReflection = $this->matchParentConstructorReflection($node); + if (! $parentMethodReflection instanceof ExtendedMethodReflection) { + return null; + } + + $soleStmt = $node->stmts[0]; + $parentCallArgs = $this->matchParentConstructorCallArgs($soleStmt); + if ($parentCallArgs === null) { + return null; + } + + // match count and order + if (! $this->isParameterAndArgCountAndOrderIdentical($node)) { + return null; + } + + // match parameter types and parent constructor types + if (! $this->areConstructorAndParentParameterTypesMatching($node, $parentMethodReflection)) { + return null; + } + + if ($this->doAttributeDecoratedParametersExist($node)) { + return null; + } + + return NodeVisitor::REMOVE_NODE; + } + + private function matchParentConstructorReflection(ClassMethod $classMethod): ?ExtendedMethodReflection + { + $scope = ScopeFetcher::fetch($classMethod); + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $parentClassReflection = $classReflection->getParentClass(); + if (! $parentClassReflection instanceof ClassReflection) { + return null; + } + + if (! $parentClassReflection->hasConstructor()) { + return null; + } + + return $parentClassReflection->getConstructor(); + } + + /** + * Looking for parent::__construct() + * + * @return Arg[]|null + */ + private function matchParentConstructorCallArgs(Stmt $stmt): ?array + { + if (! $stmt instanceof Expression) { + return null; + } + + if (! $stmt->expr instanceof StaticCall) { + return null; + } + + $staticCall = $stmt->expr; + if ($staticCall->isFirstClassCallable()) { + return null; + } + + if (! $this->isName($staticCall->class, ObjectReference::PARENT)) { + return null; + } + + if (! $this->isName($staticCall->name, MethodName::CONSTRUCT)) { + return null; + } + + return $staticCall->getArgs(); + } + + private function isParameterAndArgCountAndOrderIdentical(ClassMethod $classMethod): bool + { + $soleStmt = $classMethod->stmts[0]; + + $parentCallArgs = $this->matchParentConstructorCallArgs($soleStmt); + if ($parentCallArgs === null) { + return false; + } + + $constructorParams = $classMethod->getParams(); + if (count($constructorParams) !== count($parentCallArgs)) { + return false; + } + + // match passed names in the same order + $paramNames = []; + foreach ($constructorParams as $constructorParam) { + $paramNames[] = $this->getName($constructorParam->var); + } + + $argNames = []; + foreach ($parentCallArgs as $parentCallArg) { + $argValue = $parentCallArg->value; + if (! $argValue instanceof Variable) { + return false; + } + + $argNames[] = $this->getName($argValue); + } + + return $paramNames === $argNames; + } + + private function areConstructorAndParentParameterTypesMatching( + ClassMethod $classMethod, + ExtendedMethodReflection $extendedMethodReflection + ): bool { + foreach ($classMethod->getParams() as $position => $param) { + if ($param->isPromoted() && $param->isPrivate()) { + return false; + } + + $parameterType = $param->type; + + // no type override + if ($parameterType === null) { + if ($param->default instanceof Expr && $this->isDifferentDefaultValue( + $param->default, + $extendedMethodReflection, + $position + )) { + return false; + } + + continue; + } + + $parametersSelector = $extendedMethodReflection->getOnlyVariant(); + + foreach ($parametersSelector->getParameters() as $index => $parameterReflection) { + if ($index !== $position) { + continue; + } + + $parentParameterType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $parameterReflection->getType(), + TypeKind::PARAM + ); + + if (! $this->nodeComparator->areNodesEqual($parameterType, $parentParameterType)) { + return false; + } + + if (! $param->default instanceof Expr) { + continue; + } + + if ($this->isDifferentDefaultValue($param->default, $extendedMethodReflection, $index)) { + return false; + } + } + } + + return true; + } + + private function isDifferentDefaultValue( + Expr $defaultExpr, + ExtendedMethodReflection $extendedMethodReflection, + int $index + ): bool { + $methodName = $extendedMethodReflection->getName(); + // native reflection is needed to get exact default value + if ($extendedMethodReflection->getDeclaringClass()->getNativeReflection()->hasMethod($methodName)) { + $parentMethod = $extendedMethodReflection->getDeclaringClass() + ->getNativeReflection() + ->getMethod($methodName); + $nativeParentParameterReflection = $parentMethod->getParameters()[$index] ?? null; + + if (! $nativeParentParameterReflection instanceof ReflectionParameter) { + return false; + } + + // when child has default value, but parent does not have default value, + // mark as different + if (! $nativeParentParameterReflection->isDefaultValueAvailable()) { + return true; + } + + $parentDefault = $nativeParentParameterReflection->getDefaultValue(); + if (! $this->valueResolver->isValue($defaultExpr, $parentDefault)) { + return true; + } + } + + return false; + } + + private function doAttributeDecoratedParametersExist(ClassMethod $classMethod): bool + { + $constructorParams = $classMethod->getParams(); + foreach ($constructorParams as $constructorParam) { + foreach ($constructorParam->attrGroups as $attrGroup) { + if ($attrGroup->attrs !== []) { + return true; + } + } + } + + return false; + } +} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector.php index e197f4034ab..73c0b6c5cef 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector.php @@ -4,13 +4,15 @@ namespace Rector\DeadCode\Rector\ClassMethod; +use PhpParser\Comment\Doc; use PhpParser\Node; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Interface_; -use Rector\Core\NodeAnalyzer\ParamAnalyzer; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PHPStan\Reflection\ClassReflection; +use Rector\DeadCode\NodeManipulator\ClassMethodParamRemover; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,7 +22,8 @@ final class RemoveUnusedConstructorParamRector extends AbstractRector { public function __construct( - private ParamAnalyzer $paramAnalyzer + private readonly ReflectionResolver $reflectionResolver, + private readonly ClassMethodParamRemover $classMethodParamRemover ) { } @@ -61,43 +64,50 @@ public function __construct($hey) */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if (! $this->isName($node, MethodName::CONSTRUCT)) { + $constructorClassMethod = $node->getMethod(MethodName::CONSTRUCT); + if (! $constructorClassMethod instanceof ClassMethod) { return null; } - if ($node->params === []) { + if ($constructorClassMethod->params === []) { return null; } - if ($this->paramAnalyzer->hasPropertyPromotion($node->params)) { + if ($constructorClassMethod->isAbstract()) { return null; } - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if ($classLike instanceof Interface_) { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { return null; } - if ($node->isAbstract()) { - return null; + $interfaces = $classReflection->getInterfaces(); + foreach ($interfaces as $interface) { + // parameters are contract required → skip + if ($interface->hasNativeMethod(MethodName::CONSTRUCT)) { + return null; + } } - foreach ($node->params as $param) { - if ($this->paramAnalyzer->isParamUsedInClassMethod($node, $param)) { - continue; - } + // attributes can be used as markers + if ($classReflection->isAttributeClass() && $constructorClassMethod->getDocComment() instanceof Doc) { + return null; + } - $this->nodeRemover->removeParam($node, $param); + $changedConstructorClassMethod = $this->classMethodParamRemover->processRemoveParams($constructorClassMethod); + if (! $changedConstructorClassMethod instanceof ClassMethod) { + return null; } - return null; + return $node; } } diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector.php index ff4f7a537c0..8b3d2677ab5 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector.php @@ -5,14 +5,20 @@ namespace Rector\DeadCode\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Identifier; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; +use PHPStan\Type\ObjectType; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; -use Rector\Core\Rector\AbstractRector; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\DeadCode\NodeCollector\UnusedParameterResolver; -use Rector\DeadCode\NodeManipulator\VariadicFunctionLikeDetector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,9 +28,11 @@ final class RemoveUnusedPrivateMethodParameterRector extends AbstractRector { public function __construct( - private VariadicFunctionLikeDetector $variadicFunctionLikeDetector, - private UnusedParameterResolver $unusedParameterResolver, - private PhpDocTagRemover $phpDocTagRemover + private readonly UnusedParameterResolver $unusedParameterResolver, + private readonly PhpDocTagRemover $phpDocTagRemover, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly BetterNodeFinder $betterNodeFinder, ) { } @@ -63,43 +71,144 @@ private function run($value) */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { - return null; + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if (! $classMethod->isPrivate()) { + continue; + } + + $unusedParameters = $this->unusedParameterResolver->resolve($classMethod); + if ($unusedParameters === []) { + continue; + } + + // early remove callers + if (! $this->removeCallerArgs($node, $classMethod, $unusedParameters)) { + continue; + } + + $unusedParameterPositions = array_keys($unusedParameters); + foreach (array_keys($classMethod->params) as $key) { + if (! in_array($key, $unusedParameterPositions, true)) { + continue; + } + + unset($classMethod->params[$key]); + } + + // reset param keys + $classMethod->params = array_values($classMethod->params); + + $this->clearPhpDocInfo($classMethod, $unusedParameters); + + $hasChanged = true; } - /** @var string|null $className */ - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return null; + if ($hasChanged) { + return $node; + } + + return null; + } + + /** + * @param Param[] $unusedParameters + */ + private function removeCallerArgs(Class_ $class, ClassMethod $classMethod, array $unusedParameters): bool + { + $classMethods = $class->getMethods(); + if ($classMethods === []) { + return false; } - $unusedParameters = $this->unusedParameterResolver->resolve($node); - $this->removeNodes($unusedParameters); + $methodName = $this->getName($classMethod); + $keysArg = array_keys($unusedParameters); - $this->clearPhpDocInfo($node, $unusedParameters); + $classObjectType = new ObjectType((string) $this->getName($class)); + $callers = []; + foreach ($classMethods as $classMethod) { + /** @var MethodCall[]|StaticCall[] $callers */ + $callers = array_merge($callers, $this->resolveCallers($classMethod, $methodName, $classObjectType)); + } - return $node; + foreach ($callers as $caller) { + if ($caller->isFirstClassCallable()) { + return false; + } + + foreach ($caller->getArgs() as $key => $arg) { + if ($arg->unpack) { + return false; + } + + if ($arg->name instanceof Identifier) { + if (isset($unusedParameters[$key]) && $this->isName( + $unusedParameters[$key], + (string) $this->getName($arg->name) + )) { + continue; + } + + return false; + } + } + } + + foreach ($callers as $caller) { + $this->cleanupArgs($caller, $keysArg); + } + + return true; } - private function shouldSkip(ClassMethod $classMethod): bool + /** + * @param int[] $keysArg + */ + private function cleanupArgs(MethodCall|StaticCall $call, array $keysArg): void { - if (! $classMethod->isPrivate()) { - return true; + $args = $call->getArgs(); + foreach (array_keys($args) as $key) { + if (in_array($key, $keysArg, true)) { + unset($args[$key]); + } } - if ($classMethod->params === []) { - return true; - } + // reset arg keys + $call->args = array_values($args); + } - return $this->variadicFunctionLikeDetector->isVariadic($classMethod); + /** + * @return MethodCall[]|StaticCall[] + */ + private function resolveCallers(ClassMethod $classMethod, string $methodName, ObjectType $classObjectType): array + { + return $this->betterNodeFinder->find($classMethod, function (Node $subNode) use ( + $methodName, + $classObjectType + ): bool { + if (! $subNode instanceof MethodCall && ! $subNode instanceof StaticCall) { + return false; + } + + $nodeToCheck = $subNode instanceof MethodCall + ? $subNode->var + : $subNode->class; + + if (! $this->isObjectType($nodeToCheck, $classObjectType)) { + return false; + } + + return $this->isName($subNode->name, $methodName); + }); } /** @@ -108,6 +217,7 @@ private function shouldSkip(ClassMethod $classMethod): bool private function clearPhpDocInfo(ClassMethod $classMethod, array $unusedParameters): void { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + $hasChanged = false; foreach ($unusedParameters as $unusedParameter) { $parameterName = $this->getName($unusedParameter->var); @@ -124,7 +234,14 @@ private function clearPhpDocInfo(ClassMethod $classMethod, array $unusedParamete continue; } - $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $paramTagValueNode); + $hasTagRemoved = $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $paramTagValueNode); + if ($hasTagRemoved) { + $hasChanged = true; + } + } + + if ($hasChanged) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); } } } diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php index 1494bb16bb2..89ab3af618e 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php @@ -5,13 +5,18 @@ namespace Rector\DeadCode\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; use Rector\DeadCode\NodeAnalyzer\IsClassMethodUsedAnalyzer; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\TypeDeclarationDocblocks\NodeFinder\DataProviderMethodsFinder; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,7 +26,10 @@ final class RemoveUnusedPrivateMethodRector extends AbstractRector { public function __construct( - private IsClassMethodUsedAnalyzer $isClassMethodUsedAnalyzer + private readonly IsClassMethodUsedAnalyzer $isClassMethodUsedAnalyzer, + private readonly ReflectionResolver $reflectionResolver, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly DataProviderMethodsFinder $dataProviderMethodsFinder ) { } @@ -62,40 +70,69 @@ public function run() */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + if ($node->getMethods() === []) { return null; } - if ($this->isClassMethodUsedAnalyzer->isClassMethodUsed($node)) { + $hasChanged = false; + + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { return null; } - $this->removeNode($node); + $dataProviderMethodNames = $this->resolveDataProviderMethodNames($node); - return $node; - } + foreach ($node->stmts as $classStmtKey => $classStmt) { + if (! $classStmt instanceof ClassMethod) { + continue; + } - private function shouldSkip(ClassMethod $classMethod): bool - { - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return true; + if (! $classStmt->isPrivate()) { + continue; + } + + $classMethod = $classStmt; + if ($this->hasDynamicMethodCallOnFetchThis($classStmt)) { + continue; + } + + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkip($classStmt, $classReflection)) { + continue; + } + + if ($this->isClassMethodUsedAnalyzer->isClassMethodUsed($node, $classStmt, $scope)) { + continue; + } + + if ($this->isNames($classMethod, $dataProviderMethodNames)) { + continue; + } + + unset($node->stmts[$classStmtKey]); + $hasChanged = true; } - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return true; + if ($hasChanged) { + return $node; } - // unreliable to detect trait, interface doesn't make sense + return null; + + } + + private function shouldSkip(ClassMethod $classMethod, ClassReflection $classReflection): bool + { + // unreliable to detect trait, interface, anonymous class: doesn't make sense if ($classReflection->isTrait()) { return true; } @@ -108,11 +145,6 @@ private function shouldSkip(ClassMethod $classMethod): bool return true; } - // skips interfaces by default too - if (! $classMethod->isPrivate()) { - return true; - } - // skip magic methods - @see https://www.php.net/manual/en/language.oop5.magic.php if ($classMethod->isMagic()) { return true; @@ -120,4 +152,35 @@ private function shouldSkip(ClassMethod $classMethod): bool return $classReflection->hasMethod(MethodName::CALL); } + + private function hasDynamicMethodCallOnFetchThis(ClassMethod $classMethod): bool + { + return (bool) $this->betterNodeFinder->findFirst( + (array) $classMethod->stmts, + function (Node $subNode): bool { + if (! $subNode instanceof MethodCall) { + return false; + } + + if (! $subNode->var instanceof Variable) { + return false; + } + + if (! $this->isName($subNode->var, 'this')) { + return false; + } + + return $subNode->name instanceof Variable; + } + ); + } + + /** + * @return string[] + */ + private function resolveDataProviderMethodNames(Class_ $class): array + { + $dataProviderClassMethods = $this->dataProviderMethodsFinder->findDataProviderNodesInClass($class); + return $this->nodeNameResolver->getNames($dataProviderClassMethods); + } } diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector.php index 75c154db4da..d47ee0983a8 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPromotedPropertyRector.php @@ -8,12 +8,22 @@ use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\NodeManipulator\PropertyManipulator; -use Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\Node\Stmt\TraitUse; +use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; +use PHPStan\Reflection\ClassReflection; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\DeadCode\NodeAnalyzer\PropertyWriteonlyAnalyzer; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\NodeFinder\PropertyFetchFinder; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; +use Rector\ValueObject\Visibility; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -24,8 +34,14 @@ final class RemoveUnusedPromotedPropertyRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private PropertyFetchFinder $propertyFetchFinder, - private PropertyManipulator $propertyManipulator, + private readonly PropertyFetchFinder $propertyFetchFinder, + private readonly VisibilityManipulator $visibilityManipulator, + private readonly PropertyWriteonlyAnalyzer $propertyWriteonlyAnalyzer, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly ReflectionResolver $reflectionResolver, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly PhpDocTagRemover $phpDocTagRemover, + private readonly DocBlockUpdater $docBlockUpdater ) { } @@ -73,56 +89,108 @@ public function getUsedDependency() */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if (! $this->isName($node, MethodName::CONSTRUCT)) { + $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { return null; } - $class = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { + if ($constructClassMethod->params === []) { return null; } - foreach ($node->getParams() as $param) { - // only private local scope; removing public property might be dangerous - if ($param->flags !== Class_::MODIFIER_PRIVATE) { - continue; - } + if ($this->shouldSkipClass($node)) { + return null; + } + + $hasChanged = false; + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($constructClassMethod); - if ($this->propertyManipulator->isPropertyUsedInReadContext($param)) { + foreach ($constructClassMethod->params as $key => $param) { + // only private local scope; removing public property might be dangerous + if (! $this->visibilityManipulator->hasVisibility($param, Visibility::PRIVATE)) { continue; } $paramName = $this->getName($param); - $propertyFetches = $this->propertyFetchFinder->findLocalPropertyFetchesByName($class, $paramName); + $propertyFetches = $this->propertyFetchFinder->findLocalPropertyFetchesByName($node, $paramName); if ($propertyFetches !== []) { continue; } + if (! $this->propertyWriteonlyAnalyzer->arePropertyFetchesExclusivelyBeingAssignedTo($propertyFetches)) { + continue; + } + + // always changed on below code + $hasChanged = true; + // is variable used? only remove property, keep param - $variable = $this->betterNodeFinder->findVariableOfName((array) $node->stmts, $paramName); + $variable = $this->betterNodeFinder->findVariableOfName((array) $constructClassMethod->stmts, $paramName); if ($variable instanceof Variable) { $param->flags = 0; continue; } + if ($phpDocInfo instanceof PhpDocInfo) { + $paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName); + if ($paramTagValueNode instanceof ParamTagValueNode) { + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $paramTagValueNode); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($constructClassMethod); + } + } + // remove param - $this->removeNode($param); + unset($constructClassMethod->params[$key]); + } + + if ($hasChanged) { + return $node; } - return $node; + return null; } public function provideMinPhpVersion(): int { return PhpVersionFeature::PROPERTY_PROMOTION; } + + private function shouldSkipClass(Class_ $class): bool + { + if ($class->attrGroups !== []) { + return true; + } + + $magicGetMethod = $class->getMethod(MethodName::__GET); + if ($magicGetMethod instanceof ClassMethod) { + return true; + } + + foreach ($class->stmts as $stmt) { + if ($stmt instanceof TraitUse) { + return true; + } + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($class); + if ($classReflection instanceof ClassReflection) { + $interfaces = $classReflection->getInterfaces(); + foreach ($interfaces as $interface) { + if ($interface->hasNativeMethod(MethodName::CONSTRUCT)) { + return true; + } + } + } + + return $this->propertyWriteonlyAnalyzer->hasClassDynamicPropertyNames($class); + } } diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector.php new file mode 100644 index 00000000000..842d7ce3581 --- /dev/null +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPublicMethodParameterRector.php @@ -0,0 +1,128 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + // may have child, or override parent that needs to follow the signature + if (! $node->isFinal() && FeatureFlags::treatClassesAsFinal($node) === false) { + return null; + } + + if ($node->extends instanceof FullyQualified || $node->implements !== []) { + return null; + } + + $hasChanged = false; + foreach ($node->getMethods() as $classMethod) { + if ($this->shouldSkipClassMethod($classMethod, $node)) { + continue; + } + + $changedMethod = $this->classMethodParamRemover->processRemoveParams($classMethod); + if (! $changedMethod instanceof ClassMethod) { + continue; + } + + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function shouldSkipClassMethod(ClassMethod $classMethod, Class_ $class): bool + { + // private method is handled by different rule + if (! $classMethod->isPublic()) { + return true; + } + + if ($classMethod->params === []) { + return true; + } + + // parameter is required for contract coupling + if ($this->isName($classMethod->name, MethodName::INVOKE) && $this->phpAttributeAnalyzer->hasPhpAttribute( + $class, + 'Symfony\Component\Messenger\Attribute\AsMessageHandler' + )) { + return true; + } + + return $this->magicClassMethodAnalyzer->isUnsafeOverridden($classMethod); + } +} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector.php new file mode 100644 index 00000000000..35f345b4418 --- /dev/null +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUselessAssignFromPropertyPromotionRector.php @@ -0,0 +1,135 @@ +std = $std; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function __construct(private \stdClass $std) + { + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node, MethodName::CONSTRUCT)) { + return null; + } + + if ($node->stmts === null || $node->stmts == []) { + return null; + } + + $variableNames = []; + foreach ($node->params as $param) { + if (! $param->isPromoted()) { + continue; + } + + // re-assign will cause error on the first place, no need to collect names + // on readonly property promotion + if ($param->isReadonly()) { + continue; + } + + $variableNames[] = $this->getName($param); + } + + if ($variableNames === []) { + return null; + } + + $removeStmtKeys = []; + + foreach ($node->stmts as $key => $stmt) { + // has non direct expression with assign, skip + if (! $stmt instanceof Expression || ! $stmt->expr instanceof Assign) { + return null; + } + + $assign = $stmt->expr; + + // has non property fetches assignments, skip + if (! $assign->var instanceof PropertyFetch) { + return null; + } + + // collect first, ensure not removed too early on next non property fetch assignment + // which may have side effect + if ($assign->var->var instanceof Variable && $this->isName($assign->var->var, 'this') && $this->isNames( + $assign->var->name, + $variableNames + ) && $assign->expr instanceof Variable && $this->isName( + $assign->expr, + (string) $this->getName($assign->var->name) + )) { + $removeStmtKeys[] = $key; + continue; + } + + // early return, if not all are property fetches from $this and its param + return null; + } + + // empty data? nothing to remove + if ($removeStmtKeys === []) { + return null; + } + + foreach ($removeStmtKeys as $removeStmtKey) { + unset($node->stmts[$removeStmtKey]); + } + + return $node; + } +} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector.php index 101df1ad552..93e70626895 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUselessParamTagRector.php @@ -6,10 +6,11 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; -use Rector\Core\Rector\AbstractRector; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\DeadCode\PhpDoc\TagRemover\ParamTagRemover; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,7 +20,8 @@ final class RemoveUselessParamTagRector extends AbstractRector { public function __construct( - private ParamTagRemover $paramTagRemover + private readonly ParamTagRemover $paramTagRemover, + private readonly PhpDocInfoFactory $phpDocInfoFactory ) { } @@ -63,23 +65,30 @@ public function foo(string $a, string $b) */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [ClassMethod::class, Function_::class]; } /** - * @param ClassMethod $node + * @param ClassMethod|Function_ $node */ public function refactor(Node $node): ?Node { + // skip as no comments + if ($node->getComments() === []) { + return null; + } + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); if (! $phpDocInfo instanceof PhpDocInfo) { return null; } - $this->paramTagRemover->removeParamTagsIfUseless($phpDocInfo, $node); + if ($phpDocInfo->getParamTagValueNodes() === []) { + return null; + } - if ($phpDocInfo->hasChanged()) { - $node->setAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED, true); + $hasChanged = $this->paramTagRemover->removeParamTagsIfUseless($phpDocInfo, $node); + if ($hasChanged) { return $node; } diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector.php new file mode 100644 index 00000000000..c8b1c1a3aae --- /dev/null +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnExprInConstructRector.php @@ -0,0 +1,133 @@ +init(); + return true; + } + + if (rand(2, 3)) { + return parent::construct(); + } + + $this->execute(); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function __construct() + { + if (rand(0, 1)) { + $this->init(); + return; + } + + if (rand(2, 3)) { + parent::construct(); + return; + } + + $this->execute(); + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + if (! $this->isName($node, MethodName::CONSTRUCT)) { + return null; + } + + $hasChanged = false; + $this->traverseNodesWithCallable($node->stmts, function (Node $subNode) use ( + &$hasChanged + ): int|null|array|Return_ { + if ($subNode instanceof Class_ || $subNode instanceof Function_ || $subNode instanceof Closure) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $subNode instanceof Return_) { + return null; + } + + if (! $subNode->expr instanceof Expr) { + return null; + } + + $hasChanged = true; + if ($this->exprAnalyzer->isDynamicExpr($subNode->expr)) { + return [new Expression($subNode->expr), new Return_()]; + } + + $subNode->expr = null; + return $subNode; + }); + + if ($hasChanged) { + return $node; + } + + return null; + } +} diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php index 578e3189ecb..e56709d2954 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php @@ -6,9 +6,11 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Stmt\Function_; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\DeadCode\PhpDoc\TagRemover\ReturnTagRemover; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,7 +20,9 @@ final class RemoveUselessReturnTagRector extends AbstractRector { public function __construct( - private ReturnTagRemover $returnTagRemover + private readonly ReturnTagRemover $returnTagRemover, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly PhpDocInfoFactory $phpDocInfoFactory, ) { } @@ -62,22 +66,22 @@ public function foo(): stdClass */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [ClassMethod::class, Function_::class]; } /** - * @param ClassMethod $node + * @param ClassMethod|Function_ $node */ public function refactor(Node $node): ?Node { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $this->returnTagRemover->removeReturnTagIfUseless($phpDocInfo, $node); - - if ($phpDocInfo->hasChanged()) { - $node->setAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED, true); - return $node; + $hasChanged = $this->returnTagRemover->removeReturnTagIfUseless($phpDocInfo, $node); + if (! $hasChanged) { + return null; } - return null; + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; } } diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector.php new file mode 100644 index 00000000000..30b88b28329 --- /dev/null +++ b/rules/DeadCode/Rector/ClassMethod/RemoveVoidDocblockFromMagicMethodRector.php @@ -0,0 +1,108 @@ +returnType instanceof Node) { + return null; + } + + if (! $this->isNames($node, [MethodName::CONSTRUCT, MethodName::DESTRUCT, MethodName::CLONE])) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + $returnTagValueNode = $phpDocInfo->getReturnTagValue(); + if (! $returnTagValueNode instanceof ReturnTagValueNode) { + return null; + } + + if ($returnTagValueNode->description !== '') { + return null; + } + + if (! $returnTagValueNode->type instanceof IdentifierTypeNode || $returnTagValueNode->type->name !== 'void') { + return null; + } + + $phpDocInfo->removeByType(ReturnTagValueNode::class); + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + return $node; + } +} diff --git a/rules/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector.php b/rules/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector.php new file mode 100644 index 00000000000..c6e9f0f6488 --- /dev/null +++ b/rules/DeadCode/Rector/Closure/RemoveUnusedClosureVariableUseRector.php @@ -0,0 +1,96 @@ +> + */ + public function getNodeTypes(): array + { + return [Closure::class]; + } + + /** + * @param Closure $node + */ + public function refactor(Node $node): ?Node + { + if ($node->uses === []) { + return null; + } + + $hasChanged = false; + + foreach ($node->uses as $key => $useVariable) { + $useVariableName = $this->getName($useVariable->var); + if (! is_string($useVariableName)) { + continue; + } + + $isUseUsed = (bool) $this->betterNodeFinder->findFirst( + $node->stmts, + fn (Node $subNode): bool => $this->exprUsedInNodeAnalyzer->isUsed($subNode, $useVariable->var) + ); + if ($isUseUsed) { + continue; + } + + unset($node->uses[$key]); + $hasChanged = true; + + } + + if ($hasChanged) { + return $node; + } + + return null; + } +} diff --git a/rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php b/rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php index 028ae14473c..6205fe4c468 100644 --- a/rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php +++ b/rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php @@ -6,9 +6,11 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Expr\Cast\String_; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -24,7 +26,7 @@ public function getRuleDefinition(): RuleDefinition [ new CodeSample( <<<'CODE_SAMPLE' -class SomeConcatingClass +class SomeConcatenatingClass { public function run($value) { @@ -34,7 +36,7 @@ public function run($value) CODE_SAMPLE , <<<'CODE_SAMPLE' -class SomeConcatingClass +class SomeConcatenatingClass { public function run($value) { @@ -72,6 +74,26 @@ public function refactor(Node $node): ?Node private function removeStringCast(Expr $expr): Expr { - return $expr instanceof String_ ? $expr->expr : $expr; + if (! $expr instanceof String_) { + return $expr; + } + + $targetExpr = $expr->expr; + $tokens = $this->getFile() + ->getOldTokens(); + + if ($expr->expr instanceof BinaryOp) { + $castStartTokenPos = $expr->getStartTokenPos(); + $targetExprStartTokenPos = $targetExpr->getStartTokenPos(); + + while (++$castStartTokenPos < $targetExprStartTokenPos) { + if (isset($tokens[$castStartTokenPos]) && (string) $tokens[$castStartTokenPos] === '(') { + $targetExpr->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + break; + } + } + } + + return $targetExpr; } } diff --git a/rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php b/rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php index 2e9f49a56c1..15528b4a3ba 100644 --- a/rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php +++ b/rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php @@ -10,50 +10,38 @@ use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual; use PhpParser\Node\Expr\BinaryOp\Smaller; use PhpParser\Node\Expr\ConstFetch; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\If_; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Util\PhpVersionFactory; -use Rector\Core\ValueObject\PhpVersion; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; +use PhpParser\NodeVisitor; +use Rector\Php\PhpVersionProvider; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersion; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\DeadCode\Rector\ConstFetch\RemovePhpVersionIdCheckRector\RemovePhpVersionIdCheckRectorTest */ -final class RemovePhpVersionIdCheckRector extends AbstractRector implements ConfigurableRectorInterface +final class RemovePhpVersionIdCheckRector extends AbstractRector { /** - * @var string + * @var PhpVersion::*|null */ - public const PHP_VERSION_CONSTRAINT = 'phpVersionConstraint'; - - private string | int | null $phpVersionConstraint; + private int | null $phpVersion = null; public function __construct( - private PhpVersionFactory $phpVersionFactory + private readonly PhpVersionProvider $phpVersionProvider, ) { - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $this->phpVersionConstraint = $configuration[self::PHP_VERSION_CONSTRAINT] ?? null; + $this->phpVersion = $this->phpVersionProvider->provide(); } public function getRuleDefinition(): RuleDefinition { - $exampleConfiguration = [ - self::PHP_VERSION_CONSTRAINT => PhpVersion::PHP_80, - ]; return new RuleDefinition( - 'Remove unneded PHP_VERSION_ID check', + 'Remove unneeded PHP_VERSION_ID conditional checks', [ - new ConfiguredCodeSample( + new CodeSample( <<<'CODE_SAMPLE' class SomeClass { @@ -62,11 +50,12 @@ public function run() if (PHP_VERSION_ID < 80000) { return; } + echo 'do something'; } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -76,8 +65,6 @@ public function run() } } CODE_SAMPLE -, - $exampleConfiguration ), ], ); @@ -88,195 +75,224 @@ public function run() */ public function getNodeTypes(): array { - return [ConstFetch::class]; + return [If_::class]; } /** - * @param ConstFetch $node + * @param If_ $node + * @return null|NodeVisitor::REMOVE_NODE|Stmt[] */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): null|array|int { - if (! $this->isName($node, 'PHP_VERSION_ID')) { - return null; - } - /** * $this->phpVersionProvider->provide() fallback is here as $currentFileProvider must be accessed after initialization */ - $phpVersionConstraint = $this->phpVersionConstraint ?? $this->phpVersionProvider->provide(); - - // ensure cast to (string) first to allow string like "8.0" value to be converted to the int value - $this->phpVersionConstraint = $this->phpVersionFactory->createIntVersion((string) $phpVersionConstraint); - - $if = $this->betterNodeFinder->findParentType($node, If_::class); - if (! $if instanceof If_) { - return null; + if ($this->phpVersion === null) { + $this->phpVersion = $this->phpVersionProvider->provide(); } - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($this->shouldSkip($node, $if, $parent)) { + if (! $node->cond instanceof BinaryOp) { return null; } - if ($parent instanceof Smaller) { - return $this->processSmaller($node, $parent, $if); - } - - if ($parent instanceof GreaterOrEqual) { - return $this->processGreaterOrEqual($node, $parent, $if); + $binaryOp = $node->cond; + if ($binaryOp->left instanceof ConstFetch && $this->isName($binaryOp->left->name, 'PHP_VERSION_ID')) { + return $this->refactorConstFetch($binaryOp->left, $node, $binaryOp); } - if ($parent instanceof Greater) { - return $this->processGreater($node, $parent, $if); - } - - return null; - } - - private function shouldSkip(ConstFetch $constFetch, ?If_ $if, ?Node $node): bool - { - $if = $this->betterNodeFinder->findParentType($constFetch, If_::class); - if (! $if instanceof If_) { - return true; + if (! $binaryOp->right instanceof ConstFetch) { + return null; } - $node = $constFetch->getAttribute(AttributeKey::PARENT_NODE); - if (! $node instanceof BinaryOp) { - return true; + if (! $this->isName($binaryOp->right->name, 'PHP_VERSION_ID')) { + return null; } - return $if->cond !== $node; + return $this->refactorConstFetch($binaryOp->right, $node, $binaryOp); } - private function processSmaller(ConstFetch $constFetch, Smaller $smaller, If_ $if): ?ConstFetch + /** + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE + */ + private function refactorSmaller(ConstFetch $constFetch, Smaller $smaller, If_ $if): null|array|int { if ($smaller->left === $constFetch) { - return $this->processSmallerLeft($constFetch, $smaller, $if); + return $this->refactorSmallerLeft($smaller); } if ($smaller->right === $constFetch) { - return $this->processSmallerRight($constFetch, $smaller, $if); + return $this->refactorSmallerRight($smaller, $if); } return null; } - private function processGreaterOrEqual(ConstFetch $constFetch, GreaterOrEqual $greaterOrEqual, If_ $if): ?ConstFetch - { + /** + * @return null|NodeVisitor::REMOVE_NODE|Stmt[] + */ + private function processGreaterOrEqual( + ConstFetch $constFetch, + GreaterOrEqual $greaterOrEqual, + If_ $if, + ): null|int|array { if ($greaterOrEqual->left === $constFetch) { - return $this->processGreaterOrEqualLeft($constFetch, $greaterOrEqual, $if); + return $this->refactorGreaterOrEqualLeft($greaterOrEqual, $if); } if ($greaterOrEqual->right === $constFetch) { - return $this->processGreaterOrEqualRight($constFetch, $greaterOrEqual, $if); + return $this->refactorGreaterOrEqualRight($greaterOrEqual); } return null; } - private function processSmallerLeft(ConstFetch $constFetch, Smaller $smaller, If_ $if): ?ConstFetch + /** + * @return null|NodeVisitor::REMOVE_NODE + */ + private function refactorSmallerLeft(Smaller $smaller): ?int { $value = $smaller->right; - if (! $value instanceof LNumber) { + if (! $value instanceof Int_) { return null; } - if ($this->phpVersionConstraint >= $value->value) { - $this->removeNode($if); + if ($this->phpVersion >= $value->value) { + return NodeVisitor::REMOVE_NODE; } - return $constFetch; + return null; } - private function processSmallerRight(ConstFetch $constFetch, Smaller $smaller, If_ $if): ?ConstFetch + /** + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE + */ + private function refactorSmallerRight(Smaller $smaller, If_ $if): null|array|int { $value = $smaller->left; - if (! $value instanceof LNumber) { + if (! $value instanceof Int_) { + return null; + } + + if ($this->phpVersion < $value->value) { return null; } - if ($this->phpVersionConstraint >= $value->value) { - $this->addNodesBeforeNode($if->stmts, $if); - $this->removeNode($if); + if ($if->stmts === []) { + return NodeVisitor::REMOVE_NODE; } - return $constFetch; + return $if->stmts; } - private function processGreaterOrEqualLeft( - ConstFetch $constFetch, - GreaterOrEqual $greaterOrEqual, - If_ $if - ): ?ConstFetch { + /** + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE + */ + private function refactorGreaterOrEqualLeft(GreaterOrEqual $greaterOrEqual, If_ $if): null|array|int + { $value = $greaterOrEqual->right; - if (! $value instanceof LNumber) { + if (! $value instanceof Int_) { return null; } - if ($this->phpVersionConstraint >= $value->value) { - $this->addNodesBeforeNode($if->stmts, $if); - $this->removeNode($if); + if ($this->phpVersion < $value->value) { + return null; } - return $constFetch; + if ($if->stmts === []) { + return NodeVisitor::REMOVE_NODE; + } + + return $if->stmts; } - private function processGreaterOrEqualRight( - ConstFetch $constFetch, - GreaterOrEqual $greaterOrEqual, - If_ $if - ): ?ConstFetch { + /** + * @return NodeVisitor::REMOVE_NODE|null + */ + private function refactorGreaterOrEqualRight(GreaterOrEqual $greaterOrEqual): ?int + { $value = $greaterOrEqual->left; - if (! $value instanceof LNumber) { + if (! $value instanceof Int_) { return null; } - if ($this->phpVersionConstraint >= $value->value) { - $this->removeNode($if); + if ($this->phpVersion >= $value->value) { + return NodeVisitor::REMOVE_NODE; } - return $constFetch; + return null; } - private function processGreater(ConstFetch $constFetch, Greater $greater, If_ $if): ?ConstFetch + /** + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE + */ + private function refactorGreater(ConstFetch $constFetch, Greater $greater, If_ $if): null|array|int { if ($greater->left === $constFetch) { - return $this->processGreaterLeft($constFetch, $greater, $if); + return $this->refactorGreaterLeft($greater, $if); } if ($greater->right === $constFetch) { - return $this->processGreaterRight($constFetch, $greater, $if); + return $this->refactorGreaterRight($greater); } return null; } - private function processGreaterLeft(ConstFetch $constFetch, Greater $greater, If_ $if): ?ConstFetch + /** + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE + */ + private function refactorGreaterLeft(Greater $greater, If_ $if): null|array|int { $value = $greater->right; - if (! $value instanceof LNumber) { + if (! $value instanceof Int_) { return null; } - if ($this->phpVersionConstraint >= $value->value) { - $this->addNodesBeforeNode($if->stmts, $if); - $this->removeNode($if); + if ($this->phpVersion < $value->value) { + return null; + } + + if ($if->stmts === []) { + return NodeVisitor::REMOVE_NODE; } - return $constFetch; + return $if->stmts; } - private function processGreaterRight(ConstFetch $constFetch, Greater $greater, If_ $if): ?ConstFetch + /** + * @return NodeVisitor::REMOVE_NODE|null + */ + private function refactorGreaterRight(Greater $greater): ?int { $value = $greater->left; - if (! $value instanceof LNumber) { + if (! $value instanceof Int_) { return null; } - if ($this->phpVersionConstraint >= $value->value) { - $this->removeNode($if); + if ($this->phpVersion >= $value->value) { + return NodeVisitor::REMOVE_NODE; + } + + return null; + } + + /** + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE + */ + private function refactorConstFetch(ConstFetch $constFetch, If_ $if, BinaryOp $binaryOp): null|array|int + { + if ($binaryOp instanceof Smaller) { + return $this->refactorSmaller($constFetch, $binaryOp, $if); + } + + if ($binaryOp instanceof GreaterOrEqual) { + return $this->processGreaterOrEqual($constFetch, $binaryOp, $if); + } + + if ($binaryOp instanceof Greater) { + return $this->refactorGreater($constFetch, $binaryOp, $if); } - return $constFetch; + return null; } } diff --git a/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php b/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php index 827524c602e..8b499eca188 100644 --- a/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php +++ b/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php @@ -4,17 +4,16 @@ namespace Rector\DeadCode\Rector\Expression; +use PhpParser\Comment\Doc; use PhpParser\Node; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeVisitor; use PHPStan\Reflection\Php\PhpPropertyReflection; -use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Reflection\ReflectionResolver; use Rector\DeadCode\NodeManipulator\LivingCodeManipulator; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -24,15 +23,15 @@ final class RemoveDeadStmtRector extends AbstractRector { public function __construct( - private LivingCodeManipulator $livingCodeManipulator, - private PropertyFetchAnalyzer $propertyFetchAnalyzer, - private ReflectionResolver $reflectionResolver + private readonly LivingCodeManipulator $livingCodeManipulator, + private readonly PropertyFetchAnalyzer $propertyFetchAnalyzer, + private readonly ReflectionResolver $reflectionResolver, ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Removes dead code statements', [ + return new RuleDefinition('Remove dead code statements', [ new CodeSample( <<<'CODE_SAMPLE' $value = 5; @@ -56,9 +55,9 @@ public function getNodeTypes(): array /** * @param Expression $node - * @return Node[]|Node|null + * @return Node[]|Node|null|NodeVisitor::REMOVE_NODE */ - public function refactor(Node $node) + public function refactor(Node $node): array|Node|null|int { if ($this->hasGetMagic($node)) { return null; @@ -73,15 +72,15 @@ public function refactor(Node $node) return null; } - $firstExpr = array_shift($livingCode); - $node->expr = $firstExpr; + $newNode = clone $node; + $newNode->expr = array_shift($livingCode); $newNodes = []; foreach ($livingCode as $singleLivingCode) { $newNodes[] = new Expression($singleLivingCode); } - $newNodes[] = $node; + $newNodes[] = $newNode; return $newNodes; } @@ -92,7 +91,6 @@ private function hasGetMagic(Expression $expression): bool return false; } - /** @var PropertyFetch|StaticPropertyFetch $propertyFetch */ $propertyFetch = $expression->expr; $phpPropertyReflection = $this->reflectionResolver->resolvePropertyReflectionFromPropertyFetch($propertyFetch); @@ -103,21 +101,18 @@ private function hasGetMagic(Expression $expression): bool return ! $phpPropertyReflection instanceof PhpPropertyReflection; } - private function removeNodeAndKeepComments(Expression $expression): ?Node + /** + * @return NodeVisitor::REMOVE_NODE|Node + */ + private function removeNodeAndKeepComments(Expression $expression): int|Node { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($expression); - - if ($expression->getComments() !== []) { + if ($expression->getDocComment() instanceof Doc) { $nop = new Nop(); - $nop->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); - - $this->phpDocInfoFactory->createFromNode($nop); + $nop->setDocComment($expression->getDocComment()); return $nop; } - $this->removeNode($expression); - - return null; + return NodeVisitor::REMOVE_NODE; } } diff --git a/rules/DeadCode/Rector/Expression/SimplifyMirrorAssignRector.php b/rules/DeadCode/Rector/Expression/SimplifyMirrorAssignRector.php index 19c0c08b6f6..5f350d121e5 100644 --- a/rules/DeadCode/Rector/Expression/SimplifyMirrorAssignRector.php +++ b/rules/DeadCode/Rector/Expression/SimplifyMirrorAssignRector.php @@ -7,7 +7,8 @@ use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,12 +19,17 @@ final class SimplifyMirrorAssignRector extends AbstractRector { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Removes unneeded $a = $a assigns', [new CodeSample( - 'function run() { - $a = $a; - }', - 'function run() { - }' + return new RuleDefinition('Remove unneeded `$value = $value` assigns', [new CodeSample( + <<<'CODE_SAMPLE' +function run() { + $result = $result; +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +function run() { +} +CODE_SAMPLE )]); } @@ -38,19 +44,18 @@ public function getNodeTypes(): array /** * @param Expression $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?int { if (! $node->expr instanceof Assign) { return null; } - /** @var Assign $assignNode */ - $assignNode = $node->expr; + $assign = $node->expr; - if ($this->nodeComparator->areNodesEqual($assignNode->var, $assignNode->expr)) { - $this->removeNode($node); + if (! $this->nodeComparator->areNodesEqual($assign->var, $assign->expr)) { + return null; } - return null; + return NodeVisitor::REMOVE_NODE; } } diff --git a/rules/DeadCode/Rector/For_/RemoveDeadContinueRector.php b/rules/DeadCode/Rector/For_/RemoveDeadContinueRector.php new file mode 100644 index 00000000000..e2d152f20ad --- /dev/null +++ b/rules/DeadCode/Rector/For_/RemoveDeadContinueRector.php @@ -0,0 +1,96 @@ +> + */ + public function getNodeTypes(): array + { + return [Do_::class, For_::class, Foreach_::class, While_::class]; + } + + /** + * @param Do_|For_|Foreach_|While_ $node + */ + public function refactor(Node $node): ?Node + { + $modified = false; + while ($this->canRemoveLastStatement($node->stmts)) { + array_pop($node->stmts); + $modified = true; + } + + return $modified ? $node : null; + } + + /** + * @param Stmt[] $stmts + */ + private function canRemoveLastStatement(array $stmts): bool + { + if ($stmts === []) { + return false; + } + + $lastKey = array_key_last($stmts); + $lastStmt = $stmts[$lastKey]; + + return $this->isRemovable($lastStmt); + } + + private function isRemovable(Stmt $stmt): bool + { + if (! $stmt instanceof Continue_) { + return false; + } + + if ($stmt->num instanceof Int_) { + return $stmt->num->value < 2; + } + + return true; + } +} diff --git a/rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php b/rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php index 235574a162d..fef1be4bb0f 100644 --- a/rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php +++ b/rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php @@ -6,13 +6,18 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Scalar; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\For_; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; -use Rector\Core\Rector\AbstractRector; use Rector\EarlyReturn\NodeTransformer\ConditionInverter; +use Rector\NodeManipulator\StmtsManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,8 +26,12 @@ */ final class RemoveDeadIfForeachForRector extends AbstractRector { + private bool $hasChanged = false; + public function __construct( - private ConditionInverter $conditionInverter + private readonly ConditionInverter $conditionInverter, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly StmtsManipulator $stmtsManipulator ) { } @@ -35,19 +44,15 @@ public function getRuleDefinition(): RuleDefinition <<<'CODE_SAMPLE' class SomeClass { - public function run($someObject) + public function run($value, $differentValue) { - $value = 5; if ($value) { } - if ($someObject->run()) { - } - foreach ($values as $value) { } - return $value; + return $differentValue; } } CODE_SAMPLE @@ -55,13 +60,9 @@ public function run($someObject) <<<'CODE_SAMPLE' class SomeClass { - public function run($someObject) + public function run($value, $differentValue) { - $value = 5; - if ($someObject->run()) { - } - - return $value; + return $differentValue; } } CODE_SAMPLE @@ -75,81 +76,114 @@ public function run($someObject) */ public function getNodeTypes(): array { - return [For_::class, If_::class, Foreach_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param For_|If_|Foreach_ $node + * @param StmtsAware $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): Node|null { - if ($node instanceof If_) { - $this->processIf($node); + if ($node->stmts === null) { return null; } - if ($node instanceof Foreach_) { - $this->processForeach($node); - return null; - } + $this->hasChanged = false; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof If_ && ! $stmt instanceof For_ && ! $stmt instanceof Foreach_) { + continue; + } - // For - if ($node->stmts !== []) { - return null; + if ($stmt->stmts !== []) { + continue; + } + + if ($stmt instanceof If_) { + $this->processIf($stmt, $key, $node); + continue; + } + + $this->processForForeach($stmt, $key, $node); } - $this->removeNode($node); + if ($this->hasChanged) { + return $node; + } return null; } - private function processIf(If_ $if): void + /** + * @param StmtsAware $stmtsAware + */ + private function processIf(If_ $if, int $key, Node $stmtsAware): void { - if ($if->stmts !== []) { - return; - } - if ($if->elseifs !== []) { return; } - if ($if->else !== null) { - $if->cond = $this->conditionInverter->createInvertedCondition($if->cond); - $if->stmts = $if->else->stmts; - $if->else = null; + // useless if () + if (! $if->else instanceof Else_) { + if ($this->hasNodeSideEffect($if->cond)) { + return; + } + unset($stmtsAware->stmts[$key]); + $this->hasChanged = true; return; } - if ($this->isNodeWithSideEffect($if->cond)) { - return; - } + $if->cond = $this->conditionInverter->createInvertedCondition($if->cond); + $if->stmts = $if->else->stmts; + $if->else = null; - $this->removeNode($if); + $this->hasChanged = true; } - private function processForeach(Foreach_ $foreach): void + /** + * @param StmtsAware $stmtsAware + */ + private function processForForeach(For_|Foreach_ $for, int $key, Node $stmtsAware): void { - if ($foreach->stmts !== []) { + if ($for instanceof For_) { + $variables = $this->betterNodeFinder->findInstanceOf( + [...$for->init, ...$for->cond, ...$for->loop], + Variable::class + ); + foreach ($variables as $variable) { + if ($this->stmtsManipulator->isVariableUsedInNextStmt( + $stmtsAware, + $key + 1, + (string) $this->getName($variable) + )) { + return; + } + } + + unset($stmtsAware->stmts[$key]); + $this->hasChanged = true; + return; } - if ($this->isNodeWithSideEffect($foreach->expr)) { - return; + $exprs = [$for->expr, $for->valueVar, $for->valueVar]; + $variables = $this->betterNodeFinder->findInstanceOf($exprs, Variable::class); + foreach ($variables as $variable) { + if ($this->stmtsManipulator->isVariableUsedInNextStmt( + $stmtsAware, + $key + 1, + (string) $this->getName($variable) + )) { + return; + } } - $this->removeNode($foreach); + unset($stmtsAware->stmts[$key]); + $this->hasChanged = true; } - private function isNodeWithSideEffect(Expr $expr): bool + private function hasNodeSideEffect(Expr $expr): bool { - if ($expr instanceof Variable) { - return false; - } - - if ($expr instanceof Scalar) { - return false; - } - return ! $this->valueResolver->isTrueOrFalse($expr); + return $this->betterNodeFinder->hasInstancesOf($expr, [CallLike::class, Assign::class]); } } diff --git a/rules/DeadCode/Rector/For_/RemoveDeadLoopRector.php b/rules/DeadCode/Rector/For_/RemoveDeadLoopRector.php index e1c7f764290..db9e4e9d37c 100644 --- a/rules/DeadCode/Rector/For_/RemoveDeadLoopRector.php +++ b/rules/DeadCode/Rector/For_/RemoveDeadLoopRector.php @@ -5,11 +5,14 @@ namespace Rector\DeadCode\Rector\For_; use PhpParser\Node; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Stmt\Do_; use PhpParser\Node\Stmt\For_; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\While_; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; +use Rector\DeadCode\SideEffect\SideEffectNodeDetector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,6 +21,11 @@ */ final class RemoveDeadLoopRector extends AbstractRector { + public function __construct( + private readonly SideEffectNodeDetector $sideEffectNodeDetector + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -59,13 +67,30 @@ public function getNodeTypes(): array /** * @param Do_|For_|Foreach_|While_ $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?int { if ($node->stmts !== []) { return null; } - $this->removeNode($node); - return $node; + if ($node instanceof Do_ || $node instanceof While_) { + $exprs = [$node->cond]; + } elseif ($node instanceof For_) { + $exprs = [...$node->init, ...$node->cond, ...$node->loop]; + } else { + $exprs = [$node->expr, $node->valueVar]; + } + + foreach ($exprs as $expr) { + if ($expr instanceof Assign) { + $expr = $expr->expr; + } + + if ($this->sideEffectNodeDetector->detect($expr)) { + return null; + } + } + + return NodeVisitor::REMOVE_NODE; } } diff --git a/rules/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector.php b/rules/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector.php index 910e6747053..c6b71861dc2 100644 --- a/rules/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector.php +++ b/rules/DeadCode/Rector/Foreach_/RemoveUnusedForeachKeyRector.php @@ -5,8 +5,17 @@ namespace Rector\DeadCode\Rector\Foreach_; use PhpParser\Node; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Foreach_; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeFinder; +use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\DeadCode\NodeAnalyzer\ExprUsedInNodeAnalyzer; +use Rector\NodeManipulator\StmtsManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -15,6 +24,15 @@ */ final class RemoveUnusedForeachKeyRector extends AbstractRector { + public function __construct( + private readonly NodeFinder $nodeFinder, + private readonly StmtsManipulator $stmtsManipulator, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly ExprUsedInNodeAnalyzer $exprUsedInNodeAnalyzer + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Remove unused key in foreach', [ @@ -41,31 +59,67 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Foreach_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Foreach_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if ($node->keyVar === null) { + if ($node->stmts === null) { return null; } - $keyVar = $node->keyVar; + $hasChanged = false; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } - $isNodeUsed = (bool) $this->betterNodeFinder->findFirst( - $node->stmts, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $keyVar) - ); + if (! $stmt->keyVar instanceof Variable) { + continue; + } - if ($isNodeUsed) { - return null; + $keyVar = $stmt->keyVar; + + $isNodeUsed = (bool) $this->nodeFinder->findFirst( + $stmt->stmts, + fn (Node $node): bool => $this->exprUsedInNodeAnalyzer->isUsed($node, $keyVar) + ); + + if ($isNodeUsed) { + continue; + } + + $keyVarName = (string) $this->getName($keyVar); + if ($this->stmtsManipulator->isVariableUsedInNextStmt($node, $key + 1, $keyVarName)) { + continue; + } + + $stmt->keyVar = null; + $hasChanged = true; + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($stmt); + if (! $phpDocInfo instanceof PhpDocInfo) { + continue; + } + + $varTagValues = $phpDocInfo->getPhpDocNode() + ->getVarTagValues(); + foreach ($varTagValues as $varTagValue) { + $variableName = $varTagValue->variableName; + if ($varTagValue->variableName === '$' . $keyVarName) { + $phpDocInfo->removeByType(VarTagValueNode::class, $variableName); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); + } + } } - $node->keyVar = null; + if ($hasChanged) { + return $node; + } - return $node; + return null; } } diff --git a/rules/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector.php b/rules/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector.php new file mode 100644 index 00000000000..482c0b9a152 --- /dev/null +++ b/rules/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector.php @@ -0,0 +1,96 @@ +isFirstClassCallable()) { + return null; + } + + if (! $this->isName($node, 'filter_var')) { + return null; + } + + // we need exact 2nd arg to assess value type + if (count($node->getArgs()) !== 2) { + return null; + } + + $firstArgValue = $node->getArgs()[0] + ->value; + $secondArgValue = $node->getArgs()[1] + ->value; + + if (! $secondArgValue instanceof ConstFetch) { + return null; + } + + $constantFilterName = $secondArgValue->name->toString(); + + $valueType = $this->nodeTypeResolver->getNativeType($firstArgValue); + + if ($constantFilterName === 'FILTER_VALIDATE_INT' && $valueType->isInteger()->yes()) { + return $firstArgValue; + } + + if ($constantFilterName === 'FILTER_VALIDATE_FLOAT' && $valueType->isFloat()->yes()) { + return $firstArgValue; + } + + if (in_array( + $constantFilterName, + ['FILTER_VALIDATE_BOOLEAN', 'FILTER_VALIDATE_BOOL'], + true + ) && $valueType->isBoolean() + ->yes()) { + return $firstArgValue; + } + + return null; + } +} diff --git a/rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php b/rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php new file mode 100644 index 00000000000..2ecc8ddd341 --- /dev/null +++ b/rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php @@ -0,0 +1,308 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class]; + } + + /** + * @param ClassMethod|Function_|Closure|ArrowFunction $node + */ + public function refactor(Node $node): ?Node + { + if ($this->shouldSkipNode($node)) { + return null; + } + + if ($this->shouldSkipByDocblock($node)) { + return null; + } + + $returnStatements = $this->betterNodeFinder->findReturnsScoped($node); + + if ($returnStatements === []) { + return null; + } + + $hasImplicitNullReturn = $this->silentVoidResolver->hasSilentVoid($node) + || $this->hasImplicitNullReturn($returnStatements); + + /** @var UnionType|NullableType $returnType */ + $returnType = $node->returnType; + + $returnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($returnType); + + $actualReturnTypes = $this->collectActualReturnTypes($returnStatements); + + if ($hasImplicitNullReturn) { + $actualReturnTypes[] = new NullType(); + } + + $unusedTypes = $this->getUnusedType($returnType, $actualReturnTypes); + + if ($unusedTypes === []) { + return null; + } + + $newReturnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + TypeCombinator::remove($returnType, TypeCombinator::union(...$unusedTypes)), + TypeKind::RETURN + ); + + if (! $newReturnType instanceof Node) { + return null; + } + + // mostly placeholder + if ($this->isName($newReturnType, 'null')) { + return null; + } + + $node->returnType = $newReturnType; + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + + if ($phpDocInfo?->hasByName('@return') === true) { + $this->changePhpDocReturnType($node, $phpDocInfo, $unusedTypes); + } + + return $node; + } + + private function shouldSkipByDocblock(ClassMethod|Function_|Closure|ArrowFunction $node): bool + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + + if (! $phpDocInfo instanceof PhpDocInfo) { + return false; + } + + $returnTag = $phpDocInfo->getReturnTagValue(); + + if (! $returnTag instanceof ReturnTagValueNode) { + return false; + } + + $returnType = $phpDocInfo->getReturnType(); + if (! $returnType instanceof \PHPStan\Type\UnionType) { + return false; + } + + $type = $this->typeFactory->createMixedPassedOrUnionType($returnType->getTypes()); + return ! $type->equals($returnType); + } + + private function shouldSkipNode(ClassMethod|Function_|Closure|ArrowFunction $node): bool + { + $returnType = $node->returnType; + + if (! $returnType instanceof UnionType && ! $returnType instanceof NullableType) { + return true; + } + + $types = $returnType instanceof UnionType + ? $returnType->types + : [new ConstFetch(new Name('null')), $returnType->type]; + + foreach ($types as $type) { + if ($this->isNames($type, ['true', 'false'])) { + return true; + } + } + + if (! $node instanceof ClassMethod) { + return false; + } + + if ($node->isPrivate() || $node->isFinal()) { + return false; + } + + if ($node->isAbstract()) { + return true; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + + if (! $classReflection instanceof ClassReflection) { + return true; + } + + if (! $classReflection->isClass()) { + return true; + } + + return ! $classReflection->isFinalByKeyword(); + } + + /** + * @param Return_[] $returnStatements + */ + private function hasImplicitNullReturn(array $returnStatements): bool + { + foreach ($returnStatements as $returnStatement) { + if ($returnStatement->expr === null) { + return true; + } + } + + return false; + } + + /** + * @param Return_[] $returnStatements + * @return Type[] + */ + private function collectActualReturnTypes(array $returnStatements): array + { + $returnTypes = []; + + foreach ($returnStatements as $returnStatement) { + if ($returnStatement->expr === null) { + continue; + } + + $returnTypes[] = $this->nodeTypeResolver->getNativeType($returnStatement->expr); + } + + return $returnTypes; + } + + /** + * @param Type[] $actualReturnTypes + * @return Type[] + */ + private function getUnusedType(Type $returnType, array $actualReturnTypes): array + { + $types = $returnType instanceof PHPStanUnionType ? $returnType->getTypes() : [$returnType]; + $unusedTypes = []; + + foreach ($types as $type) { + foreach ($actualReturnTypes as $actualReturnType) { + if (! $type->isSuperTypeOf($actualReturnType)->no()) { + continue 2; + } + } + + $unusedTypes[] = $type; + } + + return $unusedTypes; + } + + /** + * @param Type[] $unusedTypes + */ + private function changePhpDocReturnType( + FunctionLike $functionLike, + PhpDocInfo $phpDocInfo, + array $unusedTypes, + ): void { + $returnTagValueNode = $phpDocInfo->getReturnTagValue(); + + if (! $returnTagValueNode instanceof ReturnTagValueNode) { + return; + } + + $newReturnType = TypeCombinator::remove($phpDocInfo->getReturnType(), TypeCombinator::union(...$unusedTypes)); + $this->phpDocTypeChanger->changeReturnType($functionLike, $phpDocInfo, $newReturnType); + } +} diff --git a/rules/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector.php b/rules/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector.php deleted file mode 100644 index 262b8c66b2e..00000000000 --- a/rules/DeadCode/Rector/FunctionLike/RemoveCodeAfterReturnRector.php +++ /dev/null @@ -1,91 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Closure::class, ClassMethod::class, Function_::class]; - } - - /** - * @param Closure|ClassMethod|Function_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node->stmts === null) { - return null; - } - - $isDeadAfterReturn = false; - foreach ($node->stmts as $key => $stmt) { - if ($isDeadAfterReturn) { - if (! isset($node->stmts[$key])) { - throw new ShouldNotHappenException(); - } - - // keep comment - /** @var int $key */ - if ($node->stmts[$key] instanceof Nop) { - continue; - } - - $this->nodeRemover->removeStmt($node, $key); - } - - if ($stmt instanceof Return_) { - $isDeadAfterReturn = true; - } - } - - return null; - } -} diff --git a/rules/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector.php b/rules/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector.php index 1b09f6a734d..141279cb8df 100644 --- a/rules/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector.php +++ b/rules/DeadCode/Rector/FunctionLike/RemoveDeadReturnRector.php @@ -5,11 +5,14 @@ namespace Rector\DeadCode\Rector\FunctionLike; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -71,26 +74,54 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($node->stmts === []) { - return null; - } - if ($node->stmts === null) { + if ($node->stmts === [] || $node->stmts === null) { return null; } - $stmtValues = array_values($node->stmts); - $lastStmt = end($stmtValues); + $lastStmtKey = array_key_last($node->stmts); + + $lastStmt = $node->stmts[$lastStmtKey]; + + if ($lastStmt instanceof If_) { + if (! $this->isBareIfWithOnlyStmtEmptyReturn($lastStmt)) { + return null; + } + + $lastStmt->stmts = []; + return $node; + } if (! $lastStmt instanceof Return_) { return null; } - if ($lastStmt->expr !== null) { + if ($lastStmt->expr instanceof Expr) { return null; } - $this->removeNode($lastStmt); - + unset($node->stmts[$lastStmtKey]); return $node; } + + private function isBareIfWithOnlyStmtEmptyReturn(If_ $if): bool + { + if ($if->else instanceof Else_) { + return false; + } + + if ($if->elseifs !== []) { + return false; + } + + if (count($if->stmts) !== 1) { + return false; + } + + $onlyStmt = $if->stmts[0]; + if (! $onlyStmt instanceof Return_) { + return false; + } + + return ! $onlyStmt->expr instanceof Expr; + } } diff --git a/rules/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector.php b/rules/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector.php deleted file mode 100644 index df911ce3067..00000000000 --- a/rules/DeadCode/Rector/FunctionLike/RemoveDuplicatedIfReturnRector.php +++ /dev/null @@ -1,186 +0,0 @@ -nodeComparator = $nodeComparator; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove duplicated if stmt with return in function/method body', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($value) - { - if ($value) { - return true; - } - - $value2 = 100; - - if ($value) { - return true; - } - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($value) - { - if ($value) { - return true; - } - - $value2 = 100; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [FunctionLike::class]; - } - - /** - * @param FunctionLike $node - */ - public function refactor(Node $node): ?Node - { - $ifWithOnlyReturnsByHash = $this->collectDuplicatedIfWithOnlyReturnByHash($node); - if ($ifWithOnlyReturnsByHash === []) { - return null; - } - - foreach ($ifWithOnlyReturnsByHash as $ifWithOnlyReturns) { - // keep first one - array_shift($ifWithOnlyReturns); - - foreach ($ifWithOnlyReturns as $ifWithOnlyReturn) { - $this->removeNode($ifWithOnlyReturn); - } - } - - return $node; - } - - /** - * @return If_[][] - */ - private function collectDuplicatedIfWithOnlyReturnByHash(FunctionLike $functionLike): array - { - $ifWithOnlyReturnsByHash = []; - $modifiedVariableNames = []; - - foreach ((array) $functionLike->getStmts() as $stmt) { - if (! $this->ifManipulator->isIfWithOnly($stmt, Return_::class)) { - // variable modification - $modifiedVariableNames = array_merge( - $modifiedVariableNames, - $this->modifiedVariableNamesCollector->collectModifiedVariableNames($stmt) - ); - continue; - } - - if ($this->containsVariableNames($stmt, $modifiedVariableNames)) { - continue; - } - - /** @var If_ $stmt */ - $isFoundPropertyFetch = (bool) $this->betterNodeFinder->findFirst( - $stmt->cond, - fn (Node $node): bool => $this->propertyFetchAnalyzer->isPropertyFetch($node) - ); - if ($isFoundPropertyFetch) { - continue; - } - - $hash = $this->nodeComparator->printWithoutComments($stmt); - $ifWithOnlyReturnsByHash[$hash][] = $stmt; - } - - return $this->filterOutSingleItemStmts($ifWithOnlyReturnsByHash); - } - - /** - * @param string[] $modifiedVariableNames - */ - private function containsVariableNames(Stmt $stmt, array $modifiedVariableNames): bool - { - if ($modifiedVariableNames === []) { - return false; - } - - $containsVariableNames = false; - $this->traverseNodesWithCallable($stmt, function (Node $node) use ( - $modifiedVariableNames, - &$containsVariableNames - ): ?int { - if (! $node instanceof Variable) { - return null; - } - - if (! $this->isNames($node, $modifiedVariableNames)) { - return null; - } - - $containsVariableNames = true; - - return NodeTraverser::STOP_TRAVERSAL; - }); - - return $containsVariableNames; - } - - /** - * @param array $ifWithOnlyReturnsByHash - * @return array - */ - private function filterOutSingleItemStmts(array $ifWithOnlyReturnsByHash): array - { - return array_filter($ifWithOnlyReturnsByHash, fn (array $stmts): bool => count($stmts) >= 2); - } -} diff --git a/rules/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector.php b/rules/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector.php deleted file mode 100644 index 809a77ee85c..00000000000 --- a/rules/DeadCode/Rector/FunctionLike/RemoveOverriddenValuesRector.php +++ /dev/null @@ -1,255 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FunctionLike::class]; - } - - /** - * @param FunctionLike $node - */ - public function refactor(Node $node): ?Node - { - // 1. collect assigns - $assignedVariables = $this->resolveAssignedVariables($node); - $assignedVariableNames = $this->getNodeNames($assignedVariables); - - // 2. collect use of those variables - $assignedVariablesUse = $this->variableUseFinder->resolveUsedVariables($node, $assignedVariables); - - $nodesByTypeAndPosition = $this->nodeByTypeAndPositionCollector->collectNodesByTypeAndPosition( - $assignedVariables, - $assignedVariablesUse, - $node - ); - - $nodesToRemove = $this->resolveNodesToRemove($assignedVariableNames, $nodesByTypeAndPosition); - if ($nodesToRemove === []) { - return null; - } - - $this->removeNodes($nodesToRemove); - - return $node; - } - - /** - * @return Variable[] - */ - private function resolveAssignedVariables(FunctionLike $functionLike): array - { - return $this->betterNodeFinder->find($functionLike, function (Node $node): bool { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Assign) { - return false; - } - - if (! $node instanceof Variable) { - return false; - } - - // skin in if - if ($this->contextAnalyzer->isInIf($node)) { - return false; - } - - // is variable on the left - /** @var Assign $assignNode */ - $assignNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($assignNode->var !== $node) { - return false; - } - - if ($this->sideEffectNodeDetector->detectCallExpr($assignNode->expr)) { - return false; - } - - // simple variable only - if (! is_string($node->name)) { - return false; - } - - return ! $this->reservedKeywordAnalyzer->isNativeVariable($node->name); - }); - } - - /** - * @param Node[] $nodes - * @return string[] - */ - private function getNodeNames(array $nodes): array - { - $nodeNames = []; - foreach ($nodes as $node) { - $nodeName = $this->getName($node); - if ($nodeName) { - $nodeNames[] = $nodeName; - } - } - - return array_unique($nodeNames); - } - - /** - * @param string[] $assignedVariableNames - * @param VariableNodeUse[] $nodesByTypeAndPosition - * @return Node[] - */ - private function resolveNodesToRemove(array $assignedVariableNames, array $nodesByTypeAndPosition): array - { - $nodesToRemove = []; - - foreach ($assignedVariableNames as $assignedVariableName) { - $previousNode = null; - - foreach ($nodesByTypeAndPosition as $nodes) { - $variableNode = $nodes->getVariableNode(); - $comments = $variableNode->getAttribute(AttributeKey::COMMENTS); - - if ($comments !== null) { - continue; - } - $nodesIsName = $nodes->isName($assignedVariableName); - - if (! $nodesIsName) { - continue; - } - - if ($this->isAssignNodeUsed($previousNode, $nodes)) { - // continue - // instant override → remove - } elseif ($this->shouldRemoveAssignNode($previousNode, $nodes)) { - /** @var VariableNodeUse $previousNode */ - $nodesToRemove[] = $previousNode->getParentNode(); - } - - $previousNode = $nodes; - } - } - - return $nodesToRemove; - } - - private function isAssignNodeUsed( - ?VariableNodeUse $previousNode, - VariableNodeUse $nodeByTypeAndPosition - ): bool { - // this node was just used, skip to next one - if (! $previousNode instanceof VariableNodeUse) { - return false; - } - - if (! $previousNode->isType(VariableNodeUse::TYPE_ASSIGN)) { - return false; - } - - return $nodeByTypeAndPosition->isType(VariableNodeUse::TYPE_USE); - } - - private function shouldRemoveAssignNode( - ?VariableNodeUse $previousNode, - VariableNodeUse $nodeByTypeAndPosition - ): bool { - if ($previousNode === null) { - return false; - } - - if (! $previousNode->isType(VariableNodeUse::TYPE_ASSIGN)) { - return false; - } - - if (! $nodeByTypeAndPosition->isType(VariableNodeUse::TYPE_ASSIGN)) { - return false; - } - - // check the nesting level, e.g. call in if/while/else etc. - if ($previousNode->getNestingHash() !== $nodeByTypeAndPosition->getNestingHash()) { - return false; - } - - // check previous node doesn't contain the node on the right, e.g. - // $someNode = 1; - // $someNode = $someNode ?: 1; - /** @var Assign $assignNode */ - $assignNode = $nodeByTypeAndPosition->getParentNode(); - - $isVariableAssigned = (bool) $this->betterNodeFinder->findFirst( - $assignNode->expr, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual( - $node, - $nodeByTypeAndPosition->getVariableNode() - ) - ); - - return ! $isVariableAssigned; - } -} diff --git a/rules/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector.php b/rules/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector.php new file mode 100644 index 00000000000..6bdc90ac488 --- /dev/null +++ b/rules/DeadCode/Rector/If_/ReduceAlwaysFalseIfOrRector.php @@ -0,0 +1,93 @@ + 50) { + return 'yes'; + } + + return 'no'; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run(int $number) + { + if ($number > 50) { + return 'yes'; + } + + return 'no'; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [If_::class]; + } + + /** + * @param If_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->cond instanceof BooleanOr) { + return null; + } + + $booleanOr = $node->cond; + + $conditionStaticType = $this->getType($booleanOr->left); + if (! $conditionStaticType->isFalse()->yes()) { + return null; + } + + if (! $this->safeLeftTypeBooleanAndOrAnalyzer->isSafe($booleanOr)) { + return null; + } + + $node->cond = $booleanOr->right; + + return $node; + } +} diff --git a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php index f7edf0f6c16..b17a1b2678e 100644 --- a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php +++ b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php @@ -5,9 +5,27 @@ namespace Rector\DeadCode\Rector\If_; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ArrayDimFetch; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\StaticPropertyFetch; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; -use PHPStan\Type\Constant\ConstantBooleanType; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Type\IntersectionType; +use Rector\DeadCode\NodeAnalyzer\SafeLeftTypeBooleanAndOrAnalyzer; +use Rector\NodeAnalyzer\ExprAnalyzer; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -16,6 +34,13 @@ */ final class RemoveAlwaysTrueIfConditionRector extends AbstractRector { + public function __construct( + private readonly ExprAnalyzer $exprAnalyzer, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly SafeLeftTypeBooleanAndOrAnalyzer $safeLeftTypeBooleanAndOrAnalyzer + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Remove if condition that is always true', [ @@ -59,10 +84,15 @@ public function getNodeTypes(): array /** * @param If_ $node + * @return int|null|Stmt[]|If_ */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): int|null|array|If_ { - if ($node->else !== null) { + if ($node->cond instanceof BooleanAnd) { + return $this->refactorIfWithBooleanAnd($node); + } + + if ($node->else instanceof Else_) { return null; } @@ -71,20 +101,105 @@ public function refactor(Node $node): ?Node return null; } - $conditionStaticType = $this->getStaticType($node->cond); - if (! $conditionStaticType instanceof ConstantBooleanType) { + $conditionStaticType = $this->nodeTypeResolver->getNativeType($node->cond); + if (! $conditionStaticType->isTrue()->yes()) { + return null; + } + + if ($this->shouldSkipExpr($node->cond)) { + return null; + } + + if ($this->shouldSkipFromVariable($node->cond)) { + return null; + } + + $hasAssign = (bool) $this->betterNodeFinder->findFirstInstanceOf($node->cond, Assign::class); + if ($hasAssign) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + $type = $scope->getNativeType($node->cond); + if (! $type->isTrue()->yes()) { + return null; + } + + $classReflection = $scope->getClassReflection(); + if ($classReflection instanceof ClassReflection && $classReflection->isTrait()) { + return null; + } + + if ($node->stmts === []) { + return NodeVisitor::REMOVE_NODE; + } + + // keep original comments + if ($node->getComments() !== []) { + $node->stmts[0]->setAttribute(AttributeKey::COMMENTS, array_merge( + $node->getComments(), + $node->stmts[0]->getComments(), + )); + } + + return $node->stmts; + } + + private function shouldSkipFromVariable(Expr $expr): bool + { + /** @var Variable[] $variables */ + $variables = $this->betterNodeFinder->findInstancesOf($expr, [Variable::class]); + + foreach ($variables as $variable) { + if ($this->exprAnalyzer->isNonTypedFromParam($variable)) { + return true; + } + + $type = $this->nodeTypeResolver->getNativeType($variable); + if ($type instanceof IntersectionType) { + foreach ($type->getTypes() as $subType) { + if ($subType->isArray()->yes()) { + return true; + } + } + } + } + + return false; + } + + private function shouldSkipExpr(Expr $expr): bool + { + return (bool) $this->betterNodeFinder->findInstancesOf( + $expr, + [ + PropertyFetch::class, + StaticPropertyFetch::class, + ArrayDimFetch::class, + MethodCall::class, + StaticCall::class, + ] + ); + } + + private function refactorIfWithBooleanAnd(If_ $if): ?If_ + { + if (! $if->cond instanceof BooleanAnd) { return null; } - if (! $conditionStaticType->getValue()) { + $booleanAnd = $if->cond; + + $leftType = $this->getType($booleanAnd->left); + if (! $leftType->isTrue()->yes()) { return null; } - if (count($node->stmts) !== 1) { - // unable to handle now + if (! $this->safeLeftTypeBooleanAndOrAnalyzer->isSafe($booleanAnd)) { return null; } - return $node->stmts[0]; + $if->cond = $booleanAnd->right; + return $if; } } diff --git a/rules/DeadCode/Rector/If_/RemoveDeadIfBlockRector.php b/rules/DeadCode/Rector/If_/RemoveDeadIfBlockRector.php new file mode 100644 index 00000000000..b581deb9a06 --- /dev/null +++ b/rules/DeadCode/Rector/If_/RemoveDeadIfBlockRector.php @@ -0,0 +1,158 @@ +> + */ + public function getNodeTypes(): array + { + return [If_::class]; + } + + /** + * @param If_ $node + * @return NodeVisitor::REMOVE_NODE|null|If_ + */ + public function refactor(Node $node): int|null|If_ + { + if ($node->else instanceof Else_ && $node->else->stmts === []) { + $node->else = null; + return $this->refactor($node) ?? $node; + } + + foreach ($node->elseifs as $elseif) { + $keep_elseifs = array_values(array_filter( + $node->elseifs, + fn (ElseIf_ $elseif): bool => $elseif->stmts !== [] || $this->sideEffectNodeDetector->detect( + $elseif->cond + ) + )); + if (count($node->elseifs) !== count($keep_elseifs)) { + $node->elseifs = $keep_elseifs; + return $this->refactor($node) ?? $node; + } + } + + if ($node->stmts !== []) { + return null; + } + + // Skip commented blocks because we can't know + // if the comment will make sense after merging. + if ($node->getComments() !== []) { + return null; + } + + if ($this->sideEffectNodeDetector->detect($node->cond)) { + return null; + } + + // When the if body is blank but it has an elseif, + // merge the negated if condition with the elseif condition + if ($node->elseifs !== []) { + $firstElseIf = $node->elseifs[0]; + $cond = new BooleanAnd( + $this->conditionInverter->createInvertedCondition($node->cond), + $firstElseIf->cond + ); + $if = new If_($cond, [ + 'stmts' => $firstElseIf->stmts, + ]); + if (count($node->elseifs) > 1) { + $if->elseifs = \array_slice($node->elseifs, 1); + } + + return $this->refactor($if) ?? $if; + } + + if ($node->else instanceof Else_) { + $node->cond = $this->conditionInverter->createInvertedCondition($node->cond); + $node->stmts = $node->else->stmts; + $node->else = null; + return $node; + } + + return NodeVisitor::REMOVE_NODE; + } +} diff --git a/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php b/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php index 6b7df0a34ef..2d2d07dcc08 100644 --- a/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php +++ b/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php @@ -6,23 +6,25 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\BinaryOp\BooleanAnd; use PhpParser\Node\Expr\BooleanNot; +use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticPropertyFetch; -use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name; -use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; -use PhpParser\Node\Stmt\Property; -use PHPStan\Analyser\Scope; -use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeNestingScope\ContextAnalyzer; +use PhpParser\NodeVisitor; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Type\MixedType; +use PHPStan\Type\ObjectType; +use Rector\NodeManipulator\IfManipulator; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\Php80\NodeAnalyzer\PromotedPropertyResolver; -use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -32,11 +34,8 @@ final class RemoveDeadInstanceOfRector extends AbstractRector { public function __construct( - private IfManipulator $ifManipulator, - private PropertyFetchAnalyzer $propertyFetchAnalyzer, - private ConstructorAssignDetector $constructorAssignDetector, - private PromotedPropertyResolver $promotedPropertyResolver, - private ContextAnalyzer $contextAnalyzer + private readonly IfManipulator $ifManipulator, + private readonly ReflectionResolver $reflectionResolver ) { } @@ -45,26 +44,20 @@ public function getRuleDefinition(): RuleDefinition return new RuleDefinition('Remove dead instanceof check on type hinted variable', [ new CodeSample( <<<'CODE_SAMPLE' -final class SomeClass +function run(stdClass $stdClass) { - public function go(stdClass $stdClass) - { - if (! $stdClass instanceof stdClass) { - return false; - } - - return true; + if (! $stdClass instanceof stdClass) { + return false; } + + return true; } CODE_SAMPLE , <<<'CODE_SAMPLE' -final class SomeClass +function run(stdClass $stdClass) { - public function go(stdClass $stdClass) - { - return true; - } + return true; } CODE_SAMPLE ), @@ -81,121 +74,118 @@ public function getNodeTypes(): array /** * @param If_ $node + * @return Stmt[]|null|NodeVisitor::REMOVE_NODE|If_ */ - public function refactor(Node $node): ?If_ + public function refactor(Node $node): array|null|int|If_ { - $scope = $node->getAttribute(AttributeKey::SCOPE); - - // a trait - if (! $scope instanceof Scope) { - return null; - } - if (! $this->ifManipulator->isIfWithoutElseAndElseIfs($node)) { return null; } - if ($this->contextAnalyzer->isInLoop($node)) { - return null; + if ($node->cond instanceof BooleanNot && $node->cond->expr instanceof Instanceof_) { + return $this->refactorStmtAndInstanceof($node, $node->cond->expr); } - if ($node->cond instanceof BooleanNot && $node->cond->expr instanceof Instanceof_) { - return $this->processMayDeadInstanceOf($node, $node->cond->expr); + if ($node->cond instanceof BooleanAnd) { + return $this->refactorIfWithBooleanAnd($node); } if ($node->cond instanceof Instanceof_) { - return $this->processMayDeadInstanceOf($node, $node->cond); + return $this->refactorStmtAndInstanceof($node, $node->cond); } - return $node; + return null; } - private function processMayDeadInstanceOf(If_ $if, Instanceof_ $instanceof): ?If_ + /** + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE + */ + private function refactorStmtAndInstanceof(If_ $if, Instanceof_ $instanceof): null|array|int { - if (! $instanceof->class instanceof Name) { + if ($this->isInstanceofTheSameType($instanceof) !== true) { return null; } - $classType = $this->nodeTypeResolver->resolve($instanceof->class); - $exprType = $this->nodeTypeResolver->resolve($instanceof->expr); - - $isSameStaticTypeOrSubtype = $classType->equals($exprType) || $classType->isSuperTypeOf($exprType) - ->yes(); - - if (! $isSameStaticTypeOrSubtype) { + if ($this->shouldSkipFromNotTypedParam($instanceof)) { return null; } - if (! $instanceof->expr instanceof Variable && ! $this->isInPropertyPromotedParams( - $instanceof->expr - ) && $this->isSkippedPropertyFetch($instanceof->expr)) { - return null; + if ($instanceof->expr instanceof Assign) { + $instanceof->expr->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, false); + $assignExpression = new Expression($instanceof->expr); + return array_merge([$assignExpression], $if->stmts); } - if ($if->cond === $instanceof) { - $this->addNodesBeforeNode($if->stmts, $if); + if ($if->cond !== $instanceof) { + return NodeVisitor::REMOVE_NODE; } - $this->removeNode($if); - return $if; + if ($if->stmts === []) { + return NodeVisitor::REMOVE_NODE; + } + + // unwrap stmts + return $if->stmts; + } + + private function shouldSkipFromNotTypedParam(Instanceof_ $instanceof): bool + { + $nativeParamType = $this->nodeTypeResolver->getNativeType($instanceof->expr); + return $nativeParamType instanceof MixedType; } - private function isSkippedPropertyFetch(Expr $expr): bool + private function isPropertyFetch(Expr $expr): bool { - if (! $this->propertyFetchAnalyzer->isPropertyFetch($expr)) { + if ($expr instanceof PropertyFetch) { return true; } - /** @var PropertyFetch|StaticPropertyFetch $propertyFetch */ - $propertyFetch = $expr; - $classLike = $propertyFetch->getAttribute(AttributeKey::CLASS_NODE); + return $expr instanceof StaticPropertyFetch; + } - if (! $classLike instanceof Class_) { - return true; + private function isInstanceofTheSameType(Instanceof_ $instanceof): ?bool + { + if (! $instanceof->class instanceof Name) { + return null; } - /** @var string $propertyName */ - $propertyName = $this->nodeNameResolver->getName($propertyFetch); - $property = $classLike->getProperty($propertyName); - - if (! $property instanceof Property) { - return true; + // handled in another rule + if ($this->isPropertyFetch($instanceof->expr) || $instanceof->expr instanceof CallLike) { + return null; } - $isFilledByConstructParam = $this->propertyFetchAnalyzer->isFilledByConstructParam($property); - if ($this->isInPropertyPromotedParams($propertyFetch)) { - return false; + $classReflection = $this->reflectionResolver->resolveClassReflection($instanceof); + if ($classReflection instanceof ClassReflection && $classReflection->isTrait()) { + return null; } - $isPropertyAssignedInConstuctor = $this->constructorAssignDetector->isPropertyAssigned( - $classLike, - $propertyName - ); + $exprType = $this->nodeTypeResolver->getNativeType($instanceof->expr); + if (! $exprType instanceof ObjectType) { + return null; + } - return $property->type === null && ! $isPropertyAssignedInConstuctor && ! $isFilledByConstructParam; + $className = $instanceof->class->toString(); + return $exprType->isInstanceOf($className) + ->yes(); } - private function isInPropertyPromotedParams(Expr $expr): bool + private function refactorIfWithBooleanAnd(If_ $if): null|If_ { - if (! $expr instanceof PropertyFetch) { - return false; + if (! $if->cond instanceof BooleanAnd) { + return null; } - $classLike = $expr->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return false; + $booleanAnd = $if->cond; + if (! $booleanAnd->left instanceof Instanceof_) { + return null; } - /** @var string $propertyName */ - $propertyName = $this->nodeNameResolver->getName($expr); - $params = $this->promotedPropertyResolver->resolveFromClass($classLike); - - foreach ($params as $param) { - if ($this->nodeNameResolver->isName($param, $propertyName)) { - return true; - } + $instanceof = $booleanAnd->left; + if ($this->isInstanceofTheSameType($instanceof) !== true) { + return null; } - return false; + $if->cond = $booleanAnd->right; + return $if; } } diff --git a/rules/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector.php b/rules/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector.php new file mode 100644 index 00000000000..6edae6c5282 --- /dev/null +++ b/rules/DeadCode/Rector/If_/RemoveTypedPropertyDeadInstanceOfRector.php @@ -0,0 +1,220 @@ +someObject = $someObject; + } + + public function run() + { + if ($this->someObject instanceof SomeObject) { + return true; + } + + return false; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private $someObject; + + public function __construct(SomeObject $someObject) + { + $this->someObject = $someObject; + } + + public function run() + { + return true; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + $hasChanged = false; + $class = $node; + + $this->traverseNodesWithCallable($node->getMethods(), function (Node $node) use ( + &$hasChanged, + $class + ): int|null|array { + // avoid loop ifs + if ($node instanceof While_ || $node instanceof Foreach_ || $node instanceof For_ || $node instanceof Do_) { + return NodeVisitor::STOP_TRAVERSAL; + } + + if (! $node instanceof If_) { + return null; + } + + if (! $this->ifManipulator->isIfWithoutElseAndElseIfs($node)) { + return null; + } + + if ($node->cond instanceof BooleanNot && $node->cond->expr instanceof Instanceof_) { + $result = $this->refactorStmtAndInstanceof($class, $node, $node->cond->expr); + if ($result !== null) { + $hasChanged = true; + return $result; + } + } + + if ($node->cond instanceof Instanceof_) { + $result = $this->refactorStmtAndInstanceof($class, $node, $node->cond); + if ($result !== null) { + $hasChanged = true; + return $result; + } + } + + return null; + }); + + if ($hasChanged) { + return $node; + } + + return null; + } + + /** + * @return null|Stmt[]|int + */ + private function refactorStmtAndInstanceof(Class_ $class, If_ $if, Instanceof_ $instanceof): null|array|int + { + // check local property only + if (! $instanceof->expr instanceof PropertyFetch || ! $this->isName($instanceof->expr->var, 'this')) { + return null; + } + + if (! $instanceof->class instanceof Name) { + return null; + } + + $classType = $this->nodeTypeResolver->getType($instanceof->class); + $exprType = $this->nodeTypeResolver->getType($instanceof->expr); + + $isSameStaticTypeOrSubtype = $classType->equals($exprType) || $classType->isSuperTypeOf($exprType) + ->yes(); + + if (! $isSameStaticTypeOrSubtype) { + return null; + } + + if (! $this->isInPropertyPromotedParams($class, $instanceof->expr) && $this->isSkippedPropertyFetch( + $class, + $instanceof->expr + )) { + return null; + } + + if ($if->cond !== $instanceof) { + return NodeVisitor::REMOVE_NODE; + } + + if ($if->stmts === []) { + return NodeVisitor::REMOVE_NODE; + } + + return $if->stmts; + } + + private function isSkippedPropertyFetch(Class_ $class, PropertyFetch|StaticPropertyFetch $propertyFetch): bool + { + $propertyName = $this->getName($propertyFetch->name); + if ($propertyName === null) { + return true; + } + + if ($this->constructorAssignDetector->isPropertyAssigned($class, $propertyName)) { + return false; + } + + $property = $class->getProperty($propertyName); + if (! $property instanceof Property) { + return true; + } + + return ! $property->type instanceof Node; + } + + private function isInPropertyPromotedParams(Class_ $class, PropertyFetch|StaticPropertyFetch $propertyFetch): bool + { + /** @var string $propertyName */ + $propertyName = $this->getName($propertyFetch); + $params = $this->promotedPropertyResolver->resolveFromClass($class); + + foreach ($params as $param) { + if ($this->isName($param, $propertyName)) { + return true; + } + } + + return false; + } +} diff --git a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php index c512a67d05f..a6eab21fcf7 100644 --- a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php +++ b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php @@ -5,15 +5,24 @@ namespace Rector\DeadCode\Rector\If_; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ArrayDimFetch; +use PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PhpParser\Node\Expr\Empty_; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Php\ReservedKeywordAnalyzer; -use Rector\Core\Rector\AbstractRector; +use PHPStan\Analyser\Scope; use Rector\DeadCode\NodeManipulator\CountManipulator; use Rector\DeadCode\UselessIfCondBeforeForeachDetector; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; +use Rector\NodeManipulator\IfManipulator; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Php\ReservedKeywordAnalyzer; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,10 +32,11 @@ final class RemoveUnusedNonEmptyArrayBeforeForeachRector extends AbstractRector { public function __construct( - private CountManipulator $countManipulator, - private IfManipulator $ifManipulator, - private UselessIfCondBeforeForeachDetector $uselessIfCondBeforeForeachDetector, - private ReservedKeywordAnalyzer $reservedKeywordAnalyzer + private readonly CountManipulator $countManipulator, + private readonly IfManipulator $ifManipulator, + private readonly UselessIfCondBeforeForeachDetector $uselessIfCondBeforeForeachDetector, + private readonly ReservedKeywordAnalyzer $reservedKeywordAnalyzer, + private readonly PropertyFetchAnalyzer $propertyFetchAnalyzer ) { } @@ -50,7 +60,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -73,30 +83,24 @@ public function run() */ public function getNodeTypes(): array { - return [If_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param If_ $node + * @param StmtsAware $node + * @return StmtsAware|null */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): Node|null { - if (! $this->isUselessBeforeForeachCheck($node)) { - return null; + if ($node instanceof If_) { + $scope = ScopeFetcher::fetch($node); + return $this->refactorIf($node, $scope); } - $stmt = $node->stmts[0]; - - $ifComments = $node->getAttribute(AttributeKey::COMMENTS) ?? []; - $stmtComments = $stmt->getAttribute(AttributeKey::COMMENTS) ?? []; - - $comments = array_merge($ifComments, $stmtComments); - $stmt->setAttribute(AttributeKey::COMMENTS, $comments); - - return $stmt; + return $this->refactorStmtsAware($node); } - private function isUselessBeforeForeachCheck(If_ $if): bool + private function isUselessBeforeForeachCheck(If_ $if, Scope $scope): bool { if (! $this->ifManipulator->isIfWithOnly($if, Foreach_::class)) { return false; @@ -106,21 +110,140 @@ private function isUselessBeforeForeachCheck(If_ $if): bool $foreach = $if->stmts[0]; $foreachExpr = $foreach->expr; - if ($foreachExpr instanceof Variable) { - $variableName = $this->nodeNameResolver->getName($foreachExpr); - if (is_string($variableName) && $this->reservedKeywordAnalyzer->isNativeVariable($variableName)) { - return false; - } + if ($this->shouldSkipForeachExpr($foreachExpr, $scope)) { + return false; + } + + $ifCond = $if->cond; + if ($ifCond instanceof BooleanAnd) { + return $this->isUselessBooleanAnd($ifCond, $foreachExpr); + } + + if (($ifCond instanceof Variable || $this->propertyFetchAnalyzer->isPropertyFetch($ifCond)) + && $this->nodeComparator->areNodesEqual($ifCond, $foreachExpr) + ) { + $ifType = $scope->getNativeType($ifCond); + return $ifType->isArray() + ->yes(); } if ($this->uselessIfCondBeforeForeachDetector->isMatchingNotIdenticalEmptyArray($if, $foreachExpr)) { return true; } - if ($this->uselessIfCondBeforeForeachDetector->isMatchingNotEmpty($if, $foreachExpr)) { + if ($this->uselessIfCondBeforeForeachDetector->isMatchingNotEmpty($if, $foreachExpr, $scope)) { return true; } return $this->countManipulator->isCounterHigherThanOne($if->cond, $foreachExpr); } + + private function isUselessBooleanAnd(BooleanAnd $booleanAnd, Expr $foreachExpr): bool + { + if (! $booleanAnd->left instanceof Variable) { + return false; + } + + if (! $this->nodeComparator->areNodesEqual($booleanAnd->left, $foreachExpr)) { + return false; + } + + return $this->countManipulator->isCounterHigherThanOne($booleanAnd->right, $foreachExpr); + } + + /** + * @param StmtsAware $stmtsAware + * @return StmtsAware|null + */ + private function refactorStmtsAware(Node $stmtsAware): ?Node + { + if ($stmtsAware->stmts === null) { + return null; + } + + foreach ($stmtsAware->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } + + $previousStmt = $stmtsAware->stmts[$key - 1] ?? null; + if (! $previousStmt instanceof If_) { + continue; + } + + // not followed by any stmts + $nextStmt = $stmtsAware->stmts[$key + 1] ?? null; + if ($nextStmt instanceof Stmt) { + continue; + } + + if (! $this->uselessIfCondBeforeForeachDetector->isMatchingEmptyAndForeachedExpr( + $previousStmt, + $stmt->expr + )) { + continue; + } + + /** @var Empty_ $empty */ + $empty = $previousStmt->cond; + // scope need to be pulled from Empty_ node to ensure it get correct type + $scope = $empty->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + continue; + } + + $ifType = $scope->getNativeType($empty->expr); + if (! $ifType->isArray()->yes()) { + continue; + } + + unset($stmtsAware->stmts[$key - 1]); + return $stmtsAware; + } + + return null; + } + + private function refactorIf(If_ $if, Scope $scope): ?Foreach_ + { + if (! $this->isUselessBeforeForeachCheck($if, $scope)) { + return null; + } + + /** @var Foreach_ $stmt */ + $stmt = $if->stmts[0]; + + $ifComments = $if->getAttribute(AttributeKey::COMMENTS) ?? []; + $stmtComments = $stmt->getAttribute(AttributeKey::COMMENTS) ?? []; + $comments = array_merge($ifComments, $stmtComments); + + $stmt->setAttribute(AttributeKey::COMMENTS, $comments); + + return $stmt; + } + + private function shouldSkipForeachExpr(Expr $foreachExpr, Scope $scope): bool + { + if ($foreachExpr instanceof ArrayDimFetch && $foreachExpr->dim instanceof Expr) { + $exprType = $this->nodeTypeResolver->getNativeType($foreachExpr->var); + $dimType = $this->nodeTypeResolver->getNativeType($foreachExpr->dim); + if (! $exprType->hasOffsetValueType($dimType)->yes()) { + return true; + } + } + + if ($foreachExpr instanceof Variable) { + $variableName = $this->getName($foreachExpr); + if (is_string($variableName) && $this->reservedKeywordAnalyzer->isNativeVariable($variableName)) { + return true; + } + + $ifType = $scope->getNativeType($foreachExpr); + if (! $ifType->isArray()->yes()) { + return true; + } + } + + return false; + } } diff --git a/rules/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector.php b/rules/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector.php index cac2dbd3082..829bf2405a2 100644 --- a/rules/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector.php +++ b/rules/DeadCode/Rector/If_/SimplifyIfElseWithSameContentRector.php @@ -5,10 +5,13 @@ namespace Rector\DeadCode\Rector\If_; use PhpParser\Node; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; +use Rector\Exception\ShouldNotHappenException; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -17,6 +20,11 @@ */ final class SimplifyIfElseWithSameContentRector extends AbstractRector { + public function __construct( + private readonly BetterStandardPrinter $betterStandardPrinter, + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Remove if/else if they have same content', [ @@ -58,10 +66,11 @@ public function getNodeTypes(): array /** * @param If_ $node + * @return Stmt[]|null|int */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): int|null|array { - if ($node->else === null) { + if (! $node->else instanceof Else_) { return null; } @@ -69,22 +78,20 @@ public function refactor(Node $node): ?Node return null; } - foreach ($node->stmts as $stmt) { - $this->addNodeBeforeNode($stmt, $node); + if ($node->stmts === []) { + return NodeVisitor::REMOVE_NODE; } - $this->removeNode($node); - - return $node; + return $node->stmts; } private function isIfWithConstantReturns(If_ $if): bool { $possibleContents = []; - $possibleContents[] = $this->print($if->stmts); + $possibleContents[] = $this->betterStandardPrinter->print($if->stmts); foreach ($if->elseifs as $elseif) { - $possibleContents[] = $this->print($elseif->stmts); + $possibleContents[] = $this->betterStandardPrinter->print($elseif->stmts); } $else = $if->else; @@ -92,7 +99,7 @@ private function isIfWithConstantReturns(If_ $if): bool throw new ShouldNotHappenException(); } - $possibleContents[] = $this->print($else->stmts); + $possibleContents[] = $this->betterStandardPrinter->print($else->stmts); $uniqueContents = array_unique($possibleContents); diff --git a/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector.php b/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector.php deleted file mode 100644 index 155ff76207c..00000000000 --- a/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector.php +++ /dev/null @@ -1,115 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [If_::class]; - } - - /** - * @param If_ $node - * @return null|Stmt[] - */ - public function refactor(Node $node): ?array - { - if ($this->shouldSkip($node)) { - return null; - } - - $match = $this->ifManipulator->isIfOrIfElseWithFunctionCondition($node, 'function_exists'); - if (! $match) { - return null; - } - - /** @var FuncCall $funcCall */ - $funcCall = $node->cond; - - $functionToExistName = $this->valueResolver->getValue($funcCall->args[0]->value); - if (! is_string($functionToExistName)) { - return null; - } - - if (! $this->functionSupportResolver->isFunctionSupported($functionToExistName)) { - return null; - } - - return $node->stmts; - } - - private function shouldSkip(If_ $if): bool - { - $classLike = $if->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof ClassLike) { - return false; - } - - // skip rector rules, as they decided if function exists in that particular projects - return $this->isObjectType($classLike, new ObjectType('Rector\Core\Contract\Rector\RectorInterface')); - } -} diff --git a/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php b/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php index c484df62d79..1d76110c2c7 100644 --- a/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php +++ b/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php @@ -6,24 +6,25 @@ use PhpParser\Node; use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; use Rector\DeadCode\ConditionEvaluator; use Rector\DeadCode\ConditionResolver; use Rector\DeadCode\Contract\ConditionInterface; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://www.php.net/manual/en/function.version-compare.php - * * @see \Rector\Tests\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector\UnwrapFutureCompatibleIfPhpVersionRectorTest */ final class UnwrapFutureCompatibleIfPhpVersionRector extends AbstractRector { public function __construct( - private ConditionEvaluator $conditionEvaluator, - private ConditionResolver $conditionResolver + private readonly ConditionEvaluator $conditionEvaluator, + private readonly ConditionResolver $conditionResolver ) { } @@ -41,7 +42,7 @@ public function getRuleDefinition(): RuleDefinition return 'is PHP 7.2+'; } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' // current PHP: 7.2 return 'is PHP 7.2+'; @@ -61,11 +62,11 @@ public function getNodeTypes(): array /** * @param If_ $node - * @return Stmt[]|null + * @return Stmt[]|null|NodeVisitor::REMOVE_NODE */ - public function refactor(Node $node): ?array + public function refactor(Node $node): array|int|null { - if ($node->elseifs) { + if ($node->elseifs !== []) { return null; } @@ -80,7 +81,11 @@ public function refactor(Node $node): ?array } // if is skipped - if ($result) { + if ($result === true) { + return $this->refactorIsMatch($node); + } + + if ($result > 0) { return $this->refactorIsMatch($node); } @@ -92,25 +97,39 @@ public function refactor(Node $node): ?array */ private function refactorIsMatch(If_ $if): ?array { - if ($if->elseifs) { + if ($if->elseifs !== []) { return null; } - return $if->stmts; + return $this->keepIfLevelComments($if, $if->stmts); } /** - * @return Stmt[]|null + * @return Stmt[]|NodeVisitor::REMOVE_NODE */ - private function refactorIsNotMatch(If_ $if): ?array + private function refactorIsNotMatch(If_ $if): array|int { // no else → just remove the node - if ($if->else === null) { - $this->removeNode($if); - return null; + if (! $if->else instanceof Else_) { + return NodeVisitor::REMOVE_NODE; + } + + return $this->keepIfLevelComments($if, $if->else->stmts); + } + + /** + * @param Stmt[] $stmts + * @return Stmt[] + */ + private function keepIfLevelComments(If_ $if, array $stmts): array + { + if ($stmts !== []) { + $stmts[0]->setAttribute( + AttributeKey::COMMENTS, + array_merge($if->getComments(), $stmts[0]->getComments()) + ); } - // else is always used - return $if->else->stmts; + return $stmts; } } diff --git a/rules/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector.php b/rules/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector.php deleted file mode 100644 index f360ff98a21..00000000000 --- a/rules/DeadCode/Rector/MethodCall/RemoveEmptyMethodCallRector.php +++ /dev/null @@ -1,191 +0,0 @@ -callThis(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function callThis() - { - } -} - -$some = new SomeClass(); -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - $scope = $this->getScope($node); - - if (! $scope instanceof Scope) { - return null; - } - - $type = $scope->getType($node->var); - if (! $type instanceof TypeWithClassName) { - return null; - } - - $classLike = $this->reflectionAstResolver->resolveClassFromObjectType($type); - if ($classLike === null) { - return null; - } - - if ($this->shouldSkipClassMethod($classLike, $node, $type)) { - return null; - } - - // if->cond cannot removed, it has to be replaced with false, see https://3v4l.org/U9S9i - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof If_ && $parent->cond === $node) { - return $this->nodeFactory->createFalse(); - } - - if ($parent instanceof Assign) { - return $this->nodeFactory->createFalse(); - } - - if ($parent instanceof ArrowFunction && $this->nodeComparator->areNodesEqual($parent->expr, $node)) { - return $this->processArrowFunction($parent, $node); - } - - $this->removeNode($node); - - return $node; - } - - private function getScope(MethodCall $methodCall): ?Scope - { - if ($this->callAnalyzer->isObjectCall($methodCall->var)) { - return null; - } - - $parentArg = $this->betterNodeFinder->findParentType($methodCall, Arg::class); - if ($parentArg instanceof Arg) { - return null; - } - - $scope = $methodCall->var->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - return $scope; - } - - private function shouldSkipClassMethod( - Class_ | Trait_ | Interface_ $classLike, - MethodCall $methodCall, - TypeWithClassName $typeWithClassName - ): bool { - if (! $classLike instanceof Class_) { - return true; - } - - $methodName = $this->getName($methodCall->name); - if ($methodName === null) { - return true; - } - - $classMethod = $classLike->getMethod($methodName); - if (! $classMethod instanceof ClassMethod) { - return true; - } - - if ($classMethod->isAbstract()) { - return true; - } - - if ((array) $classMethod->stmts !== []) { - return true; - } - - $class = $methodCall->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { - return false; - } - if (! $typeWithClassName instanceof ThisType) { - return false; - } - if ($class->isFinal()) { - return false; - } - return ! $classMethod->isPrivate(); - } - - private function processArrowFunction(ArrowFunction $arrowFunction, MethodCall $methodCall): MethodCall | ConstFetch - { - $parentOfParent = $arrowFunction->getAttribute(AttributeKey::PARENT_NODE); - if ($parentOfParent instanceof Expression) { - $this->removeNode($arrowFunction); - return $methodCall; - } - - return $this->nodeFactory->createFalse(); - } -} diff --git a/rules/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector.php b/rules/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector.php new file mode 100644 index 00000000000..a47ec0f4757 --- /dev/null +++ b/rules/DeadCode/Rector/MethodCall/RemoveNullArgOnNullDefaultParamRector.php @@ -0,0 +1,136 @@ +execute(null); + } +} + +class ExternalClass +{ + public function execute(?SomeClass $someClass = null) + { + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' + +class SomeClass +{ + public function call(ExternalClass $externalClass) + { + $externalClass->execute(); + } +} + +class ExternalClass +{ + public function execute(?SomeClass $someClass = null) + { + } +} +CODE_SAMPLE + ), + + ]); + } + + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class, New_::class, FuncCall::class]; + } + + /** + * @param MethodCall|StaticCall|New_|FuncCall $node + */ + public function refactor(Node $node): StaticCall|MethodCall|New_|FuncCall|null + { + if ($node->isFirstClassCallable()) { + return null; + } + + if ($node->getArgs() === []) { + return null; + } + + $nullPositions = $this->callLikeParamDefaultResolver->resolveNullPositions($node); + if ($nullPositions === []) { + return null; + } + + $hasChanged = false; + $args = $node->getArgs(); + $lastArgPosition = count($args) - 1; + for ($position = $lastArgPosition; $position >= 0; --$position) { + if (! isset($args[$position])) { + continue; + } + + $arg = $args[$position]; + if ($arg->unpack) { + break; + } + + // stop when found named arg and position not match + if ($arg->name instanceof Identifier && + $position !== $this->callLikeParamDefaultResolver->resolvePositionParameterByName( + $node, + $arg->name->toString() + )) { + break; + } + + if (! $this->valueResolver->isNull($arg->value)) { + break; + } + + if (! in_array($position, $nullPositions, true)) { + break; + } + + unset($node->args[$position]); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } +} diff --git a/rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php b/rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php index b70490b33d1..bee534d6154 100644 --- a/rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php +++ b/rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php @@ -4,47 +4,49 @@ namespace Rector\DeadCode\Rector\Node; -use PhpParser\Comment; use PhpParser\Node; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\AssignRef; +use PhpParser\Node\Expr\CallLike; +use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Echo_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; +use PhpParser\Node\Stmt\InlineHTML; use PhpParser\Node\Stmt\Nop; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\Stmt\Static_; use PhpParser\Node\Stmt\Switch_; -use PhpParser\Node\Stmt\Throw_; use PhpParser\Node\Stmt\While_; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; -use Rector\Comments\CommentRemover; -use Rector\Core\Rector\AbstractRector; -use Symplify\PackageBuilder\Php\TypeChecker; +use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\NodeManipulator\StmtsManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\DeadCode\Rector\Node\RemoveNonExistingVarAnnotationRector\RemoveNonExistingVarAnnotationRectorTest - * - * @changelog https://github.com/phpstan/phpstan/commit/d17e459fd9b45129c5deafe12bca56f30ea5ee99#diff-9f3541876405623b0d18631259763dc1 */ final class RemoveNonExistingVarAnnotationRector extends AbstractRector { /** - * @var array> + * @var array> */ - private const NODES_TO_MATCH = [ - Assign::class, - AssignRef::class, + private const array NODE_TYPES = [ Foreach_::class, Static_::class, Echo_::class, Return_::class, Expression::class, - Throw_::class, If_::class, While_::class, Switch_::class, @@ -52,8 +54,11 @@ final class RemoveNonExistingVarAnnotationRector extends AbstractRector ]; public function __construct( - private TypeChecker $typeChecker, - private CommentRemover $commentRemover + private readonly StmtsManipulator $stmtsManipulator, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly ValueResolver $valueResolver, + private readonly BetterNodeFinder $betterNodeFinder, ) { } @@ -93,50 +98,131 @@ public function get() */ public function getNodeTypes(): array { - return [Node::class]; + return NodeGroup::STMTS_AWARE; } + /** + * @param StmtsAware $node + */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + if ($node->stmts === null) { return null; } - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $varTagValueNode = $phpDocInfo->getVarTagValueNode(); - if (! $varTagValueNode instanceof VarTagValueNode) { - return null; - } + $hasChanged = false; + $extractValues = []; - $variableName = ltrim($varTagValueNode->variableName, '$'); - if ($this->hasVariableName($node, $variableName)) { - return null; + foreach ($node->stmts as $key => $stmt) { + $hasChangedStmt = false; + if ($stmt instanceof Expression && $stmt->expr instanceof FuncCall && $this->isName( + $stmt->expr, + 'extract' + ) && ! $stmt->expr->isFirstClassCallable()) { + $appendExtractValues = $this->valueResolver->getValue($stmt->expr->getArgs()[0]->value); + if (! is_array($appendExtractValues)) { + // nothing can do as value is dynamic + break; + } + + $extractValues = [...$extractValues, ...array_keys($appendExtractValues)]; + continue; + } + + if ($stmt instanceof Static_) { + foreach ($stmt->vars as $staticVar) { + $staticVarName = $this->getName($staticVar->var); + + if ($staticVarName === null) { + continue; + } + + $extractValues[] = $staticVarName; + } + } + + if ($this->shouldSkip($node, $key, $stmt, $extractValues)) { + continue; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($stmt); + + $varTagValueNodes = $phpDocInfo->getPhpDocNode() + ->getVarTagValues(); + + foreach ($varTagValueNodes as $varTagValueNode) { + if ($this->isObjectShapePseudoType($varTagValueNode)) { + continue; + } + + $variableName = ltrim($varTagValueNode->variableName, '$'); + + if ($variableName === '' && $this->isAllowedEmptyVariableName($stmt)) { + continue; + } + + if ($this->hasVariableName($stmt, $variableName)) { + continue; + } + + $comments = $node->getComments(); + if (isset($comments[1])) { + // skip edge case with double comment, as impossible to resolve by PHPStan doc parser + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt($node, $key + 1, $variableName)) { + continue; + } + + if ($variableName === '') { + $phpDocInfo->removeByType(VarTagValueNode::class); + } else { + $phpDocInfo->removeByType(VarTagValueNode::class, $variableName); + } + + $hasChangedStmt = true; + } + + if ($hasChangedStmt) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); + $hasChanged = true; + } } - $comments = $node->getComments(); - if (isset($comments[1]) && $comments[1] instanceof Comment) { - $this->commentRemover->rollbackComments($node, $comments[1]); + if ($hasChanged) { return $node; } - $phpDocInfo->removeByType(VarTagValueNode::class); - return $node; + return null; } - private function shouldSkip(Node $node): bool + /** + * @param StmtsAware $stmtsAware + * @param string[] $extractValues + */ + private function shouldSkip(Node $stmtsAware, int $key, Stmt $stmt, array $extractValues): bool { - if (! $node instanceof Nop) { - return ! $this->typeChecker->isInstanceOf($node, self::NODES_TO_MATCH); + if (! in_array($stmt::class, self::NODE_TYPES, true)) { + return true; } - if (count($node->getComments()) <= 1) { - return ! $this->typeChecker->isInstanceOf($node, self::NODES_TO_MATCH); + + if (count($stmt->getComments()) !== 1) { + return true; + } + + foreach ($extractValues as $extractValue) { + if ($this->stmtsManipulator->isVariableUsedInNextStmt($stmtsAware, $key + 1, $extractValue)) { + return true; + } } - return true; + + return isset($stmtsAware->stmts[$key + 1]) && $stmtsAware->stmts[$key + 1] instanceof InlineHTML; } - private function hasVariableName(Node $node, string $variableName): bool + private function hasVariableName(Stmt $stmt, string $variableName): bool { - return (bool) $this->betterNodeFinder->findFirst($node, function (Node $node) use ($variableName): bool { + return (bool) $this->betterNodeFinder->findFirst($stmt, function (Node $node) use ($variableName): bool { if (! $node instanceof Variable) { return false; } @@ -144,4 +230,34 @@ private function hasVariableName(Node $node, string $variableName): bool return $this->isName($node, $variableName); }); } + + /** + * This is a hack, + * that waits on phpdoc-parser to get merged - https://github.com/phpstan/phpdoc-parser/pull/145 + */ + private function isObjectShapePseudoType(VarTagValueNode $varTagValueNode): bool + { + if (! $varTagValueNode->type instanceof IdentifierTypeNode) { + return false; + } + + if ($varTagValueNode->type->name !== 'object') { + return false; + } + + if (! str_starts_with($varTagValueNode->description, '{')) { + return false; + } + + return str_contains($varTagValueNode->description, '}'); + } + + private function isAllowedEmptyVariableName(Stmt $stmt): bool + { + if ($stmt instanceof Return_ && $stmt->expr instanceof CallLike && ! $stmt->expr instanceof New_) { + return true; + } + + return $stmt instanceof Expression && $stmt->expr instanceof Assign && $stmt->expr->var instanceof Variable; + } } diff --git a/rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php b/rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php index 5df2dd3e9f4..f68584a58da 100644 --- a/rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php +++ b/rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php @@ -11,18 +11,21 @@ use PhpParser\Node\Expr\AssignOp\Minus as AssignMinus; use PhpParser\Node\Expr\AssignOp\Mul as AssignMul; use PhpParser\Node\Expr\AssignOp\Plus as AssignPlus; +use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\Div; use PhpParser\Node\Expr\BinaryOp\Minus; use PhpParser\Node\Expr\BinaryOp\Mul; use PhpParser\Node\Expr\BinaryOp\Plus; +use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\UnaryMinus; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Scalar\Int_; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\Application\File; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/I0BGs - * * @see \Rector\Tests\DeadCode\Rector\Plus\RemoveDeadZeroAndOneOperationRector\RemoveDeadZeroAndOneOperationRectorTest */ final class RemoveDeadZeroAndOneOperationRector extends AbstractRector @@ -81,9 +84,6 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { -// $changedNode = null; -// $previousNode = $node; - if ($node instanceof AssignOp) { return $this->processAssignOp($node); } @@ -92,26 +92,27 @@ public function refactor(Node $node): ?Node return $this->processBinaryOp($node); } - private function processAssignOp(Node $node): ?Expr + private function processAssignOp(AssignOp $assignOp): ?Expr { // +=, -= - if ($node instanceof AssignPlus || $node instanceof AssignMinus) { - if (! $this->valueResolver->isValue($node->expr, 0)) { + if ($assignOp instanceof AssignPlus || $assignOp instanceof AssignMinus) { + if (! $this->isLiteralZero($assignOp->expr)) { return null; } - if ($this->nodeTypeResolver->isNumberType($node->var)) { - return $node->var; + if ($this->nodeTypeResolver->isNumberType($assignOp->var)) { + return $assignOp->var; } } // *, / - if ($node instanceof AssignMul || $node instanceof AssignDiv) { - if (! $this->valueResolver->isValue($node->expr, 1)) { + if ($assignOp instanceof AssignMul || $assignOp instanceof AssignDiv) { + if (! $this->isLiteralOne($assignOp->expr)) { return null; } - if ($this->nodeTypeResolver->isNumberType($node->var)) { - return $node->var; + + if ($this->nodeTypeResolver->isNumberType($assignOp->var)) { + return $assignOp->var; } } @@ -128,53 +129,112 @@ private function processBinaryOp(Node $node): ?Expr if ($node instanceof Mul) { return $this->processBinaryMulAndDiv($node); } + if ($node instanceof Div) { return $this->processBinaryMulAndDiv($node); } + return null; } - private function processBinaryPlusAndMinus( - \PhpParser\Node\Expr\BinaryOp\Plus | \PhpParser\Node\Expr\BinaryOp\Minus $binaryOp - ): ?Expr { - if ($this->valueResolver->isValue($binaryOp->left, 0) && $this->nodeTypeResolver->isNumberType( - $binaryOp->right - )) { + private function areNumberType(BinaryOp $binaryOp): bool + { + if (! $this->nodeTypeResolver->isNumberType($binaryOp->left)) { + return false; + } + + return $this->nodeTypeResolver->isNumberType($binaryOp->right); + } + + private function processBinaryPlusAndMinus(Plus | Minus $binaryOp): ?Expr + { + if (! $this->areNumberType($binaryOp)) { + return null; + } + + if ($this->isLiteralZero($binaryOp->left) && $this->nodeTypeResolver->isNumberType($binaryOp->right)) { if ($binaryOp instanceof Minus) { return new UnaryMinus($binaryOp->right); } + return $binaryOp->right; } - if (! $this->valueResolver->isValue($binaryOp->right, 0)) { + if (! $this->isLiteralZero($binaryOp->right)) { return null; } - if (! $this->nodeTypeResolver->isNumberType($binaryOp->left)) { - return null; + return $binaryOp->left; + } + + private function isMulParenthesized(File $file, Mul $mul): bool + { + if (! $mul->right instanceof BinaryOp) { + return false; } - return $binaryOp->left; + $oldTokens = $file->getOldTokens(); + $endTokenPost = $mul->getEndTokenPos(); + + if (isset($oldTokens[$endTokenPost]) && (string) $oldTokens[$endTokenPost] === ')') { + $startTokenPos = $mul->right->getStartTokenPos(); + $previousEndTokenPost = $mul->left->getEndTokenPos(); + + while ($startTokenPos > $previousEndTokenPost) { + --$startTokenPos; + + if (! isset($oldTokens[$startTokenPos])) { + return false; + } + + // handle space before open parentheses + if (trim((string) $oldTokens[$startTokenPos]) === '') { + continue; + } + + return (string) $oldTokens[$startTokenPos] === '('; + } + + return false; + } + + return false; } - private function processBinaryMulAndDiv( - \PhpParser\Node\Expr\BinaryOp\Mul | \PhpParser\Node\Expr\BinaryOp\Div $binaryOp - ): ?Expr { - if ($binaryOp instanceof Mul && $this->valueResolver->isValue( - $binaryOp->left, - 1 - ) && $this->nodeTypeResolver->isNumberType($binaryOp->right)) { - return $binaryOp->right; + private function processBinaryMulAndDiv(Mul | Div $binaryOp): ?Expr + { + if ($binaryOp->left instanceof ClassConstFetch || $binaryOp->right instanceof ClassConstFetch) { + return null; } - if (! $this->valueResolver->isValue($binaryOp->right, 1)) { + if (! $this->areNumberType($binaryOp)) { return null; } - if (! $this->nodeTypeResolver->isNumberType($binaryOp->left)) { + if ($binaryOp instanceof Mul && $this->isLiteralOne($binaryOp->left) && $this->nodeTypeResolver->isNumberType( + $binaryOp->right + )) { + if ($this->isMulParenthesized($this->getFile(), $binaryOp)) { + $binaryOp->right->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + } + + return $binaryOp->right; + } + + if (! $this->isLiteralOne($binaryOp->right)) { return null; } return $binaryOp->left; } + + private function isLiteralOne(Expr $expr): bool + { + return $expr instanceof Int_ && $expr->value === 1; + } + + private function isLiteralZero(Expr $expr): bool + { + return $expr instanceof Int_ && $expr->value === 0; + } } diff --git a/rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php b/rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php index 21a41272806..68adc8295cb 100644 --- a/rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php +++ b/rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php @@ -5,14 +5,20 @@ namespace Rector\DeadCode\Rector\Property; use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\Interface_; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; -use PhpParser\Node\Stmt\Trait_; -use Rector\Core\NodeManipulator\PropertyManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\Removing\NodeManipulator\ComplexNodeRemover; +use PhpParser\Node\Stmt\Return_; +use PhpParser\Node\Stmt\TraitUse; +use PhpParser\NodeVisitor; +use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\DeadCode\NodeAnalyzer\PropertyWriteonlyAnalyzer; +use Rector\PhpParser\NodeFinder\PropertyFetchFinder; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,8 +28,9 @@ final class RemoveUnusedPrivatePropertyRector extends AbstractRector { public function __construct( - private PropertyManipulator $propertyManipulator, - private ComplexNodeRemover $complexNodeRemover, + private readonly PropertyFetchFinder $propertyFetchFinder, + private readonly PropertyWriteonlyAnalyzer $propertyWriteonlyAnalyzer, + private readonly PhpDocInfoFactory $phpDocInfoFactory, ) { } @@ -52,29 +59,55 @@ class SomeClass */ public function getNodeTypes(): array { - return [Property::class]; + return [Class_::class]; } /** - * @param Property $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkipProperty($node)) { + if ($this->shouldSkipClass($node)) { return null; } - if ($this->propertyManipulator->isPropertyUsedInReadContext($node)) { - return null; + $hasChanged = false; + + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Property) { + continue; + } + + if ($this->shouldSkipProperty($stmt)) { + continue; + } + + if (! $this->shouldRemoveProperty($node, $stmt)) { + continue; + } + + // remove property + unset($node->stmts[$key]); + $propertyName = $this->getName($stmt); + $this->removePropertyAssigns($node, $propertyName); + + $hasChanged = true; } - $this->complexNodeRemover->removePropertyAndUsages($node); + if ($hasChanged) { + return $node; + } - return $node; + return null; } private function shouldSkipProperty(Property $property): bool { + // has some attribute logic + if ($property->attrGroups !== []) { + return true; + } + if (count($property->props) !== 1) { return true; } @@ -83,8 +116,77 @@ private function shouldSkipProperty(Property $property): bool return true; } - /** @var Class_|Interface_|Trait_|null $classLike */ - $classLike = $property->getAttribute(AttributeKey::CLASS_NODE); - return ! $classLike instanceof Class_; + // has some possible magic + if ($property->isStatic()) { + return true; + } + + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNode($property); + if (! $propertyPhpDocInfo instanceof PhpDocInfo) { + return false; + } + + // skip as might contain important metadata + return $propertyPhpDocInfo->hasByType(DoctrineAnnotationTagValueNode::class); + } + + private function shouldRemoveProperty(Class_ $class, Property $property): bool + { + $propertyName = $this->getName($property); + + $propertyFetches = $this->propertyFetchFinder->findLocalPropertyFetchesByName($class, $propertyName); + if ($propertyFetches === []) { + return true; + } + + return $this->propertyWriteonlyAnalyzer->arePropertyFetchesExclusivelyBeingAssignedTo($propertyFetches); + } + + private function shouldSkipClass(Class_ $class): bool + { + foreach ($class->stmts as $stmt) { + // unclear what property can be used there + if ($stmt instanceof TraitUse) { + return true; + } + } + + return $this->propertyWriteonlyAnalyzer->hasClassDynamicPropertyNames($class); + } + + private function removePropertyAssigns(Class_ $class, string $propertyName): void + { + $this->traverseNodesWithCallable($class, function (Node $node) use ( + $class, + $propertyName + ): null|int|Return_|Arg { + if (! $node instanceof Expression && ! $node instanceof Return_) { + if ($node instanceof Arg && $node->value instanceof Assign) { + $assign = $node->value; + if ($this->propertyFetchFinder->isLocalPropertyFetchByName($assign->var, $class, $propertyName)) { + $node->value = $assign->expr; + return $node; + } + } + + return null; + } + + if (! $node->expr instanceof Assign) { + return null; + } + + $assign = $node->expr; + if (! $this->propertyFetchFinder->isLocalPropertyFetchByName($assign->var, $class, $propertyName)) { + return null; + } + + if ($node instanceof Expression) { + return NodeVisitor::REMOVE_NODE; + } + + $node->expr = $node->expr->expr; + return $node; + }); } } diff --git a/rules/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector.php b/rules/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector.php new file mode 100644 index 00000000000..125d1574fa3 --- /dev/null +++ b/rules/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector.php @@ -0,0 +1,115 @@ +name = $name; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private readonly string $name; + + public function __construct(string $name) + { + $this->name = $name; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class, Property::class, Param::class]; + } + + /** + * @param Class_|Property|Param $node + */ + public function refactor(Node $node): ?Node + { + // for param, only on property promotion + if ($node instanceof Param && ! $node->isPromoted()) { + return null; + } + + if (! $this->visibilityManipulator->isReadonly($node)) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $readonlyDoc = $phpDocInfo->getByName('readonly'); + if (! $readonlyDoc instanceof PhpDocTagNode) { + return null; + } + + if (! $readonlyDoc->value instanceof GenericTagValueNode) { + return null; + } + + if ($readonlyDoc->value->value !== '') { + return null; + } + + $phpDocInfo->removeByName('readonly'); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::READONLY_PROPERTY; + } +} diff --git a/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php b/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php index bce2bacab7d..1c07d2c5419 100644 --- a/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php +++ b/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php @@ -5,10 +5,12 @@ namespace Rector\DeadCode\Rector\Property; use PhpParser\Node; +use PhpParser\Node\Stmt\ClassConst; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; -use Rector\Core\Rector\AbstractRector; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,13 +20,14 @@ final class RemoveUselessVarTagRector extends AbstractRector { public function __construct( - private VarTagRemover $varTagRemover + private readonly VarTagRemover $varTagRemover, + private readonly PhpDocInfoFactory $phpDocInfoFactory, ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Remove unused @var annotation for properties', [ + return new RuleDefinition('Remove unused `@var` annotation for properties and class constants', [ new CodeSample( <<<'CODE_SAMPLE' final class SomeClass @@ -52,22 +55,21 @@ final class SomeClass */ public function getNodeTypes(): array { - return [Property::class]; + return [Property::class, ClassConst::class, Expression::class]; } /** - * @param Property $node + * @param Property|ClassConst|Expression $node */ public function refactor(Node $node): ?Node { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $this->varTagRemover->removeVarTagIfUseless($phpDocInfo, $node); - if ($phpDocInfo->hasChanged()) { - $node->setAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED, true); - return $node; + $hasChanged = $this->varTagRemover->removeVarTagIfUseless($phpDocInfo, $node); + if (! $hasChanged) { + return null; } - return null; + return $node; } } diff --git a/rules/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector.php b/rules/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector.php index add5654db64..6913f1d161b 100644 --- a/rules/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector.php +++ b/rules/DeadCode/Rector/PropertyProperty/RemoveNullPropertyInitializationRector.php @@ -7,11 +7,8 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\ConstFetch; -use PhpParser\Node\NullableType; use PhpParser\Node\Stmt\Property; -use PhpParser\Node\Stmt\PropertyProperty; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use function strtolower; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -50,41 +47,41 @@ class SunshineCommand extends ParentClassWithNewConstructor */ public function getNodeTypes(): array { - return [PropertyProperty::class]; + return [Property::class]; } /** - * @param PropertyProperty $node + * @param Property $node */ public function refactor(Node $node): ?Node { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - - // skip typed properties - if ($parent instanceof Property && $parent->type) { + if ($node->type instanceof Node) { return null; } - $defaultValueNode = $node->default; - if (! $defaultValueNode instanceof Expr) { - return null; - } + $hasChanged = false; + foreach ($node->props as $prop) { + $defaultValueNode = $prop->default; + if (! $defaultValueNode instanceof Expr) { + continue; + } - if (! $defaultValueNode instanceof ConstFetch) { - return null; - } + if (! $defaultValueNode instanceof ConstFetch) { + continue; + } - if (strtolower((string) $defaultValueNode->name) !== 'null') { - return null; - } - $nodeNode = $node->getAttribute(AttributeKey::PREVIOUS_NODE); + if (strtolower((string) $defaultValueNode->name) !== 'null') { + continue; + } - if ($nodeNode instanceof NullableType) { - return null; + $prop->default = null; + $hasChanged = true; } - $node->default = null; + if ($hasChanged) { + return $node; + } - return $node; + return null; } } diff --git a/rules/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector.php b/rules/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector.php index e25d7464898..c43c9a3f510 100644 --- a/rules/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector.php +++ b/rules/DeadCode/Rector/Return_/RemoveDeadConditionAboveReturnRector.php @@ -5,11 +5,13 @@ namespace Rector\DeadCode\Rector\Return_; use PhpParser\Node; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\DeadCode\SideEffect\SideEffectNodeDetector; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,6 +20,11 @@ */ final class RemoveDeadConditionAboveReturnRector extends AbstractRector { + public function __construct( + private readonly SideEffectNodeDetector $sideEffectNodeDetector, + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Remove dead condition above return', [ @@ -54,47 +61,66 @@ public function go() */ public function getNodeTypes(): array { - return [Return_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Return_ $node + * @param StmtsAware $node + * @return StmtsAware */ public function refactor(Node $node): ?Node { - $previousNode = $node->getAttribute(AttributeKey::PREVIOUS_NODE); - if (! $previousNode instanceof If_) { - return null; - } + foreach ((array) $node->stmts as $key => $stmt) { + if (! $stmt instanceof Return_) { + continue; + } - if ($previousNode->elseifs !== []) { - return null; - } + $previousNode = $node->stmts[$key - 1] ?? null; + if (! $this->isBareIf($previousNode)) { + continue; + } - if ($previousNode->else instanceof Else_) { - return null; - } + /** @var If_ $previousNode */ + if ($this->sideEffectNodeDetector->detect($previousNode->cond)) { + continue; + } + + $countStmt = count($previousNode->stmts); + if ($countStmt === 0) { + unset($node->stmts[$key - 1]); + return $node; + } - $countStmt = count($previousNode->stmts); - if ($countStmt === 0) { - $this->removeNode($previousNode); + if ($countStmt > 1) { + return null; + } + + $previousFirstStmt = $previousNode->stmts[0]; + if (! $previousFirstStmt instanceof Return_) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($previousFirstStmt, $stmt)) { + return null; + } + + unset($node->stmts[$key - 1]); return $node; } - if ($countStmt > 1) { - return null; - } + return null; + } - $stmt = $previousNode->stmts[0]; - if (! $stmt instanceof Return_) { - return null; + private function isBareIf(?Stmt $stmt): bool + { + if (! $stmt instanceof If_) { + return false; } - if (! $this->nodeComparator->areNodesEqual($stmt, $node)) { - return null; + if ($stmt->elseifs !== []) { + return false; } - $this->removeNode($previousNode); - return $node; + return ! $stmt->else instanceof Else_; } } diff --git a/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php b/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php index 25ca5d6d73b..c989a42642a 100644 --- a/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php +++ b/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php @@ -5,16 +5,18 @@ namespace Rector\DeadCode\Rector\StaticCall; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; -use Rector\Core\NodeAnalyzer\ClassAnalyzer; -use Rector\Core\NodeManipulator\ClassMethodManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PHPStan\Reflection\ReflectionProvider; +use Rector\Enum\ObjectReference; +use Rector\NodeAnalyzer\ClassAnalyzer; +use Rector\NodeManipulator\ClassMethodManipulator; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -24,9 +26,9 @@ final class RemoveParentCallWithoutParentRector extends AbstractRector { public function __construct( - private ClassMethodManipulator $classMethodManipulator, - private ParentClassScopeResolver $parentClassScopeResolver, - private ClassAnalyzer $classAnalyzer + private readonly ClassMethodManipulator $classMethodManipulator, + private readonly ClassAnalyzer $classAnalyzer, + private readonly ReflectionProvider $reflectionProvider ) { } @@ -64,55 +66,100 @@ public function __construct() */ public function getNodeTypes(): array { - return [StaticCall::class]; + return [Class_::class]; } /** - * @param StaticCall $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { + if ($this->shouldSkipClass($node)) { return null; } - if (! $node->class instanceof Name) { - return null; + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->stmts === null) { + continue; + } + + foreach ($classMethod->stmts as $key => $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + if ($stmt->expr instanceof StaticCall && $this->isParentStaticCall($stmt->expr)) { + if ($this->doesCalledMethodExistInParent($stmt->expr, $node)) { + continue; + } + + unset($classMethod->stmts[$key]); + $hasChanged = true; + } + + if ($stmt->expr instanceof Assign) { + $assign = $stmt->expr; + if ($assign->expr instanceof StaticCall && $this->isParentStaticCall($assign->expr)) { + $staticCall = $assign->expr; + + // is valid call + if ($this->doesCalledMethodExistInParent($staticCall, $node)) { + continue; + } + + $assign->expr = $this->nodeFactory->createNull(); + $hasChanged = true; + } + } + } } - if (! $this->isName($node->class, 'parent')) { - return null; + if ($hasChanged) { + return $node; } - $parentClassName = $this->parentClassScopeResolver->resolveParentClassName($node); - if ($parentClassName === null) { - $this->removeNode($node); - return null; - } + return null; + } - $classMethod = $node->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return null; + private function isParentStaticCall(Expr $expr): bool + { + if (! $expr instanceof StaticCall) { + return false; } - if ($this->classAnalyzer->isAnonymousClass($classLike)) { - // currently the classMethodManipulator isn't able to find usages of anonymous classes - return null; + if ($expr->name instanceof Expr) { + return false; } - $calledMethodName = $this->getName($node->name); - if ($this->classMethodManipulator->hasParentMethodOrInterfaceMethod($classMethod, $calledMethodName)) { - return null; + return $this->isName($expr->class, ObjectReference::PARENT); + } + + private function shouldSkipClass(Class_ $class): bool + { + // skip cases when parent class reflection is not found + if ($class->extends instanceof FullyQualified && ! $this->reflectionProvider->hasClass( + $class->extends->toString() + )) { + return true; } - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Expression) { - return null; + // currently the classMethodManipulator isn't able to find usages of anonymous classes + return $this->classAnalyzer->isAnonymousClass($class); + } + + private function doesCalledMethodExistInParent(StaticCall $staticCall, Class_ $class): bool + { + if (! $class->extends instanceof Name) { + return false; } - $this->removeNode($node); + $calledMethodName = $this->getName($staticCall->name); + if (! is_string($calledMethodName)) { + return false; + } - return null; + return $this->classMethodManipulator->hasParentMethodOrInterfaceMethod($class, $calledMethodName); } } diff --git a/rules/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector.php b/rules/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector.php new file mode 100644 index 00000000000..07de40ec681 --- /dev/null +++ b/rules/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector.php @@ -0,0 +1,147 @@ +> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof If_) { + continue; + } + + $soleIfReturn = $this->matchSoleIfReturn($stmt); + if (! $soleIfReturn instanceof Return_) { + continue; + } + + if (! $stmt->cond instanceof Identical && ! $stmt->cond instanceof Equal) { + continue; + } + + $identicalOrEqual = $stmt->cond; + + if ($this->sideEffectNodeDetector->detect($identicalOrEqual->right)) { + continue; + } + + if (! $this->nodeComparator->areNodesEqual($identicalOrEqual->right, $soleIfReturn->expr)) { + continue; + } + + $comparedVariable = $identicalOrEqual->left; + + // next if must be return of the same var + $nextStmt = $node->stmts[$key + 1] ?? null; + if (! $nextStmt instanceof Return_) { + continue; + } + + if (! $nextStmt->expr instanceof Expr) { + continue; + } + + if (! $this->nodeComparator->areNodesEqual($nextStmt->expr, $comparedVariable)) { + continue; + } + + // remove next if + unset($node->stmts[$key + 1]); + + // replace if with return + $node->stmts[$key] = $nextStmt; + + return $node; + } + + return null; + } + + private function matchSoleIfReturn(If_ $if): ?Return_ + { + if (count($if->stmts) !== 1) { + return null; + } + + $soleIfStmt = $if->stmts[0]; + if (! $soleIfStmt instanceof Return_) { + return null; + } + + return $soleIfStmt; + } +} diff --git a/rules/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector.php b/rules/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector.php new file mode 100644 index 00000000000..bfb0a1485b8 --- /dev/null +++ b/rules/DeadCode/Rector/Stmt/RemoveNextSameValueConditionRector.php @@ -0,0 +1,163 @@ +> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof If_) { + continue; + } + + // only when no elseif/else in current if + if ($stmt->elseifs !== []) { + continue; + } + + if ($stmt->else instanceof Else_) { + continue; + } + + // first condition must be without side effect + if ($this->sideEffectNodeDetector->detect($stmt->cond)) { + continue; + } + + if ($this->isCondVariableUsedInIfBody($stmt)) { + continue; + } + + $nextStmt = $node->stmts[$key + 1] ?? null; + if (! $nextStmt instanceof If_) { + continue; + } + + if (! $this->nodeComparator->areNodesEqual($stmt->cond, $nextStmt->cond)) { + continue; + } + + // only when no elseif/else in next stmt + if ($nextStmt->elseifs !== []) { + continue; + } + + if ($nextStmt->else instanceof Else_) { + continue; + } + + $stmt->setAttribute(AttributeKey::COMMENTS, array_merge( + $stmt->getAttribute(AttributeKey::COMMENTS) ?? [], + $nextStmt->getAttribute(AttributeKey::COMMENTS) ?? [] + )); + $stmt->stmts = array_merge($stmt->stmts, $nextStmt->stmts); + + // remove next node + unset($node->stmts[$key + 1]); + + return $node; + } + + return null; + } + + private function isCondVariableUsedInIfBody(If_ $if): bool + { + $condVariables = $this->betterNodeFinder->findInstancesOf($if->cond, [Variable::class]); + + foreach ($condVariables as $condVariable) { + $condVariableName = $this->getName($condVariable); + if ($condVariableName === null) { + continue; + } + + if ($this->betterNodeFinder->findVariableOfName($if->stmts, $condVariableName) instanceof Variable) { + return true; + } + } + + return false; + } +} diff --git a/rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php b/rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php index a5a2d795e69..05d0e944390 100644 --- a/rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php +++ b/rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php @@ -5,35 +5,23 @@ namespace Rector\DeadCode\Rector\Stmt; use PhpParser\Node; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\FunctionLike; use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\ClassLike; -use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Do_; -use PhpParser\Node\Stmt\Else_; -use PhpParser\Node\Stmt\Function_; -use PhpParser\Node\Stmt\If_; -use PhpParser\Node\Stmt\Namespace_; -use PhpParser\Node\Stmt\Nop; -use PhpParser\Node\Stmt\While_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeAnalyzer\TerminatedNodeAnalyzer; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\FileNode; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://github.com/phpstan/phpstan/blob/83078fe308a383c618b8c1caec299e5765d9ac82/src/Node/UnreachableStatementNode.php - * * @see \Rector\Tests\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector\RemoveUnreachableStatementRectorTest */ final class RemoveUnreachableStatementRector extends AbstractRector { - /** - * @var array> - */ - private const STMTS_WITH_IS_UNREACHABLE = [If_::class, While_::class, Do_::class]; + public function __construct( + private readonly TerminatedNodeAnalyzer $terminatedNodeAnalyzer + ) { + } public function getRuleDefinition(): RuleDefinition { @@ -69,123 +57,61 @@ public function run() */ public function getNodeTypes(): array { - return [Stmt::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Stmt $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkipNode($node)) { + if ($node instanceof FileNode && $node->isNamespaced()) { + // handled in Namespace_ node return null; } - // might be PHPStan false positive, better skip - $previousStatement = $node->getAttribute(AttributeKey::PREVIOUS_STATEMENT); - if ($previousStatement instanceof Stmt && in_array( - $previousStatement::class, - self::STMTS_WITH_IS_UNREACHABLE, - true - )) { - $node->setAttribute( - AttributeKey::IS_UNREACHABLE, - $previousStatement->getAttribute(AttributeKey::IS_UNREACHABLE) - ); - } - - if (! $this->isUnreachable($node)) { + if ($node->stmts === null) { return null; } - if ($this->isAfterMarkTestSkippedMethodCall($node)) { - $node->setAttribute(AttributeKey::IS_UNREACHABLE, false); + // at least 2 items are needed + if (count($node->stmts) < 2) { return null; } - $this->removeNode($node); - - return null; - } - - private function shouldSkipNode(Stmt $stmt): bool - { - if ($stmt instanceof Nop) { - return true; - } - - if ($stmt instanceof ClassLike) { - return true; - } - - return $stmt instanceof FunctionLike; - } - - private function isUnreachable(Stmt $stmt): bool - { - $isUnreachable = $stmt->getAttribute(AttributeKey::IS_UNREACHABLE); - if ($isUnreachable === true) { - return true; - } - - // traverse up for unreachable node in the same scope - $previousNode = $stmt->getAttribute(AttributeKey::PREVIOUS_STATEMENT); - - while ($previousNode instanceof Stmt && ! $this->isBreakingScopeNode($previousNode)) { - $isUnreachable = $previousNode->getAttribute(AttributeKey::IS_UNREACHABLE); - if ($isUnreachable === true) { - return true; - } + $originalStmts = $node->stmts; + $cleanedStmts = $this->processCleanUpUnreachableStmts($node, $node->stmts); - $previousNode = $previousNode->getAttribute(AttributeKey::PREVIOUS_STATEMENT); + if ($cleanedStmts === $originalStmts) { + return null; } - return false; + $node->stmts = $cleanedStmts; + return $node; } /** - * Keep content after markTestSkipped(), intentional temporary + * @param StmtsAware $stmtsAware + * + * @param Stmt[] $stmts + * @return Stmt[] */ - private function isAfterMarkTestSkippedMethodCall(Stmt $stmt): bool + private function processCleanUpUnreachableStmts(Node $stmtsAware, array $stmts): array { - return (bool) $this->betterNodeFinder->findFirstPrevious($stmt, function (Node $node): bool { - if (! $node instanceof MethodCall) { - return false; + foreach ($stmts as $key => $stmt) { + if (! isset($stmts[$key - 1])) { + continue; } - if ($node->name instanceof MethodCall) { - return false; - } + $previousStmt = $stmts[$key - 1]; - if ($node->name instanceof StaticCall) { - return false; + // unset... + if ($this->terminatedNodeAnalyzer->isAlwaysTerminated($stmtsAware, $previousStmt, $stmt)) { + array_splice($stmts, $key); + return $stmts; } - - return $this->isNames($node->name, ['markTestSkipped', 'markTestIncomplete']); - }); - } - - /** - * Check nodes that breaks scope while traversing up - */ - private function isBreakingScopeNode(Stmt $stmt): bool - { - if ($stmt instanceof ClassLike) { - return true; - } - - if ($stmt instanceof ClassMethod) { - return true; - } - - if ($stmt instanceof Function_) { - return true; - } - - if ($stmt instanceof Namespace_) { - return true; } - return $stmt instanceof Else_; + return $stmts; } } diff --git a/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php b/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php index 4bba06ab13e..2461eff9636 100644 --- a/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php +++ b/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php @@ -9,7 +9,8 @@ use PhpParser\Node\Stmt\Case_; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\Stmt\Switch_; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,6 +19,13 @@ */ final class RemoveDuplicatedCaseInSwitchRector extends AbstractRector { + private bool $hasChanged = false; + + public function __construct( + private readonly BetterStandardPrinter $betterStandardPrinter + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -82,35 +90,110 @@ public function refactor(Node $node): ?Node return null; } - /** @var Case_|null $previousCase */ - $previousCase = null; - foreach ($node->cases as $case) { - if ($previousCase && $this->areSwitchStmtsEqualsAndWithBreak($case, $previousCase)) { - $previousCase->stmts = []; - } + $this->hasChanged = false; - $previousCase = $case; + $this->removeDuplicatedCases($node); + if (! $this->hasChanged) { + return null; } return $node; } - private function areSwitchStmtsEqualsAndWithBreak(Case_ $currentCase, Case_ $previousCase): bool + private function removeDuplicatedCases(Switch_ $switch): void + { + /** @var Case_[] */ + $result = []; + + /** @var int[] */ + $processedCasesKeys = []; + + foreach ($switch->cases as $outerCaseKey => $outerCase) { + if (in_array($outerCaseKey, $processedCasesKeys)) { + continue; + } + + $processedCasesKeys[] = $outerCaseKey; + + if ($outerCase->stmts === []) { + $result[] = $outerCase; + continue; + } + + /** @var array */ + $casesWithoutStmts = []; + + /** @var Case_[] */ + $equalCases = []; + + foreach ($switch->cases as $innerCaseKey => $innerCase) { + if (in_array($innerCaseKey, $processedCasesKeys)) { + continue; + } + + if ($innerCase->stmts === []) { + $casesWithoutStmts[$innerCaseKey] = $innerCase; + continue; + } + + if ($this->areSwitchStmtsEqualsAndWithBreak($outerCase, $innerCase)) { + foreach ($casesWithoutStmts as $caseWithoutStmtsKey => $caseWithoutStmts) { + $equalCases[] = $caseWithoutStmts; + $processedCasesKeys[] = $caseWithoutStmtsKey; + } + + $innerCase->stmts = []; + $equalCases[] = $innerCase; + $processedCasesKeys[] = $innerCaseKey; + } + + $casesWithoutStmts = []; + } + + if ($equalCases === []) { + $result[] = $outerCase; + continue; + } + + $this->hasChanged = true; + + $equalCases[array_key_last($equalCases)]->stmts = $outerCase->stmts; + $outerCase->stmts = []; + + $result = array_merge($result, [$outerCase, ...$equalCases]); + } + + $switch->cases = $result; + } + + private function areSwitchStmtsEqualsAndWithBreak(Case_ $currentCase, Case_ $nextCase): bool { - if (! $this->nodeComparator->areNodesEqual($currentCase->stmts, $previousCase->stmts)) { + /** + * Skip multi no stmts + * @see rules-tests/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector/Fixture/skip_multi_no_stmts.php.inc + */ + if ($currentCase->stmts[0] instanceof Break_ && $nextCase->stmts[0] instanceof Break_) { + return $this->areSwitchStmtsEqualsConsideringComments($currentCase, $nextCase); + } + + if (! $this->nodeComparator->areNodesEqual($currentCase->stmts, $nextCase->stmts)) { return false; } foreach ($currentCase->stmts as $stmt) { - if ($stmt instanceof Break_) { - return true; - } - - if ($stmt instanceof Return_) { + if ($stmt instanceof Break_ || $stmt instanceof Return_) { return true; } } return false; } + + private function areSwitchStmtsEqualsConsideringComments(Case_ $currentCase, Case_ $nextCase): bool + { + $currentCasePrintResult = $this->betterStandardPrinter->print($currentCase->stmts); + $nextCasePrintResult = $this->betterStandardPrinter->print($nextCase->stmts); + + return $currentCasePrintResult === $nextCasePrintResult; + } } diff --git a/rules/DeadCode/Rector/Ternary/RemoveUselessTernaryRector.php b/rules/DeadCode/Rector/Ternary/RemoveUselessTernaryRector.php new file mode 100644 index 00000000000..7d30ee12c63 --- /dev/null +++ b/rules/DeadCode/Rector/Ternary/RemoveUselessTernaryRector.php @@ -0,0 +1,100 @@ +> + */ + public function getNodeTypes(): array + { + return [Ternary::class]; + } + + /** + * @param Ternary $node + */ + public function refactor(Node $node): ?Node + { + /** + * if condition is negated, skip + * switch negated ternary condition early via SwitchNegatedTernaryRector for that + * if needed + */ + if ($node->cond instanceof BooleanNot) { + return null; + } + + $nativeType = $this->nodeTypeResolver->getNativeType($node->cond); + if ($nativeType instanceof BooleanType + && $node->if instanceof Expr + && $this->valueResolver->isTrue($node->if) + && $this->valueResolver->isFalse($node->else)) { + return $node->cond; + } + + if ($node->if instanceof Expr && ! $this->nodeComparator->areNodesEqual($node->if, $node->cond)) { + return null; + } + + if ($nativeType instanceof BooleanType && $this->valueResolver->isFalse($node->else)) { + return $node->cond; + } + + if ($nativeType instanceof ArrayType && $node->else instanceof Array_ && $node->else->items === []) { + return $node->cond; + } + + if ($nativeType instanceof IntegerType && $node->else instanceof Int_ && $node->else->value === 0) { + return $node->cond; + } + + return null; + } +} diff --git a/rules/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector.php b/rules/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector.php index 7463bce9243..f2bbef9da05 100644 --- a/rules/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector.php +++ b/rules/DeadCode/Rector/Ternary/TernaryToBooleanOrFalseToBooleanAndRector.php @@ -5,10 +5,11 @@ namespace Rector\DeadCode\Rector\Ternary; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; use PhpParser\Node\Expr\Ternary; -use PHPStan\Type\BooleanType; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -17,9 +18,14 @@ */ final class TernaryToBooleanOrFalseToBooleanAndRector extends AbstractRector { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change ternary of bool : false to && bool', [ + return new RuleDefinition('Change ternary of `bool : false` to `&& bool`', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -35,7 +41,7 @@ private function getBool(): bool } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -67,7 +73,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($node->if === null) { + if (! $node->if instanceof Expr) { return null; } @@ -79,8 +85,8 @@ public function refactor(Node $node): ?Node return null; } - $ifType = $this->getStaticType($node->if); - if (! $ifType instanceof BooleanType) { + $ifType = $this->getType($node->if); + if (! $ifType->isBoolean()->yes()) { return null; } diff --git a/rules/DeadCode/Rector/TryCatch/RemoveDeadCatchRector.php b/rules/DeadCode/Rector/TryCatch/RemoveDeadCatchRector.php new file mode 100644 index 00000000000..d8859548602 --- /dev/null +++ b/rules/DeadCode/Rector/TryCatch/RemoveDeadCatchRector.php @@ -0,0 +1,184 @@ +getMessage()); + } catch (Throwable $throwable) { + throw $throwable; + } + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + try { + // some code + } catch (RuntimeException $exception) { + throw new InvalidArgumentException($exception->getMessage()); + } + } +} +CODE_SAMPLE + )]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [TryCatch::class]; + } + + /** + * @param TryCatch $node + * @return TryCatch|null + */ + public function refactor(Node $node): ?Node + { + $catches = $node->catches; + if (count($catches) === 1) { + return null; + } + + $hasChanged = false; + $maxIndexCatches = count($catches) - 1; + + foreach ($catches as $key => $catchItem) { + if (! $this->isJustThrownSameVariable($catchItem)) { + continue; + } + + /** @var FullyQualified $type */ + $type = $catchItem->types[0]; + if ($this->shouldSkipNextCatchClassParentWithSpecialTreatment($catches, $type, $key, $maxIndexCatches)) { + continue; + } + + unset($catches[$key]); + $hasChanged = true; + } + + if (! $hasChanged || $catches === []) { + return null; + } + + $node->catches = $catches; + + return $node; + } + + private function isJustThrownSameVariable(Catch_ $catch): bool + { + if ($this->isEmpty($catch->stmts)) { + return false; + } + + $catchItemStmt = $catch->stmts[0]; + if (! ($catchItemStmt instanceof Expression && $catchItemStmt->expr instanceof Throw_)) { + return false; + } + + if (! $this->nodeComparator->areNodesEqual($catch->var, $catchItemStmt->expr->expr)) { + return false; + } + + // too complex to check + if (count($catch->types) !== 1) { + return false; + } + + $type = $catch->types[0]; + + return $type instanceof FullyQualified; + } + + /** + * @param Catch_[] $catches + */ + private function shouldSkipNextCatchClassParentWithSpecialTreatment( + array $catches, + FullyQualified $fullyQualified, + int $key, + int $maxIndexCatches + ): bool { + for ($index = $key + 1; $index <= $maxIndexCatches; ++$index) { + if (! isset($catches[$index])) { + continue; + } + + $nextCatch = $catches[$index]; + + // too complex to check + if (count($nextCatch->types) !== 1) { + return true; + } + + $nextCatchType = $nextCatch->types[0]; + if (! $nextCatchType instanceof FullyQualified) { + return true; + } + + if (! $this->isObjectType($fullyQualified, new ObjectType($nextCatchType->toString()))) { + continue; + } + + if (! $this->isJustThrownSameVariable($nextCatch)) { + return true; + } + } + + return false; + } + + /** + * @param Stmt[] $stmts + */ + private function isEmpty(array $stmts): bool + { + if ($stmts === []) { + return \true; + } + + if (\count($stmts) > 1) { + return \false; + } + + return $stmts[0] instanceof Nop; + } +} diff --git a/rules/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector.php b/rules/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector.php index bf9f1562645..de2d51d8883 100644 --- a/rules/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector.php +++ b/rules/DeadCode/Rector/TryCatch/RemoveDeadTryCatchRector.php @@ -5,11 +5,14 @@ namespace Rector\DeadCode\Rector\TryCatch; use PhpParser\Node; +use PhpParser\Node\Expr\Throw_; use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Catch_; -use PhpParser\Node\Stmt\Throw_; +use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Finally_; +use PhpParser\Node\Stmt\Nop; use PhpParser\Node\Stmt\TryCatch; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -36,13 +39,12 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { public function run() { - // some code } } CODE_SAMPLE @@ -60,33 +62,55 @@ public function getNodeTypes(): array /** * @param TryCatch $node - * @return Stmt[]|null + * @return Stmt[]|null|int */ - public function refactor(Node $node): ?array + public function refactor(Node $node): array|null|int { - if (count($node->catches) !== 1) { + $isEmptyFinallyStmts = ! $node->finally instanceof Finally_ || $this->isEmpty($node->finally->stmts); + + // not empty stmts on finally always executed + if (! $isEmptyFinallyStmts) { return null; } - /** @var Catch_ $onlyCatch */ - $onlyCatch = $node->catches[0]; - if (count($onlyCatch->stmts) !== 1) { + if ($this->isEmpty($node->stmts)) { + return NodeVisitor::REMOVE_NODE; + } + + if (count($node->catches) !== 1) { return null; } - if ($node->finally !== null && $node->finally->stmts !== []) { + $onlyCatch = $node->catches[0]; + if ($this->isEmpty($onlyCatch->stmts)) { return null; } $onlyCatchStmt = $onlyCatch->stmts[0]; - if (! $onlyCatchStmt instanceof Throw_) { + if (! ($onlyCatchStmt instanceof Expression && $onlyCatchStmt->expr instanceof Throw_)) { return null; } - if (! $this->nodeComparator->areNodesEqual($onlyCatch->var, $onlyCatchStmt->expr)) { + if (! $this->nodeComparator->areNodesEqual($onlyCatch->var, $onlyCatchStmt->expr->expr)) { return null; } return $node->stmts; } + + /** + * @param Stmt[] $stmts + */ + private function isEmpty(array $stmts): bool + { + if ($stmts === []) { + return true; + } + + if (count($stmts) > 1) { + return false; + } + + return $stmts[0] instanceof Nop; + } } diff --git a/rules/DeadCode/SideEffect/PureFunctionDetector.php b/rules/DeadCode/SideEffect/PureFunctionDetector.php index e33f575e49e..7c61fd4de55 100644 --- a/rules/DeadCode/SideEffect/PureFunctionDetector.php +++ b/rules/DeadCode/SideEffect/PureFunctionDetector.php @@ -9,115 +9,9 @@ use PHPStan\Reflection\Native\NativeFunctionReflection; use PHPStan\Reflection\ReflectionProvider; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -final class PureFunctionDetector +final readonly class PureFunctionDetector { - /** - * @see https://github.com/vimeo/psalm/blob/d470903722cfcbc1cd04744c5491d3e6d13ec3d9/src/Psalm/Internal/Codebase/Functions.php#L288 - * @var string[] - */ - private const IMPURE_FUNCTIONS = [ - 'chdir', 'chgrp', 'chmod', 'chown', 'chroot', 'closedir', 'copy', 'file_put_contents', - 'fopen', 'fread', 'fwrite', 'fclose', 'touch', 'fpassthru', 'fputs', 'fscanf', 'fseek', - 'ftruncate', 'fprintf', 'symlink', 'mkdir', 'unlink', 'rename', 'rmdir', 'popen', 'pclose', - 'fputcsv', 'umask', 'finfo_close', 'readline_add_history', 'stream_set_timeout', 'fflush', - - // stream/socket io - 'stream_context_set_option', 'socket_write', 'stream_set_blocking', 'socket_close', - 'socket_set_option', 'stream_set_write_buffer', - - // meta calls - 'call_user_func', 'call_user_func_array', 'define', 'create_function', - - // http - 'header', 'header_remove', 'http_response_code', 'setcookie', - - // output buffer - 'ob_start', 'ob_end_clean', 'ob_get_clean', 'readfile', 'printf', 'var_dump', 'phpinfo', - 'ob_implicit_flush', 'vprintf', - - // mcrypt - 'mcrypt_generic_init', 'mcrypt_generic_deinit', 'mcrypt_module_close', - - // internal optimisation - 'opcache_compile_file', 'clearstatcache', - - // process-related - 'pcntl_signal', 'posix_kill', 'cli_set_process_title', 'pcntl_async_signals', 'proc_close', - 'proc_nice', 'proc_open', 'proc_terminate', - - // curl - 'curl_setopt', 'curl_close', 'curl_multi_add_handle', 'curl_multi_remove_handle', - 'curl_multi_select', 'curl_multi_close', 'curl_setopt_array', - - // apc, apcu - 'apc_store', 'apc_delete', 'apc_clear_cache', 'apc_add', 'apc_inc', 'apc_dec', 'apc_cas', - 'apcu_store', 'apcu_delete', 'apcu_clear_cache', 'apcu_add', 'apcu_inc', 'apcu_dec', 'apcu_cas', - - // gz - 'gzwrite', 'gzrewind', 'gzseek', 'gzclose', - - // newrelic - 'newrelic_start_transaction', 'newrelic_name_transaction', 'newrelic_add_custom_parameter', - 'newrelic_add_custom_tracer', 'newrelic_background_job', 'newrelic_end_transaction', - 'newrelic_set_appname', - - // execution - 'shell_exec', 'exec', 'system', 'passthru', 'pcntl_exec', - - // well-known functions - 'libxml_use_internal_errors', 'libxml_disable_entity_loader', 'curl_exec', - 'mt_srand', 'openssl_pkcs7_sign', - 'mt_rand', 'rand', 'random_int', 'random_bytes', - 'wincache_ucache_delete', 'wincache_ucache_set', 'wincache_ucache_inc', - 'class_alias', - - // php environment - 'ini_set', 'sleep', 'usleep', 'register_shutdown_function', - 'error_reporting', 'register_tick_function', 'unregister_tick_function', - 'set_error_handler', 'user_error', 'trigger_error', 'restore_error_handler', - 'date_default_timezone_set', 'assert_options', 'setlocale', - 'set_exception_handler', 'set_time_limit', 'putenv', 'spl_autoload_register', - 'microtime', 'array_rand', - - // logging - 'openlog', 'syslog', 'error_log', 'define_syslog_variables', - - // session - 'session_id', 'session_name', 'session_set_cookie_params', 'session_set_save_handler', - 'session_regenerate_id', 'mb_internal_encoding', 'session_start', - - // ldap - 'ldap_set_option', - - // iterators - 'rewind', 'iterator_apply', - - // mysqli - 'mysqli_select_db', 'mysqli_dump_debug_info', 'mysqli_kill', 'mysqli_multi_query', - 'mysqli_next_result', 'mysqli_options', 'mysqli_ping', 'mysqli_query', 'mysqli_report', - 'mysqli_rollback', 'mysqli_savepoint', 'mysqli_set_charset', 'mysqli_ssl_set', - - // postgres - 'pg_exec', 'pg_execute', 'pg_connect', 'pg_query_params', - - // ftp - 'ftp_close', - - // bcmath - 'bcscale', 'bcdiv', - - // json - 'json_encode', 'json_decode', 'json_last_error', - - // array - 'array_pop', 'array_push', 'array_shift', 'next', 'prev', - - // stream - 'stream_filter_append', - ]; - public function __construct( private NodeNameResolver $nodeNameResolver, private ReflectionProvider $reflectionProvider @@ -131,19 +25,20 @@ public function detect(FuncCall $funcCall): bool return false; } - $scope = $funcCall->getAttribute(AttributeKey::SCOPE); $name = new Name($funcCallName); - $hasFunction = $this->reflectionProvider->hasFunction($name, $scope); + $hasFunction = $this->reflectionProvider->hasFunction($name, null); if (! $hasFunction) { return false; } - $function = $this->reflectionProvider->getFunction($name, $scope); - if (! $function instanceof NativeFunctionReflection) { + $functionReflection = $this->reflectionProvider->getFunction($name, null); + if (! $functionReflection instanceof NativeFunctionReflection) { return false; } - return ! $this->nodeNameResolver->isNames($funcCall, self::IMPURE_FUNCTIONS); + // yes() and maybe() may have side effect + return $functionReflection->hasSideEffects() + ->no(); } } diff --git a/rules/DeadCode/SideEffect/SideEffectNodeDetector.php b/rules/DeadCode/SideEffect/SideEffectNodeDetector.php index 7bf3946c87b..6e7b83276d6 100644 --- a/rules/DeadCode/SideEffect/SideEffectNodeDetector.php +++ b/rules/DeadCode/SideEffect/SideEffectNodeDetector.php @@ -9,32 +9,25 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\NullsafeMethodCall; -use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; -use PhpParser\Node\Scalar\Encapsed; -use PHPStan\Type\ConstantType; use PHPStan\Type\ObjectType; +use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\BetterNodeFinder; -final class SideEffectNodeDetector +final readonly class SideEffectNodeDetector { /** * @var array> */ - private const SIDE_EFFECT_NODE_TYPES = [Encapsed::class, New_::class, Concat::class, PropertyFetch::class]; - - /** - * @var array> - */ - private const CALL_EXPR_SIDE_EFFECT_NODE_TYPES = [ + private const array CALL_EXPR_SIDE_EFFECT_NODE_TYPES = [ MethodCall::class, New_::class, NullsafeMethodCall::class, @@ -42,8 +35,10 @@ final class SideEffectNodeDetector ]; public function __construct( + private PureFunctionDetector $pureFunctionDetector, + private BetterNodeFinder $betterNodeFinder, private NodeTypeResolver $nodeTypeResolver, - private PureFunctionDetector $pureFunctionDetector + private NodeNameResolver $nodeNameResolver ) { } @@ -53,28 +48,7 @@ public function detect(Expr $expr): bool return true; } - $exprStaticType = $this->nodeTypeResolver->resolve($expr); - if ($exprStaticType instanceof ConstantType) { - return false; - } - - foreach (self::SIDE_EFFECT_NODE_TYPES as $sideEffectNodeType) { - if (is_a($expr, $sideEffectNodeType, true)) { - return false; - } - } - - if ($expr instanceof FuncCall) { - return ! $this->pureFunctionDetector->detect($expr); - } - - if ($expr instanceof Variable || $expr instanceof ArrayDimFetch) { - $variable = $this->resolveVariable($expr); - // variables don't have side effects - return ! $variable instanceof Variable; - } - - return true; + return (bool) $this->betterNodeFinder->findFirst($expr, $this->detectCallExpr(...)); } public function detectCallExpr(Node $node): bool @@ -91,6 +65,10 @@ public function detectCallExpr(Node $node): bool return false; } + if (($node instanceof MethodCall || $node instanceof StaticCall) && $this->isTestMock($node)) { + return false; + } + $exprClass = $node::class; if (in_array($exprClass, self::CALL_EXPR_SIDE_EFFECT_NODE_TYPES, true)) { return true; @@ -100,9 +78,27 @@ public function detectCallExpr(Node $node): bool return ! $this->pureFunctionDetector->detect($node); } + if ($node instanceof Variable || $node instanceof ArrayDimFetch) { + $variable = $this->resolveVariable($node); + // variables don't have side effects + return ! $variable instanceof Variable; + } + return false; } + private function isTestMock(MethodCall|StaticCall $node): bool + { + $objectType = new ObjectType('PHPUnit\Framework\TestCase'); + $nodeCaller = $node instanceof MethodCall ? $node->var : $node->class; + + if (! $this->nodeTypeResolver->isObjectType($nodeCaller, $objectType)) { + return false; + } + + return $this->nodeNameResolver->isName($node->name, 'createMock'); + } + private function isPhpParser(New_ $new): bool { if (! $new->class instanceof FullyQualified) { @@ -129,7 +125,7 @@ private function isClassCallerThrowable(StaticCall $staticCall): bool ->yes(); } - private function resolveVariable(Expr $expr): ?Variable + private function resolveVariable(ArrayDimFetch|Variable $expr): ?Variable { while ($expr instanceof ArrayDimFetch) { $expr = $expr->var; diff --git a/rules/DeadCode/TypeNodeAnalyzer/GenericTypeNodeAnalyzer.php b/rules/DeadCode/TypeNodeAnalyzer/GenericTypeNodeAnalyzer.php new file mode 100644 index 00000000000..74e797ecdb9 --- /dev/null +++ b/rules/DeadCode/TypeNodeAnalyzer/GenericTypeNodeAnalyzer.php @@ -0,0 +1,24 @@ +types; + + foreach ($types as $type) { + if ($type instanceof GenericTypeNode) { + return true; + } + } + + return false; + } +} diff --git a/rules/DeadCode/TypeNodeAnalyzer/MixedArrayTypeNodeAnalyzer.php b/rules/DeadCode/TypeNodeAnalyzer/MixedArrayTypeNodeAnalyzer.php new file mode 100644 index 00000000000..46c21cc4fed --- /dev/null +++ b/rules/DeadCode/TypeNodeAnalyzer/MixedArrayTypeNodeAnalyzer.php @@ -0,0 +1,32 @@ +types; + + foreach ($types as $type) { + if ($type instanceof SpacingAwareArrayTypeNode) { + $typeNode = $type->type; + if (! $typeNode instanceof IdentifierTypeNode) { + continue; + } + + if ($typeNode->name === 'mixed') { + return true; + } + } + } + + return false; + } +} diff --git a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php index d9e0e8fd237..71402a15f4e 100644 --- a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php +++ b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php @@ -4,68 +4,70 @@ namespace Rector\DeadCode; -use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Empty_; -use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Param; use PhpParser\Node\Stmt\If_; -use PHPStan\Type\ArrayType; -use Rector\Core\NodeAnalyzer\ParamAnalyzer; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\NodeTypeResolver\NodeTypeResolver; +use PhpParser\Node\Stmt\Return_; +use PHPStan\Analyser\Scope; +use Rector\PhpParser\Comparing\NodeComparator; -final class UselessIfCondBeforeForeachDetector +final readonly class UselessIfCondBeforeForeachDetector { public function __construct( - private NodeTypeResolver $nodeTypeResolver, - private NodeComparator $nodeComparator, - private BetterNodeFinder $betterNodeFinder, - private ParamAnalyzer $paramAnalyzer + private NodeComparator $nodeComparator ) { } /** * Matches: - * !empty($values) + * empty($values) */ - public function isMatchingNotEmpty(If_ $if, Expr $foreachExpr): bool + public function isMatchingEmptyAndForeachedExpr(If_ $if, Expr $foreachExpr): bool { - $cond = $if->cond; - if (! $cond instanceof BooleanNot) { + if (! $if->cond instanceof Empty_) { return false; } - if (! $cond->expr instanceof Empty_) { + $empty = $if->cond; + + if (! $this->nodeComparator->areNodesEqual($empty->expr, $foreachExpr)) { return false; } - /** @var Empty_ $empty */ - $empty = $cond->expr; + if ($if->stmts === []) { + return true; + } - if (! $this->nodeComparator->areNodesEqual($empty->expr, $foreachExpr)) { + if (count($if->stmts) !== 1) { return false; } - // is array though? - $arrayType = $this->nodeTypeResolver->resolve($empty->expr); - if (! $arrayType instanceof ArrayType) { + $stmt = $if->stmts[0]; + return $stmt instanceof Return_ && ! $stmt->expr instanceof Expr; + } + + /** + * Matches: + * !empty($values) + */ + public function isMatchingNotEmpty(If_ $if, Expr $foreachExpr, Scope $scope): bool + { + $cond = $if->cond; + if (! $cond instanceof BooleanNot) { return false; } - $previousParam = $this->fromPreviousParam($foreachExpr); - if (! $previousParam instanceof Param) { - return true; - } - if ($this->paramAnalyzer->isNullable($previousParam)) { + if (! $cond->expr instanceof Empty_) { return false; } - return ! $this->paramAnalyzer->hasDefaultNull($previousParam); + + $empty = $cond->expr; + + return $this->areCondExprAndForeachExprSame($empty, $foreachExpr, $scope); } /** @@ -81,27 +83,11 @@ public function isMatchingNotIdenticalEmptyArray(If_ $if, Expr $foreachExpr): bo return false; } - /** @var NotIdentical|NotEqual $notIdentical */ $notIdentical = $if->cond; return $this->isMatchingNotBinaryOp($notIdentical, $foreachExpr); } - private function fromPreviousParam(Expr $expr): ?Node - { - return $this->betterNodeFinder->findFirstPreviousOfNode($expr, function (Node $node) use ($expr): bool { - if (! $node instanceof Param) { - return false; - } - - if (! $node->var instanceof Variable) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node->var, $expr); - }); - } - private function isMatchingNotBinaryOp(NotIdentical | NotEqual $binaryOp, Expr $foreachExpr): bool { if ($this->isEmptyArrayAndForeachedVariable($binaryOp->left, $binaryOp->right, $foreachExpr)) { @@ -128,4 +114,16 @@ private function isEmptyArray(Expr $expr): bool return $expr->items === []; } + + private function areCondExprAndForeachExprSame(Empty_ $empty, Expr $foreachExpr, Scope $scope): bool + { + if (! $this->nodeComparator->areNodesEqual($empty->expr, $foreachExpr)) { + return false; + } + + // is array though? + $arrayType = $scope->getType($empty->expr); + return $arrayType->isArray() + ->yes(); + } } diff --git a/rules/DeadCode/ValueObject/BinaryToVersionCompareCondition.php b/rules/DeadCode/ValueObject/BinaryToVersionCompareCondition.php index 479187301e3..edbff80eebf 100644 --- a/rules/DeadCode/ValueObject/BinaryToVersionCompareCondition.php +++ b/rules/DeadCode/ValueObject/BinaryToVersionCompareCondition.php @@ -6,15 +6,12 @@ use Rector\DeadCode\Contract\ConditionInterface; -final class BinaryToVersionCompareCondition implements ConditionInterface +final readonly class BinaryToVersionCompareCondition implements ConditionInterface { - /** - * @param mixed $expectedValue - */ public function __construct( private VersionCompareCondition $versionCompareCondition, private string $binaryClass, - private $expectedValue + private mixed $expectedValue ) { } @@ -28,10 +25,7 @@ public function getBinaryClass(): string return $this->binaryClass; } - /** - * @return mixed - */ - public function getExpectedValue() + public function getExpectedValue(): mixed { return $this->expectedValue; } diff --git a/rules/DeadCode/ValueObject/VariableNodeUse.php b/rules/DeadCode/ValueObject/VariableNodeUse.php deleted file mode 100644 index 3a3ecf07cef..00000000000 --- a/rules/DeadCode/ValueObject/VariableNodeUse.php +++ /dev/null @@ -1,67 +0,0 @@ -variableName === $name; - } - - public function getStartTokenPosition(): int - { - return $this->startTokenPosition; - } - - public function isType(string $type): bool - { - return $this->type === $type; - } - - public function getVariableNode(): Variable - { - return $this->variable; - } - - public function getParentNode(): Node - { - $parentNode = $this->variable->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - throw new ShouldNotHappenException(); - } - - return $parentNode; - } - - public function getNestingHash(): ?string - { - return $this->nestingHash; - } -} diff --git a/rules/DeadCode/ValueObject/VersionCompareCondition.php b/rules/DeadCode/ValueObject/VersionCompareCondition.php index d61c19870ee..c7f8c5e4479 100644 --- a/rules/DeadCode/ValueObject/VersionCompareCondition.php +++ b/rules/DeadCode/ValueObject/VersionCompareCondition.php @@ -6,7 +6,7 @@ use Rector\DeadCode\Contract\ConditionInterface; -final class VersionCompareCondition implements ConditionInterface +final readonly class VersionCompareCondition implements ConditionInterface { public function __construct( private int $firstVersion, diff --git a/rules/Defluent/ConflictGuard/ParentClassMethodTypeOverrideGuard.php b/rules/Defluent/ConflictGuard/ParentClassMethodTypeOverrideGuard.php deleted file mode 100644 index 511afcddb58..00000000000 --- a/rules/Defluent/ConflictGuard/ParentClassMethodTypeOverrideGuard.php +++ /dev/null @@ -1,112 +0,0 @@ -getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - $methodName = $classMethod->name->toString(); - - foreach ($classReflection->getAncestors() as $ancestorClassReflection) { - if ($classReflection === $ancestorClassReflection) { - continue; - } - - if (! $ancestorClassReflection->hasMethod($methodName)) { - continue; - } - - if ($this->vendorLocationDetector->detectFunctionLikeReflection($ancestorClassReflection)) { - return true; - } - } - - return false; - } - - public function isReturnTypeChangeAllowed(ClassMethod $classMethod): bool - { - // make sure return type is not protected by parent contract - $parentClassMethodReflection = $this->getParentClassMethod($classMethod); - - // nothing to check - if (! $parentClassMethodReflection instanceof MethodReflection) { - return true; - } - - $classReflection = $parentClassMethodReflection->getDeclaringClass(); - $fileName = $classReflection->getFileName(); - - // probably internal - if ($fileName === false) { - return false; - } - - $normalizedFileName = $this->pathNormalizer->normalizePath($fileName, '/'); - return ! str_contains($normalizedFileName, '/vendor/'); - } - - public function hasParentClassMethod(ClassMethod $classMethod): bool - { - return $this->getParentClassMethod($classMethod) instanceof MethodReflection; - } - - private function getParentClassMethod(ClassMethod $classMethod): ?MethodReflection - { - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - /** @var string $methodName */ - $methodName = $this->nodeNameResolver->getName($classMethod); - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - foreach ($classReflection->getAncestors() as $parentClassReflection) { - if ($classReflection === $parentClassReflection) { - continue; - } - - if (! $parentClassReflection->hasNativeMethod($methodName)) { - continue; - } - - return $parentClassReflection->getNativeMethod($methodName); - } - - return null; - } -} diff --git a/rules/Defluent/Contract/ValueObject/FirstCallFactoryAwareInterface.php b/rules/Defluent/Contract/ValueObject/FirstCallFactoryAwareInterface.php deleted file mode 100644 index 428451dfdbd..00000000000 --- a/rules/Defluent/Contract/ValueObject/FirstCallFactoryAwareInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -fluentChainMethodCallNodeAnalyzer->collectAllMethodCallsInChain($methodCall); - if (! $this->sameClassMethodCallAnalyzer->haveSingleClass($chainMethodCalls)) { - return null; - } - - $assignAndRootExpr = $this->fluentChainMethodCallRootExtractor->extractFromMethodCalls( - $chainMethodCalls, - $kind - ); - - if (! $assignAndRootExpr instanceof AssignAndRootExpr) { - return null; - } - - if ($this->fluentMethodCallSkipper->shouldSkipMethodCalls($assignAndRootExpr, $chainMethodCalls)) { - return null; - } - - $parentMethodCall = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - $nodesToAdd = $this->nonFluentChainMethodCallFactory->createFromAssignObjectAndMethodCalls( - $assignAndRootExpr, - $chainMethodCalls, - $kind, - $parentMethodCall - ); - - return new AssignAndRootExprAndNodesToAdd($assignAndRootExpr, $nodesToAdd); - } -} diff --git a/rules/Defluent/NodeAnalyzer/ExprStringTypeResolver.php b/rules/Defluent/NodeAnalyzer/ExprStringTypeResolver.php deleted file mode 100644 index c048c5e4598..00000000000 --- a/rules/Defluent/NodeAnalyzer/ExprStringTypeResolver.php +++ /dev/null @@ -1,37 +0,0 @@ -nodeTypeResolver->getStaticType($expr); - $exprStaticType = $this->typeUnwrapper->unwrapNullableType($exprStaticType); - - if (! $exprStaticType instanceof TypeWithClassName) { - // nothing we can do, unless - return null; - } - - if ($exprStaticType instanceof AliasedObjectType) { - return $exprStaticType->getFullyQualifiedClass(); - } - - return $exprStaticType->getClassName(); - } -} diff --git a/rules/Defluent/NodeAnalyzer/FluentCallStaticTypeResolver.php b/rules/Defluent/NodeAnalyzer/FluentCallStaticTypeResolver.php deleted file mode 100644 index ea1ef7f1431..00000000000 --- a/rules/Defluent/NodeAnalyzer/FluentCallStaticTypeResolver.php +++ /dev/null @@ -1,85 +0,0 @@ -exprStringTypeResolver->resolve($lastMethodCall->var); - if ($rootType !== null) { - $callerClassTypes[] = $rootType; - } - - // chain method calls are inversed - $lastChainMethodCallKey = array_key_first($chainMethodCalls); - - foreach ($chainMethodCalls as $key => $chainMethodCall) { - $chainMethodCallType = $this->exprStringTypeResolver->resolve($chainMethodCall); - if ($chainMethodCallType === null) { - // last method call does not need a type - if ($lastChainMethodCallKey === $key) { - continue; - } - - return []; - } - - $callerClassTypes[] = $chainMethodCallType; - } - - $uniqueCallerClassTypes = array_unique($callerClassTypes); - return $this->filterOutAlreadyPresentParentClasses($uniqueCallerClassTypes); - } - - /** - * If a child class is with the parent class in the list, count them as 1 - * - * @param class-string[] $types - * @return class-string[] - */ - private function filterOutAlreadyPresentParentClasses(array $types): array - { - $secondTypes = $types; - - foreach ($types as $key => $type) { - foreach ($secondTypes as $secondType) { - if ($type === $secondType) { - continue; - } - - if (! $this->reflectionProvider->hasClass($type)) { - continue; - } - - $firstClassReflection = $this->reflectionProvider->getClass($type); - if ($firstClassReflection->isSubclassOf($secondType)) { - unset($types[$key]); - continue 2; - } - } - } - - return array_values($types); - } -} diff --git a/rules/Defluent/NodeAnalyzer/FluentChainMethodCallNodeAnalyzer.php b/rules/Defluent/NodeAnalyzer/FluentChainMethodCallNodeAnalyzer.php deleted file mode 100644 index 4af49898f61..00000000000 --- a/rules/Defluent/NodeAnalyzer/FluentChainMethodCallNodeAnalyzer.php +++ /dev/null @@ -1,273 +0,0 @@ -methodCall()->chainedMethodCall()" - */ -final class FluentChainMethodCallNodeAnalyzer -{ - /** - * Types that look like fluent interface, but actually create a new object. - * Should be skipped, as they return different object. Not an fluent interface! - * - * @var class-string[] - */ - private const KNOWN_FACTORY_FLUENT_TYPES = ['PHPStan\Analyser\MutatingScope']; - - public function __construct( - private NodeNameResolver $nodeNameResolver, - private NodeTypeResolver $nodeTypeResolver, - private NodeFinder $nodeFinder, - private AstResolver $astResolver, - private BetterNodeFinder $betterNodeFinder, - private ReturnTypeInferer $returnTypeInferer, - private CallAnalyzer $callAnalyzer - ) { - } - - /** - * Checks that in: - * $this->someCall(); - * - * The method is fluent class method === returns self - * public function someClassMethod() - * { - * return $this; - * } - */ - public function isFluentClassMethodOfMethodCall(MethodCall $methodCall): bool - { - if ($this->callAnalyzer->isObjectCall($methodCall->var)) { - return false; - } - - $calleeStaticType = $this->nodeTypeResolver->getStaticType($methodCall->var); - - // we're not sure - if ($calleeStaticType instanceof MixedType) { - return false; - } - - $methodReturnStaticType = $this->nodeTypeResolver->getStaticType($methodCall); - - // is fluent type - if (! $calleeStaticType->equals($methodReturnStaticType)) { - return false; - } - - if (! $calleeStaticType instanceof ObjectType) { - return false; - } - - foreach (self::KNOWN_FACTORY_FLUENT_TYPES as $knownFactoryFluentType) { - if ($calleeStaticType->isInstanceOf($knownFactoryFluentType)->yes()) { - return false; - } - } - - if ($this->isInterface($calleeStaticType)) { - return false; - } - - return ! $this->isMethodCallCreatingNewInstance($methodCall); - } - - public function isLastChainMethodCall(MethodCall $methodCall): bool - { - // is chain method call - if (! $methodCall->var instanceof MethodCall && ! $methodCall->var instanceof New_) { - return false; - } - - $nextNode = $methodCall->getAttribute(AttributeKey::NEXT_NODE); - - // is last chain call - return ! $nextNode instanceof Node; - } - - /** - * @return string[]|null[] - */ - public function collectMethodCallNamesInChain(MethodCall $desiredMethodCall): array - { - $methodCalls = $this->collectAllMethodCallsInChain($desiredMethodCall); - - $methodNames = []; - foreach ($methodCalls as $methodCall) { - $methodNames[] = $this->nodeNameResolver->getName($methodCall->name); - } - - return $methodNames; - } - - /** - * @return MethodCall[] - */ - public function collectAllMethodCallsInChain(MethodCall $methodCall): array - { - $chainMethodCalls = [$methodCall]; - - // traverse up - $currentNode = $methodCall->var; - while ($currentNode instanceof MethodCall) { - $chainMethodCalls[] = $currentNode; - $currentNode = $currentNode->var; - } - - // traverse down - if (count($chainMethodCalls) === 1) { - $currentNode = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - while ($currentNode instanceof MethodCall) { - $chainMethodCalls[] = $currentNode; - $currentNode = $currentNode->getAttribute(AttributeKey::PARENT_NODE); - } - } - - return $chainMethodCalls; - } - - /** - * @return MethodCall[] - */ - public function collectAllMethodCallsInChainWithoutRootOne(MethodCall $methodCall): array - { - $chainMethodCalls = $this->collectAllMethodCallsInChain($methodCall); - - foreach ($chainMethodCalls as $key => $chainMethodCall) { - if (! $chainMethodCall->var instanceof MethodCall && ! $chainMethodCall->var instanceof New_) { - unset($chainMethodCalls[$key]); - break; - } - } - - return array_values($chainMethodCalls); - } - - /** - * Checks "$this->someMethod()->anotherMethod()" - * - * @param string[] $methods - */ - public function isTypeAndChainCalls(Node $node, Type $type, array $methods): bool - { - if (! $node instanceof MethodCall) { - return false; - } - - // node chaining is in reverse order than code - $methods = array_reverse($methods); - - foreach ($methods as $method) { - if (! $this->nodeNameResolver->isName($node->name, $method)) { - return false; - } - - $node = $node->var; - } - - $variableType = $this->nodeTypeResolver->resolve($node); - if ($variableType instanceof MixedType) { - return false; - } - - return $variableType->isSuperTypeOf($type) - ->yes(); - } - - public function resolveRootExpr(MethodCall $methodCall): Expr | Name - { - $callerNode = $methodCall->var; - - while ($callerNode instanceof MethodCall || $callerNode instanceof StaticCall) { - $callerNode = $callerNode instanceof StaticCall ? $callerNode->class : $callerNode->var; - } - - return $callerNode; - } - - public function resolveRootMethodCall(MethodCall $methodCall): ?MethodCall - { - $callerNode = $methodCall->var; - - while ($callerNode instanceof MethodCall && $callerNode->var instanceof MethodCall) { - $callerNode = $callerNode->var; - } - - if ($callerNode instanceof MethodCall) { - return $callerNode; - } - - return null; - } - - public function isMethodCallReturnThis(MethodCall $methodCall): bool - { - $classMethod = $this->astResolver->resolveClassMethodFromMethodCall($methodCall); - if (! $classMethod instanceof ClassMethod) { - return false; - } - - $inferFunctionLike = $this->returnTypeInferer->inferFunctionLike($classMethod); - return $inferFunctionLike instanceof ThisType; - } - - private function isInterface(ObjectType $objectType): bool - { - $classLike = $this->astResolver->resolveClassFromObjectType($objectType); - return $classLike instanceof Interface_; - } - - private function isMethodCallCreatingNewInstance(MethodCall $methodCall): bool - { - $classMethod = $this->astResolver->resolveClassMethodFromMethodCall($methodCall); - if (! $classMethod instanceof ClassMethod) { - return false; - } - - /** @var Return_[] $returns */ - $returns = $this->nodeFinder->findInstanceOf($classMethod, Return_::class); - - foreach ($returns as $return) { - $expr = $return->expr; - - if (! $expr instanceof Expr) { - continue; - } - - if (! $this->callAnalyzer->isNewInstance($this->betterNodeFinder, $expr)) { - continue; - } - - return true; - } - - return false; - } -} diff --git a/rules/Defluent/NodeAnalyzer/FluentChainMethodCallRootExtractor.php b/rules/Defluent/NodeAnalyzer/FluentChainMethodCallRootExtractor.php deleted file mode 100644 index 7fb97afd923..00000000000 --- a/rules/Defluent/NodeAnalyzer/FluentChainMethodCallRootExtractor.php +++ /dev/null @@ -1,160 +0,0 @@ -var instanceof Variable || $methodCall->var instanceof PropertyFetch) { - return $this->createAssignAndRootExprForVariableOrPropertyFetch($methodCall); - } - - if ($methodCall->var instanceof New_) { - // direct = no parent - if ($kind === FluentCallsKind::IN_ARGS) { - return $this->resolveKindInArgs($methodCall); - } - - return $this->matchMethodCallOnNew($methodCall->var); - } - } - - return null; - } - - /** - * beware: fluent vs. factory - * A. FLUENT: $cook->bake()->serve() // only "Cook" - * B. FACTORY: $food = $cook->bake()->warmUp(); // only "Food" - */ - public function isFirstMethodCallFactory(MethodCall $methodCall): bool - { - $variableStaticType = $this->exprStringTypeResolver->resolve($methodCall->var); - $calledMethodStaticType = $this->exprStringTypeResolver->resolve($methodCall); - - // get next method call - $nextMethodCall = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - if (! $nextMethodCall instanceof MethodCall) { - return false; - } - - $nestedCallStaticType = $this->exprStringTypeResolver->resolve($nextMethodCall); - if ($nestedCallStaticType === null) { - return false; - } - - if ($nestedCallStaticType !== $calledMethodStaticType) { - return false; - } - - return $variableStaticType !== $calledMethodStaticType; - } - - private function createAssignAndRootExprForVariableOrPropertyFetch(MethodCall $methodCall): AssignAndRootExpr - { - $isFirstCallFactory = $this->isFirstMethodCallFactory($methodCall); - - // the method call, does not belong to the - $staticType = $this->nodeTypeResolver->getStaticType($methodCall); - $parentNode = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - - // no assign - if ($parentNode instanceof Expression) { - $variableName = $this->propertyNaming->fqnToVariableName($staticType); - - // the assign expresison must be break - // pesuero code bsaed on type - $variable = new Variable($variableName); - return new AssignAndRootExpr($methodCall->var, $methodCall->var, $variable, $isFirstCallFactory); - } - - return new AssignAndRootExpr($methodCall->var, $methodCall->var, null, $isFirstCallFactory); - } - - private function resolveKindInArgs(MethodCall $methodCall): AssignAndRootExpr - { - $variableName = $this->variableNaming->resolveFromNode($methodCall->var); - if ($variableName === null) { - throw new ShouldNotHappenException(); - } - - $silentVariable = new Variable($variableName); - return new AssignAndRootExpr($methodCall->var, $methodCall->var, $silentVariable); - } - - private function matchMethodCallOnNew(New_ $new): ?AssignAndRootExpr - { - // we need assigned left variable here - $previousAssignOrReturn = $this->betterNodeFinder->findFirstPreviousOfTypes( - $new, - [Assign::class, Return_::class] - ); - - if ($previousAssignOrReturn instanceof Assign) { - return new AssignAndRootExpr($previousAssignOrReturn->var, $new); - } - - if ($previousAssignOrReturn instanceof Return_) { - $className = $this->nodeNameResolver->getName($new->class); - if ($className === null) { - return null; - } - - $fullyQualifiedObjectType = new FullyQualifiedObjectType($className); - $expectedName = $this->propertyNaming->getExpectedNameFromType($fullyQualifiedObjectType); - if (! $expectedName instanceof ExpectedName) { - return null; - } - - $variable = new Variable($expectedName->getName()); - return new AssignAndRootExpr($new, $new, $variable); - } - - // no assign, just standalone call - return null; - } -} diff --git a/rules/Defluent/NodeAnalyzer/GetterMethodCallAnalyzer.php b/rules/Defluent/NodeAnalyzer/GetterMethodCallAnalyzer.php deleted file mode 100644 index 265b5583a1e..00000000000 --- a/rules/Defluent/NodeAnalyzer/GetterMethodCallAnalyzer.php +++ /dev/null @@ -1,29 +0,0 @@ -var instanceof MethodCall) { - return false; - } - - $methodCallStaticType = $this->nodeTypeResolver->getStaticType($methodCall); - $methodCallVarStaticType = $this->nodeTypeResolver->getStaticType($methodCall->var); - - // getter short call type - return ! $methodCallStaticType->equals($methodCallVarStaticType); - } -} diff --git a/rules/Defluent/NodeAnalyzer/MethodCallSkipAnalyzer.php b/rules/Defluent/NodeAnalyzer/MethodCallSkipAnalyzer.php deleted file mode 100644 index e97b609403c..00000000000 --- a/rules/Defluent/NodeAnalyzer/MethodCallSkipAnalyzer.php +++ /dev/null @@ -1,61 +0,0 @@ -fluentMethodCallSkipper->shouldSkipRootMethodCall($methodCall)) { - return true; - } - - $chainRootExpr = $this->fluentChainMethodCallNodeAnalyzer->resolveRootExpr($methodCall); - return $chainRootExpr instanceof New_; - } - - public function shouldSkipLastCallNotReturnThis(MethodCall $methodCall): bool - { - return ! $this->fluentChainMethodCallNodeAnalyzer->isMethodCallReturnThis($methodCall); - } - - public function shouldSkipDependsWithOtherExpr(MethodCall|Cast $node): bool - { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - return false; - } - - if ($parentNode instanceof Return_) { - return false; - } - - if ($parentNode instanceof Assign) { - return ! $parentNode->getAttribute(AttributeKey::PARENT_NODE) instanceof Expression; - } - - if ($parentNode instanceof Cast) { - return $this->shouldSkipDependsWithOtherExpr($parentNode); - } - - return ! $parentNode instanceof Expression; - } -} diff --git a/rules/Defluent/NodeAnalyzer/NewFluentChainMethodCallNodeAnalyzer.php b/rules/Defluent/NodeAnalyzer/NewFluentChainMethodCallNodeAnalyzer.php deleted file mode 100644 index abdfbf8b238..00000000000 --- a/rules/Defluent/NodeAnalyzer/NewFluentChainMethodCallNodeAnalyzer.php +++ /dev/null @@ -1,56 +0,0 @@ -nodeTypeResolver->getStaticType($methodCall->var); - $methodCallStaticType = $this->nodeTypeResolver->getStaticType($methodCall); - - return $methodCallStaticType->equals($newStaticType); - } - - /** - * Method call with "new X", that returns "X"? - * e.g. - * - * $this->setItem(new Item) // → returns "Item" - */ - public function matchNewInFluentSetterMethodCall(MethodCall $methodCall): ?New_ - { - if (count($methodCall->args) !== 1) { - return null; - } - - $onlyArgValue = $methodCall->args[0]->value; - if (! $onlyArgValue instanceof New_) { - return null; - } - - $newType = $this->nodeTypeResolver->resolve($onlyArgValue); - if ($newType instanceof MixedType) { - return null; - } - - $parentMethodCallReturnType = $this->nodeTypeResolver->resolve($methodCall); - if (! $newType->equals($parentMethodCallReturnType)) { - return null; - } - - return $onlyArgValue; - } -} diff --git a/rules/Defluent/NodeAnalyzer/SameClassMethodCallAnalyzer.php b/rules/Defluent/NodeAnalyzer/SameClassMethodCallAnalyzer.php deleted file mode 100644 index 3e8817cf553..00000000000 --- a/rules/Defluent/NodeAnalyzer/SameClassMethodCallAnalyzer.php +++ /dev/null @@ -1,59 +0,0 @@ -reflectionResolver->resolveMethodReflectionFromMethodCall($chainMethodCall); - - if ($methodReflection instanceof MethodReflection) { - $declaringClass = $methodReflection->getDeclaringClass(); - $classOfClassMethod[] = $declaringClass->getName(); - } else { - $classOfClassMethod[] = null; - } - } - - $uniqueClasses = array_unique($classOfClassMethod); - return count($uniqueClasses) < 2; - } - - /** - * @param string[] $calleeUniqueTypes - */ - public function isCorrectTypeCount( - array $calleeUniqueTypes, - FirstCallFactoryAwareInterface $firstCallFactoryAware - ): bool { - if ($calleeUniqueTypes === []) { - return false; - } - - // in case of factory method, 2 methods are allowed - if ($firstCallFactoryAware->isFirstCallFactory()) { - return count($calleeUniqueTypes) === 2; - } - - return count($calleeUniqueTypes) === 1; - } -} diff --git a/rules/Defluent/NodeFactory/FluentMethodCallAsArgFactory.php b/rules/Defluent/NodeFactory/FluentMethodCallAsArgFactory.php deleted file mode 100644 index 38632441dd6..00000000000 --- a/rules/Defluent/NodeFactory/FluentMethodCallAsArgFactory.php +++ /dev/null @@ -1,26 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE); - /** @var MethodCall $parentParent */ - $parentParent = $parent->getAttribute(AttributeKey::PARENT_NODE); - - $lastMethodCall = new MethodCall($parentParent->var, $parentParent->name); - $lastMethodCall->args[] = new Arg($variable); - - return $lastMethodCall; - } -} diff --git a/rules/Defluent/NodeFactory/NonFluentChainMethodCallFactory.php b/rules/Defluent/NodeFactory/NonFluentChainMethodCallFactory.php deleted file mode 100644 index e67319396f7..00000000000 --- a/rules/Defluent/NodeFactory/NonFluentChainMethodCallFactory.php +++ /dev/null @@ -1,149 +0,0 @@ -variableNaming->resolveFromNode($new); - if ($variableName === null) { - throw new ShouldNotHappenException(); - } - - $newVariable = new Variable($variableName); - - $newStmts = []; - $newStmts[] = $this->createAssignExpression($newVariable, $new); - - // resolve chain calls - $chainMethodCalls = $this->fluentChainMethodCallNodeAnalyzer->collectAllMethodCallsInChainWithoutRootOne( - $rootMethodCall - ); - - $chainMethodCalls = array_reverse($chainMethodCalls); - foreach ($chainMethodCalls as $chainMethodCall) { - $methodCall = new MethodCall($newVariable, $chainMethodCall->name, $chainMethodCall->args); - $newStmts[] = new Expression($methodCall); - } - - return $newStmts; - } - - /** - * @param MethodCall[] $chainMethodCalls - * @return Assign[]|Cast[]|MethodCall[]|Return_[] - */ - public function createFromAssignObjectAndMethodCalls( - AssignAndRootExpr $assignAndRootExpr, - array $chainMethodCalls, - string $kind, - ?Node $node = null - ): array { - $nodesToAdd = []; - - $isNewNodeNeeded = $this->isNewNodeNeeded($assignAndRootExpr); - if ($isNewNodeNeeded) { - $nodesToAdd[] = $assignAndRootExpr->createFirstAssign(); - } - - $decoupledMethodCalls = $this->createNonFluentMethodCalls( - $chainMethodCalls, - $assignAndRootExpr, - $isNewNodeNeeded - ); - - $nodesToAdd = array_merge($nodesToAdd, $decoupledMethodCalls); - - if ($assignAndRootExpr->getSilentVariable() !== null && $kind !== FluentCallsKind::IN_ARGS) { - $nodesToAdd[] = $assignAndRootExpr->getReturnSilentVariable(); - } - - if ($node instanceof Cast) { - $lastNodeToAdd = $nodesToAdd[array_key_last($nodesToAdd)]; - $cast = $node::class; - $nodesToAdd[array_key_last($nodesToAdd)] = new $cast($lastNodeToAdd); - } - - return $nodesToAdd; - } - - private function createAssignExpression(Variable $newVariable, New_ $new): Expression - { - $assign = new Assign($newVariable, $new); - return new Expression($assign); - } - - private function isNewNodeNeeded(AssignAndRootExpr $assignAndRootExpr): bool - { - if ($assignAndRootExpr->isFirstCallFactory()) { - return true; - } - - if ($assignAndRootExpr->getRootExpr() === $assignAndRootExpr->getAssignExpr()) { - return false; - } - - return $assignAndRootExpr->getRootExpr() instanceof New_; - } - - /** - * @param MethodCall[] $chainMethodCalls - * @return Assign[]|MethodCall[] - */ - private function createNonFluentMethodCalls( - array $chainMethodCalls, - AssignAndRootExpr $assignAndRootExpr, - bool $isNewNodeNeeded - ): array { - $decoupledMethodCalls = []; - - $lastKey = array_key_last($chainMethodCalls); - - foreach ($chainMethodCalls as $key => $chainMethodCall) { - // skip first, already handled - if ($key === $lastKey && $assignAndRootExpr->isFirstCallFactory() && $isNewNodeNeeded) { - continue; - } - - $chainMethodCall->var = $this->firstMethodCallVarResolver->resolve($assignAndRootExpr, $key); - $decoupledMethodCalls[] = $chainMethodCall; - } - - if ($assignAndRootExpr->getRootExpr() instanceof New_ && $assignAndRootExpr->getSilentVariable() !== null) { - $decoupledMethodCalls[] = new Assign( - $assignAndRootExpr->getSilentVariable(), - $assignAndRootExpr->getRootExpr() - ); - } - - return array_reverse($decoupledMethodCalls); - } -} diff --git a/rules/Defluent/NodeFactory/ReturnFluentMethodCallFactory.php b/rules/Defluent/NodeFactory/ReturnFluentMethodCallFactory.php deleted file mode 100644 index 90057510348..00000000000 --- a/rules/Defluent/NodeFactory/ReturnFluentMethodCallFactory.php +++ /dev/null @@ -1,59 +0,0 @@ -getRootMethodCall(); - - // this means the 1st method creates different object then it runs on - // e.g. $sheet->getRow(), creates a "Row" object - $isFirstMethodCallFactory = $this->fluentChainMethodCallRootExtractor->isFirstMethodCallFactory( - $rootMethodCall - ); - - $lastMethodCall = $fluentMethodCalls->getRootMethodCall(); - - if ($lastMethodCall->var instanceof PropertyFetch) { - $assignExpr = $lastMethodCall->var; - } else { - // we need a variable to assign the stuff into - // the method call, does not belong to the - $staticType = $this->nodeTypeResolver->getStaticType($rootMethodCall); - if (! $staticType instanceof ObjectType) { - return null; - } - - $variableName = $this->propertyNaming->fqnToVariableName($staticType); - $assignExpr = new Variable($variableName); - } - - return new FirstAssignFluentCall( - $assignExpr, - $rootMethodCall, - $isFirstMethodCallFactory, - $fluentMethodCalls - ); - } -} diff --git a/rules/Defluent/NodeFactory/SeparateReturnMethodCallFactory.php b/rules/Defluent/NodeFactory/SeparateReturnMethodCallFactory.php deleted file mode 100644 index 6431aa6c48a..00000000000 --- a/rules/Defluent/NodeFactory/SeparateReturnMethodCallFactory.php +++ /dev/null @@ -1,74 +0,0 @@ -getAssignExpr() instanceof PropertyFetch) { - $nodesToAdd[] = $firstAssignFluentCall->createFirstAssign(); - } - - $decoupledMethodCalls = $this->createNonFluentMethodCalls( - $fluentMethodCalls->getFluentMethodCalls(), - $firstAssignFluentCall, - true - ); - - $nodesToAdd = array_merge($nodesToAdd, $decoupledMethodCalls); - - // return the first value - $nodesToAdd[] = new Return_($firstAssignFluentCall->getAssignExpr()); - - return $nodesToAdd; - } - - /** - * @param MethodCall[] $chainMethodCalls - * @return MethodCall[] - */ - private function createNonFluentMethodCalls( - array $chainMethodCalls, - FirstAssignFluentCall $firstAssignFluentCall, - bool $isNewNodeNeeded - ): array { - $decoupledMethodCalls = []; - - $lastKey = array_key_last($chainMethodCalls); - - foreach ($chainMethodCalls as $key => $chainMethodCall) { - // skip first, already handled - if ($key === $lastKey && $firstAssignFluentCall->isFirstCallFactory() && $isNewNodeNeeded) { - continue; - } - - $chainMethodCall->var = $this->firstMethodCallVarResolver->resolve($firstAssignFluentCall, $key); - $decoupledMethodCalls[] = $chainMethodCall; - } - - return array_reverse($decoupledMethodCalls); - } -} diff --git a/rules/Defluent/NodeFactory/VariableFromNewFactory.php b/rules/Defluent/NodeFactory/VariableFromNewFactory.php deleted file mode 100644 index 3fec9e149ad..00000000000 --- a/rules/Defluent/NodeFactory/VariableFromNewFactory.php +++ /dev/null @@ -1,28 +0,0 @@ -variableNaming->resolveFromNode($new); - if ($variableName === null) { - throw new ShouldNotHappenException(); - } - - return new Variable($variableName); - } -} diff --git a/rules/Defluent/NodeResolver/FirstMethodCallVarResolver.php b/rules/Defluent/NodeResolver/FirstMethodCallVarResolver.php deleted file mode 100644 index 5bbc89f66da..00000000000 --- a/rules/Defluent/NodeResolver/FirstMethodCallVarResolver.php +++ /dev/null @@ -1,26 +0,0 @@ -isFirstCallFactory()) { - return $firstCallFactoryAware->getCallerExpr(); - } - - // very first call - if ($key !== 0) { - return $firstCallFactoryAware->getCallerExpr(); - } - - return $firstCallFactoryAware->getFactoryAssignVariable(); - } -} diff --git a/rules/Defluent/Rector/ClassMethod/NormalToFluentRector.php b/rules/Defluent/Rector/ClassMethod/NormalToFluentRector.php deleted file mode 100644 index 367f74a3c54..00000000000 --- a/rules/Defluent/Rector/ClassMethod/NormalToFluentRector.php +++ /dev/null @@ -1,221 +0,0 @@ -someFunction(); -$someObject->otherFunction(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someObject = new SomeClass(); -$someObject->someFunction() - ->otherFunction(); -CODE_SAMPLE - , - [ - self::CALLS_TO_FLUENT => [new NormalToFluent('SomeClass', ['someFunction', 'otherFunction'])], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - // process only existing statements - if ($node->stmts === null) { - return null; - } - - $classMethodStatementCount = count($node->stmts); - - // iterate from bottom to up, so we can merge - for ($i = $classMethodStatementCount - 1; $i >= 0; --$i) { - if (! isset($node->stmts[$i])) { - continue; - } - - /** @var Expression $stmt */ - $stmt = $node->stmts[$i]; - if ($this->shouldSkipPreviousStmt($node, $i)) { - continue; - } - - /** @var Expression $prevStmt */ - $prevStmt = $node->stmts[$i - 1]; - - // here are 2 method calls statements in a row, while current one is first one - if (! $this->isBothMethodCallMatch($stmt, $prevStmt)) { - if (count($this->collectedMethodCalls) >= 2) { - $this->fluentizeCollectedMethodCalls($node); - } - - // reset for new type - $this->collectedMethodCalls = []; - continue; - } - - // add all matching fluent calls - /** @var MethodCall $currentMethodCall */ - $currentMethodCall = $stmt->expr; - $this->collectedMethodCalls[$i] = $currentMethodCall; - - /** @var MethodCall $previousMethodCall */ - $previousMethodCall = $prevStmt->expr; - $this->collectedMethodCalls[$i - 1] = $previousMethodCall; - } - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $callsToFluent = $configuration[self::CALLS_TO_FLUENT] ?? []; - Assert::allIsInstanceOf($callsToFluent, NormalToFluent::class); - $this->callsToFluent = $callsToFluent; - } - - private function shouldSkipPreviousStmt(ClassMethod $classMethod, int $i): bool - { - // we look only for 2+ stmts - if (! isset($classMethod->stmts[$i - 1])) { - return true; - } - - $prevStmt = $classMethod->stmts[$i - 1]; - - return ! $prevStmt instanceof Expression; - } - - private function isBothMethodCallMatch(Expression $firstExpression, Expression $secondExpression): bool - { - if (! $firstExpression->expr instanceof MethodCall) { - return false; - } - - if (! $secondExpression->expr instanceof MethodCall) { - return false; - } - - $firstMethodCallMatchObjectType = $this->matchMethodCall($firstExpression->expr); - if (! $firstMethodCallMatchObjectType instanceof ObjectType) { - return false; - } - - $secondMethodCallMatchObjectType = $this->matchMethodCall($secondExpression->expr); - if (! $secondMethodCallMatchObjectType instanceof ObjectType) { - return false; - } - - // is the same type - return $firstMethodCallMatchObjectType->equals($secondMethodCallMatchObjectType); - } - - private function fluentizeCollectedMethodCalls(ClassMethod $classMethod): void - { - $i = 0; - $fluentMethodCallIndex = null; - $methodCallsToAdd = []; - foreach ($this->collectedMethodCalls as $statementIndex => $methodCall) { - if ($i === 0) { - // first method call, add it - $fluentMethodCallIndex = $statementIndex; - } else { - $methodCallsToAdd[] = $methodCall; - // next method calls, unset them - unset($classMethod->stmts[$statementIndex]); - } - - ++$i; - } - - $stmt = $classMethod->stmts[$fluentMethodCallIndex]; - if (! $stmt instanceof Expression) { - throw new ShouldNotHappenException(); - } - - /** @var MethodCall $fluentMethodCall */ - $fluentMethodCall = $stmt->expr; - - // they are added in reversed direction - $methodCallsToAdd = array_reverse($methodCallsToAdd); - - foreach ($methodCallsToAdd as $methodCallToAdd) { - // make var a parent method call - $fluentMethodCall->var = new MethodCall( - $fluentMethodCall->var, - $methodCallToAdd->name, - $methodCallToAdd->args - ); - } - } - - private function matchMethodCall(MethodCall $methodCall): ?ObjectType - { - foreach ($this->callsToFluent as $callToFluent) { - if (! $this->isObjectType($methodCall->var, $callToFluent->getObjectType())) { - continue; - } - - if ($this->isNames($methodCall->name, $callToFluent->getMethodNames())) { - return $callToFluent->getObjectType(); - } - } - - return null; - } -} diff --git a/rules/Defluent/Rector/ClassMethod/ReturnThisRemoveRector.php b/rules/Defluent/Rector/ClassMethod/ReturnThisRemoveRector.php deleted file mode 100644 index eb295bd5877..00000000000 --- a/rules/Defluent/Rector/ClassMethod/ReturnThisRemoveRector.php +++ /dev/null @@ -1,156 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $returnThis = $this->matchSingleReturnThis($node); - if (! $returnThis instanceof Return_) { - return null; - } - - if ($this->shouldSkip($returnThis, $node)) { - return null; - } - - $this->removeNode($returnThis); - - $classMethod = $node->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - throw new ShouldNotHappenException(); - } - - if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::VOID_TYPE)) { - $classMethod->returnType = new Identifier('void'); - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $phpDocInfo->removeByType(ReturnTagValueNode::class); - - return null; - } - - /** - * Matches only 1st level "return $this;" - */ - private function matchSingleReturnThis(ClassMethod $classMethod): ?Return_ - { - /** @var Return_[] $returns */ - $returns = $this->betterNodeFinder->findInstanceOf($classMethod, Return_::class); - - // can be only 1 return - if (count($returns) !== 1) { - return null; - } - - $return = $returns[0]; - if (! $return->expr instanceof Variable) { - return null; - } - - if (! $this->nodeNameResolver->isName($return->expr, 'this')) { - return null; - } - - $parentNode = $return->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode !== $classMethod) { - return null; - } - - return $return; - } - - private function shouldSkip(Return_ $return, ClassMethod $classMethod): bool - { - if ($this->parentClassMethodTypeOverrideGuard->hasParentMethodOutsideVendor($classMethod)) { - return true; - } - - if ($return->expr === null) { - throw new ShouldNotHappenException(); - } - - $class = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof ClassLike) { - return false; - } - - return $class->getMethod('__call') instanceof ClassMethod; - } -} diff --git a/rules/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector.php b/rules/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector.php deleted file mode 100644 index d71445b910f..00000000000 --- a/rules/Defluent/Rector/MethodCall/FluentChainMethodCallToNormalMethodCallRector.php +++ /dev/null @@ -1,142 +0,0 @@ -someFunction() - ->otherFunction(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someClass = new SomeClass(); -$someClass->someFunction(); -$someClass->otherFunction(); -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->isHandledByAnotherRule($node)) { - return null; - } - - if ($this->methodCallSkipAnalyzer->shouldSkipMethodCallIncludingNew($node)) { - return null; - } - - if ($this->methodCallSkipAnalyzer->shouldSkipDependsWithOtherExpr($node)) { - return null; - } - - $assignAndRootExprAndNodesToAdd = $this->assignAndRootExprAndNodesToAddMatcher->match( - $node, - FluentCallsKind::NORMAL - ); - - if (! $assignAndRootExprAndNodesToAdd instanceof AssignAndRootExprAndNodesToAdd) { - return null; - } - - $currentStatement = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - $nodesToAdd = $assignAndRootExprAndNodesToAdd->getNodesToAdd(); - - if ($currentStatement instanceof Return_) { - $lastNodeToAdd = end($nodesToAdd); - - if (! $lastNodeToAdd) { - return null; - } - - if (! $lastNodeToAdd instanceof Return_) { - $nodesToAdd[array_key_last($nodesToAdd)] = new Return_($lastNodeToAdd); - } - } - - $this->removeCurrentNode($node); - $this->addNodesAfterNode($nodesToAdd, $node); - - return null; - } - - private function removeCurrentNode(MethodCall $methodCall): void - { - $parent = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - while ($parent instanceof Cast) { - $parent = $parent->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Cast) { - $this->fluentNodeRemover->removeCurrentNode($parent); - return; - } - } - - $this->fluentNodeRemover->removeCurrentNode($methodCall); - } - - /** - * Is handled by: - * @see DefluentReturnMethodCallRector - * @see InArgFluentChainMethodCallToStandaloneMethodCallRector - */ - private function isHandledByAnotherRule(MethodCall $methodCall): bool - { - $parent = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Return_) { - return true; - } - - return $parent instanceof Arg; - } -} diff --git a/rules/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector.php b/rules/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector.php deleted file mode 100644 index 205af51f82d..00000000000 --- a/rules/Defluent/Rector/MethodCall/InArgFluentChainMethodCallToStandaloneMethodCallRector.php +++ /dev/null @@ -1,161 +0,0 @@ -processFluentClass($someClass->someFunction()->otherFunction()); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} - -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class UsedAsParameter -{ - public function someFunction(FluentClass $someClass) - { - $someClass->someFunction(); - $someClass->otherFunction(); - $this->processFluentClass($someClass); - } - - public function processFluentClass(FluentClass $someClass) - { - } -} -CODE_SAMPLE - ), - - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Arg) { - return null; - } - - $parentMethodCall = $parent->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentMethodCall instanceof MethodCall) { - return null; - } - - if (! $this->fluentChainMethodCallNodeAnalyzer->isLastChainMethodCall($node)) { - return null; - } - - $isInReturnOrCast = (bool) $this->parentFinder->findByTypes($node, [Return_::class, Cast::class]); - if ($isInReturnOrCast) { - return null; - } - - // create instances from (new ...)->call, re-use from - if ($node->var instanceof New_) { - $this->refactorNew($node, $node->var); - return null; - } - - $assignAndRootExprAndNodesToAdd = $this->assignAndRootExprAndNodesToAddMatcher->match( - $node, - FluentCallsKind::IN_ARGS - ); - - if (! $assignAndRootExprAndNodesToAdd instanceof AssignAndRootExprAndNodesToAdd) { - return null; - } - - $this->addNodesBeforeNode($assignAndRootExprAndNodesToAdd->getNodesToAdd(), $node); - return $assignAndRootExprAndNodesToAdd->getRootCallerExpr(); - } - - private function refactorNew(MethodCall $methodCall, New_ $new): void - { - if (! $this->newFluentChainMethodCallNodeAnalyzer->isNewMethodCallReturningSelf($methodCall)) { - return; - } - - $nodesToAdd = $this->nonFluentChainMethodCallFactory->createFromNewAndRootMethodCall($new, $methodCall); - - $newVariable = $this->variableFromNewFactory->create($new); - $nodesToAdd[] = $this->fluentMethodCallAsArgFactory->createFluentAsArg($methodCall, $newVariable); - - $this->addNodesBeforeNode($nodesToAdd, $methodCall); - $this->removeParentParent($methodCall); - } - - private function removeParentParent(MethodCall $methodCall): void - { - /** @var Arg $parent */ - $parent = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - - /** @var MethodCall $parentParent */ - $parentParent = $parent->getAttribute(AttributeKey::PARENT_NODE); - - $this->removeNode($parentParent); - } -} diff --git a/rules/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector.php b/rules/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector.php deleted file mode 100644 index ee67c7bcb88..00000000000 --- a/rules/Defluent/Rector/MethodCall/MethodCallOnSetterMethodCallToStandaloneAssignRector.php +++ /dev/null @@ -1,112 +0,0 @@ -anotherMethod(new AnotherClass()) - ->someFunction(); - } - - public function anotherMethod(AnotherClass $anotherClass) - { - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function some() - { - $anotherClass = new AnotherClass(); - $anotherClass->someFunction(); - $this->anotherMethod($anotherClass); - } - - public function anotherMethod(AnotherClass $anotherClass) - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->fluentMethodCallSkipper->shouldSkipRootMethodCall($node)) { - return null; - } - - $rootMethodCall = $this->fluentChainMethodCallNodeAnalyzer->resolveRootMethodCall($node); - if (! $rootMethodCall instanceof MethodCall) { - return null; - } - - $new = $this->newFluentChainMethodCallNodeAnalyzer->matchNewInFluentSetterMethodCall($rootMethodCall); - if (! $new instanceof New_) { - return null; - } - - $newStmts = $this->nonFluentChainMethodCallFactory->createFromNewAndRootMethodCall($new, $node); - $this->addNodesBeforeNode($newStmts, $node); - - // change new arg to root variable - $newVariable = $this->variableFromNewFactory->create($new); - $rootMethodCall->args = [new Arg($newVariable)]; - - return $rootMethodCall; - } -} diff --git a/rules/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector.php b/rules/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector.php deleted file mode 100644 index f11d31ecc30..00000000000 --- a/rules/Defluent/Rector/MethodCall/NewFluentChainMethodCallToNonFluentRector.php +++ /dev/null @@ -1,144 +0,0 @@ -someFunction() - ->otherFunction(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someClass = new SomeClass(); -$someClass->someFunction(); -$someClass->otherFunction(); -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - // handled by another rule - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - - // may happen if another rule make parent null - if (! $parent instanceof Node) { - return null; - } - - if ($this->typeChecker->isInstanceOf($parent, [Return_::class, Arg::class])) { - return null; - } - - if (! $parent instanceof Assign) { - return null; - } - - $parentParent = $parent->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentParent instanceof Expression) { - return null; - } - - $statement = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - $previous = $node->getAttribute(AttributeKey::PREVIOUS_NODE); - - if ($this->isFoundInPrevious($statement, $previous)) { - return null; - } - - if ($this->fluentMethodCallSkipper->shouldSkipRootMethodCall($node)) { - return null; - } - - $assignAndRootExprAndNodesToAdd = $this->assignAndRootExprAndNodesToAddMatcher->match( - $node, - FluentCallsKind::NORMAL - ); - if (! $assignAndRootExprAndNodesToAdd instanceof AssignAndRootExprAndNodesToAdd) { - return null; - } - - $this->fluentNodeRemover->removeCurrentNode($node); - $this->addNodesAfterNode($assignAndRootExprAndNodesToAdd->getNodesToAdd(), $node); - - return null; - } - - private function isFoundInPrevious(Stmt $stmt, ?Node $previous): bool - { - if (! $previous instanceof Node) { - return false; - } - - $isFoundInPreviousAssign = (bool) $this->betterNodeFinder->findFirstPreviousOfNode($stmt, function (Node $node) use ( - $previous - ): bool { - if (! $node instanceof Assign) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node->var, $previous); - }); - - if ($isFoundInPreviousAssign) { - return true; - } - - $previous = $previous->getAttribute(AttributeKey::PREVIOUS_NODE); - return $this->isFoundInPrevious($stmt, $previous); - } -} diff --git a/rules/Defluent/Rector/Return_/DefluentReturnMethodCallRector.php b/rules/Defluent/Rector/Return_/DefluentReturnMethodCallRector.php deleted file mode 100644 index 3f4b5023fcd..00000000000 --- a/rules/Defluent/Rector/Return_/DefluentReturnMethodCallRector.php +++ /dev/null @@ -1,80 +0,0 @@ -someFunction(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someClass = new SomeClass(); -$someClass->someFunction(); -return $someClass; -CODE_SAMPLE - ), - - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Return_::class]; - } - - /** - * @param Return_ $node - * @return null|Expression[]|Return_[] - */ - public function refactor(Node $node): ?array - { - if (! $node->expr instanceof MethodCall) { - return null; - } - - $methodCall = $node->expr; - if (! $methodCall->var instanceof Variable) { - return null; - } - - if (! $this->fluentChainMethodCallNodeAnalyzer->isFluentClassMethodOfMethodCall($methodCall)) { - return null; - } - - $variableReturn = new Return_($methodCall->var); - - return [new Expression($methodCall), $variableReturn]; - } -} diff --git a/rules/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector.php b/rules/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector.php deleted file mode 100644 index af5879b1606..00000000000 --- a/rules/Defluent/Rector/Return_/ReturnFluentChainMethodCallToNormalMethodCallRector.php +++ /dev/null @@ -1,136 +0,0 @@ -someFunction() - ->otherFunction(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someClass = new SomeClass(); -$someClass->someFunction(); -$someClass->otherFunction(); - -return $someClass; -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Return_::class]; - } - - /** - * @param Return_ $node - */ - public function refactor(Node $node): ?Node - { - $methodCall = $this->matchReturnMethodCall($node); - if (! $methodCall instanceof MethodCall) { - return null; - } - - if ($this->methodCallSkipAnalyzer->shouldSkipMethodCallIncludingNew($methodCall)) { - return null; - } - - if ($this->methodCallSkipAnalyzer->shouldSkipLastCallNotReturnThis($methodCall)) { - return null; - } - - $nodesToAdd = $this->createStandaloneNodesToAddFromReturnFluentMethodCalls($methodCall); - if ($nodesToAdd === []) { - return null; - } - - $this->fluentNodeRemover->removeCurrentNode($node); - $this->addNodesAfterNode($nodesToAdd, $node); - - return null; - } - - /** - * @return Node[] - */ - private function createStandaloneNodesToAddFromReturnFluentMethodCalls(MethodCall $methodCall): array - { - $fluentMethodCalls = $this->fluentMethodCallsFactory->createFromLastMethodCall($methodCall); - if (! $fluentMethodCalls instanceof FluentMethodCalls) { - return []; - } - - $firstAssignFluentCall = $this->returnFluentMethodCallFactory->createFromFluentMethodCalls( - $fluentMethodCalls - ); - - if (! $firstAssignFluentCall instanceof FirstAssignFluentCall) { - return []; - } - - // should be skipped? - if ($this->fluentMethodCallSkipper->shouldSkipFirstAssignFluentCall($firstAssignFluentCall)) { - return []; - } - - return $this->separateReturnMethodCallFactory->createReturnFromFirstAssignFluentCallAndFluentMethodCalls( - $firstAssignFluentCall, - $fluentMethodCalls - ); - } - - private function matchReturnMethodCall(Return_ $return): ?MethodCall - { - $returnExpr = $return->expr; - if (! $returnExpr instanceof MethodCall) { - return null; - } - - return $returnExpr; - } -} diff --git a/rules/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector.php b/rules/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector.php deleted file mode 100644 index 6b0c09488ac..00000000000 --- a/rules/Defluent/Rector/Return_/ReturnNewFluentChainMethodCallToNonFluentRector.php +++ /dev/null @@ -1,117 +0,0 @@ -someFunction() - ->otherFunction(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someClass = new SomeClass(); -$someClass->someFunction(); -$someClass->otherFunction(); -return $someClass; -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Return_::class]; - } - - /** - * @param Return_ $node - */ - public function refactor(Node $node): ?Node - { - $methodCall = $this->matchReturnMethodCall($node); - if (! $methodCall instanceof MethodCall) { - return null; - } - - if ($this->fluentMethodCallSkipper->shouldSkipRootMethodCall($methodCall)) { - return null; - } - - $assignAndRootExprAndNodesToAdd = $this->assignAndRootExprAndNodesToAddMatcher->match( - $methodCall, - FluentCallsKind::NORMAL - ); - - if (! $assignAndRootExprAndNodesToAdd instanceof AssignAndRootExprAndNodesToAdd) { - return null; - } - - $nodesToAdd = $assignAndRootExprAndNodesToAdd->getNodesToAdd(); - $lastNodeToAdd = end($nodesToAdd); - - if (! $lastNodeToAdd) { - return null; - } - - if (! $lastNodeToAdd instanceof Return_) { - $nodesToAdd[array_key_last($nodesToAdd)] = new Return_($lastNodeToAdd); - } - - $this->fluentNodeRemover->removeCurrentNode($node); - $this->addNodesAfterNode($nodesToAdd, $node); - - return $node; - } - - private function matchReturnMethodCall(Return_ $return): ?Expr - { - if ($return->expr === null) { - return null; - } - - if (! $return->expr instanceof MethodCall) { - return null; - } - - return $return->expr; - } -} diff --git a/rules/Defluent/Skipper/FluentMethodCallSkipper.php b/rules/Defluent/Skipper/FluentMethodCallSkipper.php deleted file mode 100644 index e734e5a3995..00000000000 --- a/rules/Defluent/Skipper/FluentMethodCallSkipper.php +++ /dev/null @@ -1,113 +0,0 @@ -fluentChainMethodCallNodeAnalyzer->isLastChainMethodCall($methodCall)) { - return true; - } - - return $this->getterMethodCallAnalyzer->isGetterMethodCall($methodCall); - } - - public function shouldSkipFirstAssignFluentCall(FirstAssignFluentCall $firstAssignFluentCall): bool - { - $calleeUniqueTypes = $this->fluentCallStaticTypeResolver->resolveCalleeUniqueTypes( - $firstAssignFluentCall->getFluentMethodCalls() - ); - - if (! $this->sameClassMethodCallAnalyzer->isCorrectTypeCount($calleeUniqueTypes, $firstAssignFluentCall)) { - return true; - } - - $calleeUniqueType = $this->resolveCalleeUniqueType($firstAssignFluentCall, $calleeUniqueTypes); - return $this->isAllowedType($calleeUniqueType); - } - - /** - * @param MethodCall[] $fluentMethodCalls - */ - public function shouldSkipMethodCalls(AssignAndRootExpr $assignAndRootExpr, array $fluentMethodCalls): bool - { - $calleeUniqueTypes = $this->fluentCallStaticTypeResolver->resolveCalleeUniqueTypes($fluentMethodCalls); - if (! $this->sameClassMethodCallAnalyzer->isCorrectTypeCount($calleeUniqueTypes, $assignAndRootExpr)) { - return true; - } - - $calleeUniqueType = $this->resolveCalleeUniqueType($assignAndRootExpr, $calleeUniqueTypes); - return $this->isAllowedType($calleeUniqueType); - } - - /** - * @param string[] $calleeUniqueTypes - */ - private function resolveCalleeUniqueType( - FirstCallFactoryAwareInterface $firstCallFactoryAware, - array $calleeUniqueTypes - ): string { - if (! $firstCallFactoryAware->isFirstCallFactory()) { - return $calleeUniqueTypes[0]; - } - - return $calleeUniqueTypes[1] ?? $calleeUniqueTypes[0]; - } - - private function isAllowedType(string $class): bool - { - $objectType = new ObjectType($class); - - foreach (self::ALLOWED_FLUENT_TYPES as $allowedFluentType) { - $allowedObjectType = new ObjectType($allowedFluentType); - if ($allowedObjectType->isSuperTypeOf($objectType)->yes()) { - return true; - } - } - - return false; - } -} diff --git a/rules/Defluent/ValueObject/AbstractRootExpr.php b/rules/Defluent/ValueObject/AbstractRootExpr.php deleted file mode 100644 index cde462ce403..00000000000 --- a/rules/Defluent/ValueObject/AbstractRootExpr.php +++ /dev/null @@ -1,85 +0,0 @@ -isFirstCallFactory && $this->getFirstAssign() !== null) { - return $this->createFactoryAssign(); - } - - return $this->createAssign($this->assignExpr, $this->rootExpr); - } - - protected function createAssign(Expr $assignVar, Expr $assignExpr): Assign - { - if ($assignVar === $assignExpr) { - throw new ShouldNotHappenException(); - } - - return new Assign($assignVar, $assignExpr); - } - - protected function getFirstAssign(): ?Assign - { - $currentStmt = $this->assignExpr->getAttribute(AttributeKey::CURRENT_STATEMENT); - if (! $currentStmt instanceof Expression) { - return null; - } - - $currentExpr = $currentStmt->expr; - - if ($currentExpr instanceof Assign) { - return $currentExpr; - } - - return null; - } - - private function createFactoryAssign(): Assign - { - /** @var Assign $firstAssign */ - $firstAssign = $this->getFirstAssign(); - $currentMethodCall = $firstAssign->expr; - - if (! $currentMethodCall instanceof MethodCall) { - throw new ShouldNotHappenException(); - } - - $currentMethodCall = $this->resolveLastMethodCall($currentMethodCall); - - // ensure var and expr are different - $assignVar = $firstAssign->var; - $assignExpr = $currentMethodCall; - - return $this->createAssign($assignVar, $assignExpr); - } - - private function resolveLastMethodCall(MethodCall $currentMethodCall): MethodCall - { - while ($currentMethodCall->var instanceof MethodCall) { - $currentMethodCall = $currentMethodCall->var; - } - - return $currentMethodCall; - } -} diff --git a/rules/Defluent/ValueObject/AssignAndRootExpr.php b/rules/Defluent/ValueObject/AssignAndRootExpr.php deleted file mode 100644 index 4c204b95f99..00000000000 --- a/rules/Defluent/ValueObject/AssignAndRootExpr.php +++ /dev/null @@ -1,75 +0,0 @@ -assignExpr = $assignExpr; - $this->rootExpr = $rootExpr; - $this->isFirstCallFactory = $isFirstCallFactory; - } - - public function getAssignExpr(): Expr - { - return $this->assignExpr; - } - - public function getRootExpr(): Expr - { - return $this->rootExpr; - } - - public function getSilentVariable(): ?Variable - { - return $this->silentVariable; - } - - public function getReturnSilentVariable(): Return_ - { - if (! $this->silentVariable instanceof Variable) { - throw new ShouldNotHappenException(); - } - - return new Return_($this->silentVariable); - } - - public function getCallerExpr(): Expr - { - if ($this->silentVariable !== null) { - return $this->silentVariable; - } - - return $this->assignExpr; - } - - public function isFirstCallFactory(): bool - { - return $this->isFirstCallFactory; - } - - public function getFactoryAssignVariable(): Expr - { - $firstAssign = $this->getFirstAssign(); - if (! $firstAssign instanceof Assign) { - return $this->getCallerExpr(); - } - - return $firstAssign->var; - } -} diff --git a/rules/Defluent/ValueObject/AssignAndRootExprAndNodesToAdd.php b/rules/Defluent/ValueObject/AssignAndRootExprAndNodesToAdd.php deleted file mode 100644 index 20477c1ed31..00000000000 --- a/rules/Defluent/ValueObject/AssignAndRootExprAndNodesToAdd.php +++ /dev/null @@ -1,33 +0,0 @@ - $nodesToAdd - */ - public function __construct( - private AssignAndRootExpr $assignAndRootExpr, - private array $nodesToAdd - ) { - } - - /** - * @return Expr[]|Return_[] - */ - public function getNodesToAdd(): array - { - return $this->nodesToAdd; - } - - public function getRootCallerExpr(): Expr - { - return $this->assignAndRootExpr->getCallerExpr(); - } -} diff --git a/rules/Defluent/ValueObject/FirstAssignFluentCall.php b/rules/Defluent/ValueObject/FirstAssignFluentCall.php deleted file mode 100644 index 644d016ac3d..00000000000 --- a/rules/Defluent/ValueObject/FirstAssignFluentCall.php +++ /dev/null @@ -1,63 +0,0 @@ -assignExpr = $assignExpr; - $this->rootExpr = $rootExpr; - $this->isFirstCallFactory = $isFirstCallFactory; - } - - public function getAssignExpr(): Expr - { - return $this->assignExpr; - } - - public function getRootExpr(): Expr - { - return $this->rootExpr; - } - - public function getCallerExpr(): Expr - { - return $this->assignExpr; - } - - public function isFirstCallFactory(): bool - { - return $this->isFirstCallFactory; - } - - public function getFactoryAssignVariable(): Expr - { - $firstAssign = $this->getFirstAssign(); - if (! $firstAssign instanceof Assign) { - return $this->assignExpr; - } - - return $firstAssign->var; - } - - /** - * @return MethodCall[] - */ - public function getFluentMethodCalls(): array - { - return $this->fluentMethodCalls->getFluentMethodCalls(); - } -} diff --git a/rules/Defluent/ValueObject/FluentCallsKind.php b/rules/Defluent/ValueObject/FluentCallsKind.php deleted file mode 100644 index 0ff758ca785..00000000000 --- a/rules/Defluent/ValueObject/FluentCallsKind.php +++ /dev/null @@ -1,18 +0,0 @@ -rootMethodCall; - } - - /** - * @return MethodCall[] - */ - public function getFluentMethodCalls(): array - { - return $this->fluentMethodCalls; - } - - public function getLastMethodCall(): MethodCall - { - return $this->lastMethodCall; - } -} diff --git a/rules/Defluent/ValueObject/NormalToFluent.php b/rules/Defluent/ValueObject/NormalToFluent.php deleted file mode 100644 index 14d899578c2..00000000000 --- a/rules/Defluent/ValueObject/NormalToFluent.php +++ /dev/null @@ -1,32 +0,0 @@ -class); - } - - /** - * @return string[] - */ - public function getMethodNames(): array - { - return $this->methodNames; - } -} diff --git a/rules/Defluent/ValueObjectFactory/FluentMethodCallsFactory.php b/rules/Defluent/ValueObjectFactory/FluentMethodCallsFactory.php deleted file mode 100644 index e161108653a..00000000000 --- a/rules/Defluent/ValueObjectFactory/FluentMethodCallsFactory.php +++ /dev/null @@ -1,50 +0,0 @@ -fluentChainMethodCallNodeAnalyzer->collectAllMethodCallsInChain($lastMethodCall); - if (! $this->sameClassMethodCallAnalyzer->haveSingleClass($chainMethodCalls)) { - return null; - } - - // we need at least 2 method call for fluent - if (count($chainMethodCalls) < 2) { - return null; - } - - $rootMethodCall = $this->resolveRootMethodCall($chainMethodCalls); - - if (! $rootMethodCall->var instanceof PropertyFetch) { - return null; - } - - return new FluentMethodCalls($rootMethodCall, $chainMethodCalls, $lastMethodCall); - } - - /** - * @param MethodCall[] $chainMethodCalls - */ - private function resolveRootMethodCall(array $chainMethodCalls): MethodCall - { - $lastKey = array_key_last($chainMethodCalls); - return $chainMethodCalls[$lastKey]; - } -} diff --git a/rules/DependencyInjection/Collector/VariablesToPropertyFetchCollection.php b/rules/DependencyInjection/Collector/VariablesToPropertyFetchCollection.php deleted file mode 100644 index e11146a42bc..00000000000 --- a/rules/DependencyInjection/Collector/VariablesToPropertyFetchCollection.php +++ /dev/null @@ -1,28 +0,0 @@ -variableNameAndType[$name] = $type; - } - - /** - * @return Type[] - */ - public function getVariableNamesAndTypes(): array - { - return $this->variableNameAndType; - } -} diff --git a/rules/DependencyInjection/NodeAnalyzer/ControllerClassMethodAnalyzer.php b/rules/DependencyInjection/NodeAnalyzer/ControllerClassMethodAnalyzer.php deleted file mode 100644 index 36e68387111..00000000000 --- a/rules/DependencyInjection/NodeAnalyzer/ControllerClassMethodAnalyzer.php +++ /dev/null @@ -1,33 +0,0 @@ -getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return false; - } - - if (! \str_ends_with($className, 'Controller')) { - return false; - } - - $classMethod = $variable->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return false; - } - - // is probably in controller action - return $classMethod->isPublic(); - } -} diff --git a/rules/DependencyInjection/NodeFactory/InjectMethodFactory.php b/rules/DependencyInjection/NodeFactory/InjectMethodFactory.php deleted file mode 100644 index 780d9d7421b..00000000000 --- a/rules/DependencyInjection/NodeFactory/InjectMethodFactory.php +++ /dev/null @@ -1,66 +0,0 @@ -typeFactory->uniquateTypes($objectTypes); - - $shortClassName = $this->classNaming->getShortName($className); - - $methodBuilder = new MethodBuilder('inject' . $shortClassName); - $methodBuilder->makePublic(); - - foreach ($objectTypes as $objectType) { - /** @var ObjectType $objectType */ - $propertyName = $this->propertyNaming->fqnToVariableName($objectType); - - $paramBuilder = new ParamBuilder($propertyName); - $paramBuilder->setType(new FullyQualified($objectType->getClassName())); - $methodBuilder->addParam($paramBuilder); - - $assign = $this->nodeFactory->createPropertyAssignment($propertyName); - - $methodBuilder->addStmt($assign); - } - - $classMethod = $methodBuilder->getNode(); - - if ($framework === FrameworkName::SYMFONY) { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - $phpDocInfo->addPhpDocTagNode(new PhpDocTagNode('@required', new GenericTagValueNode(''))); - } - - return $classMethod; - } -} diff --git a/rules/DependencyInjection/NodeManipulator/PropertyConstructorInjectionManipulator.php b/rules/DependencyInjection/NodeManipulator/PropertyConstructorInjectionManipulator.php deleted file mode 100644 index 64c1dc1fb34..00000000000 --- a/rules/DependencyInjection/NodeManipulator/PropertyConstructorInjectionManipulator.php +++ /dev/null @@ -1,50 +0,0 @@ -nodeNameResolver->getName($property); - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $type); - $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineAnnotationTagValueNode); - - $classLike = $property->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - throw new ShouldNotHappenException(); - } - - $propertyMetadata = new PropertyMetadata($propertyName, $type, $property->flags); - $this->propertyToAddCollector->addPropertyToClass($classLike, $propertyMetadata); - } -} diff --git a/rules/DependencyInjection/NodeRemover/ClassMethodNodeRemover.php b/rules/DependencyInjection/NodeRemover/ClassMethodNodeRemover.php deleted file mode 100644 index e49c5c1767e..00000000000 --- a/rules/DependencyInjection/NodeRemover/ClassMethodNodeRemover.php +++ /dev/null @@ -1,135 +0,0 @@ -params !== []) { - return; - } - - if ((array) $classMethod->stmts !== []) { - return; - } - - $this->nodesToRemoveCollector->addNodeToRemove($classMethod); - } - - public function removeParamFromMethodBody(ClassMethod $classMethod, Param $param): void - { - /** @var string $paramName */ - $paramName = $this->nodeNameResolver->getName($param->var); - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $classMethod->stmts, - function (Node $node) use ($paramName) { - if (! $this->isParentConstructStaticCall($node)) { - return null; - } - - /** @var StaticCall $node */ - $this->removeParamFromArgs($node, $paramName); - - if ($node->args === []) { - $this->nodesToRemoveCollector->addNodeToRemove($node); - } - - return null; - } - ); - - foreach ((array) $classMethod->stmts as $key => $stmt) { - if ($stmt instanceof Expression) { - $stmt = $stmt->expr; - } - - if (! $this->isParentConstructStaticCall($stmt)) { - continue; - } - - /** @var StaticCall $stmt */ - if ($stmt->args !== []) { - continue; - } - - unset($classMethod->stmts[$key]); - } - - $this->removeParamFromAssign($classMethod, $paramName); - } - - private function isParentConstructStaticCall(Node $node): bool - { - return $this->isStaticCallNamed($node, 'parent', MethodName::CONSTRUCT); - } - - private function removeParamFromArgs(StaticCall $staticCall, string $paramName): void - { - foreach ($staticCall->args as $key => $arg) { - if (! $this->nodeNameResolver->isName($arg->value, $paramName)) { - continue; - } - - unset($staticCall->args[$key]); - } - } - - private function removeParamFromAssign(ClassMethod $classMethod, string $paramName): void - { - foreach ((array) $classMethod->stmts as $key => $stmt) { - if ($stmt instanceof Expression) { - $stmt = $stmt->expr; - } - - if (! $stmt instanceof Assign) { - continue; - } - - if (! $stmt->expr instanceof Variable) { - continue; - } - - if (! $this->nodeNameResolver->isName($stmt->expr, $paramName)) { - continue; - } - - unset($classMethod->stmts[$key]); - } - } - - private function isStaticCallNamed(Node $node, string $class, string $method): bool - { - if (! $node instanceof StaticCall) { - return false; - } - - if (! $this->nodeNameResolver->isName($node->class, $class)) { - return false; - } - - return $this->nodeNameResolver->isName($node->name, $method); - } -} diff --git a/rules/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector.php b/rules/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector.php deleted file mode 100644 index 6162b02ffb0..00000000000 --- a/rules/DependencyInjection/Rector/ClassMethod/AddMethodParentCallRector.php +++ /dev/null @@ -1,158 +0,0 @@ - - */ - private array $methodByParentTypes = []; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Add method parent call, in case new parent method is added', - [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -class SunshineCommand extends ParentClassWithNewConstructor -{ - public function __construct() - { - $value = 5; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SunshineCommand extends ParentClassWithNewConstructor -{ - public function __construct() - { - $value = 5; - - parent::__construct(); - } -} -CODE_SAMPLE - , - [ - self::METHODS_BY_PARENT_TYPES => [ - 'ParentClassWithNewConstructor' => MethodName::CONSTRUCT, - ], - ] - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof ClassLike) { - return null; - } - - /** @var string $className */ - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - - foreach ($this->methodByParentTypes as $type => $method) { - if (! $this->isObjectType($classLike, new ObjectType($type))) { - continue; - } - - // not itself - if ($className === $type) { - continue; - } - - if ($this->shouldSkipMethod($node, $method)) { - continue; - } - - $node->stmts[] = $this->createParentStaticCall($method); - - return $node; - } - - return null; - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->methodByParentTypes = $configuration[self::METHODS_BY_PARENT_TYPES] ?? []; - } - - private function shouldSkipMethod(ClassMethod $classMethod, string $method): bool - { - if (! $this->isName($classMethod, $method)) { - return true; - } - - return $this->hasParentCallOfMethod($classMethod, $method); - } - - private function createParentStaticCall(string $method): Expression - { - $staticCall = $this->nodeFactory->createStaticCall('parent', $method); - return new Expression($staticCall); - } - - /** - * Looks for "parent:: - */ - private function hasParentCallOfMethod(ClassMethod $classMethod, string $method): bool - { - return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node) use ( - $method - ): bool { - if (! $node instanceof StaticCall) { - return false; - } - - if (! $this->isName($node->class, 'parent')) { - return false; - } - - return $this->isName($node->name, $method); - }); - } -} diff --git a/rules/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector.php b/rules/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector.php deleted file mode 100644 index cf4f4f6e71e..00000000000 --- a/rules/DependencyInjection/Rector/Class_/ActionInjectionToConstructorInjectionRector.php +++ /dev/null @@ -1,136 +0,0 @@ -fetchAll(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeController -{ - /** - * @var ProductRepository - */ - private $productRepository; - public function __construct(ProductRepository $productRepository) - { - $this->productRepository = $productRepository; - } - - public function default() - { - $products = $this->productRepository->fetchAll(); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, '*Controller')) { - return null; - } - - foreach ($node->getMethods() as $classMethod) { - $this->processClassMethod($node, $classMethod); - } - - return $node; - } - - private function processClassMethod(Class_ $class, ClassMethod $classMethod): void - { - foreach ($classMethod->params as $key => $paramNode) { - if (! $this->isActionInjectedParamNode($paramNode)) { - continue; - } - - $paramType = $this->getObjectType($paramNode); - - /** @var string $paramName */ - $paramName = $this->getName($paramNode->var); - - $propertyMetadata = new PropertyMetadata($paramName, $paramType, Class_::MODIFIER_PRIVATE); - $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); - - $this->nodeRemover->removeParam($classMethod, $key); - - $this->variablesToPropertyFetchCollection->addVariableNameAndType($paramName, $paramType); - } - } - - private function isActionInjectedParamNode(Param $param): bool - { - if ($param->type === null) { - return false; - } - - $typehint = $this->getName($param->type); - if ($typehint === null) { - return false; - } - - $paramStaticType = $this->getObjectType($param); - if (! $paramStaticType instanceof ObjectType) { - return false; - } - - $serviceMap = $this->applicationServiceMapProvider->provide(); - - return $serviceMap->hasService($paramStaticType->getClassName()); - } -} diff --git a/rules/DependencyInjection/Rector/Variable/ReplaceVariableByPropertyFetchRector.php b/rules/DependencyInjection/Rector/Variable/ReplaceVariableByPropertyFetchRector.php deleted file mode 100644 index 6e8cc34ad41..00000000000 --- a/rules/DependencyInjection/Rector/Variable/ReplaceVariableByPropertyFetchRector.php +++ /dev/null @@ -1,109 +0,0 @@ -productRepository = $productRepository; - } - - public function default() - { - $products = $productRepository->fetchAll(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeController -{ - /** - * @var ProductRepository - */ - private $productRepository; - - public function __construct(ProductRepository $productRepository) - { - $this->productRepository = $productRepository; - } - - public function default() - { - $products = $this->productRepository->fetchAll(); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Variable::class]; - } - - /** - * @param Variable $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->controllerClassMethodAnalyzer->isInControllerActionMethod($node)) { - return null; - } - - foreach ($this->variablesToPropertyFetchCollection->getVariableNamesAndTypes() as $name => $type) { - if (! $this->isName($node, $name)) { - continue; - } - - /** @var ObjectType $type */ - if (! $this->isObjectType($node, $type)) { - continue; - } - - return $this->nodeFactory->createPropertyFetch('this', $name); - } - - return null; - } -} diff --git a/rules/DowngradePhp53/Rector/Dir/DirConstToFileConstRector.php b/rules/DowngradePhp53/Rector/Dir/DirConstToFileConstRector.php deleted file mode 100644 index 9433fafeb46..00000000000 --- a/rules/DowngradePhp53/Rector/Dir/DirConstToFileConstRector.php +++ /dev/null @@ -1,65 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Dir::class]; - } - - /** - * @param Dir $node - */ - public function refactor(Node $node): FuncCall - { - return $this->nodeFactory->createFuncCall('dirname', [new File()]); - } -} diff --git a/rules/DowngradePhp70/NodeFactory/ClassFromAnonymousFactory.php b/rules/DowngradePhp70/NodeFactory/ClassFromAnonymousFactory.php deleted file mode 100644 index 3a727006727..00000000000 --- a/rules/DowngradePhp70/NodeFactory/ClassFromAnonymousFactory.php +++ /dev/null @@ -1,21 +0,0 @@ - $newClass->flags, - 'extends' => $newClass->extends, - 'implements' => $newClass->implements, - 'stmts' => $newClass->stmts, - 'attrGroups' => $newClass->attrGroups, - ]); - } -} diff --git a/rules/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector.php b/rules/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector.php deleted file mode 100644 index 1d882ead67c..00000000000 --- a/rules/DowngradePhp70/Rector/ClassMethod/DowngradeParentTypeDeclarationRector.php +++ /dev/null @@ -1,104 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove "parent" return type, add a "@return parent" tag instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class ParentClass -{ -} - -class SomeClass extends ParentClass -{ - public function foo(): parent - { - return $this; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class ParentClass -{ -} - -class SomeClass extends ParentClass -{ - /** - * @return parent - */ - public function foo() - { - return $this; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $scope = $node->getAttribute(AttributeKey::SCOPE); - if ($scope === null) { - // in a trait - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - $classReflection = $this->reflectionProvider->getClass($className); - } else { - $classReflection = $scope->getClassReflection(); - } - - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $parentStaticType = new ParentStaticType($classReflection); - - if (! $this->phpDocFromTypeDeclarationDecorator->decorateReturnWithSpecificType($node, $parentStaticType)) { - return null; - } - - return $node; - } -} diff --git a/rules/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector.php b/rules/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector.php deleted file mode 100644 index 8c03031527d..00000000000 --- a/rules/DowngradePhp70/Rector/ClassMethod/DowngradeSelfTypeDeclarationRector.php +++ /dev/null @@ -1,93 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove "self" return type, add a "@return self" tag instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function foo(): self - { - return $this; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function foo() - { - return $this; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $scope = $node->getAttribute(AttributeKey::SCOPE); - if ($scope === null) { - // in a trait - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - $classReflection = $this->reflectionProvider->getClass($className); - } else { - $classReflection = $scope->getClassReflection(); - } - - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $thisType = new ThisType($classReflection); - - if (! $this->phpDocFromTypeDeclarationDecorator->decorateReturnWithSpecificType($node, $thisType)) { - return null; - } - - return $node; - } -} diff --git a/rules/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector.php b/rules/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector.php deleted file mode 100644 index d77ad643439..00000000000 --- a/rules/DowngradePhp70/Rector/Coalesce/DowngradeNullCoalesceRector.php +++ /dev/null @@ -1,71 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Coalesce::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change null coalesce to isset ternary check', - [ - new CodeSample( - <<<'CODE_SAMPLE' -$username = $_GET['user'] ?? 'nobody'; -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$username = isset($_GET['user']) ? $_GET['user'] : 'nobody'; -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param Coalesce $node - */ - public function refactor(Node $node): Ternary - { - $if = $node->left; - $else = $node->right; - - if ($this->coalesceAnalyzer->hasIssetableLeft($node)) { - $cond = new Isset_([$if]); - } else { - $cond = new NotIdentical($if, $this->nodeFactory->createNull()); - } - - return new Ternary($cond, $if, $else); - } -} diff --git a/rules/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector.php b/rules/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector.php deleted file mode 100644 index 508c593d2cb..00000000000 --- a/rules/DowngradePhp70/Rector/Declare_/DowngradeStrictTypeDeclarationRector.php +++ /dev/null @@ -1,70 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Declare_::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove the declare(strict_types=1)', - [ - new CodeSample( - <<<'CODE_SAMPLE' -declare(strict_types=1); -echo 'something'; -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -echo 'something'; -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param Declare_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - $this->removeNode($node); - return $node; - } - - private function shouldSkip(Declare_ $declare): bool - { - $declares = $declare->declares; - - foreach ($declares as $declare) { - if ($this->isName($declare->key, 'strict_types')) { - return false; - } - } - - return true; - } -} diff --git a/rules/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector.php b/rules/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector.php deleted file mode 100644 index 09936429f55..00000000000 --- a/rules/DowngradePhp70/Rector/Expression/DowngradeDefineArrayConstantRector.php +++ /dev/null @@ -1,106 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Expression::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change array contant definition via define to const', - [ - new CodeSample( - <<<'CODE_SAMPLE' -define('ANIMALS', [ - 'dog', - 'cat', - 'bird' -]); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -const ANIMALS = [ - 'dog', - 'cat', - 'bird' -]; -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param Expression $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->expr instanceof FuncCall) { - return null; - } - - $funcCall = $node->expr; - - if ($this->shouldSkip($funcCall)) { - return null; - } - - /** @var String_ $arg0 */ - $arg0 = $funcCall->args[0]->value; - $arg0Value = $arg0->value; - - /** @var Array_ $arg1Value */ - $arg1Value = $funcCall->args[1]->value; - - return new Node\Stmt\Const_([new Const_($arg0Value, $arg1Value)]); - } - - private function shouldSkip(FuncCall $funcCall): bool - { - if (! $this->isName($funcCall, 'define')) { - return true; - } - - $args = $funcCall->args; - if (! $args[0]->value instanceof String_) { - return true; - } - - if (! $args[1]->value instanceof Array_) { - return true; - } - - return (bool) $this->parentFinder->findByTypes($funcCall, [ClassMethod::class, Function_::class]); - } -} diff --git a/rules/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector.php b/rules/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector.php deleted file mode 100644 index 4a8665dd4d2..00000000000 --- a/rules/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector.php +++ /dev/null @@ -1,104 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Move array option of session_start($options) to before statement\'s ini_set()', - [ - new CodeSample( - <<<'CODE_SAMPLE' -session_start([ - 'cache_limiter' => 'private', -]); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -ini_set('session.cache_limiter', 'private'); -session_start(); -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - $currentStatement = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - - /** @var Array_ $options */ - $options = $node->args[0]->value; - - foreach ($options->items as $option) { - if (! $option instanceof ArrayItem) { - return null; - } - - if (! $option->key instanceof String_) { - return null; - } - - if (! $this->valueResolver->isTrueOrFalse($option->value) && ! $option->value instanceof String_) { - return null; - } - - $sessionKey = new String_('session.' . $option->key->value); - $funcName = new Name('ini_set'); - $iniSet = new FuncCall($funcName, [new Arg($sessionKey), new Arg($option->value)]); - - $this->addNodeBeforeNode(new Expression($iniSet), $currentStatement); - } - - unset($node->args[0]); - return $node; - } - - private function shouldSkip(FuncCall $funcCall): bool - { - if (! $this->isName($funcCall, 'session_start')) { - return true; - } - - if (! isset($funcCall->args[0])) { - return true; - } - - return ! $funcCall->args[0]->value instanceof Array_; - } -} diff --git a/rules/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector.php b/rules/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector.php deleted file mode 100644 index 503220de294..00000000000 --- a/rules/DowngradePhp70/Rector/FunctionLike/DowngradeScalarTypeDeclarationRector.php +++ /dev/null @@ -1,164 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Function_::class, ClassMethod::class, Closure::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove the type params and return type, add @param and @return tags instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(string $input): string - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @param string $input - * @return string - */ - public function run($input) - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param Function_|ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $recastAssigns = []; - foreach ($node->params as $param) { - if ($param->type === null) { - continue; - } - - $this->phpDocFromTypeDeclarationDecorator->decorateParam( - $param, - $node, - [StringType::class, IntegerType::class, BooleanType::class, FloatType::class] - ); - - $recastAssign = $this->resolveRecastAssign($param, $node); - if ($recastAssign instanceof Expression) { - $recastAssigns[] = $recastAssign; - } - } - - if ($recastAssigns !== []) { - $node->stmts = array_merge($recastAssigns, (array) $node->stmts); - } - - if ($node->returnType === null) { - return null; - } - - $this->phpDocFromTypeDeclarationDecorator->decorate($node); - return $node; - } - - private function resolveRecastAssign(Param $param, Function_ | ClassMethod | Closure $functionLike): ?Expression - { - if ($functionLike->stmts === null) { - return null; - } - - if ($functionLike->stmts === []) { - return null; - } - - // add possible object with __toString() re-type to keep original behavior - // @see https://twitter.com/VotrubaT/status/1390974218108538887 - - /** @var string $paramName */ - $paramName = $this->getName($param->var); - $variable = new Variable($paramName); - - $paramType = $this->getStaticType($param); - $recastedVariable = $this->recastVariabletIfScalarType($variable, $paramType); - if (! $recastedVariable instanceof Cast) { - return null; - } - - $assign = new Assign($variable, $recastedVariable); - return new Expression($assign); - } - - private function recastVariabletIfScalarType(Variable $variable, Type $type): ?Cast - { - if ($type instanceof StringType) { - return new String_($variable); - } - - if ($type instanceof IntegerType) { - return new Int_($variable); - } - - if ($type instanceof FloatType) { - return new Double($variable); - } - - if ($type instanceof BooleanType) { - return new Bool_($variable); - } - - return null; - } -} diff --git a/rules/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector.php b/rules/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector.php deleted file mode 100644 index 948f3505911..00000000000 --- a/rules/DowngradePhp70/Rector/GroupUse/SplitGroupedUseImportsRector.php +++ /dev/null @@ -1,66 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [GroupUse::class]; - } - - /** - * @param GroupUse $node - * @return Use_[] - */ - public function refactor(Node $node): array - { - $prefix = $this->getName($node->prefix); - - $uses = []; - foreach ($node->uses as $useUse) { - $useUse->name = new Name($prefix . '\\' . $this->getName($useUse->name)); - $uses[] = new Use_([$useUse], $node->type); - } - - return $uses; - } -} diff --git a/rules/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector.php b/rules/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector.php deleted file mode 100644 index 4c7b371fbc6..00000000000 --- a/rules/DowngradePhp70/Rector/New_/DowngradeAnonymousClassRector.php +++ /dev/null @@ -1,134 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [New_::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove anonymous class', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - return new class { - public function execute() - { - } - }; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class Anonymous -{ - public function execute() - { - } -} -class SomeClass -{ - public function run() - { - return new Anonymous(); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param Node[] $nodes - * @return Node[]|null - */ - public function beforeTraverse(array $nodes): ?array - { - $this->classes = []; - return parent::beforeTraverse($nodes); - } - - /** - * @param Node[] $nodes - * @return Node[] - */ - public function afterTraverse(array $nodes) - { - if ($this->classes === []) { - return $nodes; - } - - return array_merge($nodes, $this->classes); - } - - /** - * @param New_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->classAnalyzer->isAnonymousClass($node->class)) { - return null; - } - - if (! $node->class instanceof Class_) { - return null; - } - - $className = $this->createAnonymousClassName(); - $this->classes[] = $this->classFromAnonymousFactory->create($className, $node->class); - - return new New_(new Name($className), $node->args); - } - - private function createAnonymousClassName(): string - { - $smartFileInfo = $this->file->getSmartFileInfo(); - - return self::ANONYMOUS_CLASS_PREFIX . md5($smartFileInfo->getRealPath()) . '__' . count($this->classes); - } -} diff --git a/rules/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector.php b/rules/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector.php deleted file mode 100644 index 9ec52749220..00000000000 --- a/rules/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector.php +++ /dev/null @@ -1,111 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Spaceship::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change spaceship with check equal, and ternary to result 0, -1, 1', - [ - new CodeSample( - <<<'CODE_SAMPLE' -return $a <=> $b; -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$battleShipcompare = function ($left, $right) { - if ($left === $right) { - return 0; - } - return $left < $right ? -1 : 1; -}; -return $battleShipcompare($a, $b); -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param Spaceship $node - */ - public function refactor(Node $node): FuncCall - { - $leftVariableParam = new Variable('left'); - $rightVariableParam = new Variable('right'); - - $anonymousFunction = new Closure(); - $leftParam = new Param($leftVariableParam); - $rightParam = new Param($rightVariableParam); - $anonymousFunction->params = [$leftParam, $rightParam]; - - $if = $this->ifManipulator->createIfExpr( - new Identical($leftVariableParam, $rightVariableParam), - new Return_(new LNumber(0)) - ); - $anonymousFunction->stmts[0] = $if; - - $smaller = new Smaller($leftVariableParam, $rightVariableParam); - $ternaryIf = new LNumber(-1); - $ternaryElse = new LNumber(1); - $ternary = new Ternary($smaller, $ternaryIf, $ternaryElse); - $anonymousFunction->stmts[1] = new Return_($ternary); - - $currentStatement = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - $scope = $currentStatement->getAttribute(AttributeKey::SCOPE); - - $variableAssignName = $this->variableNaming->createCountedValueName('battleShipcompare', $scope); - $variableAssign = new Variable($variableAssignName); - $assignExpression = $this->getAssignExpression($anonymousFunction, $variableAssign); - $this->addNodeBeforeNode($assignExpression, $currentStatement); - - return new FuncCall($variableAssign, [new Arg($node->left), new Arg($node->right)]); - } - - private function getAssignExpression(Closure $closure, Variable $variable): Expression - { - return new Expression(new Assign($variable, $closure)); - } -} diff --git a/rules/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector.php b/rules/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector.php deleted file mode 100644 index ba6a67bd439..00000000000 --- a/rules/DowngradePhp70/Rector/String_/DowngradeGeneratedScalarTypesRector.php +++ /dev/null @@ -1,171 +0,0 @@ -phpRectors = [$downgradeScalarTypeDeclarationRector, $downgradeVoidTypeDeclarationRector]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Refactor scalar types in PHP code in string snippets, e.g. generated container code from symfony/dependency-injection', - [ - new CodeSample( - <<<'CODE_SAMPLE' -$code = <<<'EOF' - public function getParameter(string $name) - { - return $name; - } -EOF; -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -$code = <<<'EOF' - /** - * @param string $name - */ - public function getParameter($name) - { - return $name; - } -EOF; -CODE_SAMPLE - ), - - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [String_::class]; - } - - /** - * @param String_ $node - */ - public function refactor(Node $node): ?Node - { - $smartFileInfo = $this->file->getSmartFileInfo(); - - // this rule is parsing strings, so it heavy on performance; to lower it, we'll process only known opt-in files - if (! $this->isRelevantFileInfo($smartFileInfo)) { - return null; - } - - $stringKind = $node->getAttribute(AttributeKey::KIND); - if (! in_array($stringKind, [String_::KIND_NOWDOC, String_::KIND_HEREDOC], true)) { - return null; - } - - // we assume its a function list - see https://github.com/symfony/symfony/blob/ad91659ea9b2a964f933bf27d0d1f1ef60fe9417/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php#L1513-L1560 - - try { - $nodes = $this->inlineCodeParser->parse('value . ' }'); - } catch (Error) { - // nothing we can do - return null; - } - - if ($nodes === []) { - return null; - } - - // * replace scalar types with docs - // * remove return type - // somehow we want to call all Rector rules here - $nodeTraverser = $this->createNodeTraverser(); - $changedNodes = $nodeTraverser->traverse($nodes); - if (! $changedNodes[0] instanceof Class_) { - return null; - } - - $node->value = $this->printClassStmts($changedNodes[0]); - return $node; - } - - private function isRelevantFileInfo(SmartFileInfo $fileInfo): bool - { - // for tests - if (StaticPHPUnitEnvironment::isPHPUnitRun()) { - return true; - } - - foreach (self::FILES_TO_INCLUDE as $fileToInclude) { - if (\str_ends_with($fileInfo->getRealPath(), $fileToInclude)) { - return true; - } - } - - return false; - } - - private function printClassStmts(Class_ $class): string - { - $refactoredContent = ''; - foreach ($class->stmts as $classStmt) { - $refactoredContent .= $this->betterStandardPrinter->prettyPrint([$classStmt]) . PHP_EOL; - } - - return $refactoredContent; - } - - private function createNodeTraverser(): NodeTraverser - { - $nodeTraverser = new NodeTraverser(); - foreach ($this->phpRectors as $phpRector) { - $nodeTraverser->addVisitor($phpRector); - } - return $nodeTraverser; - } -} diff --git a/rules/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector.php b/rules/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector.php deleted file mode 100644 index 368572f322a..00000000000 --- a/rules/DowngradePhp71/Rector/Array_/SymmetricArrayDestructuringToListRector.php +++ /dev/null @@ -1,75 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Array_::class]; - } - - /** - * @param Array_ $node - */ - public function refactor(Node $node): ?Node - { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Assign && $this->nodeComparator->areNodesEqual($node, $parentNode->var)) { - return $this->processToList($node); - } - if (! $parentNode instanceof Foreach_) { - return null; - } - if (! $this->nodeComparator->areNodesEqual($node, $parentNode->valueVar)) { - return null; - } - return $this->processToList($node); - } - - private function processToList(Array_ $array): FuncCall - { - $args = []; - foreach ($array->items as $arrayItem) { - $args[] = $arrayItem instanceof ArrayItem ? new Arg($arrayItem->value) : null; - } - - return new FuncCall(new Name('list'), $args); - } -} diff --git a/rules/DowngradePhp71/Rector/ClassConst/DowngradeClassConstantVisibilityRector.php b/rules/DowngradePhp71/Rector/ClassConst/DowngradeClassConstantVisibilityRector.php deleted file mode 100644 index a8ca9963fb7..00000000000 --- a/rules/DowngradePhp71/Rector/ClassConst/DowngradeClassConstantVisibilityRector.php +++ /dev/null @@ -1,65 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassConst::class]; - } - - /** - * @param ClassConst $node - */ - public function refactor(Node $node): ClassConst - { - $this->visibilityManipulator->removeVisibility($node); - - return $node; - } -} diff --git a/rules/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector.php b/rules/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector.php deleted file mode 100644 index 361194b665a..00000000000 --- a/rules/DowngradePhp71/Rector/FuncCall/DowngradeIsIterableRector.php +++ /dev/null @@ -1,77 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change is_iterable with array and Traversable object type check', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($obj) - { - is_iterable($obj); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($obj) - { - is_array($obj) || $obj instanceof \Traversable; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'is_iterable')) { - return null; - } - - /** @var mixed $arg */ - $arg = $node->args[0]->value; - $funcCall = $this->nodeFactory->createFuncCall('is_array', [$arg]); - $instanceOf = new Instanceof_($arg, new FullyQualified('Traversable')); - - return new BooleanOr($funcCall, $instanceOf); - } -} diff --git a/rules/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector.php b/rules/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector.php deleted file mode 100644 index e11b745f1f3..00000000000 --- a/rules/DowngradePhp71/Rector/FunctionLike/DowngradeIterablePseudoTypeDeclarationRector.php +++ /dev/null @@ -1,89 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Function_::class, ClassMethod::class, Closure::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove the iterable pseudo type params and returns, add @param and @return tags instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(iterable $iterator): iterable - { - // do something - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @param mixed[]|\Traversable $iterator - * @return mixed[]|\Traversable - */ - public function run($iterator) - { - // do something - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param Function_|ClassMethod|Closure $node - */ - public function refactor(Node $node): ?Node - { - $iterableType = new IterableType(new MixedType(), new MixedType()); - - foreach ($node->params as $param) { - $this->phpDocFromTypeDeclarationDecorator->decorateParamWithSpecificType($param, $node, $iterableType); - } - - if (! $this->phpDocFromTypeDeclarationDecorator->decorateReturnWithSpecificType($node, $iterableType)) { - return null; - } - - return $node; - } -} diff --git a/rules/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector.php b/rules/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector.php deleted file mode 100644 index 8c8b935ff3f..00000000000 --- a/rules/DowngradePhp71/Rector/FunctionLike/DowngradeNullableTypeDeclarationRector.php +++ /dev/null @@ -1,122 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Function_::class, ClassMethod::class, Closure::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Remove the nullable type params, add @param tags instead', [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(?string $input): ?string - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @param string|null $input - * @return string|null - */ - public function run($input) - { - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @param ClassMethod|Function_|Closure $node - */ - public function refactor(Node $node): ?Node - { - $hasChanged = false; - foreach ($node->params as $param) { - if ($this->refactorParamType($param, $node)) { - $hasChanged = true; - } - } - - if ($node->returnType instanceof NullableType) { - $this->phpDocFromTypeDeclarationDecorator->decorate($node); - $hasChanged = true; - } - - if ($hasChanged) { - return $node; - } - - return null; - } - - private function refactorParamType(Param $param, ClassMethod | Function_ | Closure $functionLike): bool - { - if (! $this->paramAnalyzer->isNullable($param)) { - return false; - } - - $this->decorateWithDocBlock($functionLike, $param); - $param->type = null; - - return true; - } - - private function decorateWithDocBlock(ClassMethod | Function_ | Closure $functionLike, Param $param): void - { - if ($param->type === null) { - return; - } - - $type = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - - $paramName = $this->getName($param->var); - if ($paramName === null) { - throw new ShouldNotHappenException(); - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - $this->phpDocTypeChanger->changeParamType($phpDocInfo, $type, $param, $paramName); - } -} diff --git a/rules/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector.php b/rules/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector.php deleted file mode 100644 index 201e03adc42..00000000000 --- a/rules/DowngradePhp71/Rector/FunctionLike/DowngradeVoidTypeDeclarationRector.php +++ /dev/null @@ -1,81 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Function_::class, ClassMethod::class, Closure::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove "void" return type, add a "@return void" tag instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(): void - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @return void - */ - public function run() - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param ClassMethod|Function_|Closure $node - */ - public function refactor(Node $node): ?Node - { - $voidType = new VoidType(); - - if (! $this->phpDocFromTypeDeclarationDecorator->decorateReturnWithSpecificType($node, $voidType)) { - return null; - } - - return $node; - } -} diff --git a/rules/DowngradePhp71/Rector/List_/DowngradeKeysInListRector.php b/rules/DowngradePhp71/Rector/List_/DowngradeKeysInListRector.php deleted file mode 100644 index 8318df40d97..00000000000 --- a/rules/DowngradePhp71/Rector/List_/DowngradeKeysInListRector.php +++ /dev/null @@ -1,176 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [List_::class, Array_::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Extract keys in list to its own variable assignment', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(): void - { - $data = [ - ["id" => 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - list("id" => $id1, "name" => $name1) = $data[0]; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run(): void - { - $data = [ - ["id" => 1, "name" => 'Tom'], - ["id" => 2, "name" => 'Fred'], - ]; - $id1 = $data[0]["id"]; - $name1 = $data[0]["name"]; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param List_|Array_ $node - */ - public function refactor(Node $node): ?Node - { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { - return null; - } - - $parentExpression = $parent->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentExpression instanceof Node) { - return null; - } - - $assignExpressions = $this->processExtractToItsOwnVariable($node, $parent, $parentExpression); - if ($assignExpressions === []) { - return null; - } - - if ($parent instanceof Assign) { - $this->mirrorComments($assignExpressions[0], $parentExpression); - $this->addNodesBeforeNode($assignExpressions, $node); - $this->removeNode($parentExpression); - - return $node; - } - - if ($parent instanceof Foreach_) { - $defaultValueVar = $this->inflectorSingularResolver->resolve((string) $this->getName($parent->expr)); - $scope = $parent->getAttribute(AttributeKey::SCOPE); - $newValueVar = $this->variableNaming->createCountedValueName($defaultValueVar, $scope); - $parent->valueVar = new Variable($newValueVar); - $stmts = $parent->stmts; - - if ($stmts === []) { - $parent->stmts = $assignExpressions; - } else { - $this->addNodesBeforeNode($assignExpressions, $parent->stmts[0]); - } - - return $parent->valueVar; - } - - return null; - } - - /** - * @return Expression[] - */ - private function processExtractToItsOwnVariable(List_ | Array_ $node, Node $parent, Node $parentExpression): array - { - $items = $node->items; - $assignExpressions = []; - - foreach ($items as $item) { - if (! $item instanceof ArrayItem) { - return []; - } - - /** keyed and not keyed cannot be mixed, return early */ - if (! $item->key instanceof Expr) { - return []; - } - - if ($parentExpression instanceof Expression && $parent instanceof Assign && $parent->var === $node) { - $assignExpressions[] = new Expression( - new Assign($item->value, new ArrayDimFetch($parent->expr, $item->key)) - ); - } - - if (! $parent instanceof Foreach_) { - continue; - } - - if ($parent->valueVar !== $node) { - continue; - } - - $assignExpressions[] = $this->getExpressionFromForeachValue($parent, $item); - } - - return $assignExpressions; - } - - private function getExpressionFromForeachValue(Foreach_ $foreach, ArrayItem $arrayItem): Expression - { - $defaultValueVar = $this->inflectorSingularResolver->resolve((string) $this->getName($foreach->expr)); - $scope = $foreach->getAttribute(AttributeKey::SCOPE); - $newValueVar = $this->variableNaming->createCountedValueName($defaultValueVar, $scope); - $assign = new Assign($arrayItem->value, new ArrayDimFetch(new Variable($newValueVar), $arrayItem->key)); - - return new Expression($assign); - } -} diff --git a/rules/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector.php b/rules/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector.php deleted file mode 100644 index 835cabf2a5f..00000000000 --- a/rules/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector.php +++ /dev/null @@ -1,115 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class, String_::class, Variable::class, PropertyFetch::class, StaticPropertyFetch::class]; - } - - /** - * @param FuncCall|String_|Variable|PropertyFetch|StaticPropertyFetch $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof FuncCall) { - return $this->processForFuncCall($node); - } - - return $this->processForStringOrVariableOrProperty($node); - } - - private function processForStringOrVariableOrProperty( - String_ | Variable | PropertyFetch | StaticPropertyFetch $expr - ): ?Expr { - $nextNode = $expr->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof UnaryMinus) { - return null; - } - - $parentOfNextNode = $nextNode->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentOfNextNode instanceof ArrayDimFetch) { - return null; - } - if (! $this->nodeComparator->areNodesEqual($parentOfNextNode->dim, $nextNode)) { - return null; - } - - /** @var UnaryMinus $dim */ - $dim = $parentOfNextNode->dim; - - $strlenFuncCall = $this->nodeFactory->createFuncCall('strlen', [$expr]); - $parentOfNextNode->dim = new Minus($strlenFuncCall, $dim->expr); - - return $expr; - } - - private function processForFuncCall(FuncCall $funcCall): ?FuncCall - { - $name = $this->getName($funcCall); - if ($name !== 'strpos') { - return null; - } - - $args = $funcCall->args; - if (! isset($args[2])) { - return null; - } - - if (! $args[2]->value instanceof UnaryMinus) { - return null; - } - - $strlenFuncCall = $this->nodeFactory->createFuncCall('strlen', [$args[0]]); - $funcCall->args[2]->value = new Minus($strlenFuncCall, $args[2]->value->expr); - - return $funcCall; - } -} diff --git a/rules/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector.php b/rules/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector.php deleted file mode 100644 index acebcc0cd21..00000000000 --- a/rules/DowngradePhp71/Rector/TryCatch/DowngradePipeToMultiCatchExceptionRector.php +++ /dev/null @@ -1,83 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [TryCatch::class]; - } - - /** - * @param TryCatch $node - */ - public function refactor(Node $node): ?Node - { - $originalCatches = $node->catches; - foreach ($node->catches as $key => $catch) { - if (count($catch->types) === 1) { - continue; - } - - $types = $catch->types; - $node->catches[$key]->types = [$catch->types[0]]; - foreach ($types as $keyCatchType => $catchType) { - if ($keyCatchType === 0) { - continue; - } - - $this->addNodeAfterNode(new Catch_([$catchType], $catch->var, $catch->stmts), $node->catches[$key]); - } - } - - if ($this->nodeComparator->areNodesEqual($originalCatches, $node->catches)) { - return null; - } - - return $node; - } -} diff --git a/rules/DowngradePhp72/Contract/Rector/DowngradeTypeRectorInterface.php b/rules/DowngradePhp72/Contract/Rector/DowngradeTypeRectorInterface.php deleted file mode 100644 index 8529ddc1aab..00000000000 --- a/rules/DowngradePhp72/Contract/Rector/DowngradeTypeRectorInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -isClass()) { - return false; - } - - $methodName = $this->nodeNameResolver->getName($classMethod); - if ($this->classChildAnalyzer->hasChildClassMethod($classReflection, $methodName)) { - return false; - } - - foreach ($classReflection->getInterfaces() as $interfaceReflection) { - if (! $interfaceReflection->isBuiltIn()) { - continue; - } - - if (! $interfaceReflection->hasMethod($methodName)) { - continue; - } - - return true; - } - - return false; - } -} diff --git a/rules/DowngradePhp72/NodeAnalyzer/FunctionExistsFunCallAnalyzer.php b/rules/DowngradePhp72/NodeAnalyzer/FunctionExistsFunCallAnalyzer.php deleted file mode 100644 index e20e3d5d9b8..00000000000 --- a/rules/DowngradePhp72/NodeAnalyzer/FunctionExistsFunCallAnalyzer.php +++ /dev/null @@ -1,44 +0,0 @@ -betterNodeFinder->findParentType($expr, If_::class); - if (! $firstParentIf instanceof If_) { - return false; - } - - if (! $firstParentIf->cond instanceof FuncCall) { - return false; - } - - if (! $this->nodeNameResolver->isName($firstParentIf->cond, 'function_exists')) { - return false; - } - - /** @var FuncCall $functionExists */ - $functionExists = $firstParentIf->cond; - - return $this->valueResolver->isValue($functionExists->args[0]->value, $functionName); - } -} diff --git a/rules/DowngradePhp72/NodeAnalyzer/OverrideFromAnonymousClassMethodAnalyzer.php b/rules/DowngradePhp72/NodeAnalyzer/OverrideFromAnonymousClassMethodAnalyzer.php deleted file mode 100644 index 84ce9d14d8e..00000000000 --- a/rules/DowngradePhp72/NodeAnalyzer/OverrideFromAnonymousClassMethodAnalyzer.php +++ /dev/null @@ -1,58 +0,0 @@ -classAnalyzer->isAnonymousClass($classLike)) { - return false; - } - - /** @var Class_ $classLike */ - if (! $classLike->extends instanceof FullyQualified) { - return false; - } - - $extendsClass = $classLike->extends->toString(); - if (! $this->reflectionProvider->hasClass($extendsClass)) { - return false; - } - - $classReflection = $this->reflectionProvider->getClass($extendsClass); - $methodName = $this->nodeNameResolver->getName($classMethod); - - if (! $classReflection->hasMethod($methodName)) { - return false; - } - - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - $method = $classReflection->getMethod($methodName, $scope); - - if (! $method instanceof PhpMethodReflection) { - return false; - } - - return ! $method->isPrivate(); - } -} diff --git a/rules/DowngradePhp72/NodeAnalyzer/SealedClassAnalyzer.php b/rules/DowngradePhp72/NodeAnalyzer/SealedClassAnalyzer.php deleted file mode 100644 index ea6cf4bcb82..00000000000 --- a/rules/DowngradePhp72/NodeAnalyzer/SealedClassAnalyzer.php +++ /dev/null @@ -1,26 +0,0 @@ -isClass()) { - return false; - } - - if (! $classReflection->isFinal()) { - return false; - } - - return count($classReflection->getAncestors()) === 1; - } -} diff --git a/rules/DowngradePhp72/PhpDoc/NativeParamToPhpDocDecorator.php b/rules/DowngradePhp72/PhpDoc/NativeParamToPhpDocDecorator.php deleted file mode 100644 index a54280cd996..00000000000 --- a/rules/DowngradePhp72/PhpDoc/NativeParamToPhpDocDecorator.php +++ /dev/null @@ -1,49 +0,0 @@ -type === null) { - return; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - - $paramName = $this->nodeNameResolver->getName($param); - $mappedCurrentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - - // add default null type - if ($param->default !== null && $this->valueResolver->isNull($param->default) && ! TypeCombinator::containsNull( - $mappedCurrentParamType - )) { - $mappedCurrentParamType = new UnionType([$mappedCurrentParamType, new NullType()]); - } - - $this->phpDocTypeChanger->changeParamType($phpDocInfo, $mappedCurrentParamType, $param, $paramName); - } -} diff --git a/rules/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector.php b/rules/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector.php deleted file mode 100644 index 12e2a807586..00000000000 --- a/rules/DowngradePhp72/Rector/ClassMethod/DowngradeParameterTypeWideningRector.php +++ /dev/null @@ -1,275 +0,0 @@ - - */ - private array $safeTypesToMethods = []; - - public function __construct( - private NativeParamToPhpDocDecorator $nativeParamToPhpDocDecorator, - private ReflectionProvider $reflectionProvider, - private AutowiredClassMethodOrPropertyAnalyzer $autowiredClassMethodOrPropertyAnalyzer, - private BuiltInMethodAnalyzer $builtInMethodAnalyzer, - private OverrideFromAnonymousClassMethodAnalyzer $overrideFromAnonymousClassMethodAnalyzer, - private AstResolver $astResolver, - private SealedClassAnalyzer $sealedClassAnalyzer - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Change param type to match the lowest type in whole family tree', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -interface SomeInterface -{ - public function test(array $input); -} - -final class SomeClass implements SomeInterface -{ - public function test($input) - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -interface SomeInterface -{ - /** - * @param mixed[] $input - */ - public function test($input); -} - -final class SomeClass implements SomeInterface -{ - public function test($input) - { - } -} -CODE_SAMPLE - , - [ - self::SAFE_TYPES => [], - self::SAFE_TYPES_TO_METHODS => [], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if ($classLike === null) { - return null; - } - - if ($this->overrideFromAnonymousClassMethodAnalyzer->isOverrideParentMethod($classLike, $node)) { - $classReflection = $this->reflectionProvider->getClass($classLike->extends->toString()); - $methodName = $this->nodeNameResolver->getName($node); - /** @var ClassMethod $classMethod */ - $classMethod = $this->astResolver->resolveClassMethod($classReflection->getName(), $methodName); - - if ($this->shouldSkip($classReflection, $classMethod)) { - return null; - } - - return $this->processRemoveParamTypeFromMethod($node); - } - - $className = $this->nodeNameResolver->getName($classLike); - if ($className === null) { - return null; - } - - if (! $this->reflectionProvider->hasClass($className)) { - return null; - } - - $classReflection = $this->reflectionProvider->getClass($className); - if ($this->shouldSkip($classReflection, $node)) { - return null; - } - - if ($this->builtInMethodAnalyzer->isImplementsBuiltInInterface($classReflection, $node)) { - return null; - } - - return $this->processRemoveParamTypeFromMethod($node); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $safeTypes = $configuration[self::SAFE_TYPES] ?? []; - Assert::allString($safeTypes); - $this->safeTypes = $safeTypes; - - $safeTypesToMethods = $configuration[self::SAFE_TYPES_TO_METHODS] ?? []; - Assert::isArray($safeTypesToMethods); - foreach ($safeTypesToMethods as $key => $value) { - Assert::string($key); - Assert::allString($value); - } - - $this->safeTypesToMethods = $safeTypesToMethods; - } - - private function shouldSkip(ClassReflection $classReflection, ClassMethod $classMethod): bool - { - if ($this->sealedClassAnalyzer->isSealedClass($classReflection)) { - return true; - } - - if ($this->isSafeType($classReflection, $classMethod)) { - return true; - } - - if ($classMethod->isPrivate()) { - return true; - } - - return $this->shouldSkipClassMethod($classMethod); - } - - private function processRemoveParamTypeFromMethod(ClassMethod $classMethod): ClassMethod - { - // Downgrade every scalar parameter, just to be sure - foreach (array_keys($classMethod->params) as $paramPosition) { - $this->removeParamTypeFromMethod($classMethod, $paramPosition); - } - - return $classMethod; - } - - private function removeParamTypeFromMethod(ClassMethod $classMethod, int $paramPosition): void - { - $param = $classMethod->params[$paramPosition] ?? null; - if (! $param instanceof Param) { - return; - } - - // It already has no type => nothing to do - check original param, as it could have been removed by this rule - if ($param->type === null) { - return; - } - - // Add the current type in the PHPDoc - $this->nativeParamToPhpDocDecorator->decorate($classMethod, $param); - $param->type = null; - } - - private function shouldSkipClassMethod(ClassMethod $classMethod): bool - { - if ($classMethod->isMagic()) { - return true; - } - - if ($classMethod->params === []) { - return true; - } - - if ($this->autowiredClassMethodOrPropertyAnalyzer->detect($classMethod)) { - return true; - } - - foreach ($classMethod->params as $param) { - if ($param->type !== null) { - return false; - } - } - - return true; - } - - private function isSafeType(ClassReflection $classReflection, ClassMethod $classMethod): bool - { - foreach ($this->safeTypes as $safeType) { - if ($classReflection->isSubclassOf($safeType)) { - return true; - } - - // skip self too - if ($classReflection->getName() === $safeType) { - return true; - } - } - - foreach ($this->safeTypesToMethods as $safeType => $safeMethods) { - if (! $this->isNames($classMethod, $safeMethods)) { - continue; - } - - if ($classReflection->isSubclassOf($safeType)) { - return true; - } - - // skip self too - if ($classReflection->getName() === $safeType) { - return true; - } - } - - return false; - } -} diff --git a/rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php b/rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php deleted file mode 100644 index adcabb73ec8..00000000000 --- a/rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php +++ /dev/null @@ -1,337 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class, ClassConst::class]; - } - - /** - * @param FuncCall|ClassConst $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof ClassConst) { - return $this->processsClassConst($node); - } - - if (! $this->isRegexFunctionNames($node)) { - return null; - } - - $args = $node->args; - if (! isset($args[3])) { - return null; - } - - $flags = $args[3]->value; - /** @var Variable $variable */ - $variable = $args[2]->value; - - if ($flags instanceof BitwiseOr) { - $this->cleanBitWiseOrFlags($node, $flags); - if (! $this->nodeComparator->areNodesEqual($flags, $node->args[3]->value)) { - return $this->handleEmptyStringToNullMatch($node, $variable); - } - - return null; - } - - if (! $flags instanceof ConstFetch) { - return null; - } - - if (! $this->isName($flags, self::FLAG)) { - return null; - } - - $node = $this->handleEmptyStringToNullMatch($node, $variable); - unset($node->args[3]); - - return $node; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove PREG_UNMATCHED_AS_NULL from preg_match and set null value on empty string matched on each match', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - preg_match('/(a)(b)*(c)/', 'ac', $matches, PREG_UNMATCHED_AS_NULL); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - preg_match('/(a)(b)*(c)/', 'ac', $matches); - array_walk_recursive($matches, function (&$value) { - if ($value === '') { - $value = null; - } - }); - } -} -CODE_SAMPLE - ), - ] - ); - } - - private function processsClassConst(ClassConst $classConst): ClassConst - { - foreach ($classConst->consts as $key => $singleClassConst) { - if (! $singleClassConst->value instanceof ConstFetch) { - continue; - } - if (! $this->isName($singleClassConst->value, self::FLAG)) { - continue; - } - $classConst->consts[$key]->value = new LNumber(512); - return $classConst; - } - - return $classConst; - } - - private function isRegexFunctionNames(FuncCall $funcCall): bool - { - if ($this->isNames($funcCall, self::REGEX_FUNCTION_NAMES)) { - return true; - } - - $variable = $funcCall->name; - if (! $variable instanceof Variable) { - return false; - } - - /** @var Assign|null $assignExprVariable */ - $assignExprVariable = $this->betterNodeFinder->findFirstPreviousOfNode($funcCall, function (Node $node) use ( - $variable - ): bool { - if (! $node instanceof Assign) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node->var, $variable); - }); - - if (! $assignExprVariable instanceof Assign) { - return false; - } - - $expr = $assignExprVariable->expr; - if (! $expr instanceof Ternary) { - return false; - } - - if (! $expr->if instanceof String_) { - return false; - } - - if (! $expr->else instanceof String_) { - return false; - } - - return in_array($expr->if->value, self::REGEX_FUNCTION_NAMES, true) && in_array( - $expr->else->value, - self::REGEX_FUNCTION_NAMES, - true - ); - } - - private function cleanBitWiseOrFlags(FuncCall $funcCall, BitwiseOr $bitwiseOr, ?Expr $expr = null): void - { - if ($bitwiseOr->left instanceof BitwiseOr) { - /** @var BitwiseOr $leftLeft */ - $leftLeft = $bitwiseOr->left; - if ($leftLeft->left instanceof ConstFetch && $this->isName($leftLeft->left, self::FLAG)) { - $bitwiseOr = new BitwiseOr($leftLeft->right, $bitwiseOr->right); - } - - /** @var BitwiseOr $leftRight */ - $leftRight = $bitwiseOr->left; - if ($leftRight->right instanceof ConstFetch && $this->isName($leftRight->right, self::FLAG)) { - $bitwiseOr = new BitwiseOr($leftRight->left, $bitwiseOr->right); - } - - if ($bitwiseOr->left instanceof BitwiseOr) { - $this->cleanBitWiseOrFlags($funcCall, $bitwiseOr->left, $bitwiseOr->right); - return; - } - } - - if ($expr instanceof Expr) { - $bitwiseOr = new BitwiseOr($bitwiseOr, $expr); - } - - $this->assignThirdArgsValue($funcCall, $bitwiseOr); - } - - private function assignThirdArgsValue(FuncCall $funcCall, BitwiseOr $bitwiseOr): void - { - if ($bitwiseOr instanceof BitWiseOr && $bitwiseOr->right instanceof ConstFetch && $this->isName( - $bitwiseOr->right, - self::FLAG - )) { - $bitwiseOr = $bitwiseOr->left; - } - - if ($bitwiseOr instanceof BitWiseOr && $bitwiseOr->left instanceof ConstFetch && $this->isName( - $bitwiseOr->left, - self::FLAG - )) { - $bitwiseOr = $bitwiseOr->right; - } - - $funcCall->args[3]->value = $bitwiseOr; - } - - private function handleEmptyStringToNullMatch(FuncCall $funcCall, Variable $variable): FuncCall - { - $closure = new Closure(); - $variablePass = new Variable('value'); - $param = new Param($variablePass); - $param->byRef = true; - $closure->params = [$param]; - - $assign = new Assign($variablePass, $this->nodeFactory->createNull()); - - $if = $this->ifManipulator->createIfExpr( - new Identical($variablePass, new String_('')), - new Expression($assign) - ); - - $closure->stmts[0] = $if; - - $arguments = $this->nodeFactory->createArgs([$variable, $closure]); - $replaceEmptystringToNull = $this->nodeFactory->createFuncCall('array_walk_recursive', $arguments); - - return $this->processReplace($funcCall, $replaceEmptystringToNull); - } - - private function processReplace(FuncCall $funcCall, FuncCall $replaceEmptystringToNull): FuncCall - { - $parent = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Expression) { - $this->addNodeAfterNode($replaceEmptystringToNull, $funcCall); - return $funcCall; - } - - if ($parent instanceof If_ && $parent->cond === $funcCall) { - return $this->processInIf($parent, $funcCall, $replaceEmptystringToNull); - } - - if (! $parent instanceof Node) { - throw new NotImplementedException(); - } - - $if = $parent->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof BooleanNot) { - return $this->processInIf($if, $funcCall, $replaceEmptystringToNull); - } - - if (! $parent instanceof Identical) { - throw new NotImplementedYetException(); - } - - if (! $if instanceof If_) { - throw new NotImplementedYetException(); - } - - return $this->processInIf($if, $funcCall, $replaceEmptystringToNull); - } - - private function processInIf(If_ $if, FuncCall $funcCall, FuncCall $replaceEmptystringToNull): FuncCall - { - $cond = $if->cond; - - if (! $cond instanceof Identical && ! $cond instanceof BooleanNot) { - $this->handleNotInIdenticalAndBooleanNot($if, $replaceEmptystringToNull); - } - - if ($cond instanceof Identical) { - $valueCompare = $cond->left === $funcCall - ? $cond->right - : $cond->left; - if ($this->valueResolver->isFalse($valueCompare)) { - $this->addNodeAfterNode($replaceEmptystringToNull, $if); - } - } - - if ($cond instanceof BooleanNot) { - $this->addNodeAfterNode($replaceEmptystringToNull, $if); - } - - return $funcCall; - } - - private function handleNotInIdenticalAndBooleanNot(If_ $if, FuncCall $funcCall): void - { - if ($if->stmts !== []) { - $firstStmt = $if->stmts[0]; - $this->addNodeBeforeNode($funcCall, $firstStmt); - } else { - $if->stmts[0] = new Expression($funcCall); - } - } -} diff --git a/rules/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector.php b/rules/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector.php deleted file mode 100644 index 215948c1960..00000000000 --- a/rules/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector.php +++ /dev/null @@ -1,123 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'stream_isatty')) { - return null; - } - - if ($this->functionExistsFunCallAnalyzer->detect($node, 'stream_isatty')) { - return null; - } - - $function = $this->createClosure(); - $assign = new Assign(new Variable('streamIsatty'), $function); - - $this->addNodeBeforeNode($assign, $node); - - return new FuncCall(new Variable('streamIsatty'), $node->args); - } - - private function createClosure(): Closure - { - $stmts = $this->inlineCodeParser->parse(__DIR__ . '/../../snippet/isatty_closure.php.inc'); - - /** @var Expression $expression */ - $expression = $stmts[0]; - - $expr = $expression->expr; - if (! $expr instanceof Closure) { - throw new ShouldNotHappenException(); - } - - return $expr; - } -} diff --git a/rules/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector.php b/rules/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector.php deleted file mode 100644 index de0e85d0d65..00000000000 --- a/rules/DowngradePhp72/Rector/FunctionLike/DowngradeObjectTypeDeclarationRector.php +++ /dev/null @@ -1,90 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Function_::class, ClassMethod::class]; - } - - /** - * @param Function_|ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $objectWithoutClassType = new ObjectWithoutClassType(); - - foreach ($node->params as $param) { - $this->phpDocFromTypeDeclarationDecorator->decorateParamWithSpecificType( - $param, - $node, - $objectWithoutClassType - ); - } - - if (! $this->phpDocFromTypeDeclarationDecorator->decorateReturnWithSpecificType( - $node, - $objectWithoutClassType - )) { - return null; - } - - return $node; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove the "object" param and return type, add a @param and @return tags instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function someFunction(object $someObject): object - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @param object $someObject - * @return object - */ - public function someFunction($someObject) - { - } -} -CODE_SAMPLE - ), - ] - ); - } -} diff --git a/rules/DowngradePhp72/snippet/isatty_closure.php.inc b/rules/DowngradePhp72/snippet/isatty_closure.php.inc deleted file mode 100644 index 2d543e68cb7..00000000000 --- a/rules/DowngradePhp72/snippet/isatty_closure.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->isName($node, 'array_key_first')) { - return $this->refactorArrayKeyFirst($node); - } - - if ($this->isName($node, 'array_key_last')) { - return $this->refactorArrayKeyLast($node); - } - - return null; - } - - private function refactorArrayKeyFirst(FuncCall $funcCall): FuncCall - { - $array = $funcCall->args[0]->value; - - $resetFuncCall = $this->nodeFactory->createFuncCall('reset', [$array]); - $this->addNodeBeforeNode($resetFuncCall, $funcCall); - - $funcCall->name = new Name('key'); - - return $funcCall; - } - - private function refactorArrayKeyLast(FuncCall $funcCall): FuncCall - { - $array = $funcCall->args[0]->value; - $resetFuncCall = $this->nodeFactory->createFuncCall('end', [$array]); - $this->addNodeBeforeNode($resetFuncCall, $funcCall); - - $funcCall->name = new Name('key'); - - return $funcCall; - } -} diff --git a/rules/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector.php b/rules/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector.php deleted file mode 100644 index d7a695f9e2a..00000000000 --- a/rules/DowngradePhp73/Rector/FuncCall/DowngradeIsCountableRector.php +++ /dev/null @@ -1,63 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'is_countable')) { - return null; - } - - $isArrayFuncCall = $this->nodeFactory->createFuncCall('is_array', $node->args); - $instanceof = new Instanceof_($node->args[0]->value, new FullyQualified('Countable')); - - return new BooleanOr($isArrayFuncCall, $instanceof); - } -} diff --git a/rules/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector.php b/rules/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector.php deleted file mode 100644 index cbd84b8ce57..00000000000 --- a/rules/DowngradePhp73/Rector/FuncCall/DowngradeTrailingCommasInFunctionCallsRector.php +++ /dev/null @@ -1,91 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class, MethodCall::class, StaticCall::class]; - } - - /** - * @param FuncCall|MethodCall|StaticCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node->args) { - $lastArgumentPosition = array_key_last($node->args); - - $last = $node->args[$lastArgumentPosition]; - if (! $this->followedByCommaAnalyzer->isFollowed($this->file, $last)) { - return null; - } - - // remove comma - $last->setAttribute(AttributeKey::FUNC_ARGS_TRAILING_COMMA, false); - $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); - } - - return $node; - } -} diff --git a/rules/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector.php b/rules/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector.php deleted file mode 100644 index 2eeccf887db..00000000000 --- a/rules/DowngradePhp73/Rector/FuncCall/SetCookieOptionsArrayToArgumentsRector.php +++ /dev/null @@ -1,182 +0,0 @@ - - */ - private const ARGUMENT_ORDER = [ - 'expires' => 2, - 'path' => 3, - 'domain' => 4, - 'secure' => 5, - 'httponly' => 6, - ]; - - /** - * Conversion table from argument index to options name - * @var array - */ - private const ARGUMENT_DEFAULT_VALUES = [ - 2 => 0, - 3 => '', - 4 => '', - 5 => false, - 6 => false, - ]; - - private int $highestIndex = 1; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Convert setcookie option array to arguments', - [ - new CodeSample( - <<<'CODE_SAMPLE' -setcookie('name', $value, ['expires' => 360]); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -setcookie('name', $value, 360); -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - $node->args = $this->composeNewArgs($node); - - return $node; - } - - private function shouldSkip(FuncCall $funcCall): bool - { - if (! $this->isNames($funcCall, ['setcookie', 'setrawcookie'])) { - return true; - } - - $argsCount = count($funcCall->args); - if ($argsCount <= 2) { - return true; - } - - if (! isset($funcCall->args[2])) { - return true; - } - - return ! ($funcCall->args[2]->value instanceof Array_); - } - - /** - * @return Arg[] - */ - private function composeNewArgs(FuncCall $funcCall): array - { - $this->highestIndex = 1; - - $newArgs = [$funcCall->args[0], $funcCall->args[1]]; - - /** @var Array_ $optionsArray */ - $optionsArray = $funcCall->args[2]->value; - /** @var ArrayItem|null $arrayItem */ - foreach ($optionsArray->items as $arrayItem) { - if ($arrayItem === null) { - continue; - } - - $value = $arrayItem->value; - - /** @var String_ $key */ - $key = $arrayItem->key; - $name = $key->value; - - if (! $this->isMappableArrayKey($name)) { - continue; - } - - $order = self::ARGUMENT_ORDER[$name]; - if ($order > $this->highestIndex) { - $this->highestIndex = $order; - } - - $newArgs[$order] = new Arg($value); - } - - $newArgs = $this->fillMissingArgumentsWithDefaultValues($newArgs); - ksort($newArgs); - - return $newArgs; - } - - private function isMappableArrayKey(string $key): bool - { - return isset(self::ARGUMENT_ORDER[$key]); - } - - /** - * @param Arg[] $args - * @return Arg[] - */ - private function fillMissingArgumentsWithDefaultValues(array $args): array - { - for ($i = 1; $i < $this->highestIndex; ++$i) { - if (isset($args[$i])) { - continue; - } - - $args[$i] = $this->createDefaultValueArg($i); - } - - return $args; - } - - private function createDefaultValueArg(int $argumentIndex): Arg - { - if (! array_key_exists($argumentIndex, self::ARGUMENT_DEFAULT_VALUES)) { - throw new ShouldNotHappenException(); - } - - $argumentDefaultValue = self::ARGUMENT_DEFAULT_VALUES[$argumentIndex]; - $expr = BuilderHelpers::normalizeValue($argumentDefaultValue); - - return new Arg($expr); - } -} diff --git a/rules/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector.php b/rules/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector.php deleted file mode 100644 index 2de259f827b..00000000000 --- a/rules/DowngradePhp73/Rector/List_/DowngradeListReferenceAssignmentRector.php +++ /dev/null @@ -1,347 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [List_::class, Array_::class]; - } - - /** - * @param List_|Array_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->shouldRefactor($node)) { - return null; - } - - // Get all the params passed by reference - /** @var Assign $parentNode */ - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - - /** @var Variable $exprVariable */ - $exprVariable = $parentNode->expr; - - // Count number of params by ref on the right side, to remove them later on - $rightSideRemovableParamsCount = $this->countRightSideMostParamsByRefOrEmpty($node->items); - - // Add new nodes to do the assignment by reference - $newNodes = $this->createAssignRefArrayFromListReferences($node->items, $exprVariable, []); - $this->addNodesAfterNode($newNodes, $node); - - // Remove the stale params right-most-side - return $this->removeStaleParams($node, $rightSideRemovableParamsCount); - } - - /** - * Remove the right-side-most params by reference or empty from `list()`, - * since they are not needed anymore. - * If all of them can be removed, then directly remove `list()`. - * @return List_|Array_|null - */ - public function removeStaleParams(List_ | Array_ $node, int $rightSideRemovableParamsCount): ?Node - { - $nodeItemsCount = count($node->items); - if ($rightSideRemovableParamsCount === $nodeItemsCount) { - // Remove the parent Assign node - /** @var Assign $parentNode */ - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - $this->removeNode($parentNode); - return null; - } - if ($rightSideRemovableParamsCount > 0) { - array_splice($node->items, $nodeItemsCount - $rightSideRemovableParamsCount); - } - return $node; - } - - private function shouldRefactor(List_ | Array_ $node): bool - { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - // Check it follows `list(...) = $foo` - if (! $parentNode instanceof Assign) { - return false; - } - if ($parentNode->var !== $node) { - return false; - } - if (! $parentNode->expr instanceof Variable) { - return false; - } - return $this->hasAnyItemByRef($node->items); - } - - /** - * Count the number of params by reference placed at the end - * These params are not needed anymore, so they can be removed - * @param (ArrayItem|null)[] $listItems - */ - private function countRightSideMostParamsByRefOrEmpty(array $listItems): int - { - // Their position is kept in the array - $count = 0; - $listItemsCount = count($listItems); - // Start from the end => right-side-most params - for ($i = $listItemsCount - 1; $i >= 0; --$i) { - $listItem = $listItems[$i]; - // Also include null items, since they can be removed - if (! $listItem instanceof ArrayItem || $listItem->byRef) { - ++$count; - continue; - } - // If it is a nested list, check if all its items are by reference - $isNested = $listItem->value instanceof List_ || $listItem->value instanceof Array_; - if ($isNested) { - /** @var List_|Array_ $nestedList */ - $nestedList = $listItem->value; - if ($this->hasAllItemsByRef($nestedList->items)) { - ++$count; - continue; - } - } - // Item not by reference. Reach the end - return $count; - } - return $count; - } - - /** - * @param (ArrayItem|null)[] $listItems - * @param (int|string)[] $nestedArrayIndexes - * @return AssignRef[] - */ - private function createAssignRefArrayFromListReferences( - array $listItems, - Variable $exprVariable, - array $nestedArrayIndexes - ): array { - // After filtering, their original position is kept in the array - $newNodes = []; - foreach ($listItems as $position => $listItem) { - if (! $listItem instanceof ArrayItem) { - continue; - } - if ($listItem->value instanceof Variable && ! $listItem->byRef) { - continue; - } - // Access the key, if provided, or the position otherwise - $key = $this->getArrayItemKey($listItem, $position); - // Either the item is a variable, or a nested list - if ($listItem->value instanceof Variable) { - /** @var Variable $itemVariable */ - $itemVariable = $listItem->value; - // Remove the reference in the present node - $listItem->byRef = false; - // In its place, assign the value by reference on a new node - $assignVariable = new Variable($itemVariable->name); - $newNodes[] = $this->createAssignRefWithArrayDimFetch( - $assignVariable, - $exprVariable, - $nestedArrayIndexes, - $key - ); - continue; - } - // Nested list. Combine with the nodes from the recursive call - /** @var List_ $nestedList */ - $nestedList = $listItem->value; - $listNestedArrayIndexes = array_merge($nestedArrayIndexes, [$key]); - $newNodes = array_merge( - $newNodes, - $this->createAssignRefArrayFromListReferences( - $nestedList->items, - $exprVariable, - $listNestedArrayIndexes - ) - ); - } - return $newNodes; - } - - /** - * Indicates if there is at least 1 item passed by reference, as in: - * - list(&$a, $b) - * - list($a, $b, list(&$c, $d)) - * - * @param (ArrayItem|null)[] $items - */ - private function hasAnyItemByRef(array $items): bool - { - return $this->getItemsByRef($items, self::ANY) !== []; - } - - /** - * Indicates if there is all items are passed by reference, as in: - * - list(&$a, &$b) - * - list(&$a, &$b, list(&$c, &$d)) - * - * @param (ArrayItem|null)[] $items - */ - private function hasAllItemsByRef(array $items): bool - { - return count($this->getItemsByRef($items, self::ALL)) === count($items); - } - - /** - * Return the key inside the ArrayItem, if provided, or the position otherwise - */ - private function getArrayItemKey(ArrayItem $arrayItem, int | string $position): int | string - { - if ($arrayItem->key instanceof String_) { - return $arrayItem->key->value; - } - if ($arrayItem->key instanceof LNumber) { - return $arrayItem->key->value; - } - return $position; - } - - /** - * Re-build the path to the variable with all accumulated indexes - * @param (string|int)[] $nestedArrayIndexes The path to build nested lists - */ - private function createAssignRefWithArrayDimFetch( - Variable $assignVariable, - Variable $exprVariable, - array $nestedArrayIndexes, - string | int $arrayIndex - ): AssignRef { - $nestedExprVariable = $exprVariable; - foreach ($nestedArrayIndexes as $nestedArrayIndex) { - $nestedArrayIndexDim = BuilderHelpers::normalizeValue($nestedArrayIndex); - $nestedExprVariable = new ArrayDimFetch($nestedExprVariable, $nestedArrayIndexDim); - } - $dim = BuilderHelpers::normalizeValue($arrayIndex); - $arrayDimFetch = new ArrayDimFetch($nestedExprVariable, $dim); - return new AssignRef($assignVariable, $arrayDimFetch); - } - - /** - * @param array $arrayItems - * @return ArrayItem[] - */ - private function getItemsByRef(array $arrayItems, int $condition): array - { - $itemsByRef = []; - - foreach ($arrayItems as $arrayItem) { - if ($arrayItem === null) { - continue; - } - - if (! $this->isItemByRef($arrayItem, $condition)) { - continue; - } - - $itemsByRef[] = $arrayItem; - } - - return $itemsByRef; - } - - /** - * Indicate if the item is a variable by reference, - * or a nested list containing variables by reference - */ - private function isItemByRef(ArrayItem $arrayItem, int $condition): bool - { - // Check if the item is a nested list/nested array destructuring - $isNested = $arrayItem->value instanceof List_ || $arrayItem->value instanceof Array_; - if ($isNested) { - // Recursive call - /** @var List_|Array_ $nestedList */ - $nestedList = $arrayItem->value; - if ($condition === self::ALL) { - return $this->hasAllItemsByRef($nestedList->items); - } - // $condition === self::ANY - return $this->hasAnyItemByRef($nestedList->items); - } - if (! $arrayItem->value instanceof Variable) { - return false; - } - return $arrayItem->byRef; - } -} diff --git a/rules/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector.php b/rules/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector.php deleted file mode 100644 index e33bf1fab96..00000000000 --- a/rules/DowngradePhp73/Rector/String_/DowngradeFlexibleHeredocSyntaxRector.php +++ /dev/null @@ -1,80 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [String_::class, Encapsed::class]; - } - - /** - * @param Encapsed|String_ $node - */ - public function refactor(Node $node): ?Node - { - $stringKind = $node->getAttribute(AttributeKey::KIND); - if (! in_array($stringKind, self::HERENOW_DOC_KINDS, true)) { - return null; - } - - // skip correctly indented - $docIndentation = (string) $node->getAttribute(AttributeKey::DOC_INDENTATION); - if ($docIndentation === '') { - return null; - } - - $node->setAttribute(AttributeKey::DOC_INDENTATION, ''); - $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); - - return $node; - } -} diff --git a/rules/DowngradePhp73/Tokenizer/FollowedByCommaAnalyzer.php b/rules/DowngradePhp73/Tokenizer/FollowedByCommaAnalyzer.php deleted file mode 100644 index 4c895234aa5..00000000000 --- a/rules/DowngradePhp73/Tokenizer/FollowedByCommaAnalyzer.php +++ /dev/null @@ -1,37 +0,0 @@ -getOldTokens(); - - $nextTokenPosition = $node->getEndTokenPos() + 1; - while (isset($oldTokens[$nextTokenPosition])) { - $currentToken = $oldTokens[$nextTokenPosition]; - - // only space - if (is_array($currentToken) || Strings::match($currentToken, '#\s+#')) { - ++$nextTokenPosition; - continue; - } - - // without comma - if ($currentToken === ')') { - return false; - } - - break; - } - - return true; - } -} diff --git a/rules/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector.php b/rules/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector.php deleted file mode 100644 index f16132c30d3..00000000000 --- a/rules/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector.php +++ /dev/null @@ -1,273 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Array_::class]; - } - - /** - * @param Array_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->shouldRefactor($node)) { - return null; - } - return $this->refactorNode($node); - } - - private function shouldRefactor(Array_ $array): bool - { - // Check that any item in the array is the spread - foreach ($array->items as $item) { - if (! $item instanceof ArrayItem) { - continue; - } - - if ($item->unpack) { - return true; - } - } - - return false; - } - - private function refactorNode(Array_ $array): FuncCall - { - $newItems = $this->createArrayItems($array); - // Replace this array node with an `array_merge` - return $this->createArrayMerge($array, $newItems); - } - - /** - * Iterate all array items: - * 1. If they use the spread, remove it - * 2. If not, make the item part of an accumulating array, - * to be added once the next spread is found, or at the end - * @return ArrayItem[] - */ - private function createArrayItems(Array_ $array): array - { - $newItems = []; - $accumulatedItems = []; - foreach ($array->items as $position => $item) { - if ($item !== null && $item->unpack) { - // Spread operator found - if (! $item->value instanceof Variable) { - // If it is a not variable, transform it to a variable - $item->value = $this->createVariableFromNonVariable($array, $item, $position); - } - if ($accumulatedItems !== []) { - // If previous items were in the new array, add them first - $newItems[] = $this->createArrayItem($accumulatedItems); - // Reset the accumulated items - $accumulatedItems = []; - } - // Add the current item, still with "unpack = true" (it will be removed later on) - $newItems[] = $item; - continue; - } - - // Normal item, it goes into the accumulated array - $accumulatedItems[] = $item; - } - // Add the remaining accumulated items - if ($accumulatedItems !== []) { - $newItems[] = $this->createArrayItem($accumulatedItems); - } - return $newItems; - } - - /** - * @param (ArrayItem|null)[] $items - */ - private function createArrayMerge(Array_ $array, array $items): FuncCall - { - /** @var Scope $nodeScope */ - $nodeScope = $array->getAttribute(AttributeKey::SCOPE); - return new FuncCall(new Name('array_merge'), array_map(function (ArrayItem $item) use ($nodeScope): Arg { - if ($item !== null && $item->unpack) { - // Do not unpack anymore - $item->unpack = false; - return $this->createArgFromSpreadArrayItem($nodeScope, $item); - } - return new Arg($item); - }, $items)); - } - - /** - * If it is a variable, we add it directly - * Otherwise it could be a function, method, ternary, traversable, etc - * We must then first extract it into a variable, - * as to invoke it only once and avoid potential bugs, - * such as a method executing some side-effect - */ - private function createVariableFromNonVariable( - Array_ $array, - ArrayItem $arrayItem, - int | string $position - ): Variable { - /** @var Scope $nodeScope */ - $nodeScope = $array->getAttribute(AttributeKey::SCOPE); - // The variable name will be item0Unpacked, item1Unpacked, etc, - // depending on their position. - // The number can't be at the end of the var name, or it would - // conflict with the counter (for if that name is already taken) - $variableName = $this->variableNaming->resolveFromNodeWithScopeCountAndFallbackName( - $array, - $nodeScope, - 'item' . $position . 'Unpacked' - ); - // Assign the value to the variable, and replace the element with the variable - $newVariable = new Variable($variableName); - $this->addNodeBeforeNode(new Assign($newVariable, $arrayItem->value), $array); - return $newVariable; - } - - /** - * @param array $items - */ - private function createArrayItem(array $items): ArrayItem - { - return new ArrayItem(new Array_($items)); - } - - private function createArgFromSpreadArrayItem(Scope $nodeScope, ArrayItem $arrayItem): Arg - { - // By now every item is a variable - /** @var Variable $variable */ - $variable = $arrayItem->value; - $variableName = $this->getName($variable) ?? ''; - - // If the variable is not in scope, it's one we just added. - // Then get the type from the attribute - if ($nodeScope->hasVariableType($variableName)->yes()) { - $type = $nodeScope->getVariableType($variableName); - } else { - $originalNode = $arrayItem->getAttribute(AttributeKey::ORIGINAL_NODE); - if ($originalNode instanceof ArrayItem) { - $type = $nodeScope->getType($originalNode->value); - } else { - throw new ShouldNotHappenException(); - } - } - - $iteratorToArrayFuncCall = new FuncCall(new Name('iterator_to_array'), [new Arg($arrayItem)]); - - if ($type !== null) { - // If we know it is an array, then print it directly - // Otherwise PHPStan throws an error: - // "Else branch is unreachable because ternary operator condition is always true." - if ($type instanceof ArrayType) { - return new Arg($arrayItem); - } - // If it is iterable, then directly return `iterator_to_array` - if ($this->isIterableType($type)) { - return new Arg($iteratorToArrayFuncCall); - } - } - - // Print a ternary, handling either an array or an iterator - $inArrayFuncCall = new FuncCall(new Name('is_array'), [new Arg($arrayItem)]); - return new Arg(new Ternary($inArrayFuncCall, $arrayItem, $iteratorToArrayFuncCall)); - } - - /** - * Iterables: objects declaring the interface Traversable, - * For "iterable" type, it can be array - */ - private function isIterableType(Type $type): bool - { - if ($type instanceof IterableType) { - return false; - } - - $traversableObjectType = new ObjectType('Traversable'); - return $traversableObjectType->isSuperTypeOf($type) - ->yes(); - } -} diff --git a/rules/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector.php b/rules/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector.php deleted file mode 100644 index f5f4721169b..00000000000 --- a/rules/DowngradePhp74/Rector/ArrowFunction/ArrowFunctionToAnonymousFunctionRector.php +++ /dev/null @@ -1,76 +0,0 @@ - $delimiter . strtolower($matches[1]); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $delimiter = ","; - $callable = function ($matches) use ($delimiter) { - return $delimiter . strtolower($matches[1]); - }; - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ArrowFunction::class]; - } - - /** - * @param ArrowFunction $node - */ - public function refactor(Node $node): Closure - { - $stmts = [new Return_($node->expr)]; - - return $this->anonymousFunctionFactory->create($node->params, $stmts, $node->returnType); - } -} diff --git a/rules/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector.php b/rules/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector.php deleted file mode 100644 index 141b8c7fd42..00000000000 --- a/rules/DowngradePhp74/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector.php +++ /dev/null @@ -1,269 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, Function_::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Remove contravariant argument type declarations', [ - new CodeSample( - <<<'CODE_SAMPLE' -class ParentType {} -class ChildType extends ParentType {} - -class A -{ - public function contraVariantArguments(ChildType $type) - { - } -} - -class B extends A -{ - public function contraVariantArguments(ParentType $type) - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class ParentType {} -class ChildType extends ParentType {} - -class A -{ - public function contraVariantArguments(ChildType $type) - { - } -} - -class B extends A -{ - /** - * @param ParentType $type - */ - public function contraVariantArguments($type) - { - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @param ClassMethod|Function_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node->params === []) { - return null; - } - - foreach ($node->params as $param) { - $this->refactorParam($param, $node); - } - - return null; - } - - private function isNullableParam(Param $param, FunctionLike $functionLike): bool - { - if ($param->variadic) { - return false; - } - - if ($param->type === null) { - return false; - } - - // Don't consider for Union types - if ($param->type instanceof UnionType) { - return false; - } - - // Contravariant arguments are supported for __construct - if ($this->isName($functionLike, MethodName::CONSTRUCT)) { - return false; - } - - // Check if the type is different from the one declared in some ancestor - return $this->getDifferentParamTypeFromAncestorClass($param, $functionLike) !== null; - } - - private function getDifferentParamTypeFromAncestorClass(Param $param, FunctionLike $functionLike): ?string - { - $scope = $functionLike->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - // possibly trait - return null; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $paramName = $this->getName($param); - - // If it is the NullableType, extract the name from its inner type - /** @var Node $paramType */ - $paramType = $param->type; - - if ($this->paramAnalyzer->isNullable($param)) { - /** @var NullableType $nullableType */ - $nullableType = $paramType; - $paramTypeName = $this->getName($nullableType->type); - } else { - $paramTypeName = $this->getName($paramType); - } - - if ($paramTypeName === null) { - return null; - } - - /** @var string $methodName */ - $methodName = $this->getName($functionLike); - - // parent classes or implemented interfaces - /** @var ClassReflection[] $parentClassReflections */ - $parentClassReflections = array_merge($classReflection->getParents(), $classReflection->getInterfaces()); - - foreach ($parentClassReflections as $parentClassReflection) { - $parentReflectionMethod = $this->resolveParentReflectionMethod($parentClassReflection, $methodName); - - if (! $parentReflectionMethod instanceof ReflectionMethod) { - continue; - } - - $differentAncestorParamTypeName = $this->getDifferentParamTypeFromReflectionMethod( - $parentReflectionMethod, - $paramName, - $paramTypeName - ); - - if ($differentAncestorParamTypeName !== null) { - return $differentAncestorParamTypeName; - } - } - - return null; - } - - private function resolveParentReflectionMethod( - ClassReflection $classReflection, - string $methodName - ): ?ReflectionMethod { - if (! $classReflection->hasMethod($methodName)) { - return null; - } - - $reflectionClass = $classReflection->getNativeReflection(); - - if (! $reflectionClass->hasMethod($methodName)) { - return null; - } - - // Find the param we're looking for - return $reflectionClass->getMethod($methodName); - } - - private function getDifferentParamTypeFromReflectionMethod( - ReflectionMethod $reflectionMethod, - string $paramName, - string $paramTypeName - ): ?string { - /** @var ReflectionParameter[] $parentReflectionMethodParams */ - $parentReflectionMethodParams = $reflectionMethod->getParameters(); - - foreach ($parentReflectionMethodParams as $parentReflectionMethodParam) { - if ($parentReflectionMethodParam->getName() === $paramName) { - /** - * Getting a ReflectionNamedType works from PHP 7.1 onwards - * @see https://www.php.net/manual/en/reflectionparameter.gettype.php#125334 - */ - $reflectionParamType = $parentReflectionMethodParam->getType(); - - /** - * If the type is null, we don't have enough information - * to check if they are different. Then do nothing - */ - if (! $reflectionParamType instanceof ReflectionNamedType) { - continue; - } - - if ($reflectionParamType->getName() !== $paramTypeName) { - // We found it: a different param type in some ancestor - return $reflectionParamType->getName(); - } - } - } - - return null; - } - - private function refactorParam(Param $param, ClassMethod | Function_ $functionLike): void - { - if (! $this->isNullableParam($param, $functionLike)) { - return; - } - - $this->decorateWithDocBlock($functionLike, $param); - $param->type = null; - } - - private function decorateWithDocBlock(ClassMethod | Function_ $functionLike, Param $param): void - { - if ($param->type === null) { - return; - } - - $type = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - $paramName = $this->getName($param->var) ?? ''; - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - $this->phpDocTypeChanger->changeParamType($phpDocInfo, $type, $param, $paramName); - } -} diff --git a/rules/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector.php b/rules/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector.php deleted file mode 100644 index 0cdef870533..00000000000 --- a/rules/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector.php +++ /dev/null @@ -1,230 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - if ($node->returnType === null) { - return null; - } - - $isAlreadyDowngraded = $node->getAttribute(self::ALREADY_DOWNGRADED); - if ($isAlreadyDowngraded) { - return null; - } - - $parentReturnType = $this->resolveDifferentAncestorReturnType($node, $node->returnType); - if ($parentReturnType instanceof MixedType) { - return null; - } - - // The return type name could either be a classname, without the leading "\", - // or one among the reserved identifiers ("static", "self", "iterable", etc) - // To find out which is the case, check if this name exists as a class - $parentReturnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $parentReturnType, - TypeKind::RETURN() - ); - if (! $parentReturnTypeNode instanceof Node) { - return null; - } - - // Make it nullable? - if ($node->returnType instanceof NullableType && ! $parentReturnTypeNode instanceof NullableType && ! $parentReturnTypeNode instanceof UnionType) { - $parentReturnTypeNode = new NullableType($parentReturnTypeNode); - } - - // skip if type is already set - if ($this->nodeComparator->areNodesEqual($parentReturnTypeNode, $node->returnType)) { - return null; - } - - $node->setAttribute(self::ALREADY_DOWNGRADED, true); - - if ($parentReturnType instanceof ThisType) { - return null; - } - - // Add the docblock before changing the type - $this->addDocBlockReturn($node); - $node->returnType = $parentReturnTypeNode; - - return $node; - } - - private function resolveDifferentAncestorReturnType( - ClassMethod $classMethod, - UnionType | NullableType | Name | Identifier $returnTypeNode - ): Type { - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - // possibly trait - return new MixedType(); - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return new MixedType(); - } - - if ($returnTypeNode instanceof UnionType) { - return new MixedType(); - } - - $bareReturnType = $returnTypeNode instanceof NullableType ? $returnTypeNode->type : $returnTypeNode; - - $returnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($bareReturnType); - $methodName = $this->getName($classMethod); - - /** @var ClassReflection[] $parentClassesAndInterfaces */ - $parentClassesAndInterfaces = array_merge($classReflection->getParents(), $classReflection->getInterfaces()); - - foreach ($parentClassesAndInterfaces as $parentClassAndInterface) { - $parentClassAndInterfaceHasMethod = $parentClassAndInterface->hasMethod($methodName); - if (! $parentClassAndInterfaceHasMethod) { - continue; - } - - $classMethodScope = $classMethod->getAttribute(AttributeKey::SCOPE); - $parameterMethodReflection = $parentClassAndInterface->getMethod($methodName, $classMethodScope); - - if (! $parameterMethodReflection instanceof PhpMethodReflection) { - continue; - } - - /** @var Type $parentReturnType */ - $parentReturnType = $this->privatesCaller->callPrivateMethod( - $parameterMethodReflection, - 'getReturnType', - [] - ); - - if ($parentReturnType->equals($returnType)) { - continue; - } - - // This is an ancestor class with a different return type - return $parentReturnType; - } - - return new MixedType(); - } - - private function addDocBlockReturn(ClassMethod $classMethod): void - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - - // keep return type if already set one - if (! $phpDocInfo->getReturnType() instanceof MixedType) { - return; - } - - /** @var Node $returnType */ - $returnType = $classMethod->returnType; - $type = $this->staticTypeMapper->mapPhpParserNodePHPStanType($returnType); - - $this->phpDocTypeChanger->changeReturnType($phpDocInfo, $type); - $this->returnTagRemover->removeReturnTagIfUseless($phpDocInfo, $classMethod); - } -} diff --git a/rules/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector.php b/rules/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector.php deleted file mode 100644 index 0fbfc6641da..00000000000 --- a/rules/DowngradePhp74/Rector/Coalesce/DowngradeNullCoalescingOperatorRector.php +++ /dev/null @@ -1,53 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [AssignCoalesce::class]; - } - - /** - * @param AssignCoalesce $node - */ - public function refactor(Node $node): Assign - { - return new Assign($node->var, new Coalesce($node->var, $node->expr)); - } -} diff --git a/rules/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector.php b/rules/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector.php deleted file mode 100644 index ecaa6672adf..00000000000 --- a/rules/DowngradePhp74/Rector/FuncCall/DowngradeArrayMergeCallWithoutArgumentsRector.php +++ /dev/null @@ -1,87 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->shouldRefactor($node)) { - return null; - } - - $node->args = [new Arg(new Array_())]; - - return $node; - } - - private function shouldRefactor(FuncCall $funcCall): bool - { - if (! $this->isNames($funcCall, ['array_merge', 'array_merge_recursive'])) { - return false; - } - - // If param is provided, do nothing - if ($funcCall->args !== []) { - return false; - } - - return true; - } -} diff --git a/rules/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector.php b/rules/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector.php deleted file mode 100644 index 12a1a97ae22..00000000000 --- a/rules/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector.php +++ /dev/null @@ -1,185 +0,0 @@ -<', ['a', 'p']) . '>'); - - // Variables/consts/properties: if array, change to string - $tags = ['a', 'p']; - strip_tags($string, $tags !== null && is_array($tags) ? '<' . implode('><', $tags) . '>' : $tags); - - // Default case (eg: function call): externalize to var, then if array, change to string - $expr = getTags(); - strip_tags($string, is_array($expr) ? '<' . implode('><', $expr) . '>' : $expr); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkipFuncCall($node)) { - return null; - } - - $allowableTagsParam = $node->args[1]->value; - - if ($allowableTagsParam instanceof Array_) { - // If it is an array, convert it to string - $newExpr = $this->createArrayFromString($allowableTagsParam); - } elseif ($allowableTagsParam instanceof Variable || $allowableTagsParam instanceof PropertyFetch || $allowableTagsParam instanceof ConstFetch || $allowableTagsParam instanceof ClassConstFetch) { - // If it is a variable or a const (other than null), add logic to maybe convert to string - $newExpr = $this->createIsArrayTernaryFromExpression($allowableTagsParam); - } else { - // It is a function or method call, ternary or coalesce, or any other: - // Assign the value to a variable - // First obtain a variable name that does not exist in the node (to not override its value) - $variableName = $this->variableNaming->resolveFromFuncCallFirstArgumentWithSuffix( - $node, - 'AllowableTags', - 'allowableTags', - $node->getAttribute(AttributeKey::SCOPE) - ); - // Assign the value to the variable - $newVariable = new Variable($variableName); - $this->addNodeBeforeNode(new Assign($newVariable, $allowableTagsParam), $node); - - // Apply refactor on the variable - $newExpr = $this->createIsArrayTernaryFromExpression($newVariable); - } - - // Replace the arg with a new one - array_splice($node->args, 1, 1, [new Arg($newExpr)]); - return $node; - } - - private function shouldSkipFuncCall(FuncCall $funcCall): bool - { - if (! $this->isName($funcCall, 'strip_tags')) { - return true; - } - - // If param not provided, do nothing - if (count($funcCall->args) < 2) { - return true; - } - - // Process anything other than String and null (eg: variables, function calls) - $allowableTagsParam = $funcCall->args[1]->value; - - // Skip for string - if ($allowableTagsParam instanceof String_) { - return true; - } - - // already refactored - if ($allowableTagsParam instanceof Ternary && $allowableTagsParam->if !== null) { - return true; - } - - if ($allowableTagsParam instanceof Concat) { - return true; - } - - // Skip for null - // Allow for everything else (Array_, Variable, PropertyFetch, ConstFetch, ClassConstFetch, FuncCall, MethodCall, Coalesce, Ternary, others?) - return $this->valueResolver->isNull($allowableTagsParam); - } - - private function createArrayFromString( - Array_ | Variable | PropertyFetch | ConstFetch | ClassConstFetch $expr - ): Concat { - $args = [new Arg(new String_('><')), new Arg($expr)]; - $implodeFuncCall = new FuncCall(new Name('implode'), $args); - - $concat = new Concat(new String_('<'), $implodeFuncCall); - return new Concat($concat, new String_('>')); - } - - private function createIsArrayTernaryFromExpression( - Variable | PropertyFetch | ConstFetch | ClassConstFetch $expr - ): Ternary { - $isArrayFuncCall = new FuncCall(new Name('is_array'), [new Arg($expr)]); - $nullNotIdentical = new NotIdentical($expr, $this->nodeFactory->createNull()); - $booleanAnd = new BooleanAnd($nullNotIdentical, $isArrayFuncCall); - - return new Ternary($booleanAnd, $this->createArrayFromString($expr), $expr); - } -} diff --git a/rules/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector.php b/rules/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector.php deleted file mode 100644 index 2d9e42d060c..00000000000 --- a/rules/DowngradePhp74/Rector/Identical/DowngradeFreadFwriteFalsyToNegationRector.php +++ /dev/null @@ -1,94 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Identical::class]; - } - - /** - * @param Identical $node - */ - public function refactor(Node $node): ?Node - { - $compareValue = $this->getCompareValue($node); - if (! $compareValue instanceof Expr) { - return null; - } - - if (! $this->valueResolver->isFalse($compareValue)) { - return null; - } - - return new BooleanNot($this->getFunction($node)); - } - - private function getCompareValue(Identical $identical): ?Expr - { - if ($identical->left instanceof FuncCall && $this->isNames($identical->left, self::FUNC_FREAD_FWRITE)) { - return $identical->right; - } - if (! $identical->right instanceof FuncCall) { - return null; - } - if (! $this->isNames($identical->right, self::FUNC_FREAD_FWRITE)) { - return null; - } - return $identical->left; - } - - private function getFunction(Identical $identical): FuncCall - { - /** @var FuncCall $funcCall */ - $funcCall = $identical->left instanceof FuncCall - ? $identical->left - : $identical->right; - - return $funcCall; - } -} diff --git a/rules/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector.php b/rules/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector.php deleted file mode 100644 index 3404203f15a..00000000000 --- a/rules/DowngradePhp74/Rector/LNumber/DowngradeNumericLiteralSeparatorRector.php +++ /dev/null @@ -1,100 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [LNumber::class, DNumber::class]; - } - - /** - * @param LNumber|DNumber $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->shouldRefactor($node)) { - return null; - } - - $numberNode = clone $node; - $numberNodeValue = (string) $numberNode->value; - if (\str_contains($numberNodeValue, '+')) { - return null; - } - - $node->value = (string) $node->value; - - /** - * This code follows a guess, to avoid modifying floats needlessly. - * If the node is a float, but it doesn't contain ".", - * then it's likely that the number was forced to be a float - * by adding ".0" at the end (eg: 0.0). - * Then, add it again. - */ - if ($node instanceof DNumber && ! \str_contains($node->value, '.')) { - $node->value .= '.0'; - } - return $node; - } - - public function shouldRefactor(LNumber | DNumber $node): bool - { - // "_" notation can be applied to decimal numbers only - if ($node instanceof LNumber) { - return $node->getAttribute(AttributeKey::KIND) === LNumber::KIND_DEC; - } - return true; - } -} diff --git a/rules/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector.php b/rules/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector.php deleted file mode 100644 index 87c02f387c7..00000000000 --- a/rules/DowngradePhp74/Rector/Property/DowngradeTypedPropertyRector.php +++ /dev/null @@ -1,86 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Property::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Changes property type definition from type definitions to `@var` annotations.', [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - private string $property; -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var string - */ - private $property; -} -CODE_SAMPLE - ), - ]); - } - - /** - * @param Property $node - */ - public function refactor(Node $node): ?Node - { - if ($node->type === null) { - return null; - } - - if ($node->type instanceof NullableType) { - $node->props[0]->default = null; - } - - $this->decoratePropertyWithDocBlock($node, $node->type); - $node->type = null; - - return $node; - } - - private function decoratePropertyWithDocBlock(Property $property, Node $typeNode): void - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - if ($phpDocInfo->getVarTagValueNode() !== null) { - return; - } - - $newType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($typeNode); - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $newType); - } -} diff --git a/rules/DowngradePhp80/NodeAnalyzer/NamedToUnnamedArgs.php b/rules/DowngradePhp80/NodeAnalyzer/NamedToUnnamedArgs.php deleted file mode 100644 index 8a315ebc56f..00000000000 --- a/rules/DowngradePhp80/NodeAnalyzer/NamedToUnnamedArgs.php +++ /dev/null @@ -1,124 +0,0 @@ - $parameterReflection) { - $parameterReflectionName = $parameterReflection->getName(); - if (! in_array($parameterReflectionName, $toFillArgs, true)) { - continue; - } - - foreach ($currentArgs as $currentArg) { - if (! $currentArg->name instanceof Identifier) { - continue; - } - - if (! $this->nodeNameResolver->isName($currentArg->name, $parameterReflectionName)) { - continue; - } - - $unnamedArgs[$paramPosition] = new Arg( - $currentArg->value, - $currentArg->byRef, - $currentArg->unpack, - $currentArg->getAttributes(), - null - ); - } - } - - return $unnamedArgs; - } - - /** - * @param Arg[] $unnamedArgs - * @param ParameterReflection[]|PhpParameterReflection[] $parameters - * @return Arg[] - */ - public function fillFromJumpedNamedArgs( - FunctionReflection | MethodReflection | ReflectionFunction $functionLikeReflection, - array $unnamedArgs, - bool $isNativeFunctionReflection, - array $parameters - ): array { - $keys = array_keys($unnamedArgs); - if ($keys === []) { - return $unnamedArgs; - } - - $highestParameterPosition = max($keys); - $parametersCount = count($parameters); - for ($i = 0; $i < $parametersCount; ++$i) { - if (in_array($i, $keys, true)) { - continue; - } - if ($i > $highestParameterPosition) { - continue; - } - /** @var ParameterReflection|PhpParameterReflection $parameterReflection */ - if ($isNativeFunctionReflection) { - /** @var ReflectionFunction $functionLikeReflection */ - $parameterReflection = new PhpParameterReflection( - $functionLikeReflection->getParameters()[$i], - null, - null - ); - } else { - $parameterReflection = $parameters[$i]; - } - - $defaulValue = $this->defaultParameterValueResolver->resolveFromParameterReflection( - $parameterReflection - ); - - if (! $defaulValue instanceof Expr) { - continue; - } - - $unnamedArgs[$i] = new Arg( - $defaulValue, - $parameterReflection->passedByReference() - ->yes(), - $parameterReflection->isVariadic(), - [], - null - ); - } - - return $unnamedArgs; - } -} diff --git a/rules/DowngradePhp80/NodeAnalyzer/UnnamedArgumentResolver.php b/rules/DowngradePhp80/NodeAnalyzer/UnnamedArgumentResolver.php deleted file mode 100644 index 4c722cdaeae..00000000000 --- a/rules/DowngradePhp80/NodeAnalyzer/UnnamedArgumentResolver.php +++ /dev/null @@ -1,70 +0,0 @@ -getVariants()); - $unnamedArgs = []; - $parameters = $parametersAcceptor->getParameters(); - $isNativeFunctionReflection = $functionLikeReflection instanceof NativeFunctionReflection; - - if ($isNativeFunctionReflection) { - $functionLikeReflection = new ReflectionFunction($functionLikeReflection->getName()); - } - - /** @var Arg[] $unnamedArgs */ - $unnamedArgs = []; - $toFillArgs = []; - foreach ($currentArgs as $key => $arg) { - if ($arg->name === null) { - $unnamedArgs[$key] = new Arg($arg->value, $arg->byRef, $arg->unpack, $arg->getAttributes(), null); - - continue; - } - - /** @var string $argName */ - $argName = $this->nodeNameResolver->getName($arg->name); - $toFillArgs[] = $argName; - } - - $unnamedArgs = $this->namedToUnnamedArgs->fillFromNamedArgs( - $parameters, - $currentArgs, - $toFillArgs, - $unnamedArgs - ); - $unnamedArgs = $this->namedToUnnamedArgs->fillFromJumpedNamedArgs( - $functionLikeReflection, - $unnamedArgs, - $isNativeFunctionReflection, - $parameters - ); - ksort($unnamedArgs); - return $unnamedArgs; - } -} diff --git a/rules/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector.php b/rules/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector.php deleted file mode 100644 index c0cdb1e4073..00000000000 --- a/rules/DowngradePhp80/Rector/Catch_/DowngradeNonCapturingCatchesRector.php +++ /dev/null @@ -1,78 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Catch_::class]; - } - - /** - * @param Catch_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node->var !== null) { - return null; - } - - $node->var = new Variable('exception'); - - return $node; - } -} diff --git a/rules/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector.php b/rules/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector.php deleted file mode 100644 index 4aabb0f4f72..00000000000 --- a/rules/DowngradePhp80/Rector/ClassConstFetch/DowngradeClassOnObjectToGetClassRector.php +++ /dev/null @@ -1,73 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassConstFetch::class]; - } - - /** - * @param ClassConstFetch $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node->name, 'class')) { - return null; - } - - if (! $node->class instanceof Expr) { - return null; - } - - return new FuncCall(new Name('get_class'), [new Arg($node->class)]); - } -} diff --git a/rules/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector.php b/rules/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector.php deleted file mode 100644 index 2b4efc4526e..00000000000 --- a/rules/DowngradePhp80/Rector/ClassMethod/DowngradeAbstractPrivateMethodInTraitRector.php +++ /dev/null @@ -1,81 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - $this->visibilityManipulator->removeAbstract($node); - // Add empty array for stmts to generate empty function body - $node->stmts = []; - - return $node; - } - - private function shouldSkip(ClassMethod $classMethod): bool - { - if (! $classMethod->isAbstract()) { - return true; - } - - if (! $classMethod->isPrivate()) { - return true; - } - - $parent = $classMethod->getAttribute(AttributeKey::PARENT_NODE); - return ! $parent instanceof Trait_; - } -} diff --git a/rules/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector.php b/rules/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector.php deleted file mode 100644 index bb381e30f57..00000000000 --- a/rules/DowngradePhp80/Rector/ClassMethod/DowngradeStaticTypeDeclarationRector.php +++ /dev/null @@ -1,99 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove "static" return and param type, add a "@param $this" and "@return $this" tag instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function getStatic(): static - { - return new static(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @return static - */ - public function getStatic() - { - return new static(); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $scope = $node->getAttribute(AttributeKey::SCOPE); - if ($scope === null) { - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - $classReflection = $this->reflectionProvider->getClass($className); - } else { - $classReflection = $scope->getClassReflection(); - } - - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $staticType = new StaticType($classReflection->getName()); - - foreach ($node->getParams() as $param) { - $this->phpDocFromTypeDeclarationDecorator->decorateParamWithSpecificType($param, $node, $staticType); - } - - if (! $this->phpDocFromTypeDeclarationDecorator->decorateReturnWithSpecificType($node, $staticType)) { - return null; - } - - return $node; - } -} diff --git a/rules/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector.php b/rules/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector.php deleted file mode 100644 index ad2de118e41..00000000000 --- a/rules/DowngradePhp80/Rector/ClassMethod/DowngradeTrailingCommasInParamUseRector.php +++ /dev/null @@ -1,162 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ - ClassMethod::class, - Function_::class, - Closure::class, - StaticCall::class, - FuncCall::class, - MethodCall::class, - New_::class, - ]; - } - - /** - * @param ClassMethod|Function_|Closure|FuncCall|MethodCall|StaticCall|New_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof MethodCall || - $node instanceof FuncCall || - $node instanceof StaticCall || - $node instanceof New_ - ) { - /** @var MethodCall|FuncCall|StaticCall|New_ $node */ - return $this->processArgs($node); - } - - if ($node instanceof Closure) { - $this->processUses($node); - } - - /** @var ClassMethod|Function_ $node */ - return $this->processParams($node); - } - - private function processArgs(FuncCall | MethodCall | StaticCall | New_ $node): ?Node - { - if ($node->args === []) { - return null; - } - - return $this->cleanTrailingComma($node, $node->args); - } - - private function processUses(Closure $node): void - { - if ($node->uses === []) { - return; - } - - $this->cleanTrailingComma($node, $node->uses); - } - - private function processParams(ClassMethod | Function_ | Closure $node): ?Node - { - if ($node->params === []) { - return null; - } - - return $this->cleanTrailingComma($node, $node->params); - } - - /** - * @param ClosureUse[]|Param[]|Arg[] $array - */ - private function cleanTrailingComma(Node $node, array $array): ?Node - { - $lastPosition = array_key_last($array); - - $last = $array[$lastPosition]; - if (! $this->followedByCommaAnalyzer->isFollowed($this->file, $last)) { - return null; - } - - $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); - $last->setAttribute(AttributeKey::FUNC_ARGS_TRAILING_COMMA, false); - - return $node; - } -} diff --git a/rules/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector.php b/rules/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector.php deleted file mode 100644 index 57d2cf0a7ec..00000000000 --- a/rules/DowngradePhp80/Rector/Class_/DowngradeAttributeToAnnotationRector.php +++ /dev/null @@ -1,170 +0,0 @@ - [ - new DowngradeAttributeToAnnotation('Symfony\Component\Routing\Annotation\Route'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class, ClassMethod::class, Property::class, Interface_::class, Param::class, Function_::class]; - } - - /** - * @param Class_|ClassMethod|Property|Interface_|Param|Function_ $node - */ - public function refactor(Node $node): ?Node - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - foreach ($node->attrGroups as $attrGroup) { - foreach ($attrGroup->attrs as $key => $attribute) { - $attributeToAnnotation = $this->matchAttributeToAnnotation($attribute, $this->attributesToAnnotations); - if (! $attributeToAnnotation instanceof DowngradeAttributeToAnnotation) { - continue; - } - - unset($attrGroup->attrs[$key]); - - if (! \str_contains($attributeToAnnotation->getTag(), '\\')) { - $phpDocInfo->addPhpDocTagNode( - new PhpDocTagNode('@' . $attributeToAnnotation->getTag(), new GenericTagValueNode('')) - ); - } else { - $doctrineAnnotation = $this->doctrineAnnotationFactory->createFromAttribute( - $attribute, - $attributeToAnnotation->getTag() - ); - $phpDocInfo->addTagValueNode($doctrineAnnotation); - } - } - } - - // cleanup empty attr groups - $this->cleanupEmptyAttrGroups($node); - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $attributesToAnnotations = $configuration[self::ATTRIBUTE_TO_ANNOTATION] ?? []; - Assert::allIsInstanceOf($attributesToAnnotations, DowngradeAttributeToAnnotation::class); - - $this->attributesToAnnotations = $attributesToAnnotations; - } - - private function cleanupEmptyAttrGroups( - ClassMethod | Property | Class_ | Interface_ | Param | Function_ $node - ): void { - foreach ($node->attrGroups as $key => $attrGroup) { - if ($attrGroup->attrs !== []) { - continue; - } - - unset($node->attrGroups[$key]); - } - } - - /** - * @param DowngradeAttributeToAnnotation[] $attributesToAnnotations - */ - private function matchAttributeToAnnotation( - Attribute $attribute, - array $attributesToAnnotations - ): ?DowngradeAttributeToAnnotation { - foreach ($attributesToAnnotations as $attributeToAnnotation) { - if (! $this->isName($attribute->name, $attributeToAnnotation->getAttributeClass())) { - continue; - } - - return $attributeToAnnotation; - } - - return null; - } -} diff --git a/rules/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector.php b/rules/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector.php deleted file mode 100644 index 9303426d426..00000000000 --- a/rules/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector.php +++ /dev/null @@ -1,243 +0,0 @@ -value = $value; - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - $oldComments = $this->getOldComments($node); - $promotedParams = $this->resolvePromotedParams($node); - if ($promotedParams === []) { - return null; - } - - $properties = $this->resolvePropertiesFromPromotedParams($promotedParams, $node); - - $this->addPropertyAssignsToConstructorClassMethod($properties, $node, $oldComments); - - foreach ($promotedParams as $promotedParam) { - $promotedParam->flags = 0; - } - - return $node; - } - - /** - * @return array - */ - private function getOldComments(Class_ $class): array - { - $constructorClassMethod = $class->getMethod(MethodName::CONSTRUCT); - if (! $constructorClassMethod instanceof ClassMethod) { - return []; - } - - $oldComments = []; - foreach ($constructorClassMethod->params as $param) { - $oldComments[$this->getName($param->var)] = $param->getAttribute(AttributeKey::COMMENTS); - } - - return $oldComments; - } - - /** - * @return Param[] - */ - private function resolvePromotedParams(Class_ $class): array - { - $constructorClassMethod = $class->getMethod(MethodName::CONSTRUCT); - if (! $constructorClassMethod instanceof ClassMethod) { - return []; - } - - $promotedParams = []; - - foreach ($constructorClassMethod->params as $param) { - if ($param->flags === 0) { - continue; - } - - $this->setParamAttrGroupAsComment($param); - $promotedParams[] = $param; - } - - return $promotedParams; - } - - private function setParamAttrGroupAsComment(Param $param): void - { - $attrGroupsPrint = $this->betterStandardPrinter->print($param->attrGroups); - - $comments = $param->getAttribute(AttributeKey::COMMENTS); - if (is_array($comments)) { - foreach ($comments as $comment) { - $attrGroupsPrint = str_replace($comment->getText(), '', $attrGroupsPrint); - } - } - - $comments = $param->attrGroups !== [] - ? [new Comment($attrGroupsPrint)] - : null; - - $param->attrGroups = []; - $param->setAttribute(AttributeKey::COMMENTS, $comments); - } - - /** - * @param Param[] $promotedParams - * @return Property[] - */ - private function resolvePropertiesFromPromotedParams(array $promotedParams, Class_ $class): array - { - $properties = $this->createPropertiesFromParams($promotedParams); - $this->classInsertManipulator->addPropertiesToClass($class, $properties); - - return $properties; - } - - /** - * @param Property[] $properties - * @param array $oldComments - */ - private function addPropertyAssignsToConstructorClassMethod( - array $properties, - Class_ $class, - array $oldComments - ): void { - $assigns = []; - - foreach ($properties as $property) { - $propertyName = $this->getName($property); - $assign = $this->nodeFactory->createPropertyAssignment($propertyName); - $expression = new Expression($assign); - $expression->setAttribute(AttributeKey::COMMENTS, $oldComments[$propertyName]); - $assigns[] = $expression; - } - - /** @var ClassMethod $constructorClassMethod */ - $constructorClassMethod = $class->getMethod(MethodName::CONSTRUCT); - $constructorClassMethod->stmts = array_merge($assigns, (array) $constructorClassMethod->stmts); - } - - /** - * @param Param[] $params - * @return Property[] - */ - private function createPropertiesFromParams(array $params): array - { - $properties = []; - - foreach ($params as $param) { - /** @var string $name */ - $name = $this->getName($param->var); - - $property = $this->nodeFactory->createProperty($name); - $property->flags = $param->flags; - $property->type = $param->type; - $this->decoratePropertyWithParamDocInfo($param, $property); - - if ($param->default !== null) { - $property->props[0]->default = $param->default; - } - - $properties[] = $property; - } - - return $properties; - } - - private function decoratePropertyWithParamDocInfo(Param $param, Property $property): void - { - $constructor = $param->getAttribute(AttributeKey::METHOD_NODE); - $phpDocInfo = $this->phpDocInfoFactory->createFromNode($constructor); - if (! $phpDocInfo instanceof PhpDocInfo) { - return; - } - - $name = $this->getName($param->var); - if ($name === null) { - return; - } - - $type = $phpDocInfo->getParamType($name); - - // MixedType likely means there was no param type defined - if ($type instanceof MixedType) { - return; - } - - $propertyDocInfo = $this->phpDocInfoFactory->createEmpty($property); - $this->phpDocTypeChanger->changeVarType($propertyDocInfo, $type); - } -} diff --git a/rules/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector.php b/rules/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector.php deleted file mode 100644 index 248071a3393..00000000000 --- a/rules/DowngradePhp80/Rector/Expression/DowngradeMatchToSwitchRector.php +++ /dev/null @@ -1,158 +0,0 @@ - null, - 400 => 'not found', - default => 'unknown status code', - }; - } -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - switch ($statusCode) { - case 200: - case 300: - $message = null; - break; - case 400: - $message = 'not found'; - break; - default: - $message = 'unknown status code'; - break; - } - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Expression::class, Return_::class]; - } - - /** - * @param Expression|Return_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof Expression) { - if (! $node->expr instanceof Assign) { - return null; - } - - $assign = $node->expr; - if (! $assign->expr instanceof Match_) { - return null; - } - - /** @var Match_ $match */ - $match = $assign->expr; - } else { - if (! $node->expr instanceof Match_) { - return null; - } - - /** @var Match_ $match */ - $match = $node->expr; - } - - $switchCases = $this->createSwitchCasesFromMatchArms($node, $match->arms); - return new Switch_($match->cond, $switchCases); - } - - /** - * @param MatchArm[] $matchArms - * @return Case_[] - */ - private function createSwitchCasesFromMatchArms(Expression | Return_ $node, array $matchArms): array - { - $switchCases = []; - - foreach ($matchArms as $matchArm) { - if (count((array) $matchArm->conds) > 1) { - $lastCase = null; - - foreach ((array) $matchArm->conds as $matchArmCond) { - $lastCase = new Case_($matchArmCond); - $switchCases[] = $lastCase; - } - - if (! $lastCase instanceof Case_) { - throw new ShouldNotHappenException(); - } - - $lastCase->stmts = $this->createSwitchStmts($node, $matchArm); - } else { - $stmts = $this->createSwitchStmts($node, $matchArm); - $switchCases[] = new Case_($matchArm->conds[0] ?? null, $stmts); - } - } - return $switchCases; - } - - /** - * @return Stmt[] - */ - private function createSwitchStmts(Expression | Return_ $node, MatchArm $matchArm): array - { - $stmts = []; - - if ($node instanceof Expression) { - /** @var Assign $assign */ - $assign = $node->expr; - $stmts[] = new Expression(new Assign($assign->var, $matchArm->body)); - $stmts[] = new Break_(); - } else { - $stmts[] = new Return_($matchArm->body); - } - - return $stmts; - } -} diff --git a/rules/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector.php b/rules/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector.php deleted file mode 100644 index a51b8c05875..00000000000 --- a/rules/DowngradePhp80/Rector/Expression/DowngradeThrowExprRector.php +++ /dev/null @@ -1,174 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Expression::class]; - } - - /** - * @param Expression $node - */ - public function refactor(Node $node): ?Node - { - if ($node->expr instanceof Throw_) { - return null; - } - - if ($node->expr instanceof Assign) { - return $this->processAssign($node, $node->expr); - } - - if ($node->expr instanceof Coalesce) { - return $this->processCoalesce($node->expr, null); - } - - if ($node->expr instanceof Ternary) { - return $this->processTernary($node->expr, null); - } - - return $node; - } - - private function processAssign(Expression $expression, Assign $assign): If_ | Expression | null - { - if (! $this->hasThrowInAssignExpr($assign)) { - return null; - } - - if ($assign->expr instanceof Coalesce) { - return $this->processCoalesce($assign->expr, $assign); - } - - if ($assign->expr instanceof Throw_) { - return new Expression(($assign->expr)); - } - - if ($assign->expr instanceof Ternary) { - return $this->processTernary($assign->expr, $assign); - } - - return $expression; - } - - private function processTernary(Ternary $ternary, ?Assign $assign): ?If_ - { - if (! $ternary->else instanceof Throw_) { - return null; - } - - $inversedTernaryCond = $this->binaryOpManipulator->inverseNode($ternary->cond); - if (! $inversedTernaryCond instanceof Expr) { - return null; - } - - $if = $this->ifManipulator->createIfExpr($inversedTernaryCond, new Expression($ternary->else)); - if (! $assign instanceof Assign) { - return $if; - } - - $assign->expr = $ternary->if === null - ? $ternary->cond - : $ternary->if; - - $this->addNodeAfterNode(new Expression($assign), $if); - return $if; - } - - private function processCoalesce(Coalesce $coalesce, ?Assign $assign): ?If_ - { - if (! $coalesce->right instanceof Throw_) { - return null; - } - - if (! $this->coalesceAnalyzer->hasIssetableLeft($coalesce)) { - return null; - } - - $booleanNot = new BooleanNot(new Isset_([$coalesce->left])); - $if = $this->ifManipulator->createIfExpr($booleanNot, new Expression($coalesce->right)); - if (! $assign instanceof Assign) { - return $if; - } - - $assign->expr = $coalesce->left; - $this->addNodeAfterNode(new Expression($assign), $if); - return $if; - } - - private function hasThrowInAssignExpr(Assign $assign): bool - { - return (bool) $this->betterNodeFinder->findFirst( - $assign->expr, - fn (Node $node): bool => $node instanceof Throw_ - ); - } -} diff --git a/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector.php b/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector.php deleted file mode 100644 index d4bf952d5f1..00000000000 --- a/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrContainsRector.php +++ /dev/null @@ -1,98 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class, BooleanNot::class]; - } - - /** - * @param FuncCall|BooleanNot $node - * @return Identical|NotIdentical|null The refactored node. - */ - public function refactor(Node $node): Identical | NotIdentical | null - { - $funcCall = $this->matchStrContainsOrNotStrContains($node); - - if (! $funcCall instanceof FuncCall) { - return null; - } - - $haystack = $funcCall->args[0]->value; - $needle = $funcCall->args[1]->value; - - $funcCall = $this->nodeFactory->createFuncCall('strpos', [$haystack, $needle]); - - if ($node instanceof BooleanNot) { - return new Identical($funcCall, $this->nodeFactory->createFalse()); - } - return new NotIdentical($funcCall, $this->nodeFactory->createFalse()); - } - - /** - * @return FuncCall - */ - private function matchStrContainsOrNotStrContains(FuncCall | BooleanNot $expr): ?FuncCall - { - $expr = ($expr instanceof BooleanNot) ? $expr->expr : $expr; - if (! $expr instanceof FuncCall) { - return null; - } - if (! $this->isName($expr, 'str_contains')) { - return null; - } - return $expr; - } -} diff --git a/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector.php b/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector.php deleted file mode 100644 index 276eab4c305..00000000000 --- a/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrEndsWithRector.php +++ /dev/null @@ -1,79 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class, BooleanNot::class]; - } - - /** - * @param FuncCall|BooleanNot $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof FuncCall && $this->isName($node->name, 'str_ends_with')) { - return new Identical($this->createSubstrCompareFuncCall($node), new LNumber(0)); - } - - if ($node instanceof BooleanNot) { - $funcCall = $node->expr; - if ($funcCall instanceof FuncCall && $this->isName($funcCall->name, 'str_ends_with')) { - return new NotIdentical($this->createSubstrCompareFuncCall($funcCall), new LNumber(0)); - } - } - - return null; - } - - private function createSubstrCompareFuncCall(FuncCall $funcCall): FuncCall - { - $args = $funcCall->args; - - $strlenFuncCall = $this->createStrlenFuncCall($funcCall->args[1]->value); - $args[] = new Arg(new UnaryMinus($strlenFuncCall)); - - return new FuncCall(new Name('substr_compare'), $args); - } - - private function createStrlenFuncCall(Expr $expr): FuncCall - { - return new FuncCall(new Name('strlen'), [new Arg($expr)]); - } -} diff --git a/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector.php b/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector.php deleted file mode 100644 index ceeec36f0ec..00000000000 --- a/rules/DowngradePhp80/Rector/FuncCall/DowngradeStrStartsWithRector.php +++ /dev/null @@ -1,91 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class, BooleanNot::class]; - } - - /** - * @param FuncCall|BooleanNot $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof FuncCall && $this->isName($node, 'str_starts_with')) { - return $this->createIdentical($node); - } - - if ($node instanceof BooleanNot) { - $negatedCall = $node->expr; - if ($negatedCall instanceof FuncCall && $this->isName($negatedCall, 'str_starts_with')) { - return $this->createNotIdenticalStrncmpFuncCall($negatedCall); - } - } - - return null; - } - - private function createIdentical(FuncCall $funcCall): Identical - { - $strlenFuncCall = $this->createStrlenFuncCall($funcCall); - $strncmpFuncCall = $this->createStrncmpFuncCall($funcCall, $strlenFuncCall); - - return new Identical($strncmpFuncCall, new LNumber(0)); - } - - private function createNotIdenticalStrncmpFuncCall(FuncCall $funcCall): NotIdentical - { - $strlenFuncCall = $this->createStrlenFuncCall($funcCall); - $strncmpFuncCall = $this->createStrncmpFuncCall($funcCall, $strlenFuncCall); - - return new NotIdentical($strncmpFuncCall, new LNumber(0)); - } - - private function createStrlenFuncCall(FuncCall $funcCall): FuncCall - { - return new FuncCall(new Name('strlen'), [$funcCall->args[1]]); - } - - private function createStrncmpFuncCall(FuncCall $funcCall, FuncCall $strlenFuncCall): FuncCall - { - $newArgs = $funcCall->args; - $newArgs[] = new Arg($strlenFuncCall); - - return new FuncCall(new Name('strncmp'), $newArgs); - } -} diff --git a/rules/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector.php b/rules/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector.php deleted file mode 100644 index 0f202a10c94..00000000000 --- a/rules/DowngradePhp80/Rector/FunctionLike/DowngradeMixedTypeDeclarationRector.php +++ /dev/null @@ -1,84 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Function_::class, ClassMethod::class, Closure::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove the "mixed" param and return type, add a @param and @return tag instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function someFunction(mixed $anything): mixed - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @param mixed $anything - * @return mixed - */ - public function someFunction($anything) - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param ClassMethod|Function_ $node - */ - public function refactor(Node $node): ?Node - { - $mixedType = new MixedType(); - - foreach ($node->getParams() as $param) { - $this->phpDocFromTypeDeclarationDecorator->decorateParamWithSpecificType($param, $node, $mixedType); - } - - if (! $this->phpDocFromTypeDeclarationDecorator->decorateReturnWithSpecificType($node, $mixedType)) { - return null; - } - - return $node; - } -} diff --git a/rules/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector.php b/rules/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector.php deleted file mode 100644 index 7d002c30522..00000000000 --- a/rules/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector.php +++ /dev/null @@ -1,92 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Function_::class, ClassMethod::class, Closure::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove the union type params and returns, add @param/@return tags instead', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function echoInput(string|int $input): int|bool - { - echo $input; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @param string|int $input - * @return int|bool - */ - public function echoInput($input) - { - echo $input; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param ClassMethod|Function_ $node - */ - public function refactor(Node $node): ?Node - { - foreach ($node->getParams() as $param) { - if (! $param->type instanceof UnionType) { - continue; - } - - $this->phpDocFromTypeDeclarationDecorator->decorateParam($param, $node, [\PHPStan\Type\UnionType::class]); - } - - if (! $node->returnType instanceof UnionType) { - return null; - } - - $this->phpDocFromTypeDeclarationDecorator->decorate($node); - - return $node; - } -} diff --git a/rules/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector.php b/rules/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector.php deleted file mode 100644 index ca687d90440..00000000000 --- a/rules/DowngradePhp80/Rector/MethodCall/DowngradeNamedArgumentRector.php +++ /dev/null @@ -1,127 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [MethodCall::class, StaticCall::class, New_::class, FuncCall::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Remove named argument', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $this->execute(b: 100); - } - - private function execute($a = null, $b = null) - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $this->execute(null, 100); - } - - private function execute($a = null, $b = null) - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param MethodCall|StaticCall|New_|FuncCall $node - */ - public function refactor(Node $node): ?Node - { - $args = $node->args; - if ($this->shouldSkip($args)) { - return null; - } - - $this->removeNamedArguments($node, $args); - return $node; - } - - /** - * @param Arg[] $args - */ - private function removeNamedArguments(MethodCall | StaticCall | New_ | FuncCall $node, array $args): ?Node - { - if ($node instanceof New_) { - $functionLikeReflection = $this->reflectionResolver->resolveMethodReflectionFromNew($node); - } else { - $functionLikeReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); - } - - if (! $functionLikeReflection instanceof MethodReflection && ! $functionLikeReflection instanceof FunctionReflection) { - return null; - } - - $unnamedArgs = $this->unnamedArgumentResolver->resolveFromReflection($functionLikeReflection, $args); - $node->args = $unnamedArgs; - - return $node; - } - - /** - * @param Arg[] $args - */ - private function shouldSkip(array $args): bool - { - foreach ($args as $arg) { - if ($arg->name instanceof Identifier) { - return false; - } - } - - return true; - } -} diff --git a/rules/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector.php b/rules/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector.php deleted file mode 100644 index e3eb1f3d995..00000000000 --- a/rules/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector.php +++ /dev/null @@ -1,77 +0,0 @@ -getStartDate()?->asDateTimeString(); -$dateAsString = $booking->startDate?->dateTimeString; -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -$dateAsString = ($bookingGetStartDate = $booking->getStartDate()) ? $bookingGetStartDate->asDateTimeString() : null; -$dateAsString = ($bookingGetStartDate = $booking->startDate) ? $bookingGetStartDate->dateTimeString : null; -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [NullsafeMethodCall::class, NullsafePropertyFetch::class]; - } - - /** - * @param NullsafeMethodCall|NullsafePropertyFetch $node - */ - public function refactor(Node $node): Ternary - { - $scope = $node->getAttribute(AttributeKey::SCOPE); - - $tempVarName = $this->variableNaming->resolveFromNodeWithScopeCountAndFallbackName( - $node->var, - $scope, - '_' - ); - - $variable = new Variable($tempVarName); - $called = $node instanceof NullsafeMethodCall - ? new MethodCall($variable, $node->name, $node->args) - : new PropertyFetch($variable, $node->name); - - $assign = new Assign($variable, $node->var); - return new Ternary($assign, $called, $this->nodeFactory->createNull()); - } -} diff --git a/rules/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector.php b/rules/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector.php deleted file mode 100644 index 956995bc0e4..00000000000 --- a/rules/DowngradePhp80/Rector/Property/DowngradeUnionTypeTypedPropertyRector.php +++ /dev/null @@ -1,96 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Property::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Removes union type property type definition, adding `@var` annotations instead.', [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - private string|int $property; -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var string|int - */ - private $property; -} -CODE_SAMPLE - ), - ]); - } - - /** - * @param Property $node - */ - public function refactor(Node $node): ?Node - { - if ($node->type === null) { - return null; - } - - if (! $this->shouldRemoveProperty($node)) { - return null; - } - - $this->decoratePropertyWithDocBlock($node, $node->type); - $node->type = null; - - return $node; - } - - private function decoratePropertyWithDocBlock(Property $property, Node $typeNode): void - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - if ($phpDocInfo->getVarTagValueNode() !== null) { - return; - } - - $newType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($typeNode); - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $newType); - } - - private function shouldRemoveProperty(Property $property): bool - { - if ($property->type === null) { - return false; - } - - // Check it is the union type - return $property->type instanceof UnionType; - } -} diff --git a/rules/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector.php b/rules/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector.php deleted file mode 100644 index 03861e30e13..00000000000 --- a/rules/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector.php +++ /dev/null @@ -1,127 +0,0 @@ -getTokenName(); - $text = $phpToken->text; -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -$tokens = token_get_all($code); - -foreach ($tokens as $token) { - $name = is_array($token) ? token_name($token[0]) : null; - $text = is_array($token) ? $token[1] : $token; -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [StaticCall::class, MethodCall::class, PropertyFetch::class]; - } - - /** - * @param StaticCall|MethodCall|PropertyFetch $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof StaticCall) { - return $this->refactorStaticCall($node); - } - - if ($node instanceof MethodCall) { - return $this->refactorMethodCall($node); - } - - return $this->refactorPropertyFetch($node); - } - - private function refactorStaticCall(StaticCall $staticCall): ?FuncCall - { - if (! $this->isObjectType($staticCall->class, new ObjectType(self::PHP_TOKEN))) { - return null; - } - - if (! $this->isName($staticCall->name, 'tokenize')) { - return null; - } - - return new FuncCall(new Name('token_get_all'), $staticCall->args); - } - - private function refactorMethodCall(MethodCall $methodCall): ?Ternary - { - if (! $this->isObjectType($methodCall->var, new ObjectType(self::PHP_TOKEN))) { - return null; - } - - if (! $this->isName($methodCall->name, 'getTokenName')) { - return null; - } - - $isArrayFuncCall = new FuncCall(new Name('is_array'), [new Arg($methodCall->var)]); - $arrayDimFetch = new ArrayDimFetch($methodCall->var, new LNumber(0)); - $tokenGetNameFuncCall = new FuncCall(new Name('token_name'), [new Arg($arrayDimFetch)]); - - return new Ternary($isArrayFuncCall, $tokenGetNameFuncCall, $this->nodeFactory->createNull()); - } - - private function refactorPropertyFetch(PropertyFetch $propertyFetch): ?Ternary - { - if (! $this->isObjectType($propertyFetch->var, new ObjectType(self::PHP_TOKEN))) { - return null; - } - - if (! $this->isName($propertyFetch->name, 'text')) { - return null; - } - - $isArrayFuncCall = new FuncCall(new Name('is_array'), [new Arg($propertyFetch->var)]); - $arrayDimFetch = new ArrayDimFetch($propertyFetch->var, new LNumber(1)); - - return new Ternary($isArrayFuncCall, $arrayDimFetch, $propertyFetch->var); - } -} diff --git a/rules/DowngradePhp80/Reflection/DefaultParameterValueResolver.php b/rules/DowngradePhp80/Reflection/DefaultParameterValueResolver.php deleted file mode 100644 index f285eddaa1e..00000000000 --- a/rules/DowngradePhp80/Reflection/DefaultParameterValueResolver.php +++ /dev/null @@ -1,89 +0,0 @@ -getDefaultValue(); - if (! $defaultValue instanceof Type) { - return null; - } - - if (! $defaultValue instanceof ConstantType) { - throw new ShouldNotHappenException(); - } - - return $this->resolveValueFromType($defaultValue); - } - - public function resolveFromFunctionLikeAndPosition( - MethodReflection | FunctionReflection $functionLikeReflection, - int $position - ): ?Expr { - $parametersAcceptor = $functionLikeReflection->getVariants()[0] ?? null; - if (! $parametersAcceptor instanceof ParametersAcceptor) { - return null; - } - - $parameterReflection = $parametersAcceptor->getParameters()[$position] ?? null; - if (! $parameterReflection instanceof ParameterReflection) { - return null; - } - - return $this->resolveFromParameterReflection($parameterReflection); - } - - private function resolveValueFromType(ConstantType $constantType): ConstFetch | Expr - { - if ($constantType instanceof ConstantBooleanType) { - return $this->resolveConstantBooleanType($constantType); - } - - if ($constantType instanceof ConstantArrayType) { - $values = []; - foreach ($constantType->getValueTypes() as $valueType) { - if (! $valueType instanceof ConstantType) { - throw new ShouldNotHappenException(); - } - - $values[] = $this->resolveValueFromType($valueType); - } - - return BuilderHelpers::normalizeValue($values); - } - - return BuilderHelpers::normalizeValue($constantType->getValue()); - } - - private function resolveConstantBooleanType(ConstantBooleanType $constantBooleanType): ConstFetch - { - if (! $constantBooleanType->getValue()) { - $name = new Name('false'); - } elseif ($constantBooleanType->getValue()) { - $name = new Name('true'); - } else { - throw new NotImplementedYetException(); - } - - return new ConstFetch($name); - } -} diff --git a/rules/DowngradePhp80/ValueObject/DowngradeAttributeToAnnotation.php b/rules/DowngradePhp80/ValueObject/DowngradeAttributeToAnnotation.php deleted file mode 100644 index f2a9b19c483..00000000000 --- a/rules/DowngradePhp80/ValueObject/DowngradeAttributeToAnnotation.php +++ /dev/null @@ -1,32 +0,0 @@ -attributeClass; - } - - public function getTag(): string - { - if ($this->tag === null) { - return $this->attributeClass; - } - - return $this->tag; - } -} diff --git a/rules/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector.php b/rules/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector.php deleted file mode 100644 index b7f7d7ecb92..00000000000 --- a/rules/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector.php +++ /dev/null @@ -1,62 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassConst::class]; - } - - /** - * @param ClassConst $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->isFinal()) { - return null; - } - - $this->visibilityManipulator->removeFinal($node); - return $node; - } -} diff --git a/rules/EarlyReturn/NodeFactory/InvertedIfFactory.php b/rules/EarlyReturn/NodeFactory/InvertedIfFactory.php deleted file mode 100644 index f46b6688c90..00000000000 --- a/rules/EarlyReturn/NodeFactory/InvertedIfFactory.php +++ /dev/null @@ -1,74 +0,0 @@ -getIfNextReturn($if); - $stmt = $this->contextAnalyzer->isInLoop($if) && ! $ifNextReturn - ? [new Continue_()] - : [$return]; - - if ($ifNextReturn instanceof Return_) { - $stmt[0]->setAttribute(AttributeKey::COMMENTS, $ifNextReturn->getAttribute(AttributeKey::COMMENTS)); - } - - $getNextReturnExpr = $this->getNextReturnExpr($if); - if ($getNextReturnExpr instanceof Return_) { - $return->expr = $getNextReturnExpr->expr; - } - - foreach ($conditions as $condition) { - $invertedCondition = $this->conditionInverter->createInvertedCondition($condition); - $if = new If_($invertedCondition); - $if->stmts = $stmt; - $ifs[] = $if; - } - - return $ifs; - } - - private function getNextReturnExpr(If_ $if): ?Node - { - return $this->betterNodeFinder->findFirstNext( - $if, - fn (Node $node): bool => $node instanceof Return_ && $node->expr instanceof Expr - ); - } - - private function getIfNextReturn(If_ $if): ?Return_ - { - $nextNode = $if->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Return_) { - return null; - } - - return $nextNode; - } -} diff --git a/rules/EarlyReturn/NodeTransformer/ConditionInverter.php b/rules/EarlyReturn/NodeTransformer/ConditionInverter.php index e4194b61b64..8236264aad2 100644 --- a/rules/EarlyReturn/NodeTransformer/ConditionInverter.php +++ b/rules/EarlyReturn/NodeTransformer/ConditionInverter.php @@ -8,9 +8,9 @@ use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; use PhpParser\Node\Expr\BooleanNot; -use Rector\Core\NodeManipulator\BinaryOpManipulator; +use Rector\NodeManipulator\BinaryOpManipulator; -final class ConditionInverter +final readonly class ConditionInverter { public function __construct( private BinaryOpManipulator $binaryOpManipulator @@ -21,14 +21,16 @@ public function createInvertedCondition(Expr $expr): Expr { // inverse condition if ($expr instanceof BinaryOp) { - $inversedCondition = $this->binaryOpManipulator->invertCondition($expr); - if (! $inversedCondition instanceof BinaryOp) { + $binaryOp = $this->binaryOpManipulator->invertCondition($expr); + if (! $binaryOp instanceof BinaryOp) { return new BooleanNot($expr); } - if ($inversedCondition instanceof BooleanAnd) { + + if ($binaryOp instanceof BooleanAnd) { return new BooleanNot($expr); } - return $inversedCondition; + + return $binaryOp; } if ($expr instanceof BooleanNot) { diff --git a/rules/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector.php b/rules/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector.php index 878b07f0025..9e114ab26ff 100644 --- a/rules/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector.php +++ b/rules/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector.php @@ -14,10 +14,10 @@ use PhpParser\Node\Stmt\Continue_; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Rector\AbstractRector; use Rector\EarlyReturn\NodeTransformer\ConditionInverter; +use Rector\NodeManipulator\IfManipulator; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -27,8 +27,8 @@ final class ChangeNestedForeachIfsToEarlyContinueRector extends AbstractRector { public function __construct( - private ConditionInverter $conditionInverter, - private IfManipulator $ifManipulator + private readonly ConditionInverter $conditionInverter, + private readonly IfManipulator $ifManipulator ) { } @@ -53,7 +53,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -96,6 +96,10 @@ public function refactor(Node $node): ?Node return null; } + foreach ($nestedIfsWithOnlyNonReturn as $nestedIfWithOnlyNonReturn) { + $nestedIfWithOnlyNonReturn->cond->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + return $this->processNestedIfsWithNonBreaking($node, $nestedIfsWithOnlyNonReturn); } @@ -131,38 +135,34 @@ private function processNestedIfsWithNonBreaking(Foreach_ $foreach, array $neste return $foreach; } - private function addInvertedIfStmtWithContinue(If_ $nestedIfWithOnlyReturn, Foreach_ $foreach): void + private function addInvertedIfStmtWithContinue(If_ $onlyReturnIf, Foreach_ $foreach): void { - $invertedCondition = $this->conditionInverter->createInvertedCondition($nestedIfWithOnlyReturn->cond); + $invertedCondExpr = $this->conditionInverter->createInvertedCondition($onlyReturnIf->cond); // special case - if ($invertedCondition instanceof BooleanNot && $invertedCondition->expr instanceof BooleanAnd) { - $leftExpr = $this->negateOrDeNegate($invertedCondition->expr->left); - $if = new If_($leftExpr); - $if->stmts[] = new Continue_(); - $foreach->stmts[] = $if; + if ($invertedCondExpr instanceof BooleanNot && $invertedCondExpr->expr instanceof BooleanAnd) { + $leftExpr = $this->negateOrDeNegate($invertedCondExpr->expr->left); + $foreach->stmts[] = $this->createIfContinue($leftExpr); - $rightExpr = $this->negateOrDeNegate($invertedCondition->expr->right); - $if = new If_($rightExpr); - $if->stmts[] = new Continue_(); - $foreach->stmts[] = $if; + $rightExpr = $this->negateOrDeNegate($invertedCondExpr->expr->right); + $foreach->stmts[] = $this->createIfContinue($rightExpr); return; } // should skip for weak inversion - if ($this->isBooleanOrWithWeakComparison($nestedIfWithOnlyReturn->cond)) { - $foreach->stmts[] = $nestedIfWithOnlyReturn; + if ($this->isBooleanOrWithWeakComparison($onlyReturnIf->cond)) { + $foreach->stmts[] = $onlyReturnIf; return; } - $nestedIfWithOnlyReturn->setAttribute(AttributeKey::ORIGINAL_NODE, null); + $onlyReturnIf->setAttribute(AttributeKey::ORIGINAL_NODE, null); - $nestedIfWithOnlyReturn->cond = $invertedCondition; - $nestedIfWithOnlyReturn->stmts = [new Continue_()]; + $onlyReturnIf->cond = $invertedCondExpr; + $onlyReturnIf->stmts = [new Continue_()]; - $foreach->stmts[] = $nestedIfWithOnlyReturn; + $foreach->stmts[] = $onlyReturnIf; } /** @@ -201,4 +201,11 @@ private function negateOrDeNegate(Expr $expr): Expr return new BooleanNot($expr); } + + private function createIfContinue(Expr $expr): If_ + { + return new If_($expr, [ + 'stmts' => [new Continue_()], + ]); + } } diff --git a/rules/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector.php b/rules/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector.php deleted file mode 100644 index deb84e9888c..00000000000 --- a/rules/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector.php +++ /dev/null @@ -1,169 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Foreach_::class]; - } - - /** - * @param Foreach_ $node - */ - public function refactor(Node $node): ?Node - { - /** @var Break_[] $breaks */ - $breaks = $this->betterNodeFinder->findInstanceOf($node->stmts, Break_::class); - if (count($breaks) !== 1) { - return null; - } - - $beforeBreak = $breaks[0]->getAttribute(AttributeKey::PREVIOUS_NODE); - if (! $beforeBreak instanceof Expression) { - return null; - } - - $assign = $beforeBreak->expr; - if (! $assign instanceof Assign) { - return null; - } - - $nextForeach = $node->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextForeach instanceof Return_) { - return null; - } - - $assignVariable = $assign->var; - /** @var Expr $variablePrevious */ - $variablePrevious = $this->betterNodeFinder->findFirstPreviousOfNode($node, function (Node $node) use ( - $assignVariable - ): bool { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Assign) { - return false; - } - return $this->nodeComparator->areNodesEqual($node, $assignVariable); - }); - - if ($this->shouldSkip($nextForeach, $node, $assignVariable, $variablePrevious)) { - return null; - } - - /** @var Assign $assignPreviousVariable */ - $assignPreviousVariable = $variablePrevious->getAttribute(AttributeKey::PARENT_NODE); - $parent = $assignPreviousVariable->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Expression) { - return null; - } - - $nextParent = $parent->getAttribute(AttributeKey::NEXT_NODE); - if ($nextParent !== $node) { - return null; - } - - return $this->processEarlyReturn($beforeBreak, $assign, $breaks, $nextForeach, $assignPreviousVariable, $node); - } - - /** - * @param Break_[] $breaks - */ - private function processEarlyReturn( - Expression $expression, - Assign $assign, - array $breaks, - Return_ $return, - Assign $assignPreviousVariable, - Foreach_ $foreach - ): Foreach_ { - $this->removeNode($expression); - $this->addNodeBeforeNode(new Return_($assign->expr), $breaks[0]); - $this->removeNode($breaks[0]); - - $return->expr = $assignPreviousVariable->expr; - $this->removeNode($assignPreviousVariable); - - return $foreach; - } - - private function shouldSkip(Return_ $return, Foreach_ $foreach, Expr $assignVariable, ?Expr $expr = null): bool - { - if (! $expr instanceof Expr) { - return true; - } - - if (! $this->nodeComparator->areNodesEqual($return->expr, $expr)) { - return true; - } - - // ensure the variable only used once in foreach - $usedVariable = $this->betterNodeFinder->find( - $foreach->stmts, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $assignVariable) - ); - return count($usedVariable) > 1; - } -} diff --git a/rules/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector.php b/rules/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector.php deleted file mode 100644 index 289e09c90be..00000000000 --- a/rules/EarlyReturn/Rector/If_/ChangeAndIfToEarlyReturnRector.php +++ /dev/null @@ -1,248 +0,0 @@ -hasWheels && $car->hasFuel) { - return true; - } - - return false; - } -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function canDrive(Car $car) - { - if (!$car->hasWheels) { - return false; - } - - if (!$car->hasFuel) { - return false; - } - - return true; - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [If_::class]; - } - - /** - * @param If_ $node - * @return Node|Node[]|null - */ - public function refactor(Node $node) - { - if ($this->shouldSkip($node)) { - return null; - } - - $ifNextReturn = $this->getIfNextReturn($node); - if ($ifNextReturn instanceof Return_ && $this->isIfStmtExprUsedInNextReturn($node, $ifNextReturn)) { - return null; - } - - /** @var BooleanAnd $expr */ - $expr = $node->cond; - $booleanAndConditions = $this->booleanAndAnalyzer->findBooleanAndConditions($expr); - - if (! $ifNextReturn instanceof Return_) { - $this->addNodeAfterNode($node->stmts[0], $node); - return $this->processReplaceIfs($node, $booleanAndConditions, new Return_()); - } - - if ($ifNextReturn instanceof Return_ && $ifNextReturn->expr instanceof BooleanAnd) { - return null; - } - - $this->removeNode($ifNextReturn); - $ifNextReturn = $node->stmts[0]; - $this->addNodeAfterNode($ifNextReturn, $node); - - $ifNextReturnClone = $ifNextReturn instanceof Return_ - ? clone $ifNextReturn - : new Return_(); - - if (! $this->contextAnalyzer->isInLoop($node)) { - return $this->processReplaceIfs($node, $booleanAndConditions, $ifNextReturnClone); - } - - if (! $ifNextReturn instanceof Expression) { - return null; - } - - $this->addNodeAfterNode(new Return_(), $node); - return $this->processReplaceIfs($node, $booleanAndConditions, $ifNextReturnClone); - } - - /** - * @param Expr[] $conditions - * @return If_|Node[] - */ - private function processReplaceIfs(If_ $node, array $conditions, Return_ $ifNextReturnClone): If_ | array - { - $ifs = $this->invertedIfFactory->createFromConditions($node, $conditions, $ifNextReturnClone); - $this->mirrorComments($ifs[0], $node); - - foreach ($ifs as $if) { - $this->addNodeBeforeNode($if, $node); - } - - $this->removeNode($node); - - if (! $node->stmts[0] instanceof Return_ && $ifNextReturnClone->expr instanceof Expr) { - return [$node, $ifNextReturnClone]; - } - - return $node; - } - - private function shouldSkip(If_ $if): bool - { - if (! $this->ifManipulator->isIfWithOnlyOneStmt($if)) { - return true; - } - if (! $if->cond instanceof BooleanAnd) { - return true; - } - if (! $this->ifManipulator->isIfWithoutElseAndElseIfs($if)) { - return true; - } - - if ($this->isParentIfReturnsVoidOrParentIfHasNextNode($if)) { - return true; - } - - if ($this->isNestedIfInLoop($if)) { - return true; - } - - return ! $this->isLastIfOrBeforeLastReturn($if); - } - - private function isIfStmtExprUsedInNextReturn(If_ $if, Return_ $return): bool - { - if (! $return->expr instanceof Expr) { - return false; - } - - $ifExprs = $this->betterNodeFinder->findInstanceOf($if->stmts, Expr::class); - foreach ($ifExprs as $ifExpr) { - $isExprFoundInReturn = (bool) $this->betterNodeFinder->findFirst( - $return->expr, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $ifExpr) - ); - if ($isExprFoundInReturn) { - return true; - } - } - - return false; - } - - private function getIfNextReturn(If_ $if): ?Return_ - { - $nextNode = $if->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Return_) { - return null; - } - - return $nextNode; - } - - private function isParentIfReturnsVoidOrParentIfHasNextNode(If_ $if): bool - { - $parentNode = $if->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof If_) { - return false; - } - - $nextParent = $parentNode->getAttribute(AttributeKey::NEXT_NODE); - return $nextParent instanceof Node; - } - - private function isNestedIfInLoop(If_ $if): bool - { - if (! $this->contextAnalyzer->isInLoop($if)) { - return false; - } - - return (bool) $this->parentFinder->findByTypes($if, [If_::class, Else_::class, ElseIf_::class]); - } - - private function isLastIfOrBeforeLastReturn(If_ $if): bool - { - $nextNode = $if->getAttribute(AttributeKey::NEXT_NODE); - if ($nextNode instanceof Node) { - return $nextNode instanceof Return_; - } - - $parent = $if->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { - return false; - } - - if ($parent instanceof If_) { - return $this->isLastIfOrBeforeLastReturn($parent); - } - - return ! $this->contextAnalyzer->isHasAssignWithIndirectReturn($parent, $if); - } -} diff --git a/rules/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector.php b/rules/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector.php index 0ab78c8792c..d6a0aaa7ffa 100644 --- a/rules/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector.php +++ b/rules/EarlyReturn/Rector/If_/ChangeIfElseValueAssignToEarlyReturnRector.php @@ -5,29 +5,27 @@ namespace Rector\EarlyReturn\Rector\If_; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\NodeManipulator\StmtsManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeManipulator\IfManipulator; +use Rector\NodeManipulator\StmtsManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://engineering.helpscout.com/reducing-complexity-with-guard-clauses-in-php-and-javascript-74600fd865c7 - * * @see \Rector\Tests\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector\ChangeIfElseValueAssignToEarlyReturnRectorTest */ final class ChangeIfElseValueAssignToEarlyReturnRector extends AbstractRector { public function __construct( - private IfManipulator $ifManipulator, - private StmtsManipulator $stmtsManipulator + private readonly IfManipulator $ifManipulator, + private readonly StmtsManipulator $stmtsManipulator ) { } @@ -50,7 +48,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -72,56 +70,69 @@ public function run() */ public function getNodeTypes(): array { - return [If_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param If_ $node - * @return Stmt[]|null + * @param StmtsAware $node + * @return ?StmtsAware */ - public function refactor(Node $node): ?array + public function refactor(Node $node): ?Node { - $nextNode = $node->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Return_) { + if ($node->stmts === null) { return null; } - if ($nextNode->expr === null) { - return null; - } + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Return_) { + continue; + } - if (! $this->ifManipulator->isIfAndElseWithSameVariableAssignAsLastStmts($node, $nextNode->expr)) { - return null; - } + if (! $stmt->expr instanceof Expr) { + continue; + } - $lastIfStmtKey = array_key_last($node->stmts); + $previousStmt = $node->stmts[$key - 1] ?? null; + if (! $previousStmt instanceof If_) { + continue; + } - /** @var Assign $assign */ - $assign = $this->stmtsManipulator->getUnwrappedLastStmt($node->stmts); + $if = $previousStmt; - $return = new Return_($assign->expr); - $this->mirrorComments($return, $assign); - $node->stmts[$lastIfStmtKey] = $return; + if (! $this->ifManipulator->isIfAndElseWithSameVariableAssignAsLastStmts($if, $stmt->expr)) { + continue; + } - $else = $node->else; - if (! $else instanceof Else_) { - throw new ShouldNotHappenException(); - } + $lastIfStmtKey = array_key_last($if->stmts); + + /** @var Assign $assign */ + $assign = $this->stmtsManipulator->getUnwrappedLastStmt($if->stmts); - $elseStmts = $else->stmts; + $returnLastIf = new Return_($assign->expr); + $this->mirrorComments($returnLastIf, $assign); + $if->stmts[$lastIfStmtKey] = $returnLastIf; - /** @var Assign $assign */ - $assign = $this->stmtsManipulator->getUnwrappedLastStmt($elseStmts); + /** @var Else_ $else */ + $else = $if->else; - $lastElseStmtKey = array_key_last($elseStmts); + /** @var array $elseStmts */ + $elseStmts = $else->stmts; - $return = new Return_($assign->expr); - $this->mirrorComments($return, $assign); - $elseStmts[$lastElseStmtKey] = $return; + /** @var Assign $assign */ + $assign = $this->stmtsManipulator->getUnwrappedLastStmt($elseStmts); - $node->else = null; - $this->removeNode($nextNode); + $this->mirrorComments($stmt, $assign); - return array_merge([$node], $elseStmts); + $if->else = null; + $stmt->expr = $assign->expr; + + $lastStmt = array_pop($node->stmts); + $elseStmtsExceptLast = array_slice($elseStmts, 0, -1); + $node->stmts = [...$node->stmts, ...$elseStmtsExceptLast, $lastStmt]; + + return $node; + } + + return null; } } diff --git a/rules/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector.php b/rules/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector.php index 3a9bf14ccb6..44b14399cb1 100644 --- a/rules/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector.php +++ b/rules/EarlyReturn/Rector/If_/ChangeNestedIfsToEarlyReturnRector.php @@ -9,10 +9,10 @@ use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Rector\AbstractRector; use Rector\EarlyReturn\NodeTransformer\ConditionInverter; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeManipulator\IfManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,8 +22,8 @@ final class ChangeNestedIfsToEarlyReturnRector extends AbstractRector { public function __construct( - private ConditionInverter $conditionInverter, - private IfManipulator $ifManipulator + private readonly ConditionInverter $conditionInverter, + private readonly IfManipulator $ifManipulator ) { } @@ -73,71 +73,93 @@ public function run() */ public function getNodeTypes(): array { - return [If_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param If_ $node + * @param StmtsAware $node + * @return StmtsAware */ public function refactor(Node $node): ?Node { - // A. next node is return - $nextNode = $node->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Return_) { + if ($node->stmts === null) { return null; } - $nestedIfsWithOnlyReturn = $this->ifManipulator->collectNestedIfsWithOnlyReturn($node); - if ($nestedIfsWithOnlyReturn === []) { - return null; - } + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof If_) { + continue; + } + + $nextStmt = $node->stmts[$key + 1] ?? null; + if (! $nextStmt instanceof Return_) { + return null; + } + + $nestedIfsWithOnlyReturn = $this->ifManipulator->collectNestedIfsWithOnlyReturn($stmt); + if ($nestedIfsWithOnlyReturn === []) { + continue; + } + + $newStmts = $this->processNestedIfsWithOnlyReturn($nestedIfsWithOnlyReturn, $nextStmt); + + // replace nested ifs with many separate ifs + array_splice($node->stmts, $key, 1, $newStmts); - $this->processNestedIfsWithOnlyReturn($node, $nestedIfsWithOnlyReturn, $nextNode); - $this->removeNode($node); + return $node; + } return null; } /** * @param If_[] $nestedIfsWithOnlyReturn + * @return If_[] */ - private function processNestedIfsWithOnlyReturn(If_ $if, array $nestedIfsWithOnlyReturn, Return_ $nextReturn): void + private function processNestedIfsWithOnlyReturn(array $nestedIfsWithOnlyReturn, Return_ $nextReturn): array { // add nested if openly after this $nestedIfsWithOnlyReturnCount = count($nestedIfsWithOnlyReturn); + $newStmts = []; + /** @var int $key */ foreach ($nestedIfsWithOnlyReturn as $key => $nestedIfWithOnlyReturn) { // last item → the return node if ($nestedIfsWithOnlyReturnCount === $key + 1) { - $this->addNodeAfterNode($nestedIfWithOnlyReturn, $if); + $newStmts[] = $nestedIfWithOnlyReturn; } else { - $this->addStandaloneIfsWithReturn($nestedIfWithOnlyReturn, $if, $nextReturn); + $standaloneIfs = $this->createStandaloneIfsWithReturn($nestedIfWithOnlyReturn, $nextReturn); + $newStmts = [...$newStmts, ...$standaloneIfs]; } } + + // $newStmts[] = $nextReturn; + + return $newStmts; } - private function addStandaloneIfsWithReturn(If_ $nestedIfWithOnlyReturn, If_ $if, Return_ $return): void + /** + * @return If_[] + */ + private function createStandaloneIfsWithReturn(If_ $onlyReturnIf, Return_ $return): array { - $return = clone $return; - - $invertedCondition = $this->conditionInverter->createInvertedCondition($nestedIfWithOnlyReturn->cond); + $invertedCondExpr = $this->conditionInverter->createInvertedCondition($onlyReturnIf->cond); // special case - if ($invertedCondition instanceof BooleanNot && $invertedCondition->expr instanceof BooleanAnd) { - $booleanNotPartIf = new If_(new BooleanNot($invertedCondition->expr->left)); + if ($invertedCondExpr instanceof BooleanNot && $invertedCondExpr->expr instanceof BooleanAnd) { + $booleanNotPartIf = new If_(new BooleanNot($invertedCondExpr->expr->left)); $booleanNotPartIf->stmts = [clone $return]; - $this->addNodeAfterNode($booleanNotPartIf, $if); - $booleanNotPartIf = new If_(new BooleanNot($invertedCondition->expr->right)); - $booleanNotPartIf->stmts = [clone $return]; - $this->addNodeAfterNode($booleanNotPartIf, $if); - return; + $secondBooleanNotPartIf = new If_(new BooleanNot($invertedCondExpr->expr->right)); + $secondBooleanNotPartIf->stmts = [clone $return]; + + return [$booleanNotPartIf, $secondBooleanNotPartIf]; } - $nestedIfWithOnlyReturn->cond = $invertedCondition; - $nestedIfWithOnlyReturn->stmts = [clone $return]; + $onlyReturnIf->cond = $invertedCondExpr; + $onlyReturnIf->stmts = [$return]; - $this->addNodeAfterNode($nestedIfWithOnlyReturn, $if); + return [$onlyReturnIf]; } } diff --git a/rules/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector.php b/rules/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector.php index 5dd8a292c8d..300d8bde87b 100644 --- a/rules/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector.php +++ b/rules/EarlyReturn/Rector/If_/ChangeOrIfContinueToMultiContinueRector.php @@ -9,8 +9,8 @@ use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Stmt\Continue_; use PhpParser\Node\Stmt\If_; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeManipulator\IfManipulator; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,13 +20,13 @@ final class ChangeOrIfContinueToMultiContinueRector extends AbstractRector { public function __construct( - private IfManipulator $ifManipulator + private readonly IfManipulator $ifManipulator ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Changes if || to early return', [ + return new RuleDefinition('Change `if a || b` to early return', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -79,8 +79,9 @@ public function getNodeTypes(): array /** * @param If_ $node + * @return null|If_[] */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { if (! $this->ifManipulator->isIfWithOnly($node, Continue_::class)) { return null; @@ -93,22 +94,23 @@ public function refactor(Node $node): ?Node return $this->processMultiIfContinue($node); } - private function processMultiIfContinue(If_ $if): If_ + /** + * @return null|If_[] + */ + private function processMultiIfContinue(If_ $if): ?array { $node = clone $if; /** @var Continue_ $continue */ $continue = $if->stmts[0]; $ifs = $this->createMultipleIfs($if->cond, $continue, []); - foreach ($ifs as $key => $if) { - if ($key === 0) { - $this->mirrorComments($if, $node); - } - $this->addNodeBeforeNode($if, $node); + // ensure ifs not removed by other rules + if ($ifs === []) { + return null; } - $this->removeNode($node); - return $node; + $this->mirrorComments($ifs[0], $node); + return $ifs; } /** @@ -118,24 +120,35 @@ private function processMultiIfContinue(If_ $if): If_ private function createMultipleIfs(Expr $expr, Continue_ $continue, array $ifs): array { while ($expr instanceof BooleanOr) { - $ifs = array_merge($ifs, $this->collectLeftbooleanOrToIfs($expr, $continue, $ifs)); - $ifs[] = $this->ifManipulator->createIfExpr($expr->right, $continue); + $ifs = [...$ifs, ...$this->collectLeftBooleanOrToIfs($expr, $continue, $ifs)]; + $ifs[] = new If_($expr->right, [ + 'stmts' => [$continue], + ]); $expr = $expr->right; } - return $ifs + [$this->ifManipulator->createIfExpr($expr, $continue)]; + $lastContinueIf = new If_($expr, [ + 'stmts' => [$continue], + ]); + + // the + is on purpose here, to keep only single continue as last + return $ifs + [$lastContinueIf]; } /** * @param If_[] $ifs * @return If_[] */ - private function collectLeftbooleanOrToIfs(BooleanOr $booleanOr, Continue_ $continue, array $ifs): array + private function collectLeftBooleanOrToIfs(BooleanOr $booleanOr, Continue_ $continue, array $ifs): array { $left = $booleanOr->left; if (! $left instanceof BooleanOr) { - return [$this->ifManipulator->createIfExpr($left, $continue)]; + $if = new If_($left, [ + 'stmts' => [$continue], + ]); + + return [$if]; } return $this->createMultipleIfs($left, $continue, $ifs); diff --git a/rules/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector.php b/rules/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector.php deleted file mode 100644 index 92d615b8517..00000000000 --- a/rules/EarlyReturn/Rector/If_/ChangeOrIfReturnToEarlyReturnRector.php +++ /dev/null @@ -1,165 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [If_::class]; - } - - /** - * @param If_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->ifManipulator->isIfWithOnly($node, Return_::class)) { - return null; - } - - if (! $node->cond instanceof BooleanOr) { - return null; - } - - if ($this->isInstanceofCondOnly($node->cond)) { - return null; - } - - /** @var Return_ $return */ - $return = $node->stmts[0]; - $ifs = $this->createMultipleIfs($node->cond, $return, []); - - foreach ($ifs as $key => $if) { - if ($key === 0) { - $this->mirrorComments($if, $node); - } - - $this->addNodeBeforeNode($if, $node); - } - - $this->removeNode($node); - return $node; - } - - /** - * @param If_[] $ifs - * @return If_[] - */ - private function createMultipleIfs(Expr $expr, Return_ $return, array $ifs): array - { - while ($expr instanceof BooleanOr) { - $ifs = array_merge($ifs, $this->collectLeftBooleanOrToIfs($expr, $return, $ifs)); - $ifs[] = $this->createIf($expr->right, $return); - - $expr = $expr->right; - } - return $ifs + [$this->createIf($expr, $return)]; - } - - /** - * @param If_[] $ifs - * @return If_[] - */ - private function collectLeftBooleanOrToIfs(BooleanOr $booleanOr, Return_ $return, array $ifs): array - { - $left = $booleanOr->left; - if (! $left instanceof BooleanOr) { - return [$this->createIf($left, $return)]; - } - - return $this->createMultipleIfs($left, $return, $ifs); - } - - private function createIf(Expr $expr, Return_ $return): If_ - { - return new If_( - $expr, - [ - 'stmts' => [$return], - ] - ); - } - - private function isInstanceofCondOnly(BooleanOr $booleanOr): bool - { - $currentNode = $booleanOr; - - if ($currentNode->left instanceof BooleanOr) { - return $this->isInstanceofCondOnly($currentNode->left); - } - - if ($currentNode->right instanceof BooleanOr) { - return $this->isInstanceofCondOnly($currentNode->right); - } - - if (! $currentNode->right instanceof Instanceof_) { - return false; - } - - return $currentNode->left instanceof Instanceof_; - } -} diff --git a/rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php b/rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php index aac957ea090..8f75aab7990 100644 --- a/rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php +++ b/rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php @@ -5,17 +5,15 @@ namespace Rector\EarlyReturn\Rector\If_; use PhpParser\Node; -use PhpParser\Node\Expr\BinaryOp\BooleanAnd; -use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\Exit_; +use PhpParser\Node\Expr\Throw_; use PhpParser\Node\Stmt\Continue_; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\ElseIf_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use PhpParser\Node\Stmt\Throw_; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -43,7 +41,7 @@ public function run($value) } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -72,64 +70,81 @@ public function getNodeTypes(): array /** * @param If_ $node + * @return Node[]|null */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - if ($this->doesLastStatementBreakFlow($node)) { - return null; - } - - if ($this->shouldSkip($node)) { + if ($this->doesNotLastStatementBreakFlow($node)) { return null; } if ($node->elseifs !== []) { - $originalNode = clone $node; - $if = new If_($node->cond); - $if->stmts = $node->stmts; - - $this->addNodeBeforeNode($if, $node); - $this->mirrorComments($if, $node); - - /** @var ElseIf_ $firstElseIf */ - $firstElseIf = array_shift($node->elseifs); - $node->cond = $firstElseIf->cond; - $node->stmts = $firstElseIf->stmts; - $this->mirrorComments($node, $firstElseIf); - - $statements = $this->getStatementsElseIfs($node); - if ($statements !== []) { - $this->addNodesAfterNode($statements, $node); - } - - if ($originalNode->else instanceof Else_) { - $node->else = null; - $this->addNodeAfterNode($originalNode->else, $node); - } - - return $node; + return $this->handleElseIfs($node); } - if ($node->else !== null) { - $this->addNodesAfterNode($node->else->stmts, $node); + if ($node->else instanceof Else_) { + $stmts = $node->else->stmts; $node->else = null; - return $node; + + return [$node, ...$stmts]; } return null; } - private function shouldSkip(If_ $if): bool + /** + * @return Node[] + */ + private function handleElseIfs(If_ $if): array { - // to avoid repetitive If_ creation when used along with ChangeOrIfReturnToEarlyReturnRector - // @see https://github.com/rectorphp/rector-src/pull/651 - if ($if->cond instanceof BooleanOr && $if->elseifs !== []) { - return true; + $nodesToReturn = []; + + $originalIf = clone $if; + $firstIf = $this->createIfFromNode($if); + $nodesToReturn[] = $firstIf; + + while ($if->elseifs !== []) { + /** @var ElseIf_ $currentElseIf */ + $currentElseIf = array_shift($if->elseifs); + + // If the last statement in the `elseif` breaks flow, merge it into the original `if` and stop processing + if ($this->doesNotLastStatementBreakFlow($currentElseIf)) { + $this->updateIfWithElseIf($if, $currentElseIf); + $nodesToReturn = [...$nodesToReturn, $if, ...$this->getStatementsElseIfs($if)]; + + break; + } + + $isLastElseIf = $if->elseifs === []; + + // If it's the last `elseif`, merge it with the original `if` to keep the formatting + if ($isLastElseIf) { + $this->updateIfWithElseIf($if, $currentElseIf); + $nodesToReturn[] = $if; + + break; + } + + // Otherwise, create a separate `if` node for `elseif` + $nodesToReturn[] = $this->createIfFromNode($currentElseIf); + } + + if ($originalIf->else instanceof Else_) { + $mergeStmts = true; + foreach ($nodesToReturn as $nodeToReturn) { + if ($this->doesNotLastStatementBreakFlow($nodeToReturn)) { + $nodesToReturn[] = $originalIf->else; + $mergeStmts = false; + break; + } + } + + if ($mergeStmts) { + $nodesToReturn = array_merge($nodesToReturn, $originalIf->else->stmts); + } } - // to avoid repetitive flipped elseif above return when used along with ChangeAndIfReturnToEarlyReturnRector - // @see https://github.com/rectorphp/rector-src/pull/654 - return $if->cond instanceof BooleanAnd && count($if->elseifs) > 1; + return $nodesToReturn; } /** @@ -139,7 +154,7 @@ private function getStatementsElseIfs(If_ $if): array { $statements = []; foreach ($if->elseifs as $key => $elseif) { - if ($this->doesLastStatementBreakFlow($elseif) && $elseif->stmts !== []) { + if ($this->doesNotLastStatementBreakFlow($elseif) && $elseif->stmts !== []) { continue; } @@ -150,13 +165,46 @@ private function getStatementsElseIfs(If_ $if): array return $statements; } - private function doesLastStatementBreakFlow(If_ | ElseIf_ $node): bool + private function doesNotLastStatementBreakFlow(If_ | ElseIf_ | Else_ $node): bool { $lastStmt = end($node->stmts); + if ($lastStmt instanceof If_ && $lastStmt->else instanceof Else_) { + if ($this->doesNotLastStatementBreakFlow($lastStmt) || $this->doesNotLastStatementBreakFlow( + $lastStmt->else + )) { + return true; + } + + foreach ($lastStmt->elseifs as $elseIf) { + if ($this->doesNotLastStatementBreakFlow($elseIf)) { + return true; + } + } + + return false; + } + return ! ($lastStmt instanceof Return_ - || $lastStmt instanceof Throw_ + || ($lastStmt instanceof Expression && $lastStmt->expr instanceof Throw_) || $lastStmt instanceof Continue_ || ($lastStmt instanceof Expression && $lastStmt->expr instanceof Exit_)); } + + private function createIfFromNode(If_ | ElseIf_ $node): If_ + { + $if = new If_($node->cond); + $if->stmts = $node->stmts; + $this->mirrorComments($if, $node); + + return $if; + } + + private function updateIfWithElseIf(If_ $if, ElseIf_ $elseIf): void + { + $if->cond = $elseIf->cond; + $if->stmts = $elseIf->stmts; + $this->mirrorComments($if, $elseIf); + $if->else = null; + } } diff --git a/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php b/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php index 8552fa424c3..8bf1d100df3 100644 --- a/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php +++ b/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php @@ -7,13 +7,21 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\FunctionLike; +use PhpParser\Node\Expr\AssignOp; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Do_; use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\For_; +use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\Node\Stmt\While_; +use Rector\EarlyReturn\ValueObject\BareSingleAssignIf; +use Rector\NodeManipulator\IfManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,7 +31,8 @@ final class PreparedValueToEarlyReturnRector extends AbstractRector { public function __construct( - private IfManipulator $ifManipulator + private readonly IfManipulator $ifManipulator, + private readonly BetterNodeFinder $betterNodeFinder ) { } @@ -38,11 +47,11 @@ public function run() { $var = null; - if (rand(0,1)) { + if (rand(0, 1)) { $var = 1; } - if (rand(0,1)) { + if (rand(0, 1)) { $var = 2; } @@ -57,11 +66,11 @@ class SomeClass { public function run() { - if (rand(0,1)) { + if (rand(0, 1)) { return 1; } - if (rand(0,1)) { + if (rand(0, 1)) { return 2; } @@ -78,193 +87,223 @@ public function run() */ public function getNodeTypes(): array { - return [Return_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Return_ $node + * @param StmtsAware $node + * @return StmtsAware */ public function refactor(Node $node): ?Node { - $ifsBefore = $this->getIfsBefore($node); - - if ($this->shouldSkip($ifsBefore, $node->expr)) { + if ($node->stmts === null) { return null; } - if ($this->isAssignVarUsedInIfCond($ifsBefore, $node->expr)) { - return null; - } + /** @var If_[] $ifs */ + $ifs = []; - /** @var Expr $returnExpr */ - $returnExpr = $node->expr; + $initialAssign = null; + $initialAssignPosition = null; - /** @var Expression $previousFirstExpression */ - $previousFirstExpression = $this->getPreviousIfLinearEquals($ifsBefore[0], $returnExpr); + foreach ($node->stmts as $key => $stmt) { + if ($stmt instanceof Expression && $stmt->expr instanceof AssignOp) { + return null; + } - /** @var Assign $previousAssign */ - $previousAssign = $previousFirstExpression->expr; + if (($stmt instanceof For_ || $stmt instanceof Foreach_ || $stmt instanceof While_ || $stmt instanceof Do_) && $initialAssign instanceof Assign) { + $isReassignInLoop = (bool) $this->betterNodeFinder->findFirst( + $stmt, + fn (Node $node): bool => $node instanceof Assign && $this->nodeComparator->areNodesEqual( + $node->var, + $initialAssign->var + ) + ); + + if ($isReassignInLoop) { + return null; + } + } - if ($this->isPreviousVarUsedInAssignExpr($ifsBefore, $previousAssign->var)) { - return null; - } + if ($stmt instanceof If_) { + $ifs[$key] = $stmt; + continue; + } - foreach ($ifsBefore as $ifBefore) { - /** @var Expression $expressionIf */ - $expressionIf = $ifBefore->stmts[0]; - /** @var Assign $assignIf */ - $assignIf = $expressionIf->expr; + if ($stmt instanceof Expression && $stmt->expr instanceof Assign) { + $initialAssign = $stmt->expr; + $initialAssignPosition = $key; + $ifs = []; + continue; + } - $ifBefore->stmts[0] = new Return_($assignIf->expr); - } + if (! $stmt instanceof Return_) { + continue; + } - /** @var Assign $assignPrevious */ - $assignPrevious = $previousFirstExpression->expr; - $node->expr = $assignPrevious->expr; - $this->removeNode($previousFirstExpression); + $return = $stmt; - return $node; - } + // match exact variable + if (! $return->expr instanceof Variable) { + return null; + } - /** - * @param If_[] $ifsBefore - */ - private function isAssignVarUsedInIfCond(array $ifsBefore, ?Expr $expr): bool - { - foreach ($ifsBefore as $ifBefore) { - $isUsedInIfCond = (bool) $this->betterNodeFinder->findFirst( - $ifBefore->cond, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $expr) - ); + if (! is_int($initialAssignPosition)) { + return null; + } - if ($isUsedInIfCond) { - return true; + if (! $initialAssign instanceof Assign) { + return null; } + + $matchingBareSingleAssignIfs = $this->getMatchingBareSingleAssignIfs($ifs, $node); + + if ($matchingBareSingleAssignIfs === []) { + return null; + } + + if (! $this->isVariableSharedInAssignIfsAndReturn( + $matchingBareSingleAssignIfs, + $return->expr, + $initialAssign + )) { + return null; + } + + return $this->refactorToDirectReturns( + $node, + $initialAssignPosition, + $matchingBareSingleAssignIfs, + $initialAssign, + $return + ); } - return false; + return null; } /** - * @param If_[] $ifsBefore + * @param If_[] $ifs + * @param StmtsAware $stmtsAware + * + * @return BareSingleAssignIf[] */ - private function isPreviousVarUsedInAssignExpr(array $ifsBefore, Expr $expr): bool + private function getMatchingBareSingleAssignIfs(array $ifs, Node $stmtsAware): array { - foreach ($ifsBefore as $ifBefore) { - /** @var Expression $expression */ - $expression = $ifBefore->stmts[0]; - /** @var Assign $assign */ - $assign = $expression->expr; - - $isUsedInAssignExpr = (bool) $this->betterNodeFinder->findFirst( - $assign->expr, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $expr) - ); + $bareSingleAssignIfs = []; + foreach ($ifs as $key => $if) { + $bareSingleAssignIf = $this->matchBareSingleAssignIf($if, $key, $stmtsAware); - if ($isUsedInAssignExpr) { - return true; + if (! $bareSingleAssignIf instanceof BareSingleAssignIf) { + return []; } + + $bareSingleAssignIfs[] = $bareSingleAssignIf; } - return false; + return $bareSingleAssignIfs; } /** - * @param If_[] $ifsBefore + * @param BareSingleAssignIf[] $bareSingleAssignIfs */ - private function shouldSkip(array $ifsBefore, ?Expr $returnExpr): bool - { - if ($ifsBefore === []) { - return true; + private function isVariableSharedInAssignIfsAndReturn( + array $bareSingleAssignIfs, + Expr $returnedExpr, + Assign $initialAssign + ): bool { + if (! $this->nodeComparator->areNodesEqual($returnedExpr, $initialAssign->var)) { + return false; } - return ! (bool) $this->getPreviousIfLinearEquals($ifsBefore[0], $returnExpr); + foreach ($bareSingleAssignIfs as $bareSingleAssignIf) { + $assign = $bareSingleAssignIf->getAssign(); + + $isVariableUsed = (bool) $this->betterNodeFinder->findFirst( + [$bareSingleAssignIf->getIfCondExpr(), $assign->expr], + fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $returnedExpr) + ); + + if ($isVariableUsed) { + return false; + } + + if (! $this->nodeComparator->areNodesEqual($assign->var, $returnedExpr)) { + return false; + } + } + + return true; } - private function getPreviousIfLinearEquals(?Node $node, ?Expr $expr): ?Expression + /** + * @param StmtsAware $stmtsAware + */ + private function matchBareSingleAssignIf(Stmt $stmt, int $key, Node $stmtsAware): ?BareSingleAssignIf { - if (! $node instanceof Node) { + if (! $stmt instanceof If_) { return null; } - if (! $expr instanceof Expr) { + // is exactly single stmt + if (count($stmt->stmts) !== 1) { return null; } - $previous = $node->getAttribute(AttributeKey::PREVIOUS_NODE); - if (! $previous instanceof Expression) { - return $this->getPreviousIfLinearEquals($previous, $expr); + $onlyStmt = $stmt->stmts[0]; + if (! $onlyStmt instanceof Expression) { + return null; } - if (! $previous->expr instanceof Assign) { + $expression = $onlyStmt; + if (! $expression->expr instanceof Assign) { return null; } - if ($this->nodeComparator->areNodesEqual($previous->expr->var, $expr)) { - return $previous; + if (! $this->ifManipulator->isIfWithoutElseAndElseIfs($stmt)) { + return null; } - return null; - } - - /** - * @return If_[] - */ - private function getIfsBefore(Return_ $return): array - { - $parent = $return->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof FunctionLike && ! $parent instanceof If_) { - return []; + if (! isset($stmtsAware->stmts[$key + 1])) { + return null; } - if ($parent->stmts === []) { - return []; + if ($stmtsAware->stmts[$key + 1] instanceof If_) { + return new BareSingleAssignIf($stmt, $expression->expr); } - $firstItemPosition = array_key_last($parent->stmts); - if ($parent->stmts[$firstItemPosition] !== $return) { - return []; + if ($stmtsAware->stmts[$key + 1] instanceof Return_) { + return new BareSingleAssignIf($stmt, $expression->expr); } - return $this->collectIfs($parent->stmts, $return); + return null; } /** - * @param If_[] $stmts - * @return If_[] + * @param StmtsAware $stmtsAware + * @param BareSingleAssignIf[] $bareSingleAssignIfs + * + * @return StmtsAware */ - private function collectIfs(array $stmts, Return_ $return): array - { - /** @va If_[] $ifs */ - $ifs = $this->betterNodeFinder->findInstanceOf($stmts, If_::class); - - /** Skip entirely if found skipped ifs */ - foreach ($ifs as $if) { - /** @var If_ $if */ - if (! $this->ifManipulator->isIfWithoutElseAndElseIfs($if)) { - return []; - } - - $stmts = $if->stmts; - if (count($stmts) !== 1) { - return []; - } - - $expression = $stmts[0]; - if (! $expression instanceof Expression) { - return []; - } - - if (! $expression->expr instanceof Assign) { - return []; - } - - $assign = $expression->expr; - if (! $this->nodeComparator->areNodesEqual($assign->var, $return->expr)) { - return []; - } + private function refactorToDirectReturns( + Node $stmtsAware, + int $initialAssignPosition, + array $bareSingleAssignIfs, + Assign $initialAssign, + Return_ $return + ): Node { + // 1. remove initial assign + unset($stmtsAware->stmts[$initialAssignPosition]); + + // 2. make ifs early return + foreach ($bareSingleAssignIfs as $bareSingleAssignIf) { + $if = $bareSingleAssignIf->getIf(); + $if->stmts[0] = new Return_($bareSingleAssignIf->getAssign()->expr); } - return $ifs; + // 3. make return default value + $return->expr = $initialAssign->expr; + + return $stmtsAware; } } diff --git a/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php b/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php deleted file mode 100644 index 18af0f0e7ff..00000000000 --- a/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php +++ /dev/null @@ -1,134 +0,0 @@ -something() && $this->somethingelse(); - } -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function accept() - { - if (!$this->something()) { - return false; - } - return (bool) $this->somethingelse(); - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Return_::class]; - } - - /** - * @param Return_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->expr instanceof BooleanAnd) { - return null; - } - - $left = $node->expr->left; - $ifNegations = $this->createMultipleIfsNegation($left, $node, []); - - if (! $this->callAnalyzer->doesIfHasObjectCall($ifNegations)) { - return null; - } - - $this->mirrorComments($ifNegations[0], $node); - foreach ($ifNegations as $ifNegation) { - $this->addNodeBeforeNode($ifNegation, $node); - } - - /** @var BooleanAnd $booleanAnd */ - $booleanAnd = $node->expr; - - $lastReturnExpr = $this->assignAndBinaryMap->getTruthyExpr($booleanAnd->right); - $this->addNodeBeforeNode(new Return_($lastReturnExpr), $node); - $this->removeNode($node); - - return $node; - } - - /** - * @param If_[] $ifNegations - * @return If_[] - */ - private function createMultipleIfsNegation(Expr $expr, Return_ $return, array $ifNegations): array - { - while ($expr instanceof BooleanAnd) { - $ifNegations = array_merge($ifNegations, $this->collectLeftBooleanAndToIfs($expr, $return, $ifNegations)); - $ifNegations[] = $this->ifManipulator->createIfNegation( - $expr->right, - new Return_($this->nodeFactory->createFalse()) - ); - - $expr = $expr->right; - } - return $ifNegations + [ - $this->ifManipulator->createIfNegation($expr, new Return_($this->nodeFactory->createFalse())), - ]; - } - - /** - * @param If_[] $ifNegations - * @return If_[] - */ - private function collectLeftBooleanAndToIfs(BooleanAnd $booleanAnd, Return_ $return, array $ifNegations): array - { - $left = $booleanAnd->left; - if (! $left instanceof BooleanAnd) { - return [$this->ifManipulator->createIfNegation($left, new Return_($this->nodeFactory->createFalse()))]; - } - - return $this->createMultipleIfsNegation($left, $return, $ifNegations); - } -} diff --git a/rules/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector.php b/rules/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector.php index a0dea6fc386..bad0b22ac7b 100644 --- a/rules/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector.php +++ b/rules/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector.php @@ -10,10 +10,10 @@ use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\NodeAnalyzer\CallAnalyzer; -use Rector\Core\NodeManipulator\IfManipulator; -use Rector\Core\PhpParser\Node\AssignAndBinaryMap; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeAnalyzer\CallAnalyzer; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\AssignAndBinaryMap; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,15 +23,14 @@ final class ReturnBinaryOrToEarlyReturnRector extends AbstractRector { public function __construct( - private IfManipulator $ifManipulator, - private AssignAndBinaryMap $assignAndBinaryMap, - private CallAnalyzer $callAnalyzer + private readonly AssignAndBinaryMap $assignAndBinaryMap, + private readonly CallAnalyzer $callAnalyzer ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Changes Single return of || to early returns', [ + return new RuleDefinition('Change single return of `||` to early returns', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -65,42 +64,58 @@ public function accept() */ public function getNodeTypes(): array { - return [Return_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Return_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if (! $node->expr instanceof BooleanOr) { + if ($node->stmts === null) { return null; } - /** @var BooleanOr $booleanOr */ - $booleanOr = $node->expr; + $hasChanged = false; - $left = $booleanOr->left; - $ifs = $this->createMultipleIfs($left, $node, []); + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Return_) { + continue; + } - if ($ifs === []) { - return null; - } + if (! $stmt->expr instanceof BooleanOr) { + continue; + } - if (! $this->callAnalyzer->doesIfHasObjectCall($ifs)) { - return null; - } + $booleanOr = $stmt->expr; + + $left = $booleanOr->left; + $ifs = $this->createMultipleIfs($left, $stmt, []); + + // ensure ifs not removed by other rules + if ($ifs === []) { + continue; + } + + if (! $this->callAnalyzer->doesIfHasObjectCall($ifs)) { + continue; + } + + $this->mirrorComments($ifs[0], $stmt); + + $lastReturnExpr = $this->assignAndBinaryMap->getTruthyExpr($booleanOr->right); - $this->mirrorComments($ifs[0], $node); - foreach ($ifs as $if) { - $this->addNodeBeforeNode($if, $node); + $ifsWithLastIf = array_merge($ifs, [new Return_($lastReturnExpr)]); + + array_splice($node->stmts, $key, 1, $ifsWithLastIf); + $hasChanged = true; } - $lastReturnExpr = $this->assignAndBinaryMap->getTruthyExpr($booleanOr->right); - $this->addNodeBeforeNode(new Return_($lastReturnExpr), $node); - $this->removeNode($node); + if ($hasChanged) { + return $node; + } - return $node; + return null; } /** @@ -110,34 +125,48 @@ public function refactor(Node $node): ?Node private function createMultipleIfs(Expr $expr, Return_ $return, array $ifs): array { while ($expr instanceof BooleanOr) { - $ifs = array_merge($ifs, $this->collectLeftBooleanOrToIfs($expr, $return, $ifs)); - $ifs[] = $this->ifManipulator->createIfExpr( - $expr->right, - new Return_($this->nodeFactory->createTrue()) - ); + $ifs = [...$ifs, ...$this->collectLeftBooleanOrToIfs($expr, $return, $ifs)]; + $ifs[] = new If_($expr->right, [ + 'stmts' => [new Return_($this->nodeFactory->createTrue())], + ]); $expr = $expr->right; if ($expr instanceof BooleanAnd) { return []; } + if (! $expr instanceof BooleanOr) { continue; } + return []; } - return $ifs + [$this->ifManipulator->createIfExpr($expr, new Return_($this->nodeFactory->createTrue()))]; + $lastIf = new If_($expr, [ + 'stmts' => [new Return_($this->nodeFactory->createTrue())], + ]); + + // if empty, fallback to last if + if ($ifs === []) { + return [$lastIf]; + } + + return $ifs; } /** * @param If_[] $ifs * @return If_[] */ - private function collectLeftBooleanOrToIfs(BooleanOr $BooleanOr, Return_ $return, array $ifs): array + private function collectLeftBooleanOrToIfs(BooleanOr $booleanOr, Return_ $return, array $ifs): array { - $left = $BooleanOr->left; + $left = $booleanOr->left; if (! $left instanceof BooleanOr) { - return [$this->ifManipulator->createIfExpr($left, new Return_($this->nodeFactory->createTrue()))]; + $returnTrueIf = new If_($left, [ + 'stmts' => [new Return_($this->nodeFactory->createTrue())], + ]); + + return [$returnTrueIf]; } return $this->createMultipleIfs($left, $return, $ifs); diff --git a/rules/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector.php b/rules/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector.php new file mode 100644 index 00000000000..87532dcb0c2 --- /dev/null +++ b/rules/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector.php @@ -0,0 +1,176 @@ +> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + $stmts = (array) $node->stmts; + + foreach ($stmts as $key => $stmt) { + $returnVariable = $this->matchNextStmtReturnVariable($node, $key); + if (! $returnVariable instanceof Variable) { + continue; + } + + if ($stmt instanceof If_ && ! $stmt->else instanceof Else_ && $stmt->elseifs === []) { + // is single condition if + $if = $stmt; + if (count($if->stmts) !== 1) { + continue; + } + + $onlyIfStmt = $if->stmts[0]; + $assignedExpr = $this->matchOnlyIfStmtReturnExpr($onlyIfStmt, $returnVariable); + if (! $assignedExpr instanceof Expr) { + continue; + } + + $if->stmts[0] = new Return_($assignedExpr); + $this->mirrorComments($if->stmts[0], $onlyIfStmt); + + return $node; + } + } + + return null; + } + + private function matchOnlyIfStmtReturnExpr(Stmt $onlyIfStmt, Variable $returnVariable): Expr|null + { + if (! $onlyIfStmt instanceof Expression) { + return null; + } + + if (! $onlyIfStmt->expr instanceof Assign) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($onlyIfStmt); + if ($phpDocInfo->getVarTagValueNode() instanceof VarTagValueNode) { + return null; + } + + $assign = $onlyIfStmt->expr; + + // assign to same variable that is returned + if (! $assign->var instanceof Variable) { + return null; + } + + if ($this->variableAnalyzer->isStaticOrGlobal($assign->var)) { + return null; + } + + if ($this->variableAnalyzer->isUsedByReference($assign->var)) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($assign->var, $returnVariable)) { + return null; + } + + // return directly + return $assign->expr; + } + + /** + * @param StmtsAware $stmtsAware + */ + private function matchNextStmtReturnVariable(Node $stmtsAware, int $key): Variable|null + { + $nextStmt = $stmtsAware->stmts[$key + 1] ?? null; + + // last item → stop + if (! $nextStmt instanceof Stmt) { + return null; + } + + if (! $nextStmt instanceof Return_) { + return null; + } + + // next return must be variable + if (! $nextStmt->expr instanceof Variable) { + return null; + } + + return $nextStmt->expr; + } +} diff --git a/rules/EarlyReturn/ValueObject/BareSingleAssignIf.php b/rules/EarlyReturn/ValueObject/BareSingleAssignIf.php new file mode 100644 index 00000000000..1c9a371afa1 --- /dev/null +++ b/rules/EarlyReturn/ValueObject/BareSingleAssignIf.php @@ -0,0 +1,33 @@ +if->cond; + } + + public function getIf(): If_ + { + return $this->if; + } + + public function getAssign(): Assign + { + return $this->assign; + } +} diff --git a/rules/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector.php b/rules/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector.php new file mode 100644 index 00000000000..e2d0445e9af --- /dev/null +++ b/rules/Instanceof_/Rector/Ternary/FlipNegatedTernaryInstanceofRector.php @@ -0,0 +1,64 @@ +getPrice();', + 'echo $object instanceof Product ? $object->getPrice() : null;' + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Ternary::class]; + } + + /** + * @param Ternary $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->if instanceof Expr) { + return null; + } + + if (! $node->cond instanceof BooleanNot) { + return null; + } + + $booleanNot = $node->cond; + if (! $booleanNot->expr instanceof Instanceof_) { + return null; + } + + $node->cond = $booleanNot->expr; + + // flip if and else + [$node->if, $node->else] = [$node->else, $node->if]; + + return $node; + } +} diff --git a/rules/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector.php b/rules/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector.php deleted file mode 100644 index 6e8eb292ed1..00000000000 --- a/rules/LeagueEvent/Rector/MethodCall/DispatchStringToObjectRector.php +++ /dev/null @@ -1,176 +0,0 @@ -dispatcher->dispatch('my-event'); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - /** @var \League\Event\EventDispatcher */ - private $dispatcher; - - public function run() - { - $this->dispatcher->dispatch(new class implements \League\Event\HasEventName - { - public function eventName(): string - { - return 'my-event'; - } - }); - } -} -CODE_SAMPLE - ), - - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - return $this->updateNode($node); - } - - private function shouldSkip(MethodCall $methodCall): bool - { - if (! $this->isNames($methodCall->name, ['dispatch', 'emit'])) { - return true; - } - - if (! $this->nodeTypeResolver->isObjectTypes($methodCall->var, [ - new ObjectType('League\Event\EventDispatcher'), - new ObjectType('League\Event\Emitter'), - ])) { - return true; - } - - return ! $this->getStaticType($methodCall->args[0]->value) instanceof StringType; - } - - private function updateNode(MethodCall $methodCall): MethodCall - { - $methodCall->args[0] = new Arg($this->createNewAnonymousEventClass($methodCall->args[0]->value)); - return $methodCall; - } - - private function createNewAnonymousEventClass(Expr $expr): New_ - { - $implements = [new FullyQualified('League\Event\HasEventName')]; - - return new New_(new Class_(null, [ - 'implements' => $implements, - self::STMTS => $this->createAnonymousEventClassBody(), - ]), [new Arg($expr)]); - } - - /** - * @return Stmt[] - */ - private function createAnonymousEventClassBody(): array - { - $return = new Return_(new PropertyFetch(new Variable('this'), 'name')); - - return [ - new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty(self::NAME)]), - new ClassMethod(MethodName::CONSTRUCT, [ - 'flags' => Class_::MODIFIER_PUBLIC, - 'params' => $this->createConstructParams(), - self::STMTS => [new Expression($this->createConstructAssign())], - ]), - new ClassMethod('eventName', [ - 'flags' => Class_::MODIFIER_PUBLIC, - 'returnType' => 'string', - self::STMTS => [$return], - ]), - ]; - } - - /** - * @return Param[] - */ - private function createConstructParams(): array - { - return [new Param(new Variable(self::NAME), null, 'string')]; - } - - private function createConstructAssign(): Assign - { - $propertyFetch = new PropertyFetch(new Variable('this'), 'name'); - return new Assign($propertyFetch, new Variable(self::NAME)); - } -} diff --git a/rules/MockeryToProphecy/Collector/MockVariableCollector.php b/rules/MockeryToProphecy/Collector/MockVariableCollector.php deleted file mode 100644 index 4121c68c4e2..00000000000 --- a/rules/MockeryToProphecy/Collector/MockVariableCollector.php +++ /dev/null @@ -1,52 +0,0 @@ - - */ - public function collectMockVariableName(FuncCall | StaticCall $node): array - { - $mockVariableTypesByNames = []; - - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Assign) { - return []; - } - - if (! $parentNode->var instanceof Variable) { - return []; - } - - /** @var Variable $variable */ - $variable = $parentNode->var; - - /** @var string $variableName */ - $variableName = $this->nodeNameResolver->getName($variable); - - $type = $node->args[0]->value; - - $mockedType = $this->valueResolver->getValue($type); - $mockVariableTypesByNames[$variableName] = $mockedType; - - return $mockVariableTypesByNames; - } -} diff --git a/rules/MockeryToProphecy/Rector/ClassMethod/MockeryCreateMockToProphizeRector.php b/rules/MockeryToProphecy/Rector/ClassMethod/MockeryCreateMockToProphizeRector.php deleted file mode 100644 index 9aae8e6b72f..00000000000 --- a/rules/MockeryToProphecy/Rector/ClassMethod/MockeryCreateMockToProphizeRector.php +++ /dev/null @@ -1,150 +0,0 @@ - - */ - private array $mockVariableTypesByNames = []; - - public function __construct( - private MockVariableCollector $mockVariableCollector, - private TestsNodeAnalyzer $testsNodeAnalyzer - ) { - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->testsNodeAnalyzer->isInTestClass($node)) { - return null; - } - - $this->replaceMockCreationsAndCollectVariableNames($node); - $this->revealMockArguments($node); - - return $node; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Changes mockery mock creation to Prophesize', - [ - new CodeSample( - <<<'CODE_SAMPLE' -$mock = \Mockery::mock('MyClass'); -$service = new Service(); -$service->injectDependency($mock); -CODE_SAMPLE -, - <<<'CODE_SAMPLE' - $mock = $this->prophesize('MyClass'); - -$service = new Service(); -$service->injectDependency($mock->reveal()); -CODE_SAMPLE - ), - ] - ); - } - - private function replaceMockCreationsAndCollectVariableNames(ClassMethod $classMethod): void - { - if ($classMethod->stmts === null) { - return; - } - - $this->traverseNodesWithCallable($classMethod->stmts, function (Node $node): ?MethodCall { - if (! $node instanceof StaticCall) { - return null; - } - - $callerType = $this->nodeTypeResolver->resolve($node->class); - if (! $callerType->isSuperTypeOf(new ObjectType('Mockery'))->yes()) { - return null; - } - - if (! $this->isName($node->name, 'mock')) { - return null; - } - - $collectedVariableTypesByNames = $this->mockVariableCollector->collectMockVariableName($node); - - $this->mockVariableTypesByNames = array_merge( - $this->mockVariableTypesByNames, - $collectedVariableTypesByNames - ); - - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Arg) { - $prophesizeMethodCall = $this->createProphesizeMethodCall($node); - return $this->nodeFactory->createMethodCall($prophesizeMethodCall, 'reveal'); - } - - return $this->createProphesizeMethodCall($node); - }); - } - - private function revealMockArguments(ClassMethod $classMethod): void - { - if ($classMethod->stmts === null) { - return; - } - - $this->traverseNodesWithCallable($classMethod->stmts, function (Node $node): ?MethodCall { - if (! $node instanceof Arg) { - return null; - } - - if (! $node->value instanceof Variable) { - return null; - } - - /** @var string $variableName */ - $variableName = $this->getName($node->value); - - if (! isset($this->mockVariableTypesByNames[$variableName])) { - return null; - } - - return $this->nodeFactory->createMethodCall($node->value, 'reveal'); - }); - } - - private function createProphesizeMethodCall(StaticCall $staticCall): MethodCall - { - return $this->nodeFactory->createLocalMethodCall('prophesize', [$staticCall->args[0]]); - } -} diff --git a/rules/MockeryToProphecy/Rector/StaticCall/MockeryCloseRemoveRector.php b/rules/MockeryToProphecy/Rector/StaticCall/MockeryCloseRemoveRector.php deleted file mode 100644 index 217413b1840..00000000000 --- a/rules/MockeryToProphecy/Rector/StaticCall/MockeryCloseRemoveRector.php +++ /dev/null @@ -1,78 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [StaticCall::class]; - } - - /** - * @param StaticCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->testsNodeAnalyzer->isInTestClass($node)) { - return null; - } - - $callerType = $this->nodeTypeResolver->resolve($node->class); - if (! $callerType->isSuperTypeOf(new ObjectType('Mockery'))->yes()) { - return null; - } - - if (! $this->isName($node->name, 'close')) { - return null; - } - - $this->removeNode($node); - - return null; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Removes mockery close from test classes', - [ - new CodeSample( - <<<'CODE_SAMPLE' -public function tearDown() : void -{ - \Mockery::close(); -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -public function tearDown() : void -{ -} -CODE_SAMPLE - ), - ] - ); - } -} diff --git a/rules/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector.php b/rules/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector.php deleted file mode 100644 index 6ab3b6fde6c..00000000000 --- a/rules/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector.php +++ /dev/null @@ -1,196 +0,0 @@ - - */ - private const FIELD_TO_FIELD_DIRECT = [ - 'mysql_field_len' => 'length', - 'mysql_field_name' => 'name', - 'mysql_field_table' => 'table', - ]; - - /** - * @var string - */ - private const MYSQLI_DATA_SEEK = 'mysqli_data_seek'; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Converts more complex mysql functions to mysqli', - [ - new CodeSample( - <<<'CODE_SAMPLE' -$data = mysql_db_name($result, $row); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -mysqli_data_seek($result, $row); -$fetch = mysql_fetch_row($result); -$data = $fetch[0]; -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Assign::class]; - } - - /** - * @param Assign $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->expr instanceof FuncCall) { - return null; - } - - /** @var FuncCall $funcCallNode */ - $funcCallNode = $node->expr; - - if ($this->isName($funcCallNode, 'mysql_tablename')) { - return $this->processMysqlTableName($node, $funcCallNode); - } - - if ($this->isName($funcCallNode, 'mysql_db_name')) { - return $this->processMysqlDbName($node, $funcCallNode); - } - - if ($this->isName($funcCallNode, 'mysql_db_query')) { - return $this->processMysqliSelectDb($node, $funcCallNode); - } - - if ($this->isName($funcCallNode, 'mysql_fetch_field')) { - return $this->processMysqlFetchField($node, $funcCallNode); - } - - if ($this->isName($funcCallNode, 'mysql_result')) { - return $this->processMysqlResult($node, $funcCallNode); - } - - return $this->processFieldToFieldDirect($node, $funcCallNode); - } - - private function processMysqlTableName(Assign $assign, FuncCall $funcCall): FuncCall - { - $funcCall->name = new Name(self::MYSQLI_DATA_SEEK); - - $newFuncCall = new FuncCall(new Name('mysql_fetch_array'), [$funcCall->args[0]]); - $newAssignNode = new Assign($assign->var, new ArrayDimFetch($newFuncCall, new LNumber(0))); - - $this->addNodeAfterNode($newAssignNode, $assign); - - return $funcCall; - } - - private function processMysqlDbName(Assign $assign, FuncCall $funcCall): FuncCall - { - $funcCall->name = new Name(self::MYSQLI_DATA_SEEK); - - $mysqlFetchRowFuncCall = new FuncCall(new Name('mysqli_fetch_row'), [$funcCall->args[0]]); - $fetchVariable = new Variable('fetch'); - $newAssignNode = new Assign($fetchVariable, $mysqlFetchRowFuncCall); - $this->addNodeAfterNode($newAssignNode, $assign); - - $newAssignNode = new Assign($assign->var, new ArrayDimFetch($fetchVariable, new LNumber(0))); - $this->addNodeAfterNode($newAssignNode, $assign); - - return $funcCall; - } - - private function processMysqliSelectDb(Assign $assign, FuncCall $funcCall): FuncCall - { - $funcCall->name = new Name('mysqli_select_db'); - - $newAssignNode = new Assign($assign->var, new FuncCall(new Name('mysqli_query'), [$funcCall->args[1]])); - $this->addNodeAfterNode($newAssignNode, $assign); - - unset($funcCall->args[1]); - - return $funcCall; - } - - private function processMysqlFetchField(Assign $assign, FuncCall $funcCall): Assign - { - if (isset($funcCall->args[1])) { - $funcCall->name = new Name('mysqli_fetch_field_direct'); - } else { - $funcCall->name = new Name('mysqli_fetch_field'); - } - - return $assign; - } - - private function processMysqlResult(Assign $assign, FuncCall $funcCall): FuncCall - { - $fetchField = null; - if (isset($funcCall->args[2])) { - $fetchField = $funcCall->args[2]->value; - unset($funcCall->args[2]); - } - - $funcCall->name = new Name(self::MYSQLI_DATA_SEEK); - - $mysqlFetchArrayFuncCall = new FuncCall(new Name('mysqli_fetch_array'), [$funcCall->args[0]]); - $fetchVariable = new Variable('fetch'); - $newAssignNode = new Assign($fetchVariable, $mysqlFetchArrayFuncCall); - $this->addNodeAfterNode($newAssignNode, $assign); - - $newAssignNode = new Assign($assign->var, new ArrayDimFetch($fetchVariable, $fetchField ?? new LNumber(0))); - $this->addNodeAfterNode($newAssignNode, $assign); - - return $funcCall; - } - - private function processFieldToFieldDirect(Assign $assign, FuncCall $funcCall): ?Assign - { - foreach (self::FIELD_TO_FIELD_DIRECT as $funcName => $property) { - if ($this->isName($funcCall, $funcName)) { - $parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof PropertyFetch) { - continue; - } - if ($parentNode instanceof StaticPropertyFetch) { - continue; - } - - $funcCall->name = new Name('mysqli_fetch_field_direct'); - $assign->expr = new PropertyFetch($funcCall, $property); - - return $assign; - } - } - - return null; - } -} diff --git a/rules/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector.php b/rules/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector.php deleted file mode 100644 index 85461095229..00000000000 --- a/rules/MysqlToMysqli/Rector/FuncCall/MysqlFuncCallToMysqliRector.php +++ /dev/null @@ -1,113 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): FuncCall - { - if ($this->isName($node, 'mysql_create_db')) { - return $this->processMysqlCreateDb($node); - } - - if ($this->isName($node, 'mysql_drop_db')) { - return $this->processMysqlDropDb($node); - } - - if ($this->isName($node, 'mysql_list_dbs')) { - $node->name = new Name(self::MYSQLI_QUERY); - $node->args[0] = new Arg(new String_('SHOW DATABASES')); - } - - if ($this->isName($node, 'mysql_list_fields')) { - $node->name = new Name(self::MYSQLI_QUERY); - $node->args[0]->value = $this->joinStringWithNode('SHOW COLUMNS FROM', $node->args[1]->value); - - unset($node->args[1]); - } - - if ($this->isName($node, 'mysql_list_tables')) { - $node->name = new Name(self::MYSQLI_QUERY); - $node->args[0]->value = $this->joinStringWithNode('SHOW TABLES FROM', $node->args[0]->value); - } - - return $node; - } - - private function processMysqlCreateDb(FuncCall $funcCall): FuncCall - { - $funcCall->name = new Name(self::MYSQLI_QUERY); - $funcCall->args[0]->value = $this->joinStringWithNode('CREATE DATABASE', $funcCall->args[0]->value); - - return $funcCall; - } - - private function processMysqlDropDb(FuncCall $funcCall): FuncCall - { - $funcCall->name = new Name(self::MYSQLI_QUERY); - $funcCall->args[0]->value = $this->joinStringWithNode('DROP DATABASE', $funcCall->args[0]->value); - - return $funcCall; - } - - private function joinStringWithNode(string $string, Expr $expr): String_ | Concat - { - if ($expr instanceof String_) { - return new String_($string . ' ' . $expr->value); - } - - return new Concat(new String_($string . ' '), $expr); - } -} diff --git a/rules/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector.php b/rules/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector.php deleted file mode 100644 index c4f7ce4a9a9..00000000000 --- a/rules/MysqlToMysqli/Rector/FuncCall/MysqlPConnectToMysqliConnectRector.php +++ /dev/null @@ -1,85 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'mysql_pconnect')) { - return null; - } - - $node->name = new Name('mysqli_connect'); - - $node->args[0]->value = $this->joinStringWithNode('p:', $node->args[0]->value); - - return $node; - } - - private function joinStringWithNode(string $string, Expr $expr): String_ | Concat - { - if ($expr instanceof String_) { - return new String_($string . $expr->value); - } - - return new Concat(new String_($string), $expr); - } -} diff --git a/rules/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector.php b/rules/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector.php deleted file mode 100644 index 104e01cd5f7..00000000000 --- a/rules/MysqlToMysqli/Rector/FuncCall/MysqlQueryMysqlErrorWithLinkRector.php +++ /dev/null @@ -1,242 +0,0 @@ - - */ - private const FUNCTION_RENAME_MAP = [ - 'mysql_affected_rows' => 'mysqli_affected_rows', - 'mysql_client_encoding' => 'mysqli_character_set_name', - 'mysql_close' => 'mysqli_close', - 'mysql_errno' => 'mysqli_errno', - 'mysql_error' => 'mysqli_error', - 'mysql_escape_string' => 'mysqli_real_escape_string', - 'mysql_get_host_info' => 'mysqli_get_host_info', - 'mysql_get_proto_info' => 'mysqli_get_proto_info', - 'mysql_get_server_info' => 'mysqli_get_server_info', - 'mysql_info' => 'mysqli_info', - 'mysql_insert_id' => 'mysqli_insert_id', - 'mysql_ping' => 'mysqli_ping', - 'mysql_query' => 'mysqli_query', - 'mysql_real_escape_string' => 'mysqli_real_escape_string', - 'mysql_select_db' => 'mysqli_select_db', - 'mysql_set_charset' => 'mysqli_set_charset', - 'mysql_stat' => 'mysqli_stat', - 'mysql_thread_id' => 'mysqli_thread_id', - ]; - - /** - * @var array - */ - private const FUNCTION_CONNECTION_PARAMETER_POSITION_MAP = [ - 'mysql_affected_rows' => 0, - 'mysql_client_encoding' => 0, - 'mysql_close' => 0, - 'mysql_errno' => 0, - 'mysql_error' => 0, - 'mysql_get_host_info' => 0, - 'mysql_get_proto_info' => 0, - 'mysql_get_server_info' => 0, - 'mysql_info' => 0, - 'mysql_insert_id' => 0, - 'mysql_ping' => 0, - 'mysql_query' => 1, - 'mysql_real_escape_string' => 1, - 'mysql_select_db' => 1, - 'mysql_set_charset' => 1, - 'mysql_stat' => 0, - 'mysql_thread_id' => 0, - ]; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Add mysql_query and mysql_error with connection', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $conn = mysqli_connect('host', 'user', 'pass'); - - mysql_error(); - $sql = 'SELECT'; - - return mysql_query($sql); - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $conn = mysqli_connect('host', 'user', 'pass'); - - mysqli_error($conn); - $sql = 'SELECT'; - - return mysqli_query($conn, $sql); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - foreach (self::FUNCTION_RENAME_MAP as $oldFunction => $newFunction) { - if (! $this->isName($node, $oldFunction)) { - continue; - } - - if ( - $node->args === [] - || ! $this->isProbablyMysql($node->args[0]->value) - ) { - $connectionVariable = $this->findConnectionVariable($node); - - $this->removeExistingConnectionParameter($node); - - if (! $connectionVariable instanceof Expr) { - return null; - } - - $node->args = array_merge([new Arg($connectionVariable)], $node->args); - } - - $node->name = new Name($newFunction); - - return $node; - } - - return null; - } - - private function isProbablyMysql(Expr $expr): bool - { - if ($this->isObjectType($expr, new ObjectType('mysqli'))) { - return true; - } - - $staticType = $this->getStaticType($expr); - $resourceType = new ResourceType(); - - if ($staticType->equals($resourceType)) { - return true; - } - - if ($this->isUnionTypeWithResourceSubType($staticType, $resourceType)) { - return true; - } - if (! $expr instanceof Variable) { - return false; - } - - return $this->isMysqliConnect($expr); - } - - private function findConnectionVariable(FuncCall $funcCall): ?Expr - { - $connectionAssign = $this->betterNodeFinder->findFirstPrevious($funcCall, function (Node $node): ?bool { - if (! $node instanceof Assign) { - return null; - } - - return $this->isObjectType($node->expr, new ObjectType('mysqli')); - }); - - if (! $connectionAssign instanceof Assign) { - return null; - } - - return $connectionAssign->var; - } - - private function removeExistingConnectionParameter(FuncCall $funcCall): void - { - /** @var string $functionName */ - $functionName = $this->getName($funcCall); - if (! isset(self::FUNCTION_CONNECTION_PARAMETER_POSITION_MAP[$functionName])) { - return; - } - - $connectionPosition = self::FUNCTION_CONNECTION_PARAMETER_POSITION_MAP[$functionName]; - unset($funcCall->args[$connectionPosition]); - } - - private function isUnionTypeWithResourceSubType(Type $staticType, ResourceType $resourceType): bool - { - if ($staticType instanceof UnionType) { - foreach ($staticType->getTypes() as $type) { - if ($type->equals($resourceType)) { - return true; - } - } - } - - return false; - } - - private function isMysqliConnect(Variable $variable): bool - { - return (bool) $this->betterNodeFinder->findFirstPrevious($variable, function (Node $node) use ( - $variable - ): bool { - if (! $node instanceof Assign) { - return false; - } - - if (! $node->expr instanceof FuncCall) { - return false; - } - - if (! $this->nodeComparator->areNodesEqual($node->var, $variable)) { - return false; - } - - return $this->isName($node->expr, 'mysqli_connect'); - }); - } -} diff --git a/rules/Naming/ArrayDimFetchRenamer.php b/rules/Naming/ArrayDimFetchRenamer.php deleted file mode 100644 index 38ae40e7d0a..00000000000 --- a/rules/Naming/ArrayDimFetchRenamer.php +++ /dev/null @@ -1,58 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function ( - Node $node - ) use ($arrayDimFetch, $variableName) { - // do not rename element above - if ($node->getLine() <= $arrayDimFetch->getLine()) { - return null; - } - - if ($this->isScopeNesting($node)) { - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - if (! $this->nodeComparator->areNodesEqual($node, $arrayDimFetch)) { - return null; - } - - return new Variable($variableName); - }); - } - - private function isScopeNesting(Node $node): bool - { - return $node instanceof Closure || $node instanceof Function_ || $node instanceof ArrowFunction; - } -} diff --git a/rules/Naming/Contract/Guard/ConflictingNameGuardInterface.php b/rules/Naming/Contract/Guard/ConflictingNameGuardInterface.php deleted file mode 100644 index f15ee551451..00000000000 --- a/rules/Naming/Contract/Guard/ConflictingNameGuardInterface.php +++ /dev/null @@ -1,12 +0,0 @@ - */ - private const SINGULAR_VERB = [ + private const array SINGULARIZE_MAP = [ 'news' => 'new', ]; /** - * @var string * @see https://regex101.com/r/lbQaGC/3 */ - private const CAMELCASE_REGEX = '#(?([a-z]+|[A-Z]{1,}[a-z]+|_))#'; + private const string CAMELCASE_REGEX = '#(?([a-z\d]+|[A-Z\d]{1,}[a-z\d]+|_))#'; /** - * @var string * @see https://regex101.com/r/2aGdkZ/2 */ - private const BY_MIDDLE_REGEX = '#(?By[A-Z][a-zA-Z]+)#'; + private const string BY_MIDDLE_REGEX = '#(?By[A-Z][a-zA-Z]+)#'; - /** - * @var string - */ - private const SINGLE = 'single'; - - /** - * @var Inflector - */ - private $inflector; + private const string CAMELCASE = 'camelcase'; - public function __construct(Inflector $inflector) - { - $this->inflector = $inflector; + public function __construct( + private Inflector $inflector + ) { } public function resolve(string $currentName): string { $matchBy = Strings::match($currentName, self::BY_MIDDLE_REGEX); - if ($matchBy) { - return Strings::substring($currentName, 0, - strlen($matchBy['by'])); - } - - if (array_key_exists($currentName, self::SINGULAR_VERB)) { - return self::SINGULAR_VERB[$currentName]; + if ($matchBy !== null) { + return Strings::substring($currentName, 0, -strlen((string) $matchBy['by'])); } - if (strpos($currentName, self::SINGLE) === 0) { - return $currentName; + $resolvedValue = $this->resolveSingularizeMap($currentName); + if ($resolvedValue !== null) { + return $resolvedValue; } - $camelCases = Strings::matchAll($currentName, self::CAMELCASE_REGEX); - $singularValueVarName = ''; - foreach ($camelCases as $camelCase) { - $singularValueVarName .= $this->inflector->singularize($camelCase['camelcase']); - } + $singularValueVarName = $this->singularizeCamelParts($currentName); - if ($singularValueVarName === '' || $singularValueVarName === '_') { + if (in_array($singularValueVarName, ['', '_'], true)) { return $currentName; } - $singularValueVarName = $singularValueVarName === $currentName - ? self::SINGLE . ucfirst($singularValueVarName) - : $singularValueVarName; - if (strpos($singularValueVarName, self::SINGLE) !== 0) { - return $singularValueVarName; - } $length = strlen($singularValueVarName); if ($length < 40) { return $singularValueVarName; } + return $currentName; } + + private function resolveSingularizeMap(string $currentName): string|null + { + foreach (self::SINGULARIZE_MAP as $plural => $singular) { + if ($currentName === $plural) { + return $singular; + } + + if (StringUtils::isMatch($currentName, '#' . ucfirst($plural) . '#')) { + $resolvedValue = Strings::replace($currentName, '#' . ucfirst($plural) . '#', ucfirst($singular)); + return $this->singularizeCamelParts($resolvedValue); + } + + if (StringUtils::isMatch($currentName, '#' . $plural . '#')) { + $resolvedValue = Strings::replace($currentName, '#' . $plural . '#', $singular); + return $this->singularizeCamelParts($resolvedValue); + } + } + + return null; + } + + private function singularizeCamelParts(string $currentName): string + { + $camelCases = Strings::matchAll($currentName, self::CAMELCASE_REGEX); + + $resolvedName = ''; + foreach ($camelCases as $camelCase) { + if (in_array($camelCase[self::CAMELCASE], ['is', 'has', 'cms', 'this'], true)) { + $value = $camelCase[self::CAMELCASE]; + } else { + $value = $this->inflector->singularize((string) $camelCase[self::CAMELCASE]); + } + + $resolvedName .= $value; + } + + return $resolvedName; + } } diff --git a/rules/Naming/ExpectedNameResolver/MatchParamTypeExpectedNameResolver.php b/rules/Naming/ExpectedNameResolver/MatchParamTypeExpectedNameResolver.php index 078b7c6fc43..d309b945a9a 100644 --- a/rules/Naming/ExpectedNameResolver/MatchParamTypeExpectedNameResolver.php +++ b/rules/Naming/ExpectedNameResolver/MatchParamTypeExpectedNameResolver.php @@ -4,38 +4,30 @@ namespace Rector\Naming\ExpectedNameResolver; +use PhpParser\Node; use PhpParser\Node\Param; use Rector\Naming\Naming\PropertyNaming; use Rector\Naming\ValueObject\ExpectedName; use Rector\StaticTypeMapper\StaticTypeMapper; -final class MatchParamTypeExpectedNameResolver +final readonly class MatchParamTypeExpectedNameResolver { - /** - * @var PropertyNaming - */ - private $propertyNaming; - - /** - * @var StaticTypeMapper - */ - private $staticTypeMapper; - - public function __construct(StaticTypeMapper $staticTypeMapper, PropertyNaming $propertyNaming) - { - $this->staticTypeMapper = $staticTypeMapper; - $this->propertyNaming = $propertyNaming; + public function __construct( + private StaticTypeMapper $staticTypeMapper, + private PropertyNaming $propertyNaming, + ) { } public function resolve(Param $param): ?string { // nothing to verify - if ($param->type === null) { + if (! $param->type instanceof Node) { return null; } $staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); $expectedName = $this->propertyNaming->getExpectedNameFromType($staticType); + if (! $expectedName instanceof ExpectedName) { return null; } diff --git a/rules/Naming/ExpectedNameResolver/MatchPropertyTypeExpectedNameResolver.php b/rules/Naming/ExpectedNameResolver/MatchPropertyTypeExpectedNameResolver.php index efa93b000ab..61f6bc43d7b 100644 --- a/rules/Naming/ExpectedNameResolver/MatchPropertyTypeExpectedNameResolver.php +++ b/rules/Naming/ExpectedNameResolver/MatchPropertyTypeExpectedNameResolver.php @@ -4,54 +4,80 @@ namespace Rector\Naming\ExpectedNameResolver; +use PhpParser\Node; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Property; +use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use PHPStan\Reflection\ClassReflection; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Naming\Naming\PropertyNaming; use Rector\Naming\ValueObject\ExpectedName; +use Rector\NodeManipulator\PropertyManipulator; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\StaticTypeMapper; -final class MatchPropertyTypeExpectedNameResolver +final readonly class MatchPropertyTypeExpectedNameResolver { - /** - * @var PropertyNaming - */ - private $propertyNaming; - - /** - * @var PhpDocInfoFactory - */ - private $phpDocInfoFactory; - - /** - * @var NodeNameResolver - */ - private $nodeNameResolver; - public function __construct( - PropertyNaming $propertyNaming, - PhpDocInfoFactory $phpDocInfoFactory, - NodeNameResolver $nodeNameResolver + private PropertyNaming $propertyNaming, + private PhpDocInfoFactory $phpDocInfoFactory, + private NodeNameResolver $nodeNameResolver, + private PropertyManipulator $propertyManipulator, + private ReflectionResolver $reflectionResolver, + private StaticTypeMapper $staticTypeMapper ) { - $this->propertyNaming = $propertyNaming; - $this->phpDocInfoFactory = $phpDocInfoFactory; - $this->nodeNameResolver = $nodeNameResolver; } - public function resolve(Property $property): ?string + public function resolve(Property $property, ClassLike $classLike): ?string { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + if (! $classLike instanceof Class_) { + return null; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($property); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $propertyName = $this->nodeNameResolver->getName($property); + if ($this->propertyManipulator->isUsedByTrait($classReflection, $propertyName)) { + return null; + } - $expectedName = $this->propertyNaming->getExpectedNameFromType($phpDocInfo->getVarType()); + $expectedName = $this->resolveExpectedName($property); if (! $expectedName instanceof ExpectedName) { return null; } // skip if already has suffix - $currentName = $this->nodeNameResolver->getName($property); - if ($this->nodeNameResolver->endsWith($currentName, $expectedName->getName())) { + if (str_ends_with($propertyName, $expectedName->getName()) || str_ends_with( + $propertyName, + ucfirst($expectedName->getName()) + )) { return null; } return $expectedName->getName(); } + + private function resolveExpectedName(Property $property): ?ExpectedName + { + // property type first + if ($property->type instanceof Node) { + $propertyType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($property->type); + return $this->propertyNaming->getExpectedNameFromType($propertyType); + } + + // fallback to docblock + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($property); + $hasVarTag = $phpDocInfo instanceof PhpDocInfo && $phpDocInfo->getVarTagValueNode() instanceof VarTagValueNode; + if ($hasVarTag) { + return $this->propertyNaming->getExpectedNameFromType($phpDocInfo->getVarType()); + } + + return null; + } } diff --git a/rules/Naming/Guard/BreakingVariableRenameGuard.php b/rules/Naming/Guard/BreakingVariableRenameGuard.php index 03c9352bf40..a266c9f3975 100644 --- a/rules/Naming/Guard/BreakingVariableRenameGuard.php +++ b/rules/Naming/Guard/BreakingVariableRenameGuard.php @@ -5,42 +5,41 @@ namespace Rector\Naming\Guard; use DateTimeInterface; -use Nette\Utils\Strings; use PhpParser\Node; +use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\Error; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\Function_; -use PhpParser\Node\Stmt\If_; use PHPStan\Analyser\Scope; use PHPStan\Type\ObjectType; -use PHPStan\Type\TypeWithClassName; -use Rector\Core\PhpParser\Node\BetterNodeFinder; use Rector\Naming\Naming\ConflictingNameResolver; -use Rector\Naming\Naming\OverridenExistingNamesResolver; +use Rector\Naming\Naming\OverriddenExistingNamesResolver; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\BetterNodeFinder; use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; +use Rector\Util\StringUtils; /** * This class check if a variable name change breaks existing code in class method */ -final class BreakingVariableRenameGuard +final readonly class BreakingVariableRenameGuard { /** - * @var string * @see https://regex101.com/r/1pKLgf/1 */ - private const AT_NAMING_REGEX = '#[\w+]At$#'; + public const string AT_NAMING_REGEX = '#[\w+]At$#'; public function __construct( private BetterNodeFinder $betterNodeFinder, private ConflictingNameResolver $conflictingNameResolver, private NodeTypeResolver $nodeTypeResolver, - private OverridenExistingNamesResolver $overridenExistingNamesResolver, + private OverriddenExistingNamesResolver $overriddenExistingNamesResolver, private TypeUnwrapper $typeUnwrapper, private NodeNameResolver $nodeNameResolver ) { @@ -49,7 +48,7 @@ public function __construct( public function shouldSkipVariable( string $currentName, string $expectedName, - ClassMethod | Function_ | Closure $functionLike, + ClassMethod | Function_ | Closure | ArrowFunction $functionLike, Variable $variable ): bool { // is the suffix? → also accepted @@ -62,7 +61,10 @@ public function shouldSkipVariable( return true; } - if ($this->overridenExistingNamesResolver->hasNameInClassMethodForNew($currentName, $functionLike)) { + if (! $functionLike instanceof ArrowFunction && $this->overriddenExistingNamesResolver->hasNameInClassMethodForNew( + $currentName, + $functionLike + )) { return true; } @@ -74,21 +76,13 @@ public function shouldSkipVariable( return true; } - if ($this->isUsedInClosureUsesName($expectedName, $functionLike)) { - return true; - } - - if ($this->isUsedInForeachKeyValueVar($variable, $currentName)) { - return true; - } - - return $this->isUsedInIfAndOtherBranches($variable, $currentName); + return $functionLike instanceof Closure && $this->isUsedInClosureUsesName($expectedName, $functionLike); } public function shouldSkipParam( string $currentName, string $expectedName, - ClassMethod $classMethod, + ClassMethod|Function_|Closure|ArrowFunction $classMethod, Param $param ): bool { // is the suffix? → also accepted @@ -106,7 +100,11 @@ public function shouldSkipParam( return true; } - if ($this->overridenExistingNamesResolver->hasNameInClassMethodForParam($expectedName, $classMethod)) { + if ($this->overriddenExistingNamesResolver->hasNameInFunctionLikeForParam($expectedName, $classMethod)) { + return true; + } + + if ($param->var instanceof Error) { return true; } @@ -118,11 +116,15 @@ public function shouldSkipParam( return true; } + if ($this->isGenerator($param)) { + return true; + } + if ($this->isDateTimeAtNamingConvention($param)) { return true; } - return (bool) $this->betterNodeFinder->find((array) $classMethod->stmts, function (Node $node) use ( + return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->getStmts(), function (Node $node) use ( $expectedName ): bool { if (! $node instanceof Variable) { @@ -148,9 +150,23 @@ private function isVariableAlreadyDefined(Variable $variable, string $currentVar return $trinaryLogic->maybe(); } - private function hasConflictVariable(ClassMethod | Function_ | Closure $functionLike, string $newName): bool - { - return $this->betterNodeFinder->hasInstanceOfName((array) $functionLike->stmts, Variable::class, $newName); + private function hasConflictVariable( + ClassMethod | Function_ | Closure | ArrowFunction $functionLike, + string $newName + ): bool { + if ($functionLike instanceof ArrowFunction) { + return $this->betterNodeFinder->hasInstanceOfName( + [$functionLike->expr, ...$functionLike->params], + Variable::class, + $newName + ); + } + + return $this->betterNodeFinder->hasInstanceOfName( + [...(array) $functionLike->stmts, ...$functionLike->params], + Variable::class, + $newName + ); } private function isUsedInClosureUsesName( @@ -164,81 +180,49 @@ private function isUsedInClosureUsesName( return $this->betterNodeFinder->hasVariableOfName($functionLike->uses, $expectedName); } - private function isUsedInForeachKeyValueVar(Variable $variable, string $currentName): bool + private function isRamseyUuidInterface(Param $param): bool { - $previousForeach = $this->betterNodeFinder->findFirstPreviousOfTypes($variable, [Foreach_::class]); - if ($previousForeach instanceof Foreach_) { - if ($previousForeach->keyVar === $variable) { - return false; - } - - if ($previousForeach->valueVar === $variable) { - return false; - } - - if ($this->nodeNameResolver->isName($previousForeach->valueVar, $currentName)) { - return true; - } - - if ($previousForeach->keyVar === null) { - return false; - } - - if ($this->nodeNameResolver->isName($previousForeach->keyVar, $currentName)) { - return true; - } - } - - return false; + return $this->nodeTypeResolver->isObjectType($param, new ObjectType('Ramsey\Uuid\UuidInterface')); } - private function isUsedInIfAndOtherBranches(Variable $variable, string $currentVariableName): bool + private function isDateTimeAtNamingConvention(Param $param): bool { - // is in if branches? - $previousIf = $this->betterNodeFinder->findFirstPreviousOfTypes($variable, [If_::class]); - if ($previousIf instanceof If_) { - $variableUses = []; - - $variableUses[] = $this->betterNodeFinder->findVariableOfName($previousIf->stmts, $currentVariableName); - - $previousStmts = $previousIf->else !== null ? $previousIf->else->stmts : []; - $variableUses[] = $this->betterNodeFinder->findVariableOfName($previousStmts, $currentVariableName); - $variableUses[] = $this->betterNodeFinder->findVariableOfName($previousIf->elseifs, $currentVariableName); + $type = $this->nodeTypeResolver->getType($param); + $type = $this->typeUnwrapper->unwrapFirstObjectTypeFromUnionType($type); - $variableUses = array_filter($variableUses); - if (count($variableUses) > 1) { - return true; - } + $className = ClassNameFromObjectTypeResolver::resolve($type); + if ($className === null) { + return false; } - return false; - } + if (! is_a($className, DateTimeInterface::class, true)) { + return false; + } - /** - * @TODO Remove once ParamRenamer created - */ - private function isRamseyUuidInterface(Param $param): bool - { - return $this->nodeTypeResolver->isObjectType($param, new ObjectType('Ramsey\Uuid\UuidInterface')); + /** @var string $currentName */ + $currentName = $this->nodeNameResolver->getName($param); + return StringUtils::isMatch($currentName, self::AT_NAMING_REGEX); } - /** - * @TODO Remove once ParamRenamer created - */ - private function isDateTimeAtNamingConvention(Param $param): bool + private function isGenerator(Param $param): bool { - $type = $this->nodeTypeResolver->resolve($param); - $type = $this->typeUnwrapper->unwrapFirstObjectTypeFromUnionType($type); - if (! $type instanceof TypeWithClassName) { + if (! $param->type instanceof Node) { return false; } - if (! is_a($type->getClassName(), DateTimeInterface::class, true)) { + $paramType = $this->nodeTypeResolver->getType($param); + if (! $paramType instanceof ObjectType) { return false; } - /** @var string $currentName */ - $currentName = $this->nodeNameResolver->getName($param); - return (bool) Strings::match($currentName, self::AT_NAMING_REGEX . ''); + if (str_ends_with($paramType->getClassName(), 'Generator') || str_ends_with( + $paramType->getClassName(), + 'Iterator' + )) { + return true; + } + + return $paramType->isInstanceOf('Symfony\Component\DependencyInjection\Argument\RewindableGenerator') + ->yes(); } } diff --git a/rules/Naming/Guard/DateTimeAtNamingConventionGuard.php b/rules/Naming/Guard/DateTimeAtNamingConventionGuard.php index 25a6f577c90..985c8901b6d 100644 --- a/rules/Naming/Guard/DateTimeAtNamingConventionGuard.php +++ b/rules/Naming/Guard/DateTimeAtNamingConventionGuard.php @@ -5,49 +5,37 @@ namespace Rector\Naming\Guard; use DateTimeInterface; -use Nette\Utils\Strings; -use PHPStan\Type\TypeWithClassName; -use Rector\Naming\Contract\Guard\ConflictingNameGuardInterface; -use Rector\Naming\Contract\RenameValueObjectInterface; use Rector\Naming\ValueObject\PropertyRename; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; +use Rector\Util\StringUtils; -final class DateTimeAtNamingConventionGuard implements ConflictingNameGuardInterface +final readonly class DateTimeAtNamingConventionGuard { - /** - * @var string - * @see https://regex101.com/r/1pKLgf/1/ - */ - private const AT_NAMING_REGEX = '#[\w+]At$#'; - public function __construct( private NodeTypeResolver $nodeTypeResolver, private TypeUnwrapper $typeUnwrapper ) { } - /** - * @param PropertyRename $renameValueObject - */ - public function isConflicting(RenameValueObjectInterface $renameValueObject): bool - { - return $this->isDateTimeAtNamingConvention($renameValueObject); - } - - private function isDateTimeAtNamingConvention(PropertyRename $propertyRename): bool + public function isConflicting(PropertyRename $propertyRename): bool { - $type = $this->nodeTypeResolver->resolve($propertyRename->getProperty()); + $type = $this->nodeTypeResolver->getType($propertyRename->getProperty()); $type = $this->typeUnwrapper->unwrapFirstObjectTypeFromUnionType($type); - if (! $type instanceof TypeWithClassName) { + $className = ClassNameFromObjectTypeResolver::resolve($type); + if ($className === null) { return false; } - if (! is_a($type->getClassName(), DateTimeInterface::class, true)) { + if (! is_a($className, DateTimeInterface::class, true)) { return false; } - return (bool) Strings::match($propertyRename->getCurrentName(), self::AT_NAMING_REGEX . ''); + return StringUtils::isMatch( + $propertyRename->getCurrentName(), + BreakingVariableRenameGuard::AT_NAMING_REGEX + ); } } diff --git a/rules/Naming/Guard/HasMagicGetSetGuard.php b/rules/Naming/Guard/HasMagicGetSetGuard.php index fb26baa1dce..0c58a3f7cf6 100644 --- a/rules/Naming/Guard/HasMagicGetSetGuard.php +++ b/rules/Naming/Guard/HasMagicGetSetGuard.php @@ -5,27 +5,22 @@ namespace Rector\Naming\Guard; use PHPStan\Reflection\ReflectionProvider; -use Rector\Naming\Contract\Guard\ConflictingNameGuardInterface; -use Rector\Naming\Contract\RenameValueObjectInterface; use Rector\Naming\ValueObject\PropertyRename; -final class HasMagicGetSetGuard implements ConflictingNameGuardInterface +final readonly class HasMagicGetSetGuard { public function __construct( private ReflectionProvider $reflectionProvider ) { } - /** - * @param PropertyRename $renameValueObject - */ - public function isConflicting(RenameValueObjectInterface $renameValueObject): bool + public function isConflicting(PropertyRename $propertyRename): bool { - if (! $this->reflectionProvider->hasClass($renameValueObject->getClassLikeName())) { + if (! $this->reflectionProvider->hasClass($propertyRename->getClassLikeName())) { return false; } - $classReflection = $this->reflectionProvider->getClass($renameValueObject->getClassLikeName()); + $classReflection = $this->reflectionProvider->getClass($propertyRename->getClassLikeName()); if ($classReflection->hasMethod('__set')) { return true; } diff --git a/rules/Naming/Guard/NotPrivatePropertyGuard.php b/rules/Naming/Guard/NotPrivatePropertyGuard.php deleted file mode 100644 index 7420a6148ff..00000000000 --- a/rules/Naming/Guard/NotPrivatePropertyGuard.php +++ /dev/null @@ -1,20 +0,0 @@ -isPrivateProperty(); - } -} diff --git a/rules/Naming/Guard/PropertyConflictingNameGuard/MatchPropertyTypeConflictingNameGuard.php b/rules/Naming/Guard/PropertyConflictingNameGuard/MatchPropertyTypeConflictingNameGuard.php index 6bce4d6bc2d..e46fbcc6563 100644 --- a/rules/Naming/Guard/PropertyConflictingNameGuard/MatchPropertyTypeConflictingNameGuard.php +++ b/rules/Naming/Guard/PropertyConflictingNameGuard/MatchPropertyTypeConflictingNameGuard.php @@ -5,13 +5,12 @@ namespace Rector\Naming\Guard\PropertyConflictingNameGuard; use PhpParser\Node\Stmt\ClassLike; -use Rector\Naming\Contract\RenameValueObjectInterface; use Rector\Naming\ExpectedNameResolver\MatchPropertyTypeExpectedNameResolver; use Rector\Naming\PhpArray\ArrayFilter; use Rector\Naming\ValueObject\PropertyRename; use Rector\NodeNameResolver\NodeNameResolver; -final class MatchPropertyTypeConflictingNameGuard +final readonly class MatchPropertyTypeConflictingNameGuard { public function __construct( private MatchPropertyTypeExpectedNameResolver $matchPropertyTypeExpectedNameResolver, @@ -20,23 +19,20 @@ public function __construct( ) { } - /** - * @param PropertyRename $renameValueObject - */ - public function isConflicting(RenameValueObjectInterface $renameValueObject): bool + public function isConflicting(PropertyRename $propertyRename): bool { - $conflictingPropertyNames = $this->resolve($renameValueObject->getClassLike()); - return in_array($renameValueObject->getExpectedName(), $conflictingPropertyNames, true); + $conflictingPropertyNames = $this->resolve($propertyRename->getClassLike()); + return in_array($propertyRename->getExpectedName(), $conflictingPropertyNames, true); } /** * @return string[] */ - public function resolve(ClassLike $classLike): array + private function resolve(ClassLike $classLike): array { $expectedNames = []; foreach ($classLike->getProperties() as $property) { - $expectedName = $this->matchPropertyTypeExpectedNameResolver->resolve($property); + $expectedName = $this->matchPropertyTypeExpectedNameResolver->resolve($property, $classLike); if ($expectedName === null) { // fallback to existing name $expectedName = $this->nodeNameResolver->getName($property); @@ -45,6 +41,6 @@ public function resolve(ClassLike $classLike): array $expectedNames[] = $expectedName; } - return $this->arrayFilter->filterWithAtLeastTwoOccurences($expectedNames); + return $this->arrayFilter->filterWithAtLeastTwoOccurrences($expectedNames); } } diff --git a/rules/Naming/Guard/RamseyUuidInterfaceGuard.php b/rules/Naming/Guard/RamseyUuidInterfaceGuard.php deleted file mode 100644 index b933be288ee..00000000000 --- a/rules/Naming/Guard/RamseyUuidInterfaceGuard.php +++ /dev/null @@ -1,30 +0,0 @@ -nodeTypeResolver->isObjectType( - $renameValueObject->getProperty(), - new ObjectType('Ramsey\Uuid\UuidInterface') - ); - } -} diff --git a/rules/Naming/Matcher/ForeachMatcher.php b/rules/Naming/Matcher/ForeachMatcher.php index 47f6e2827aa..fa1a73044d7 100644 --- a/rules/Naming/Matcher/ForeachMatcher.php +++ b/rules/Naming/Matcher/ForeachMatcher.php @@ -4,6 +4,7 @@ namespace Rector\Naming\Matcher; +use PhpParser\Node; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\ClassMethod; @@ -11,21 +12,19 @@ use PhpParser\Node\Stmt\Function_; use Rector\Naming\ValueObject\VariableAndCallForeach; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeNestingScope\ParentFinder; -final class ForeachMatcher +final readonly class ForeachMatcher { public function __construct( private NodeNameResolver $nodeNameResolver, private CallMatcher $callMatcher, - private ParentFinder $parentFinder ) { } - public function match(Foreach_ $foreach): ?VariableAndCallForeach + public function match(Foreach_ $foreach, ClassMethod|Closure|Function_ $functionLike): ?VariableAndCallForeach { $call = $this->callMatcher->matchCall($foreach); - if ($call === null) { + if (! $call instanceof Node) { return null; } @@ -33,11 +32,6 @@ public function match(Foreach_ $foreach): ?VariableAndCallForeach return null; } - $functionLike = $this->getFunctionLike($foreach); - if ($functionLike === null) { - return null; - } - $variableName = $this->nodeNameResolver->getName($foreach->valueVar); if ($variableName === null) { return null; @@ -45,9 +39,4 @@ public function match(Foreach_ $foreach): ?VariableAndCallForeach return new VariableAndCallForeach($foreach->valueVar, $call, $variableName, $functionLike); } - - private function getFunctionLike(Foreach_ $foreach): ClassMethod | Function_ | Closure | null - { - return $this->parentFinder->findByTypes($foreach, [Closure::class, ClassMethod::class, Function_::class]); - } } diff --git a/rules/Naming/Matcher/VariableAndCallAssignMatcher.php b/rules/Naming/Matcher/VariableAndCallAssignMatcher.php index e163385ef04..71f6e5a0092 100644 --- a/rules/Naming/Matcher/VariableAndCallAssignMatcher.php +++ b/rules/Naming/Matcher/VariableAndCallAssignMatcher.php @@ -4,29 +4,29 @@ namespace Rector\Naming\Matcher; +use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\FunctionLike; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; use Rector\Naming\ValueObject\VariableAndCallAssign; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeNestingScope\ParentFinder; +use Rector\PhpParser\Node\BetterNodeFinder; -final class VariableAndCallAssignMatcher +final readonly class VariableAndCallAssignMatcher { public function __construct( private CallMatcher $callMatcher, private NodeNameResolver $nodeNameResolver, - private ParentFinder $parentFinder + private BetterNodeFinder $betterNodeFinder, ) { } - public function match(Assign $assign): ?VariableAndCallAssign + public function match(Assign $assign, ClassMethod|Closure|Function_ $functionLike): ?VariableAndCallAssign { $call = $this->callMatcher->matchCall($assign); - if ($call === null) { + if (! $call instanceof Node) { return null; } @@ -39,16 +39,16 @@ public function match(Assign $assign): ?VariableAndCallAssign return null; } - $functionLike = $this->getFunctionLike($assign); - if (! $functionLike instanceof FunctionLike) { + $isVariableFoundInCallArgs = (bool) $this->betterNodeFinder->findFirst( + $call->isFirstClassCallable() ? [] : $call->getArgs(), + fn (Node $subNode): bool => + $subNode instanceof Variable && $this->nodeNameResolver->isName($subNode, $variableName) + ); + + if ($isVariableFoundInCallArgs) { return null; } return new VariableAndCallAssign($assign->var, $call, $assign, $variableName, $functionLike); } - - private function getFunctionLike(Assign $assign): ClassMethod | Function_ | Closure | null - { - return $this->parentFinder->findByTypes($assign, [Closure::class, ClassMethod::class, Function_::class]); - } } diff --git a/rules/Naming/NamespaceMatcher.php b/rules/Naming/NamespaceMatcher.php deleted file mode 100644 index cd9562e0a45..00000000000 --- a/rules/Naming/NamespaceMatcher.php +++ /dev/null @@ -1,27 +0,0 @@ - $newNamespace) { - if (str_starts_with($name, $oldNamespace)) { - return new RenamedNamespace($name, $oldNamespace, $newNamespace); - } - } - - return null; - } -} diff --git a/rules/Naming/Naming/AliasNameResolver.php b/rules/Naming/Naming/AliasNameResolver.php new file mode 100644 index 00000000000..bc58c9e9715 --- /dev/null +++ b/rules/Naming/Naming/AliasNameResolver.php @@ -0,0 +1,45 @@ + $uses + */ + public function resolveByName(FullyQualified $fullyQualified, array $uses): ?string + { + $nameString = $fullyQualified->toString(); + + foreach ($uses as $use) { + $prefix = $this->useImportsResolver->resolvePrefix($use); + + foreach ($use->uses as $useUse) { + if (! $useUse->alias instanceof Identifier) { + continue; + } + + $fullyQualified = $prefix . $useUse->name->toString(); + if ($fullyQualified !== $nameString) { + continue; + } + + return (string) $useUse->getAlias(); + } + } + + return null; + } +} diff --git a/rules/Naming/Naming/ConflictingNameResolver.php b/rules/Naming/Naming/ConflictingNameResolver.php index c1f30154187..136294896e5 100644 --- a/rules/Naming/Naming/ConflictingNameResolver.php +++ b/rules/Naming/Naming/ConflictingNameResolver.php @@ -4,36 +4,38 @@ namespace Rector\Naming\Naming; +use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; -use Rector\Core\PhpParser\Node\BetterNodeFinder; use Rector\Naming\ExpectedNameResolver\MatchParamTypeExpectedNameResolver; use Rector\Naming\PhpArray\ArrayFilter; -use Rector\NodeNameResolver\NodeNameResolver; +use Rector\NodeManipulator\FunctionLikeManipulator; +use Rector\PhpParser\Node\BetterNodeFinder; final class ConflictingNameResolver { /** - * @var string[][] + * @var array */ private array $conflictingVariableNamesByClassMethod = []; public function __construct( - private ArrayFilter $arrayFilter, - private BetterNodeFinder $betterNodeFinder, - private ExpectedNameResolver $expectedNameResolver, - private NodeNameResolver $nodeNameResolver, - private MatchParamTypeExpectedNameResolver $matchParamTypeExpectedNameResolver + private readonly ArrayFilter $arrayFilter, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly ExpectedNameResolver $expectedNameResolver, + private readonly MatchParamTypeExpectedNameResolver $matchParamTypeExpectedNameResolver, + private readonly FunctionLikeManipulator $functionLikeManipulator ) { } /** * @return string[] */ - public function resolveConflictingVariableNamesForParam(ClassMethod $classMethod): array - { + public function resolveConflictingVariableNamesForParam( + ClassMethod|Function_|Closure|ArrowFunction $classMethod + ): array { $expectedNames = []; foreach ($classMethod->params as $param) { $expectedName = $this->matchParamTypeExpectedNameResolver->resolve($param); @@ -44,12 +46,12 @@ public function resolveConflictingVariableNamesForParam(ClassMethod $classMethod $expectedNames[] = $expectedName; } - return $this->arrayFilter->filterWithAtLeastTwoOccurences($expectedNames); + return $this->arrayFilter->filterWithAtLeastTwoOccurrences($expectedNames); } public function hasNameIsInFunctionLike( string $variableName, - ClassMethod | Function_ | Closure $functionLike + ClassMethod | Function_ | Closure | ArrowFunction $functionLike ): bool { $conflictingVariableNames = $this->resolveConflictingVariableNamesForNew($functionLike); return in_array($variableName, $conflictingVariableNames, true); @@ -58,23 +60,24 @@ public function hasNameIsInFunctionLike( /** * @return string[] */ - private function resolveConflictingVariableNamesForNew(ClassMethod | Function_ | Closure $functionLike): array - { + private function resolveConflictingVariableNamesForNew( + ClassMethod | Function_ | Closure | ArrowFunction $functionLike + ): array { // cache it! - $classMethodHash = spl_object_hash($functionLike); + $classMethodId = spl_object_id($functionLike); - if (isset($this->conflictingVariableNamesByClassMethod[$classMethodHash])) { - return $this->conflictingVariableNamesByClassMethod[$classMethodHash]; + if (isset($this->conflictingVariableNamesByClassMethod[$classMethodId])) { + return $this->conflictingVariableNamesByClassMethod[$classMethodId]; } - $paramNames = $this->collectParamNames($functionLike); + $paramNames = $this->functionLikeManipulator->resolveParamNames($functionLike); $newAssignNames = $this->resolveForNewAssigns($functionLike); $nonNewAssignNames = $this->resolveForNonNewAssigns($functionLike); - $protectedNames = array_merge($paramNames, $newAssignNames, $nonNewAssignNames); + $protectedNames = [...$paramNames, ...$newAssignNames, ...$nonNewAssignNames]; - $protectedNames = $this->arrayFilter->filterWithAtLeastTwoOccurences($protectedNames); - $this->conflictingVariableNamesByClassMethod[$classMethodHash] = $protectedNames; + $protectedNames = $this->arrayFilter->filterWithAtLeastTwoOccurrences($protectedNames); + $this->conflictingVariableNamesByClassMethod[$classMethodId] = $protectedNames; return $protectedNames; } @@ -82,27 +85,12 @@ private function resolveConflictingVariableNamesForNew(ClassMethod | Function_ | /** * @return string[] */ - private function collectParamNames(ClassMethod | Function_ | Closure $functionLike): array - { - $paramNames = []; - - // params - foreach ($functionLike->params as $param) { - $paramNames[] = $this->nodeNameResolver->getName($param); - } - - return $paramNames; - } - - /** - * @return string[] - */ - private function resolveForNewAssigns(ClassMethod | Function_ | Closure $functionLike): array + private function resolveForNewAssigns(ClassMethod | Function_ | Closure | ArrowFunction $functionLike): array { $names = []; /** @var Assign[] $assigns */ - $assigns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Assign::class); + $assigns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->getStmts(), Assign::class); foreach ($assigns as $assign) { $name = $this->expectedNameResolver->resolveForAssignNew($assign); if ($name === null) { @@ -118,12 +106,12 @@ private function resolveForNewAssigns(ClassMethod | Function_ | Closure $functio /** * @return string[] */ - private function resolveForNonNewAssigns(ClassMethod | Function_ | Closure $functionLike): array + private function resolveForNonNewAssigns(ClassMethod | Function_ | Closure | ArrowFunction $functionLike): array { $names = []; /** @var Assign[] $assigns */ - $assigns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Assign::class); + $assigns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->getStmts(), Assign::class); foreach ($assigns as $assign) { $name = $this->expectedNameResolver->resolveForAssignNonNew($assign); if ($name === null) { diff --git a/rules/Naming/Naming/ExpectedNameResolver.php b/rules/Naming/Naming/ExpectedNameResolver.php index 1300ea8b3b8..11112c5c54e 100644 --- a/rules/Naming/Naming/ExpectedNameResolver.php +++ b/rules/Naming/Naming/ExpectedNameResolver.php @@ -4,7 +4,7 @@ namespace Rector\Naming\Naming; -use PhpParser\Node\Expr; +use DateTimeInterface; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; @@ -13,20 +13,18 @@ use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name; use PhpParser\Node\Param; -use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\UnionType; use PHPStan\Type\ArrayType; -use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use Rector\Naming\ExpectedNameResolver\MatchParamTypeExpectedNameResolver; use Rector\Naming\ValueObject\ExpectedName; +use Rector\Naming\ValueObject\VariableAndCallForeach; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; -final class ExpectedNameResolver +final readonly class ExpectedNameResolver { public function __construct( private NodeNameResolver $nodeNameResolver, @@ -47,13 +45,8 @@ public function resolveForParamIfNotYet(Param $param): ?string return null; } - /** @var string $currentName */ - $currentName = $this->nodeNameResolver->getName($param->var); - if ($currentName === $expectedName) { - return null; - } - - if ($this->nodeNameResolver->endsWith($currentName, $expectedName)) { + $currentName = $this->nodeNameResolver->getName($param); + if ($currentName === $expectedName || str_ends_with($currentName, ucfirst($expectedName))) { return null; } @@ -70,7 +63,6 @@ public function resolveForAssignNonNew(Assign $assign): ?string return null; } - /** @var Variable $variable */ $variable = $assign->var; return $this->nodeNameResolver->getName($variable); @@ -86,7 +78,6 @@ public function resolveForAssignNew(Assign $assign): ?string return null; } - /** @var New_ $new */ $new = $assign->expr; if (! $new->class instanceof Name) { return null; @@ -95,6 +86,10 @@ public function resolveForAssignNew(Assign $assign): ?string $className = $this->nodeNameResolver->getName($new->class); $fullyQualifiedObjectType = new FullyQualifiedObjectType($className); + if ($fullyQualifiedObjectType->isInstanceOf(DateTimeInterface::class)->yes()) { + return null; + } + $expectedName = $this->propertyNaming->getExpectedNameFromType($fullyQualifiedObjectType); if (! $expectedName instanceof ExpectedName) { return null; @@ -114,19 +109,14 @@ public function resolveForCall(MethodCall | StaticCall | FuncCall $expr): ?strin return null; } - $returnedType = $this->nodeTypeResolver->getStaticType($expr); - - if ($returnedType instanceof ArrayType) { - return null; - } - - if ($returnedType instanceof MixedType) { + $returnedType = $this->nodeTypeResolver->getType($expr); + if (! $returnedType instanceof ObjectType) { return null; } $expectedName = $this->propertyNaming->getExpectedNameFromType($returnedType); - if ($expectedName !== null) { + if ($expectedName instanceof ExpectedName) { return $expectedName->getName(); } @@ -136,54 +126,63 @@ public function resolveForCall(MethodCall | StaticCall | FuncCall $expr): ?strin } $expectedNameFromMethodName = $this->propertyNaming->getExpectedNameFromMethodName($name); - if ($expectedNameFromMethodName !== null) { + if ($expectedNameFromMethodName instanceof ExpectedName) { return $expectedNameFromMethodName->getName(); } return null; } - public function resolveForForeach(MethodCall | StaticCall | FuncCall $expr): ?string + public function resolveForForeach(VariableAndCallForeach $variableAndCallForeach): ?string { - if ($this->isDynamicNameCall($expr)) { + $call = $variableAndCallForeach->getCall(); + if ($this->isDynamicNameCall($call)) { return null; } - $name = $this->nodeNameResolver->getName($expr->name); + $name = $this->nodeNameResolver->getName($call->name); if ($name === null) { return null; } - $returnedType = $this->nodeTypeResolver->getStaticType($expr); + $returnedType = $this->nodeTypeResolver->getType($call); if ($returnedType->isIterable()->no()) { return null; } + $innerReturnedType = null; if ($returnedType instanceof ArrayType) { - $returnedType = $this->resolveReturnTypeFromArrayType($expr, $returnedType); - if (! $returnedType instanceof Type) { + $innerReturnedType = $this->resolveReturnTypeFromArrayType($returnedType); + if (! $innerReturnedType instanceof Type) { return null; } } - $expectedNameFromType = $this->propertyNaming->getExpectedNameFromType($returnedType); + $expectedNameFromType = $this->propertyNaming->getExpectedNameFromType($innerReturnedType ?? $returnedType); - if ($expectedNameFromType !== null) { - return $expectedNameFromType->getSingularized(); + if ($this->isReturnedTypeAnArrayAndExpectedNameFromTypeNotNull($returnedType, $expectedNameFromType)) { + return $expectedNameFromType?->getSingularized(); } $expectedNameFromMethodName = $this->propertyNaming->getExpectedNameFromMethodName($name); if (! $expectedNameFromMethodName instanceof ExpectedName) { - return null; + return $expectedNameFromType?->getSingularized(); } if ($expectedNameFromMethodName->isSingular()) { - return null; + return $expectedNameFromType?->getSingularized(); } return $expectedNameFromMethodName->getSingularized(); } + private function isReturnedTypeAnArrayAndExpectedNameFromTypeNotNull( + Type $returnedType, + ?ExpectedName $expectedName + ): bool { + return ($returnedType instanceof ArrayType) && $expectedName instanceof ExpectedName; + } + private function isDynamicNameCall(MethodCall | StaticCall | FuncCall $expr): bool { if ($expr->name instanceof StaticCall) { @@ -197,17 +196,12 @@ private function isDynamicNameCall(MethodCall | StaticCall | FuncCall $expr): bo return $expr->name instanceof FuncCall; } - private function resolveReturnTypeFromArrayType(Expr $expr, ArrayType $arrayType): ?Type + private function resolveReturnTypeFromArrayType(ArrayType $arrayType): ?Type { - $parentNode = $expr->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Foreach_) { - return null; - } - - if (! $arrayType->getItemType() instanceof ObjectType) { + if (! $arrayType->getIterableValueType() instanceof ObjectType) { return null; } - return $arrayType->getItemType(); + return $arrayType->getIterableValueType(); } } diff --git a/rules/Naming/Naming/MethodNameResolver.php b/rules/Naming/Naming/MethodNameResolver.php deleted file mode 100644 index 4c59459a5f6..00000000000 --- a/rules/Naming/Naming/MethodNameResolver.php +++ /dev/null @@ -1,40 +0,0 @@ -variableNaming->resolveFromNode($expr); - if ($variableName === null) { - return null; - } - - return 'get' . ucfirst($variableName); - } - - public function resolveIsserFromReturnedExpr(Expr $expr): ?string - { - $variableName = $this->variableNaming->resolveFromNode($expr); - if ($variableName === null) { - return null; - } - - if (Strings::match($variableName, '#^(is)#')) { - return $variableName; - } - - return 'is' . ucfirst($variableName); - } -} diff --git a/rules/Naming/Naming/OverriddenExistingNamesResolver.php b/rules/Naming/Naming/OverriddenExistingNamesResolver.php new file mode 100644 index 00000000000..f890b14c07d --- /dev/null +++ b/rules/Naming/Naming/OverriddenExistingNamesResolver.php @@ -0,0 +1,96 @@ +> + */ + private array $overriddenExistingVariableNamesByClassMethod = []; + + public function __construct( + private readonly ArrayFilter $arrayFilter, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly NodeNameResolver $nodeNameResolver + ) { + } + + public function hasNameInClassMethodForNew( + string $variableName, + ClassMethod | Function_ | Closure $functionLike + ): bool { + $overriddenVariableNames = $this->resolveOverriddenNamesForNew($functionLike); + return in_array($variableName, $overriddenVariableNames, true); + } + + public function hasNameInFunctionLikeForParam( + string $expectedName, + ClassMethod|Function_|Closure|ArrowFunction $classMethod + ): bool { + /** @var Assign[] $assigns */ + $assigns = $this->betterNodeFinder->findInstanceOf((array) $classMethod->getStmts(), Assign::class); + + $usedVariableNames = []; + foreach ($assigns as $assign) { + if (! $assign->var instanceof Variable) { + continue; + } + + $variableName = $this->nodeNameResolver->getName($assign->var); + if ($variableName === null) { + continue; + } + + $usedVariableNames[] = $variableName; + } + + return in_array($expectedName, $usedVariableNames, true); + } + + /** + * @return string[] + */ + private function resolveOverriddenNamesForNew(ClassMethod | Function_ | Closure $functionLike): array + { + $classMethodId = spl_object_id($functionLike); + + if (isset($this->overriddenExistingVariableNamesByClassMethod[$classMethodId])) { + return $this->overriddenExistingVariableNamesByClassMethod[$classMethodId]; + } + + $currentlyUsedNames = []; + + /** @var Assign[] $assigns */ + $assigns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Assign::class); + + foreach ($assigns as $assign) { + /** @var Variable $assignVariable */ + $assignVariable = $assign->var; + $currentVariableName = $this->nodeNameResolver->getName($assignVariable); + if ($currentVariableName === null) { + continue; + } + + $currentlyUsedNames[] = $currentVariableName; + } + + $currentlyUsedNames = $this->arrayFilter->filterWithAtLeastTwoOccurrences($currentlyUsedNames); + + $this->overriddenExistingVariableNamesByClassMethod[$classMethodId] = $currentlyUsedNames; + + return $currentlyUsedNames; + } +} diff --git a/rules/Naming/Naming/OverridenExistingNamesResolver.php b/rules/Naming/Naming/OverridenExistingNamesResolver.php deleted file mode 100644 index d72fcebf67b..00000000000 --- a/rules/Naming/Naming/OverridenExistingNamesResolver.php +++ /dev/null @@ -1,94 +0,0 @@ -> - */ - private array $overridenExistingVariableNamesByClassMethod = []; - - public function __construct( - private ArrayFilter $arrayFilter, - private BetterNodeFinder $betterNodeFinder, - private NodeNameResolver $nodeNameResolver - ) { - } - - public function hasNameInClassMethodForNew( - string $variableName, - ClassMethod | Function_ | Closure $functionLike - ): bool { - $overridenVariableNames = $this->resolveOveriddenNamesForNew($functionLike); - return in_array($variableName, $overridenVariableNames, true); - } - - public function hasNameInClassMethodForParam(string $expectedName, ClassMethod $classMethod): bool - { - /** @var Assign[] $assigns */ - $assigns = $this->betterNodeFinder->findInstanceOf((array) $classMethod->stmts, Assign::class); - - $usedVariableNames = []; - foreach ($assigns as $assign) { - if (! $assign->var instanceof Variable) { - continue; - } - - $variableName = $this->nodeNameResolver->getName($assign->var); - if ($variableName === null) { - continue; - } - - $usedVariableNames[] = $variableName; - } - - return in_array($expectedName, $usedVariableNames, true); - } - - /** - * @return string[] - */ - private function resolveOveriddenNamesForNew(ClassMethod | Function_ | Closure $functionLike): array - { - $classMethodHash = spl_object_hash($functionLike); - - if (isset($this->overridenExistingVariableNamesByClassMethod[$classMethodHash])) { - return $this->overridenExistingVariableNamesByClassMethod[$classMethodHash]; - } - - $currentlyUsedNames = []; - - /** @var Assign[] $assigns */ - $assigns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Assign::class); - - foreach ($assigns as $assign) { - /** @var Variable $assignVariable */ - $assignVariable = $assign->var; - $currentVariableName = $this->nodeNameResolver->getName($assignVariable); - if ($currentVariableName === null) { - continue; - } - - $currentlyUsedNames[] = $currentVariableName; - } - - $currentlyUsedNames = array_values($currentlyUsedNames); - $currentlyUsedNames = $this->arrayFilter->filterWithAtLeastTwoOccurences($currentlyUsedNames); - - $this->overridenExistingVariableNamesByClassMethod[$classMethodHash] = $currentlyUsedNames; - - return $currentlyUsedNames; - } -} diff --git a/rules/Naming/Naming/PropertyNaming.php b/rules/Naming/Naming/PropertyNaming.php index c83883c44ab..27a72688f91 100644 --- a/rules/Naming/Naming/PropertyNaming.php +++ b/rules/Naming/Naming/PropertyNaming.php @@ -5,53 +5,52 @@ namespace Rector\Naming\Naming; use Nette\Utils\Strings; -use PHPStan\Reflection\ReflectionProvider; +use PhpParser\Node\Name; use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\ObjectType; use PHPStan\Type\StaticType; +use PHPStan\Type\ThisType; use PHPStan\Type\Type; -use PHPStan\Type\TypeWithClassName; +use PHPStan\Type\TypeCombinator; +use Rector\Enum\ClassName; +use Rector\Exception\ShouldNotHappenException; use Rector\Naming\RectorNamingInflector; use Rector\Naming\ValueObject\ExpectedName; -use Rector\NodeTypeResolver\NodeTypeResolver; -use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; use Rector\StaticTypeMapper\ValueObject\Type\SelfObjectType; +use Rector\Util\StringUtils; /** - * @deprecated - * @todo merge with very similar logic in - * @see VariableNaming * @see \Rector\Tests\Naming\Naming\PropertyNamingTest */ -final class PropertyNaming +final readonly class PropertyNaming { /** * @var string[] */ - private const EXCLUDED_CLASSES = ['#Closure#', '#^Spl#', '#FileInfo#', '#^std#', '#Iterator#', '#SimpleXML#']; + private const array EXCLUDED_CLASSES = ['#Closure#', '#^Spl#', '#FileInfo#', '#^std#', '#Iterator#', '#SimpleXML#']; /** - * @var string + * @var array */ - private const INTERFACE = 'Interface'; + private const array CONTEXT_AWARE_NAMES_BY_TYPE = [ + 'Twig\Environment' => 'twigEnvironment', + ]; + + private const string INTERFACE = 'Interface'; /** - * @var string * @see https://regex101.com/r/U78rUF/1 */ - private const I_PREFIX_REGEX = '#^I[A-Z]#'; + private const string I_PREFIX_REGEX = '#^I[A-Z]#'; /** * @see https://regex101.com/r/hnU5pm/2/ - * @var string */ - private const GET_PREFIX_REGEX = '#^get(?[A-Z].+)#'; + private const string GET_PREFIX_REGEX = '#^get(?[A-Z].+)#'; public function __construct( - private TypeUnwrapper $typeUnwrapper, private RectorNamingInflector $rectorNamingInflector, - private NodeTypeResolver $nodeTypeResolver, - private ReflectionProvider $reflectionProvider ) { } @@ -62,76 +61,79 @@ public function getExpectedNameFromMethodName(string $methodName): ?ExpectedName return null; } - $originalName = lcfirst($matches['root_name']); + $originalName = lcfirst((string) $matches['root_name']); return new ExpectedName($originalName, $this->rectorNamingInflector->singularize($originalName)); } public function getExpectedNameFromType(Type $type): ?ExpectedName { - $type = $this->typeUnwrapper->unwrapNullableType($type); - if (! $type instanceof TypeWithClassName) { - return null; - } + $type = TypeCombinator::removeNull($type); - if ($type instanceof SelfObjectType) { - return null; - } + // keep collections untouched + if ($type instanceof ObjectType) { + if ($type->isInstanceOf('Doctrine\Common\Collections\Collection')->yes()) { + return null; + } - if ($type instanceof StaticType) { - return null; - } + if ($type->isInstanceOf('Illuminate\Support\Collection')->yes()) { + return null; + } - $className = $this->nodeTypeResolver->getFullyQualifiedClassName($type); + if ($type->isInstanceOf(ClassName::DATE_TIME_INTERFACE)->yes()) { + return null; + } + } - // generic types are usually mix of parent type and specific type - various way to handle it - if ($type instanceof GenericObjectType) { + $className = $this->resolveClassNameFromType($type); + if (! is_string($className)) { return null; } foreach (self::EXCLUDED_CLASSES as $excludedClass) { - if (Strings::match($className, $excludedClass)) { + if (StringUtils::isMatch($className, $excludedClass)) { return null; } } - $shortClassName = $this->resolveShortClassName($className); - $shortClassName = $this->removePrefixesAndSuffixes($shortClassName); - - // if all is upper-cased, it should be lower-cased - if ($shortClassName === strtoupper($shortClassName)) { - $shortClassName = strtolower($shortClassName); + // special cases to keep context + foreach (self::CONTEXT_AWARE_NAMES_BY_TYPE as $specialType => $contextAwareName) { + if ($className === $specialType) { + return new ExpectedName($contextAwareName, $contextAwareName); + } } - // remove "_" - $shortClassName = Strings::replace($shortClassName, '#_#', ''); - $shortClassName = $this->normalizeUpperCase($shortClassName); + $shortClassName = $this->resolveShortClassName($className); + $shortClassName = $this->normalizeShortClassName($shortClassName); // prolong too short generic names with one namespace up $originalName = $this->prolongIfTooShort($shortClassName, $className); + return new ExpectedName($originalName, $this->rectorNamingInflector->singularize($originalName)); } - public function fqnToVariableName(ObjectType | string $objectType): string + public function fqnToVariableName(ThisType | ObjectType | Name | string $objectType): string { + if ($objectType instanceof Name) { + $objectType = $objectType->toString(); + } + + if ($objectType instanceof ThisType) { + $objectType = $objectType->getStaticObjectType(); + } + $className = $this->resolveClassName($objectType); + $shortClassName = str_contains($className, '\\') ? (string) Strings::after($className, '\\', -1) : $className; - $shortName = $this->fqnToShortName($className); - $shortName = $this->removeInterfaceSuffixPrefix($className, $shortName); + $variableName = $this->removeInterfaceSuffixPrefix($shortClassName, 'interface'); + $variableName = $this->removeInterfaceSuffixPrefix($variableName, 'abstract'); - // prolong too short generic names with one namespace up - return $this->prolongIfTooShort($shortName, $className); - } + $variableName = $this->fqnToShortName($variableName); - /** - * @changelog https://stackoverflow.com/a/2792045/1348344 - */ - public function underscoreToName(string $underscoreName): string - { - $uppercaseWords = ucwords($underscoreName, '_'); - $pascalCaseName = str_replace('_', '', $uppercaseWords); + $variableName = str_replace('_', '', $variableName); - return lcfirst($pascalCaseName); + // prolong too short generic names with one namespace up + return $this->prolongIfTooShort($variableName, $className); } private function resolveShortClassName(string $className): string @@ -157,7 +159,7 @@ private function removePrefixesAndSuffixes(string $shortClassName): string // is AbstractClass if (\str_starts_with($shortClassName, 'Abstract')) { - $shortClassName = Strings::substring($shortClassName, strlen('Abstract')); + return Strings::substring($shortClassName, strlen('Abstract')); } return $shortClassName; @@ -179,7 +181,10 @@ private function normalizeUpperCase(string $shortClassName): string private function prolongIfTooShort(string $shortClassName, string $className): string { - if (in_array($shortClassName, ['Factory', 'Repository'], true)) { + if (in_array($shortClassName, ['Factory', 'Repository'], true) && ! str_ends_with( + $className, + 'Repository' + ) && ! str_ends_with($className, 'Factory')) { $namespaceAbove = (string) Strings::after($className, '\\', -2); $namespaceAbove = (string) Strings::before($namespaceAbove, '\\'); @@ -204,32 +209,38 @@ private function fqnToShortName(string $fqn): string return $fqn; } - /** @var string $lastNamePart */ - $lastNamePart = Strings::after($fqn, '\\', - 1); + $lastNamePart = Strings::after($fqn, '\\', -1); + if (! is_string($lastNamePart)) { + throw new ShouldNotHappenException(); + } + if (\str_ends_with($lastNamePart, self::INTERFACE)) { - return Strings::substring($lastNamePart, 0, - strlen(self::INTERFACE)); + return Strings::substring($lastNamePart, 0, -strlen(self::INTERFACE)); } return $lastNamePart; } - private function removeInterfaceSuffixPrefix(string $className, string $shortName): string + private function removeInterfaceSuffixPrefix(string $className, string $category): string { - // remove interface prefix/suffix - if (! $this->reflectionProvider->hasClass($className)) { - return $shortName; + // suffix + $iSuffixMatch = Strings::match($className, '#' . $category . '$#i'); + if ($iSuffixMatch !== null) { + return Strings::substring($className, 0, -strlen($category)); } - // starts with "I\W+"? - if (Strings::match($shortName, self::I_PREFIX_REGEX)) { - return Strings::substring($shortName, 1); + // prefix + $iPrefixMatch = Strings::match($className, '#^' . $category . '#i'); + if ($iPrefixMatch !== null) { + return Strings::substring($className, strlen($category)); } - if (\str_ends_with($shortName, self::INTERFACE)) { - return Strings::substring($shortName, -strlen(self::INTERFACE)); + // starts with "I\W+"? + if (StringUtils::isMatch($className, self::I_PREFIX_REGEX)) { + return Strings::substring($className, 1); } - return $shortName; + return $className; } private function isPrefixedInterface(string $shortClassName): bool @@ -241,9 +252,11 @@ private function isPrefixedInterface(string $shortClassName): bool if (! \str_starts_with($shortClassName, 'I')) { return false; } + if (! ctype_upper($shortClassName[1])) { return false; } + return ctype_lower($shortClassName[2]); } @@ -255,4 +268,42 @@ private function isNumberOrUpper(string $char): bool return ctype_digit($char); } + + private function normalizeShortClassName(string $shortClassName): string + { + $shortClassName = $this->removePrefixesAndSuffixes($shortClassName); + + // if all is upper-cased, it should be lower-cased + if ($shortClassName === strtoupper($shortClassName)) { + $shortClassName = strtolower($shortClassName); + } + + // remove "_" + $shortClassName = Strings::replace($shortClassName, '#_#'); + return $this->normalizeUpperCase($shortClassName); + } + + private function resolveClassNameFromType(Type $type): ?string + { + $className = ClassNameFromObjectTypeResolver::resolve($type); + + if ($className === null) { + return null; + } + + if ($type instanceof SelfObjectType) { + return null; + } + + if ($type instanceof StaticType) { + return null; + } + + // generic types are usually mix of parent type and specific type - various way to handle it + if ($type instanceof GenericObjectType) { + return null; + } + + return $className; + } } diff --git a/rules/Naming/Naming/UseImportsResolver.php b/rules/Naming/Naming/UseImportsResolver.php new file mode 100644 index 00000000000..f4fcb24ea7d --- /dev/null +++ b/rules/Naming/Naming/UseImportsResolver.php @@ -0,0 +1,64 @@ + + */ + public function resolve(): array + { + $file = $this->currentFileProvider->getFile(); + if (! $file instanceof File) { + return []; + } + + $rootNode = $file->getFileNode(); + if (! $rootNode instanceof FileNode) { + return []; + } + + return $rootNode->getUsesAndGroupUses(); + } + + /** + * @api + * @return Use_[] + */ + public function resolveBareUses(): array + { + $file = $this->currentFileProvider->getFile(); + + if (! $file instanceof File) { + return []; + } + + $fileNode = $file->getFileNode(); + if (! $fileNode instanceof FileNode) { + return []; + } + + return $fileNode->getUses(); + } + + public function resolvePrefix(Use_|GroupUse $use): string + { + return $use instanceof GroupUse + ? $use->prefix . '\\' + : ''; + } +} diff --git a/rules/Naming/Naming/VariableNaming.php b/rules/Naming/Naming/VariableNaming.php index 9fa2f1ab1fa..81f7d1cb3e3 100644 --- a/rules/Naming/Naming/VariableNaming.php +++ b/rules/Naming/Naming/VariableNaming.php @@ -4,78 +4,19 @@ namespace Rector\Naming\Naming; -use Nette\Utils\Strings; -use PhpParser\Node; -use PhpParser\Node\Arg; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\ArrayDimFetch; -use PhpParser\Node\Expr\Cast; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\New_; -use PhpParser\Node\Expr\NullsafeMethodCall; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\Expr\Ternary; -use PhpParser\Node\Name; -use PhpParser\Node\Scalar; -use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; -use PHPStan\Type\ThisType; -use PHPStan\Type\Type; -use Rector\Core\Exception\NotImplementedYetException; -use Rector\Core\PhpParser\Node\Value\ValueResolver; -use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\NodeTypeResolver; -use Stringy\Stringy; +/** + * @api used in downgrade + */ final class VariableNaming { - public function __construct( - private NodeNameResolver $nodeNameResolver, - private ValueResolver $valueResolver, - private NodeTypeResolver $nodeTypeResolver, - ) { - } - - public function resolveFromNodeAndType(Node $node, Type $type): ?string - { - $variableName = $this->resolveBareFromNode($node); - if ($variableName === null) { - return null; - } - - // adjust static to specific class - if ($variableName === 'this' && $type instanceof ThisType) { - $shortClassName = $this->nodeNameResolver->getShortName($type->getClassName()); - $variableName = lcfirst($shortClassName); - } - - $stringy = new Stringy($variableName); - return (string) $stringy->camelize(); - } - - public function resolveFromNodeWithScopeCountAndFallbackName( - Expr $expr, - ?Scope $scope, - string $fallbackName - ): string { - $name = $this->resolveFromNode($expr); - if ($name === null) { - $name = $fallbackName; - } - - if (\str_contains($name, '\\')) { - $name = (string) Strings::after($name, '\\', - 1); - } - - $countedValueName = $this->createCountedValueName($name, $scope); - return lcfirst($countedValueName); - } - + /** + * @api used in downgrade + */ public function createCountedValueName(string $valueName, ?Scope $scope): string { - if ($scope === null) { + if (! $scope instanceof Scope) { return $valueName; } @@ -94,158 +35,4 @@ public function createCountedValueName(string $valueName, ?Scope $scope): string return $valueName; } - - public function resolveFromFuncCallFirstArgumentWithSuffix( - FuncCall $funcCall, - string $suffix, - string $fallbackName, - ?Scope $scope - ): string { - $bareName = $this->resolveBareFuncCallArgumentName($funcCall, $fallbackName, $suffix); - return $this->createCountedValueName($bareName, $scope); - } - - public function resolveFromNode(Node $node): ?string - { - $nodeType = $this->nodeTypeResolver->getStaticType($node); - return $this->resolveFromNodeAndType($node, $nodeType); - } - - private function resolveBareFromNode(Node $node): ?string - { - $node = $this->unwrapNode($node); - - if ($node instanceof ArrayDimFetch) { - return $this->resolveParamNameFromArrayDimFetch($node); - } - - if ($node instanceof PropertyFetch) { - return $this->resolveFromPropertyFetch($node); - } - - if ($node !== null && ($node instanceof MethodCall || $node instanceof NullsafeMethodCall || $node instanceof StaticCall)) { - return $this->resolveFromMethodCall($node); - } - - if ($node instanceof New_) { - return $this->resolveFromNew($node); - } - - if ($node instanceof FuncCall) { - return $this->resolveFromNode($node->name); - } - - if (! $node instanceof Node) { - throw new NotImplementedYetException(); - } - - $paramName = $this->nodeNameResolver->getName($node); - if ($paramName !== null) { - return $paramName; - } - - if ($node instanceof String_) { - return $node->value; - } - - return null; - } - - private function resolveParamNameFromArrayDimFetch(ArrayDimFetch $arrayDimFetch): ?string - { - while ($arrayDimFetch instanceof ArrayDimFetch) { - if ($arrayDimFetch->dim instanceof Scalar) { - $valueName = $this->nodeNameResolver->getName($arrayDimFetch->var); - $dimName = $this->valueResolver->getValue($arrayDimFetch->dim); - - $stringy = new Stringy($dimName); - $dimName = (string) $stringy->upperCamelize(); - - return $valueName . $dimName; - } - - $arrayDimFetch = $arrayDimFetch->var; - } - - return $this->resolveBareFromNode($arrayDimFetch); - } - - private function resolveFromPropertyFetch(PropertyFetch $propertyFetch): string - { - $varName = $this->nodeNameResolver->getName($propertyFetch->var); - if (! is_string($varName)) { - throw new NotImplementedYetException(); - } - - $propertyName = $this->nodeNameResolver->getName($propertyFetch->name); - if (! is_string($propertyName)) { - throw new NotImplementedYetException(); - } - - if ($varName === 'this') { - return $propertyName; - } - - return $varName . ucfirst($propertyName); - } - - private function resolveFromMethodCall(MethodCall | NullsafeMethodCall | StaticCall $node): ?string - { - if ($node->name instanceof MethodCall) { - return $this->resolveFromMethodCall($node->name); - } - - $methodName = $this->nodeNameResolver->getName($node->name); - if (! is_string($methodName)) { - return null; - } - - return $methodName; - } - - private function resolveFromNew(New_ $new): string - { - $className = $this->nodeNameResolver->getName($new->class); - if ($className === null) { - throw new NotImplementedYetException(); - } - - return $this->nodeNameResolver->getShortName($className); - } - - private function unwrapNode(Node $node): ?Node - { - if ($node instanceof Arg) { - return $node->value; - } - - if ($node instanceof Cast) { - return $node->expr; - } - - if ($node instanceof Ternary) { - return $node->if; - } - - return $node; - } - - private function resolveBareFuncCallArgumentName( - FuncCall $funcCall, - string $fallbackName, - string $suffix - ): string { - $argumentValue = $funcCall->args[0]->value; - if ($argumentValue instanceof MethodCall || $argumentValue instanceof StaticCall) { - $name = $this->nodeNameResolver->getName($argumentValue->name); - } else { - $name = $this->nodeNameResolver->getName($argumentValue); - } - - if ($name === null) { - return $fallbackName; - } - - return $name . $suffix; - } } diff --git a/rules/Naming/NamingConvention/NamingConventionAnalyzer.php b/rules/Naming/NamingConvention/NamingConventionAnalyzer.php index 5a1346cfacc..5edafa72ef0 100644 --- a/rules/Naming/NamingConvention/NamingConventionAnalyzer.php +++ b/rules/Naming/NamingConvention/NamingConventionAnalyzer.php @@ -4,13 +4,13 @@ namespace Rector\Naming\NamingConvention; -use Nette\Utils\Strings; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\Util\StringUtils; -final class NamingConventionAnalyzer +final readonly class NamingConventionAnalyzer { public function __construct( private NodeNameResolver $nodeNameResolver @@ -36,6 +36,6 @@ public function isCallMatchingVariableName( } // starts with or ends with - return (bool) Strings::match($currentName, '#^(' . $expectedName . '|' . $expectedName . '$)#i'); + return StringUtils::isMatch($currentName, '#^(' . $expectedName . '|' . $expectedName . '$)#i'); } } diff --git a/rules/Naming/ParamRenamer/ParamRenamer.php b/rules/Naming/ParamRenamer/ParamRenamer.php index a31c689b36c..72051f49edd 100644 --- a/rules/Naming/ParamRenamer/ParamRenamer.php +++ b/rules/Naming/ParamRenamer/ParamRenamer.php @@ -4,16 +4,20 @@ namespace Rector\Naming\ParamRenamer; -use PhpParser\Node\Param; -use Rector\BetterPhpDocParser\PhpDocManipulator\PropertyDocBlockManipulator; +use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\Naming\ValueObject\ParamRename; use Rector\Naming\VariableRenamer; -final class ParamRenamer +final readonly class ParamRenamer { public function __construct( private VariableRenamer $variableRenamer, - private PropertyDocBlockManipulator $propertyDocBlockManipulator + private DocBlockUpdater $docBlockUpdater, + private PhpDocInfoFactory $phpDocInfoFactory, ) { } @@ -27,11 +31,30 @@ public function rename(ParamRename $paramRename): void $this->variableRenamer->renameVariableInFunctionLike( $paramRename->getFunctionLike(), $paramRename->getCurrentName(), - $paramRename->getExpectedName(), - null + $paramRename->getExpectedName() ); // 3. rename @param variable in docblock too - $this->propertyDocBlockManipulator->renameParameterNameInDocBlock($paramRename); + $this->renameParameterNameInDocBlock($paramRename); + } + + private function renameParameterNameInDocBlock(ParamRename $paramRename): void + { + $functionLike = $paramRename->getFunctionLike(); + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($functionLike); + if (! $phpDocInfo instanceof PhpDocInfo) { + return; + } + + $paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramRename->getCurrentName()); + if (! $paramTagValueNode instanceof ParamTagValueNode) { + return; + } + + $paramTagValueNode->parameterName = '$' . $paramRename->getExpectedName(); + $paramTagValueNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($functionLike); } } diff --git a/rules/Naming/PhpArray/ArrayFilter.php b/rules/Naming/PhpArray/ArrayFilter.php index a7105e5bce0..0d20fa4f945 100644 --- a/rules/Naming/PhpArray/ArrayFilter.php +++ b/rules/Naming/PhpArray/ArrayFilter.php @@ -10,7 +10,7 @@ final class ArrayFilter * @param mixed[] $values * @return string[] */ - public function filterWithAtLeastTwoOccurences(array $values): array + public function filterWithAtLeastTwoOccurrences(array $values): array { /** @var array $valueToCount */ $valueToCount = array_count_values($values); @@ -23,7 +23,6 @@ public function filterWithAtLeastTwoOccurences(array $values): array continue; } - /** @var string $value */ $duplicatedValues[] = $value; } diff --git a/rules/Naming/PropertyRenamer/MatchTypePropertyRenamer.php b/rules/Naming/PropertyRenamer/MatchTypePropertyRenamer.php index 48da04168d2..259c7964a1b 100644 --- a/rules/Naming/PropertyRenamer/MatchTypePropertyRenamer.php +++ b/rules/Naming/PropertyRenamer/MatchTypePropertyRenamer.php @@ -5,14 +5,17 @@ namespace Rector\Naming\PropertyRenamer; use PhpParser\Node\Stmt\Property; +use PhpParser\Node\VarLikeIdentifier; use Rector\Naming\Guard\PropertyConflictingNameGuard\MatchPropertyTypeConflictingNameGuard; +use Rector\Naming\RenameGuard\PropertyRenameGuard; use Rector\Naming\ValueObject\PropertyRename; -final class MatchTypePropertyRenamer +final readonly class MatchTypePropertyRenamer { public function __construct( - private PropertyRenamer $propertyRenamer, - private MatchPropertyTypeConflictingNameGuard $matchPropertyTypeConflictingNameGuard + private MatchPropertyTypeConflictingNameGuard $matchPropertyTypeConflictingNameGuard, + private PropertyRenameGuard $propertyRenameGuard, + private PropertyFetchRenamer $propertyFetchRenamer, ) { } @@ -22,6 +25,27 @@ public function rename(PropertyRename $propertyRename): ?Property return null; } - return $this->propertyRenamer->rename($propertyRename); + if ($propertyRename->isAlreadyExpectedName()) { + return null; + } + + if ($this->propertyRenameGuard->shouldSkip($propertyRename)) { + return null; + } + + $propertyItem = $propertyRename->getPropertyProperty(); + $propertyItem->name = new VarLikeIdentifier($propertyRename->getExpectedName()); + $this->renamePropertyFetchesInClass($propertyRename); + + return $propertyRename->getProperty(); + } + + private function renamePropertyFetchesInClass(PropertyRename $propertyRename): void + { + $this->propertyFetchRenamer->renamePropertyFetchesInClass( + $propertyRename->getClassLike(), + $propertyRename->getCurrentName(), + $propertyRename->getExpectedName() + ); } } diff --git a/rules/Naming/PropertyRenamer/PropertyFetchRenamer.php b/rules/Naming/PropertyRenamer/PropertyFetchRenamer.php index e2ed8907f7c..f7cd465e0e8 100644 --- a/rules/Naming/PropertyRenamer/PropertyFetchRenamer.php +++ b/rules/Naming/PropertyRenamer/PropertyFetchRenamer.php @@ -10,14 +10,14 @@ use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\VarLikeIdentifier; -use Rector\NodeNameResolver\NodeNameResolver; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; -final class PropertyFetchRenamer +final readonly class PropertyFetchRenamer { public function __construct( private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private NodeNameResolver $nodeNameResolver + private PropertyFetchAnalyzer $propertyFetchAnalyzer ) { } @@ -27,23 +27,15 @@ public function renamePropertyFetchesInClass(ClassLike $classLike, string $curre $this->simpleCallableNodeTraverser->traverseNodesWithCallable( $classLike, function (Node $node) use ($currentName, $expectedName): ?Node { - if ($node instanceof PropertyFetch && $this->nodeNameResolver->isLocalPropertyFetchNamed( - $node, - $currentName - )) { - $node->name = new Identifier($expectedName); - return $node; - } - - if (! $node instanceof StaticPropertyFetch) { + if (! $this->propertyFetchAnalyzer->isLocalPropertyFetchName($node, $currentName)) { return null; } - if (! $this->nodeNameResolver->isName($node->name, $currentName)) { - return null; - } + /** @var StaticPropertyFetch|PropertyFetch $node */ + $node->name = $node instanceof PropertyFetch + ? new Identifier($expectedName) + : new VarLikeIdentifier($expectedName); - $node->name = new VarLikeIdentifier($expectedName); return $node; } ); diff --git a/rules/Naming/PropertyRenamer/PropertyPromotionRenamer.php b/rules/Naming/PropertyRenamer/PropertyPromotionRenamer.php index ee9c39e969c..b5d8cb5f4e0 100644 --- a/rules/Naming/PropertyRenamer/PropertyPromotionRenamer.php +++ b/rules/Naming/PropertyRenamer/PropertyPromotionRenamer.php @@ -4,22 +4,30 @@ namespace Rector\Naming\PropertyRenamer; +use PhpParser\Node\Expr\Error; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; +use PHPStan\Reflection\ClassReflection; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\Core\Php\PhpVersionProvider; -use Rector\Core\ValueObject\MethodName; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\Naming\ExpectedNameResolver\MatchParamTypeExpectedNameResolver; use Rector\Naming\ParamRenamer\ParamRenamer; use Rector\Naming\ValueObject\ParamRename; use Rector\Naming\ValueObjectFactory\ParamRenameFactory; +use Rector\Naming\VariableRenamer; +use Rector\NodeManipulator\PropertyManipulator; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\Php\PhpVersionProvider; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; -final class PropertyPromotionRenamer +final readonly class PropertyPromotionRenamer { public function __construct( private PhpVersionProvider $phpVersionProvider, @@ -28,26 +36,46 @@ public function __construct( private PhpDocInfoFactory $phpDocInfoFactory, private ParamRenamer $paramRenamer, private PropertyFetchRenamer $propertyFetchRenamer, - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, + private VariableRenamer $variableRenamer, + private DocBlockUpdater $docBlockUpdater, + private ReflectionResolver $reflectionResolver, + private PropertyManipulator $propertyManipulator ) { } - public function renamePropertyPromotion(ClassLike $classLike): void + public function renamePropertyPromotion(Class_ $class): bool { + $hasChanged = false; + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) { - return; + return false; } - $constructClassMethod = $classLike->getMethod(MethodName::CONSTRUCT); + $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); if (! $constructClassMethod instanceof ClassMethod) { - return; + return false; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($class); + if (! $classReflection instanceof ClassReflection) { + return false; } // resolve possible and existing param names $blockingParamNames = $this->resolveBlockingParamNames($constructClassMethod); foreach ($constructClassMethod->params as $param) { - if ($param->flags === 0) { + if (! $param->isPromoted()) { + continue; + } + + // skip public properties, as they can be used in external code + if ($param->isPublic()) { + continue; + } + + if (! $class->isFinal() && $param->isProtected()) { continue; } @@ -66,16 +94,52 @@ public function renamePropertyPromotion(ClassLike $classLike): void continue; } - $this->renameParamVarName($classLike, $constructClassMethod, $desiredPropertyName, $param); + if ($this->propertyManipulator->isUsedByTrait($classReflection, $currentParamName)) { + continue; + } + + $this->renameParamVarNameAndVariableUsage($class, $constructClassMethod, $desiredPropertyName, $param); + $hasChanged = true; + } + + return $hasChanged; + } + + public function renameParamDoc( + PhpDocInfo $phpDocInfo, + ClassMethod $classMethod, + Param $param, + string $paramVarName, + string $desiredPropertyName + ): void { + $paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramVarName); + if (! $paramTagValueNode instanceof ParamTagValueNode) { + return; + } + + $paramRename = $this->paramRenameFactory->createFromResolvedExpectedName( + $classMethod, + $param, + $desiredPropertyName + ); + if (! $paramRename instanceof ParamRename) { + return; } + + $this->paramRenamer->rename($paramRename); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); } - private function renameParamVarName( + private function renameParamVarNameAndVariableUsage( ClassLike $classLike, ClassMethod $classMethod, string $desiredPropertyName, Param $param ): void { + if ($param->var instanceof Error) { + return; + } + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); $currentParamName = $this->nodeNameResolver->getName($param); @@ -88,27 +152,10 @@ private function renameParamVarName( /** @var string $paramVarName */ $paramVarName = $param->var->name; - $this->renameParamDoc($classMethodPhpDocInfo, $param, $paramVarName, $desiredPropertyName); - $param->var->name = $desiredPropertyName; - } - - private function renameParamDoc( - PhpDocInfo $phpDocInfo, - Param $param, - string $paramVarName, - string $desiredPropertyName - ): void { - $paramTagValueNode = $phpDocInfo->getParamTagValueNodeByName($paramVarName); - if (! $paramTagValueNode instanceof ParamTagValueNode) { - return; - } - - $paramRename = $this->paramRenameFactory->createFromResolvedExpectedName($param, $desiredPropertyName); - if (! $paramRename instanceof ParamRename) { - return; - } + $this->renameParamDoc($classMethodPhpDocInfo, $classMethod, $param, $paramVarName, $desiredPropertyName); + $param->var = new Variable($desiredPropertyName); - $this->paramRenamer->rename($paramRename); + $this->variableRenamer->renameVariableInFunctionLike($classMethod, $paramVarName, $desiredPropertyName); } /** diff --git a/rules/Naming/PropertyRenamer/PropertyRenamer.php b/rules/Naming/PropertyRenamer/PropertyRenamer.php deleted file mode 100644 index f0bd3a5eeb8..00000000000 --- a/rules/Naming/PropertyRenamer/PropertyRenamer.php +++ /dev/null @@ -1,45 +0,0 @@ -isAlreadyExpectedName()) { - return null; - } - - if ($this->propertyRenameGuard->shouldSkip($propertyRename)) { - return null; - } - - $onlyPropertyProperty = $propertyRename->getPropertyProperty(); - $onlyPropertyProperty->name = new VarLikeIdentifier($propertyRename->getExpectedName()); - $this->renamePropertyFetchesInClass($propertyRename); - - return $propertyRename->getProperty(); - } - - private function renamePropertyFetchesInClass(PropertyRename $propertyRename): void - { - $this->propertyFetchRenamer->renamePropertyFetchesInClass( - $propertyRename->getClassLike(), - $propertyRename->getCurrentName(), - $propertyRename->getExpectedName() - ); - } -} diff --git a/rules/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector.php b/rules/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector.php index dc58f012558..6e8fd77b4c6 100644 --- a/rules/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector.php +++ b/rules/Naming/Rector/Assign/RenameVariableToMatchMethodCallReturnTypeRector.php @@ -4,16 +4,16 @@ namespace Rector\Naming\Rector\Assign; +use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\Stmt\ClassLike; -use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer; +use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Function_; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\Naming\Guard\BreakingVariableRenameGuard; use Rector\Naming\Matcher\VariableAndCallAssignMatcher; use Rector\Naming\Naming\ExpectedNameResolver; @@ -21,8 +21,7 @@ use Rector\Naming\PhpDoc\VarTagValueNodeRenamer; use Rector\Naming\ValueObject\VariableAndCallAssign; use Rector\Naming\VariableRenamer; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -31,16 +30,25 @@ */ final class RenameVariableToMatchMethodCallReturnTypeRector extends AbstractRector { + /** + * @see https://regex101.com/r/JG5w9j/1 + */ + private const string OR_BETWEEN_WORDS_REGEX = '#[a-z]Or[A-Z]#'; + + /** + * @see https://regex101.com/r/TV8YXZ/1 + */ + private const string VALID_VARIABLE_NAME_REGEX = '#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#'; + public function __construct( - private BreakingVariableRenameGuard $breakingVariableRenameGuard, - private ExpectedNameResolver $expectedNameResolver, - private NamingConventionAnalyzer $namingConventionAnalyzer, - private VarTagValueNodeRenamer $varTagValueNodeRenamer, - private VariableAndCallAssignMatcher $variableAndCallAssignMatcher, - private VariableRenamer $variableRenamer, - private TypeUnwrapper $typeUnwrapper, - private ReflectionProvider $reflectionProvider, - private FamilyRelationsAnalyzer $familyRelationsAnalyzer + private readonly BreakingVariableRenameGuard $breakingVariableRenameGuard, + private readonly ExpectedNameResolver $expectedNameResolver, + private readonly NamingConventionAnalyzer $namingConventionAnalyzer, + private readonly VarTagValueNodeRenamer $varTagValueNodeRenamer, + private readonly VariableAndCallAssignMatcher $variableAndCallAssignMatcher, + private readonly VariableRenamer $variableRenamer, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly PhpDocInfoFactory $phpDocInfoFactory ) { } @@ -51,30 +59,30 @@ public function getRuleDefinition(): RuleDefinition <<<'CODE_SAMPLE' class SomeClass { -public function run() -{ - $a = $this->getRunner(); -} + public function run() + { + $a = $this->getRunner(); + } -public function getRunner(): Runner -{ - return new Runner(); -} + public function getRunner(): Runner + { + return new Runner(); + } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { -public function run() -{ - $runner = $this->getRunner(); -} + public function run() + { + $runner = $this->getRunner(); + } -public function getRunner(): Runner -{ - return new Runner(); -} + public function getRunner(): Runner + { + return new Runner(); + } } CODE_SAMPLE ), @@ -86,84 +94,68 @@ public function getRunner(): Runner */ public function getNodeTypes(): array { - return [Assign::class]; + return [ClassMethod::class, Closure::class, Function_::class]; } /** - * @param Assign $node + * @param ClassMethod|Closure|Function_ $node */ public function refactor(Node $node): ?Node { - $variableAndCallAssign = $this->variableAndCallAssignMatcher->match($node); - if (! $variableAndCallAssign instanceof VariableAndCallAssign) { + if ($node->stmts === null) { return null; } - $call = $variableAndCallAssign->getCall(); - if ($this->isMultipleCall($call)) { - return null; - } - - $expectedName = $this->expectedNameResolver->resolveForCall($call); - if ($expectedName === null) { - return null; - } - if ($this->isName($node->var, $expectedName)) { - return null; - } - - if ($this->shouldSkip($variableAndCallAssign, $expectedName)) { - return null; - } - - $this->renameVariable($variableAndCallAssign, $expectedName); - - return $node; - } + $hasChanged = false; + foreach ($node->stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } - private function isMultipleCall(FuncCall | StaticCall | MethodCall $callNode): bool - { - $parentNode = $callNode->getAttribute(AttributeKey::PARENT_NODE); + if (! $stmt->expr instanceof Assign) { + continue; + } - $callNodeClass = $callNode::class; + $assign = $stmt->expr; - while ($parentNode) { - $usedNodes = $this->betterNodeFinder->find($parentNode, function (Node $node) use ( - $callNodeClass, - $callNode - ): bool { - $nodeClass = $node::class; - if ($callNodeClass !== $nodeClass) { - return false; - } + $variableAndCallAssign = $this->variableAndCallAssignMatcher->match($assign, $node); + if (! $variableAndCallAssign instanceof VariableAndCallAssign) { + continue; + } - /** @var FuncCall|StaticCall|MethodCall $node */ - $passedNode = clone $node; + $call = $variableAndCallAssign->getCall(); - /** @var FuncCall|StaticCall|MethodCall $callNode */ - $usedNode = clone $callNode; + $expectedName = $this->expectedNameResolver->resolveForCall($call); + if ($expectedName === null) { + continue; + } - /** @var FuncCall|StaticCall|MethodCall $passedNode */ - $passedNode->args = []; + if ($this->isName($assign->var, $expectedName)) { + continue; + } - /** @var FuncCall|StaticCall|MethodCall $usedNode */ - $usedNode->args = []; + if ($this->shouldSkip($variableAndCallAssign, $expectedName)) { + continue; + } - return $this->nodeComparator->areNodesEqual($passedNode, $usedNode); - }); + $this->renameVariable($variableAndCallAssign, $expectedName, $stmt); - if (count($usedNodes) > 1) { - return true; - } + $hasChanged = true; + } - $parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE); + if ($hasChanged) { + return $node; } - return false; + return null; } private function shouldSkip(VariableAndCallAssign $variableAndCallAssign, string $expectedName): bool { + if (Strings::match($expectedName, self::VALID_VARIABLE_NAME_REGEX) === null) { + return true; + } + if ($this->namingConventionAnalyzer->isCallMatchingVariableName( $variableAndCallAssign->getCall(), $variableAndCallAssign->getVariableName(), @@ -172,7 +164,8 @@ private function shouldSkip(VariableAndCallAssign $variableAndCallAssign, string return true; } - if ($this->isClassTypeWithChildren($variableAndCallAssign->getCall())) { + $isUnionName = Strings::match($variableAndCallAssign->getVariableName(), self::OR_BETWEEN_WORDS_REGEX); + if ($isUnionName !== null) { return true; } @@ -184,45 +177,29 @@ private function shouldSkip(VariableAndCallAssign $variableAndCallAssign, string ); } - private function renameVariable(VariableAndCallAssign $variableAndCallAssign, string $expectedName): void - { - $assign = $variableAndCallAssign->getAssign(); - $assignPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($assign); - - $this->varTagValueNodeRenamer->renameAssignVarTagVariableName( - $assignPhpDocInfo, - $variableAndCallAssign->getVariableName(), - $expectedName - ); - + private function renameVariable( + VariableAndCallAssign $variableAndCallAssign, + string $expectedName, + Expression $expression + ): void { $this->variableRenamer->renameVariableInFunctionLike( $variableAndCallAssign->getFunctionLike(), $variableAndCallAssign->getVariableName(), $expectedName, $variableAndCallAssign->getAssign() ); - } - - private function isClassTypeWithChildren(StaticCall | MethodCall | FuncCall $expr): bool - { - $callStaticType = $this->getStaticType($expr); - $callStaticType = $this->typeUnwrapper->unwrapNullableType($callStaticType); - - if (! $callStaticType instanceof ObjectType) { - return false; - } - - if (is_a($callStaticType->getClassName(), ClassLike::class, true)) { - return false; - } - if (! $this->reflectionProvider->hasClass($callStaticType->getClassName())) { - return false; + $assignPhpDocInfo = $this->phpDocInfoFactory->createFromNode($expression); + if (! $assignPhpDocInfo instanceof PhpDocInfo) { + return; } - $classReflection = $this->reflectionProvider->getClass($callStaticType->getClassName()); - $childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); + $this->varTagValueNodeRenamer->renameAssignVarTagVariableName( + $assignPhpDocInfo, + $variableAndCallAssign->getVariableName(), + $expectedName + ); - return $childrenClassReflections !== []; + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($expression); } } diff --git a/rules/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector.php b/rules/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector.php index 3e42ecffb2b..42577400960 100644 --- a/rules/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector.php +++ b/rules/Naming/Rector/ClassMethod/RenameParamToMatchTypeRector.php @@ -5,16 +5,22 @@ namespace Rector\Naming\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Expr\ArrowFunction; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; +use PhpParser\Node\Stmt\Function_; +use PHPStan\Reflection\ClassReflection; use Rector\Naming\ExpectedNameResolver\MatchParamTypeExpectedNameResolver; use Rector\Naming\Guard\BreakingVariableRenameGuard; use Rector\Naming\Naming\ExpectedNameResolver; use Rector\Naming\ParamRenamer\ParamRenamer; use Rector\Naming\ValueObject\ParamRename; use Rector\Naming\ValueObjectFactory\ParamRenameFactory; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\Skipper\FileSystem\PathNormalizer; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,14 +29,13 @@ */ final class RenameParamToMatchTypeRector extends AbstractRector { - private bool $hasChanged = false; - public function __construct( - private BreakingVariableRenameGuard $breakingVariableRenameGuard, - private ExpectedNameResolver $expectedNameResolver, - private MatchParamTypeExpectedNameResolver $matchParamTypeExpectedNameResolver, - private ParamRenameFactory $paramRenameFactory, - private ParamRenamer $paramRenamer + private readonly BreakingVariableRenameGuard $breakingVariableRenameGuard, + private readonly ExpectedNameResolver $expectedNameResolver, + private readonly MatchParamTypeExpectedNameResolver $matchParamTypeExpectedNameResolver, + private readonly ParamRenameFactory $paramRenameFactory, + private readonly ParamRenamer $paramRenamer, + private readonly ReflectionResolver $reflectionResolver, ) { } @@ -47,7 +52,7 @@ public function run(Apple $pie) } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' final class SomeClass { @@ -66,17 +71,34 @@ public function run(Apple $apple) */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class]; } /** - * @param ClassMethod $node + * @param ClassMethod|Function_|Closure|ArrowFunction $node */ public function refactor(Node $node): ?Node { - $this->hasChanged = false; + $hasChanged = false; foreach ($node->params as $param) { + // skip as array-like + if ($param->variadic) { + continue; + } + + if ($param->type === null) { + continue; + } + + if ($this->skipExactType($param)) { + return null; + } + + if ($node instanceof ClassMethod && $this->shouldSkipClassMethodFromVendor($node)) { + return null; + } + $expectedName = $this->expectedNameResolver->resolveForParamIfNotYet($param); if ($expectedName === null) { continue; @@ -91,24 +113,69 @@ public function refactor(Node $node): ?Node continue; } - $paramRename = $this->paramRenameFactory->createFromResolvedExpectedName($param, $expectedName); + $paramRename = $this->paramRenameFactory->createFromResolvedExpectedName($node, $param, $expectedName); if (! $paramRename instanceof ParamRename) { continue; } $this->paramRenamer->rename($paramRename); - $this->hasChanged = true; + $hasChanged = true; } - if (! $this->hasChanged) { + if (! $hasChanged) { return null; } return $node; } - private function shouldSkipParam(Param $param, string $expectedName, ClassMethod $classMethod): bool + /** + * Avoid renaming parameters of a class method, that is located in /vendor, + * to keep name matching for named arguments. + */ + private function shouldSkipClassMethodFromVendor(ClassMethod $classMethod): bool { + if ($classMethod->isPrivate()) { + return false; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + $ancestorClassReflections = array_filter( + $classReflection->getAncestors(), + fn (ClassReflection $ancestorClassReflection): bool => + $classReflection->getName() !== $ancestorClassReflection->getName() + ); + + $methodName = $this->getName($classMethod); + + foreach ($ancestorClassReflections as $ancestorClassReflection) { + // internal + if ($ancestorClassReflection->getFileName() === null) { + continue; + } + + if (! $ancestorClassReflection->hasNativeMethod($methodName)) { + continue; + } + + $path = PathNormalizer::normalize($ancestorClassReflection->getFileName()); + if (str_contains($path, '/vendor/')) { + return true; + } + } + + return false; + } + + private function shouldSkipParam( + Param $param, + string $expectedName, + ClassMethod|Function_|Closure|ArrowFunction $classMethod + ): bool { /** @var string $paramName */ $paramName = $this->getName($param); @@ -116,11 +183,27 @@ private function shouldSkipParam(Param $param, string $expectedName, ClassMethod return true; } + if (! $classMethod instanceof ClassMethod) { + return false; + } + // promoted property if (! $this->isName($classMethod, MethodName::CONSTRUCT)) { return false; } - return $param->flags !== 0; + return $param->isPromoted(); + } + + /** + * Skip couple quote vague types, that could be named explicitly on purpose. + */ + private function skipExactType(Param $param): bool + { + if (! $param->type instanceof Node) { + return false; + } + + return $this->isName($param->type, Node::class); } } diff --git a/rules/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector.php b/rules/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector.php index 9854d9985e3..562a9cdba2a 100644 --- a/rules/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector.php +++ b/rules/Naming/Rector/ClassMethod/RenameVariableToMatchNewTypeRector.php @@ -9,10 +9,11 @@ use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Rector\AbstractRector; use Rector\Naming\Guard\BreakingVariableRenameGuard; use Rector\Naming\Naming\ExpectedNameResolver; use Rector\Naming\VariableRenamer; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,9 +23,10 @@ final class RenameVariableToMatchNewTypeRector extends AbstractRector { public function __construct( - private BreakingVariableRenameGuard $breakingVariableRenameGuard, - private ExpectedNameResolver $expectedNameResolver, - private VariableRenamer $variableRenamer + private readonly BreakingVariableRenameGuard $breakingVariableRenameGuard, + private readonly ExpectedNameResolver $expectedNameResolver, + private readonly VariableRenamer $variableRenamer, + private readonly BetterNodeFinder $betterNodeFinder ) { } @@ -42,7 +44,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' final class SomeClass { @@ -76,11 +78,17 @@ public function refactor(Node $node): ?Node foreach ($assignsOfNew as $assignOfNew) { $expectedName = $this->expectedNameResolver->resolveForAssignNew($assignOfNew); + // skip self name as not useful + if ($expectedName === 'self') { + continue; + } + /** @var Variable $variable */ $variable = $assignOfNew->var; if ($expectedName === null) { continue; } + if ($this->isName($variable, $expectedName)) { continue; } @@ -118,6 +126,6 @@ private function getAssignsOfNew(ClassMethod $classMethod): array /** @var Assign[] $assigns */ $assigns = $this->betterNodeFinder->findInstanceOf((array) $classMethod->stmts, Assign::class); - return array_filter($assigns, fn (Assign $assign): bool => $assign->expr instanceof New_); + return array_filter($assigns, static fn (Assign $assign): bool => $assign->expr instanceof New_); } } diff --git a/rules/Naming/Rector/Class_/RenamePropertyToMatchTypeRector.php b/rules/Naming/Rector/Class_/RenamePropertyToMatchTypeRector.php index 22052f64a98..3a7b0548c39 100644 --- a/rules/Naming/Rector/Class_/RenamePropertyToMatchTypeRector.php +++ b/rules/Naming/Rector/Class_/RenamePropertyToMatchTypeRector.php @@ -5,16 +5,17 @@ namespace Rector\Naming\Rector\Class_; use PhpParser\Node; +use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassLike; -use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Property; -use Rector\Core\Rector\AbstractRector; +use PHPStan\Type\ObjectType; +use Rector\Enum\ClassName; use Rector\Naming\ExpectedNameResolver\MatchPropertyTypeExpectedNameResolver; use Rector\Naming\PropertyRenamer\MatchTypePropertyRenamer; use Rector\Naming\PropertyRenamer\PropertyPromotionRenamer; use Rector\Naming\ValueObject\PropertyRename; use Rector\Naming\ValueObjectFactory\PropertyRenameFactory; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -26,10 +27,10 @@ final class RenamePropertyToMatchTypeRector extends AbstractRector private bool $hasChanged = false; public function __construct( - private MatchTypePropertyRenamer $matchTypePropertyRenamer, - private PropertyRenameFactory $propertyRenameFactory, - private MatchPropertyTypeExpectedNameResolver $matchPropertyTypeExpectedNameResolver, - private PropertyPromotionRenamer $propertyPromotionRenamer, + private readonly MatchTypePropertyRenamer $matchTypePropertyRenamer, + private readonly PropertyRenameFactory $propertyRenameFactory, + private readonly MatchPropertyTypeExpectedNameResolver $matchPropertyTypeExpectedNameResolver, + private readonly PropertyPromotionRenamer $propertyPromotionRenamer, ) { } @@ -78,37 +79,61 @@ public function __construct(EntityManager $entityManager) */ public function getNodeTypes(): array { - return [Class_::class, Interface_::class]; + return [Class_::class]; } /** - * @param Class_|Interface_ $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { + $this->hasChanged = false; + $this->refactorClassProperties($node); - $this->propertyPromotionRenamer->renamePropertyPromotion($node); - if (! $this->hasChanged) { - return null; + $hasPromotedPropertyChanged = $this->propertyPromotionRenamer->renamePropertyPromotion($node); + if ($this->hasChanged) { + return $node; + } + + if ($hasPromotedPropertyChanged) { + return $node; } - return $node; + return null; } - private function refactorClassProperties(ClassLike $classLike): void + private function refactorClassProperties(Class_ $class): void { - foreach ($classLike->getProperties() as $property) { - $expectedPropertyName = $this->matchPropertyTypeExpectedNameResolver->resolve($property); + foreach ($class->getProperties() as $property) { + // skip public properties, as they can be used in external code + if ($property->isPublic()) { + continue; + } + + if (! $class->isFinal() && $property->isProtected()) { + continue; + } + + $expectedPropertyName = $this->matchPropertyTypeExpectedNameResolver->resolve($property, $class); if ($expectedPropertyName === null) { continue; } - $propertyRename = $this->propertyRenameFactory->createFromExpectedName($property, $expectedPropertyName); + $propertyRename = $this->propertyRenameFactory->createFromExpectedName( + $class, + $property, + $expectedPropertyName + ); + if (! $propertyRename instanceof PropertyRename) { continue; } + if ($this->skipExactTypes($property)) { + continue; + } + $renameProperty = $this->matchTypePropertyRenamer->rename($propertyRename); if (! $renameProperty instanceof Property) { continue; @@ -117,4 +142,21 @@ private function refactorClassProperties(ClassLike $classLike): void $this->hasChanged = true; } } + + /** + * Such properties can have "xMock" names that are not compatible with "MockObject" suffix + * They should be kept and handled by another naming rule that deals with mocks + */ + private function skipExactTypes(Property $property): bool + { + if (! $property->type instanceof Name) { + return false; + } + + if ($this->isObjectType($property->type, new ObjectType(ClassName::MOCK_OBJECT))) { + return true; + } + + return $this->isObjectType($property->type, new ObjectType(ClassName::DATE_TIME_INTERFACE)); + } } diff --git a/rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector.php b/rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector.php index 43889bbf623..d596af531b0 100644 --- a/rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector.php +++ b/rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector.php @@ -5,14 +5,15 @@ namespace Rector\Naming\Rector\Foreach_; use PhpParser\Node; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Foreach_; -use PHPStan\Type\ThisType; -use Rector\CodeQuality\NodeAnalyzer\ForeachAnalyzer; -use Rector\Core\Rector\AbstractRector; use Rector\Naming\ExpectedNameResolver\InflectorSingularResolver; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; +use Rector\NodeManipulator\StmtsManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,40 +23,42 @@ final class RenameForeachValueVariableToMatchExprVariableRector extends AbstractRector { public function __construct( - private InflectorSingularResolver $inflectorSingularResolver, - private ForeachAnalyzer $foreachAnalyzer + private readonly InflectorSingularResolver $inflectorSingularResolver, + private readonly PropertyFetchAnalyzer $propertyFetchAnalyzer, + private readonly StmtsManipulator $stmtsManipulator, + private readonly BetterNodeFinder $betterNodeFinder ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Renames value variable name in foreach loop to match expression variable', [ + return new RuleDefinition('Rename value variable name in foreach loop to match expression variable', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass { -public function run() -{ - $array = []; - foreach ($variables as $property) { - $array[] = $property; + public function run() + { + $array = []; + foreach ($variables as $property) { + $array[] = $property; + } } } -} CODE_SAMPLE , <<<'CODE_SAMPLE' class SomeClass { -public function run() -{ - $array = []; - foreach ($variables as $variable) { - $array[] = $variable; + public function run() + { + $array = []; + foreach ($variables as $variable) { + $array[] = $variable; + } } } -} CODE_SAMPLE ), ]); @@ -66,62 +69,82 @@ public function run() */ public function getNodeTypes(): array { - return [Foreach_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Foreach_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - if (! $node->expr instanceof Variable && ! $node->expr instanceof PropertyFetch) { + if ($node->stmts === null) { return null; } - if ($this->isNotThisTypePropertyFetch($node->expr)) { - return null; - } + $hasChanged = false; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } - $exprName = $this->getName($node->expr); - if ($exprName === null) { - return null; - } + $isPropertyFetch = $this->propertyFetchAnalyzer->isLocalPropertyFetch($stmt->expr); + if (! $stmt->expr instanceof Variable && ! $isPropertyFetch) { + continue; + } - if ($node->keyVar instanceof Node) { - return null; - } + $exprName = $this->getName($stmt->expr); + if ($exprName === null) { + continue; + } - $valueVarName = $this->getName($node->valueVar); - if ($valueVarName === null) { - return null; - } + if ($stmt->keyVar instanceof Node) { + continue; + } - $singularValueVarName = $this->inflectorSingularResolver->resolve($exprName); - if ($singularValueVarName === $exprName) { - return null; - } - if ($singularValueVarName === $valueVarName) { - return null; - } + $valueVarName = $this->getName($stmt->valueVar); + if ($valueVarName === null) { + continue; + } - if ($this->foreachAnalyzer->isValueVarUsed($node, $singularValueVarName)) { - return null; - } + $singularValueVarName = $this->inflectorSingularResolver->resolve($exprName); + if ($singularValueVarName === $exprName) { + continue; + } - return $this->processRename($node, $valueVarName, $singularValueVarName); - } + if ($singularValueVarName === $valueVarName) { + continue; + } - private function isNotThisTypePropertyFetch(Expr $expr): bool - { - if ($expr instanceof PropertyFetch) { - $variableType = $this->getStaticType($expr->var); - return ! $variableType instanceof ThisType; + $alreadyUsedVariable = $this->betterNodeFinder->findVariableOfName($stmt->stmts, $singularValueVarName); + if ($alreadyUsedVariable instanceof Variable) { + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt($node, $key + 1, $singularValueVarName)) { + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt($node, $key + 1, $valueVarName)) { + continue; + } + + $scope = ScopeFetcher::fetch($stmt); + if ($scope->hasVariableType($singularValueVarName)->yes()) { + continue; + } + + $this->processRename($stmt, $valueVarName, $singularValueVarName); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; } - return false; + return null; } - private function processRename(Foreach_ $foreach, string $valueVarName, string $singularValueVarName): Foreach_ + private function processRename(Foreach_ $foreach, string $valueVarName, string $singularValueVarName): void { $foreach->valueVar = new Variable($singularValueVarName); $this->traverseNodesWithCallable($foreach->stmts, function (Node $node) use ( @@ -135,9 +158,8 @@ private function processRename(Foreach_ $foreach, string $valueVarName, string $ if (! $this->isName($node, $valueVarName)) { return null; } + return new Variable($singularValueVarName); }); - - return $foreach; } } diff --git a/rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector.php b/rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector.php index 2e1c773528e..4f28ac5619e 100644 --- a/rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector.php +++ b/rules/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchMethodCallReturnTypeRector.php @@ -5,14 +5,19 @@ namespace Rector\Naming\Rector\Foreach_; use PhpParser\Node; +use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Foreach_; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Stmt\Function_; +use PhpParser\NodeVisitor; use Rector\Naming\Guard\BreakingVariableRenameGuard; use Rector\Naming\Matcher\ForeachMatcher; use Rector\Naming\Naming\ExpectedNameResolver; use Rector\Naming\NamingConvention\NamingConventionAnalyzer; use Rector\Naming\ValueObject\VariableAndCallForeach; use Rector\Naming\VariableRenamer; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,12 +26,17 @@ */ final class RenameForeachValueVariableToMatchMethodCallReturnTypeRector extends AbstractRector { + /** + * @var string[] + */ + private const array UNREADABLE_GENERIC_NAMES = ['traversable', 'iterable', 'generator', 'rewindableGenerator']; + public function __construct( - private BreakingVariableRenameGuard $breakingVariableRenameGuard, - private ExpectedNameResolver $expectedNameResolver, - private NamingConventionAnalyzer $namingConventionAnalyzer, - private VariableRenamer $variableRenamer, - private ForeachMatcher $foreachMatcher + private readonly BreakingVariableRenameGuard $breakingVariableRenameGuard, + private readonly ExpectedNameResolver $expectedNameResolver, + private readonly NamingConventionAnalyzer $namingConventionAnalyzer, + private readonly VariableRenamer $variableRenamer, + private readonly ForeachMatcher $foreachMatcher ) { } @@ -72,38 +82,77 @@ public function run() */ public function getNodeTypes(): array { - return [Foreach_::class]; + return [ClassMethod::class, Closure::class, Function_::class]; } /** - * @param Foreach_ $node + * @param ClassMethod|Closure|Function_ $node */ public function refactor(Node $node): ?Node { - $variableAndCallForeach = $this->foreachMatcher->match($node); - if (! $variableAndCallForeach instanceof VariableAndCallForeach) { + if ($node->stmts === null) { return null; } - $expectedName = $this->expectedNameResolver->resolveForForeach($variableAndCallForeach->getCall()); - if ($expectedName === null) { - return null; - } - if ($this->isName($variableAndCallForeach->getVariable(), $expectedName)) { - return null; - } + $hasRenamed = false; + $this->traverseNodesWithCallable( + $node->stmts, + function (Node $subNode) use ($node, &$hasRenamed): ?int { + if ($subNode instanceof Class_ || $subNode instanceof Closure || $subNode instanceof Function_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } - if ($this->shouldSkip($variableAndCallForeach, $expectedName)) { - return null; - } + if (! $subNode instanceof Foreach_) { + return null; + } + + $variableAndCallForeach = $this->foreachMatcher->match($subNode, $node); + if (! $variableAndCallForeach instanceof VariableAndCallForeach) { + return null; + } + + $expectedName = $this->expectedNameResolver->resolveForForeach($variableAndCallForeach); + if ($expectedName === null) { + return null; + } + + if ($this->isName($variableAndCallForeach->getVariable(), $expectedName)) { + return null; + } + + if ($this->shouldSkip($variableAndCallForeach, $expectedName)) { + return null; + } + + $hasChanged = $this->variableRenamer->renameVariableInFunctionLike( + $variableAndCallForeach->getFunctionLike(), + $variableAndCallForeach->getVariableName(), + $expectedName + ); - $this->renameVariable($variableAndCallForeach, $expectedName); + // use different variable on purpose to avoid variable re-assign back to false + // after go to other method + if ($hasChanged) { + $hasRenamed = true; + } - return $node; + return null; + } + ); + + if ($hasRenamed) { + return $node; + } + + return null; } private function shouldSkip(VariableAndCallForeach $variableAndCallForeach, string $expectedName): bool { + if (in_array($expectedName, self::UNREADABLE_GENERIC_NAMES, true)) { + return true; + } + if ($this->namingConventionAnalyzer->isCallMatchingVariableName( $variableAndCallForeach->getCall(), $variableAndCallForeach->getVariableName(), @@ -119,14 +168,4 @@ private function shouldSkip(VariableAndCallForeach $variableAndCallForeach, stri $variableAndCallForeach->getVariable() ); } - - private function renameVariable(VariableAndCallForeach $variableAndCallForeach, string $expectedName): void - { - $this->variableRenamer->renameVariableInFunctionLike( - $variableAndCallForeach->getFunctionLike(), - $variableAndCallForeach->getVariableName(), - $expectedName, - null - ); - } } diff --git a/rules/Naming/RectorNamingInflector.php b/rules/Naming/RectorNamingInflector.php index d29dc7b6c5c..7e651daab98 100644 --- a/rules/Naming/RectorNamingInflector.php +++ b/rules/Naming/RectorNamingInflector.php @@ -7,13 +7,12 @@ use Doctrine\Inflector\Inflector; use Nette\Utils\Strings; -final class RectorNamingInflector +final readonly class RectorNamingInflector { /** - * @var string * @see https://regex101.com/r/VqVvke/3 */ - private const DATA_INFO_SUFFIX_REGEX = '#^(?.+)(?Data|Info)$#'; + private const string DATA_INFO_SUFFIX_REGEX = '#^(?.+)(?Data|Info)$#'; public function __construct( private Inflector $inflector @@ -27,7 +26,7 @@ public function singularize(string $name): string return $this->inflector->singularize($name); } - $singularized = $this->inflector->singularize($matches['prefix']); + $singularized = $this->inflector->singularize((string) $matches['prefix']); $uninflectable = $matches['suffix']; return $singularized . $uninflectable; diff --git a/rules/Naming/RenameGuard/PropertyRenameGuard.php b/rules/Naming/RenameGuard/PropertyRenameGuard.php index 574280e20d0..4e88bbb2e62 100644 --- a/rules/Naming/RenameGuard/PropertyRenameGuard.php +++ b/rules/Naming/RenameGuard/PropertyRenameGuard.php @@ -4,27 +4,38 @@ namespace Rector\Naming\RenameGuard; -use Rector\Naming\Contract\Guard\ConflictingNameGuardInterface; -use Rector\Naming\Contract\RenameValueObjectInterface; +use PHPStan\Type\ObjectType; +use Rector\Naming\Guard\DateTimeAtNamingConventionGuard; +use Rector\Naming\Guard\HasMagicGetSetGuard; +use Rector\Naming\ValueObject\PropertyRename; +use Rector\NodeTypeResolver\NodeTypeResolver; -final class PropertyRenameGuard +final readonly class PropertyRenameGuard { - /** - * @param ConflictingNameGuardInterface[] $conflictingNameGuards - */ public function __construct( - private array $conflictingNameGuards + private NodeTypeResolver $nodeTypeResolver, + private DateTimeAtNamingConventionGuard $dateTimeAtNamingConventionGuard, + private HasMagicGetSetGuard $hasMagicGetSetGuard, ) { } - public function shouldSkip(RenameValueObjectInterface $renameValueObject): bool + public function shouldSkip(PropertyRename $propertyRename): bool { - foreach ($this->conflictingNameGuards as $conflictingNameGuard) { - if ($conflictingNameGuard->isConflicting($renameValueObject)) { - return true; - } + if (! $propertyRename->isPrivateProperty()) { + return true; } - return false; + if ($this->nodeTypeResolver->isObjectType( + $propertyRename->getProperty(), + new ObjectType('Ramsey\Uuid\UuidInterface') + )) { + return true; + } + + if ($this->dateTimeAtNamingConventionGuard->isConflicting($propertyRename)) { + return true; + } + + return $this->hasMagicGetSetGuard->isConflicting($propertyRename); } } diff --git a/rules/Naming/ValueObject/ExpectedName.php b/rules/Naming/ValueObject/ExpectedName.php index e041d2414ce..1be26b96d6d 100644 --- a/rules/Naming/ValueObject/ExpectedName.php +++ b/rules/Naming/ValueObject/ExpectedName.php @@ -4,7 +4,7 @@ namespace Rector\Naming\ValueObject; -final class ExpectedName +final readonly class ExpectedName { public function __construct( private string $name, diff --git a/rules/Naming/ValueObject/ParamRename.php b/rules/Naming/ValueObject/ParamRename.php index d37cb493643..6f374447a7c 100644 --- a/rules/Naming/ValueObject/ParamRename.php +++ b/rules/Naming/ValueObject/ParamRename.php @@ -4,21 +4,16 @@ namespace Rector\Naming\ValueObject; -use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Param; -use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Function_; -use Rector\Naming\Contract\RenameParamValueObjectInterface; +use PhpParser\Node\FunctionLike; -final class ParamRename implements RenameParamValueObjectInterface +final readonly class ParamRename { public function __construct( private string $currentName, private string $expectedName, - private Param $param, private Variable $variable, - private ClassMethod | Function_ | Closure $functionLike + private FunctionLike $functionLike ) { } @@ -32,16 +27,11 @@ public function getExpectedName(): string return $this->expectedName; } - public function getFunctionLike(): ClassMethod | Function_ | Closure + public function getFunctionLike(): FunctionLike { return $this->functionLike; } - public function getParam(): Param - { - return $this->param; - } - public function getVariable(): Variable { return $this->variable; diff --git a/rules/Naming/ValueObject/PropertyRename.php b/rules/Naming/ValueObject/PropertyRename.php index caff1bf3131..58b2ced96b2 100644 --- a/rules/Naming/ValueObject/PropertyRename.php +++ b/rules/Naming/ValueObject/PropertyRename.php @@ -4,12 +4,12 @@ namespace Rector\Naming\ValueObject; +use PhpParser\Node\PropertyItem; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Property; -use PhpParser\Node\Stmt\PropertyProperty; -use Rector\Naming\Contract\RenamePropertyValueObjectInterface; +use Rector\Validation\RectorAssert; -final class PropertyRename implements RenamePropertyValueObjectInterface +final readonly class PropertyRename { public function __construct( private Property $property, @@ -17,8 +17,11 @@ public function __construct( private string $currentName, private ClassLike $classLike, private string $classLikeName, - private PropertyProperty $propertyProperty + private PropertyItem $propertyItem ) { + // name must be valid + RectorAssert::propertyName($currentName); + RectorAssert::propertyName($expectedName); } public function getProperty(): Property @@ -56,8 +59,8 @@ public function getClassLikeName(): string return $this->classLikeName; } - public function getPropertyProperty(): PropertyProperty + public function getPropertyProperty(): PropertyItem { - return $this->propertyProperty; + return $this->propertyItem; } } diff --git a/rules/Naming/ValueObject/VariableAndCallAssign.php b/rules/Naming/ValueObject/VariableAndCallAssign.php index 665a9335cd4..7de03fd5b24 100644 --- a/rules/Naming/ValueObject/VariableAndCallAssign.php +++ b/rules/Naming/ValueObject/VariableAndCallAssign.php @@ -13,7 +13,7 @@ use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; -final class VariableAndCallAssign +final readonly class VariableAndCallAssign { public function __construct( private Variable $variable, diff --git a/rules/Naming/ValueObject/VariableAndCallForeach.php b/rules/Naming/ValueObject/VariableAndCallForeach.php index a982d1297b8..8628a964c8c 100644 --- a/rules/Naming/ValueObject/VariableAndCallForeach.php +++ b/rules/Naming/ValueObject/VariableAndCallForeach.php @@ -12,7 +12,7 @@ use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; -final class VariableAndCallForeach +final readonly class VariableAndCallForeach { public function __construct( private Variable $variable, diff --git a/rules/Naming/ValueObjectFactory/ParamRenameFactory.php b/rules/Naming/ValueObjectFactory/ParamRenameFactory.php index 39fb3bfe363..ec885949ac9 100644 --- a/rules/Naming/ValueObjectFactory/ParamRenameFactory.php +++ b/rules/Naming/ValueObjectFactory/ParamRenameFactory.php @@ -4,42 +4,29 @@ namespace Rector\Naming\ValueObjectFactory; -use PhpParser\Node\Expr\ArrowFunction; -use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\Error; use PhpParser\Node\FunctionLike; use PhpParser\Node\Param; -use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Function_; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\PhpParser\Node\BetterNodeFinder; use Rector\Naming\ValueObject\ParamRename; use Rector\NodeNameResolver\NodeNameResolver; -final class ParamRenameFactory +final readonly class ParamRenameFactory { public function __construct( - private NodeNameResolver $nodeNameResolver, - private BetterNodeFinder $betterNodeFinder + private NodeNameResolver $nodeNameResolver ) { } - public function createFromResolvedExpectedName(Param $param, string $expectedName): ?ParamRename - { - /** @var ClassMethod|Function_|Closure|ArrowFunction|null $functionLike */ - $functionLike = $this->betterNodeFinder->findParentType($param, FunctionLike::class); - if ($functionLike === null) { - throw new ShouldNotHappenException("There shouldn't be a param outside of FunctionLike"); - } - - if ($functionLike instanceof ArrowFunction) { - return null; - } - - $currentName = $this->nodeNameResolver->getName($param->var); - if ($currentName === null) { + public function createFromResolvedExpectedName( + FunctionLike $functionLike, + Param $param, + string $expectedName + ): ?ParamRename { + if ($param->var instanceof Error) { return null; } - return new ParamRename($currentName, $expectedName, $param, $param->var, $functionLike); + $currentName = $this->nodeNameResolver->getName($param); + return new ParamRename($currentName, $expectedName, $param->var, $functionLike); } } diff --git a/rules/Naming/ValueObjectFactory/PropertyRenameFactory.php b/rules/Naming/ValueObjectFactory/PropertyRenameFactory.php index 277311c1769..66a53606d0e 100644 --- a/rules/Naming/ValueObjectFactory/PropertyRenameFactory.php +++ b/rules/Naming/ValueObjectFactory/PropertyRenameFactory.php @@ -4,43 +4,39 @@ namespace Rector\Naming\ValueObjectFactory; -use PhpParser\Node\Stmt\ClassLike; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Property; use Rector\Naming\ValueObject\PropertyRename; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Webmozart\Assert\InvalidArgumentException; -/** - * @see \Rector\Tests\Naming\ValueObjectFactory\PropertyRenameFactory\PropertyRenameFactoryTest - */ -final class PropertyRenameFactory +final readonly class PropertyRenameFactory { public function __construct( - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, ) { } - public function createFromExpectedName(Property $property, string $expectedName): ?PropertyRename - { + public function createFromExpectedName( + Class_ $class, + Property $property, + string $expectedName + ): ?PropertyRename { $currentName = $this->nodeNameResolver->getName($property); - - $propertyClassLike = $property->getAttribute(AttributeKey::CLASS_NODE); - if (! $propertyClassLike instanceof ClassLike) { - return null; - } - - $propertyClassLikeName = $property->getAttribute(AttributeKey::CLASS_NAME); - if ($propertyClassLikeName === null) { - return null; + $className = (string) $this->nodeNameResolver->getName($class); + + try { + return new PropertyRename( + $property, + $expectedName, + $currentName, + $class, + $className, + $property->props[0] + ); + } catch (InvalidArgumentException) { } - return new PropertyRename( - $property, - $expectedName, - $currentName, - $propertyClassLike, - $propertyClassLikeName, - $property->props[0] - ); + return null; } } diff --git a/rules/Naming/VariableRenamer.php b/rules/Naming/VariableRenamer.php index d011e296ca6..f005b2ec036 100644 --- a/rules/Naming/VariableRenamer.php +++ b/rules/Naming/VariableRenamer.php @@ -6,63 +6,80 @@ use PhpParser\Node; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\FunctionLike; use PhpParser\Node\Param; -use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt; +use PhpParser\NodeVisitor; +use PHPStan\Analyser\MutatingScope; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\Core\PhpParser\Node\BetterNodeFinder; use Rector\Naming\PhpDoc\VarTagValueNodeRenamer; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; -final class VariableRenamer +final readonly class VariableRenamer { public function __construct( private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, private NodeNameResolver $nodeNameResolver, private VarTagValueNodeRenamer $varTagValueNodeRenamer, - private PhpDocInfoFactory $phpDocInfoFactory, - private BetterNodeFinder $betterNodeFinder + private PhpDocInfoFactory $phpDocInfoFactory ) { } public function renameVariableInFunctionLike( - ClassMethod | Function_ | Closure $functionLike, + FunctionLike $functionLike, string $oldName, string $expectedName, ?Assign $assign = null - ): void { + ): bool { $isRenamingActive = false; - if ($assign === null) { + if (! $assign instanceof Assign) { $isRenamingActive = true; } + $hasRenamed = false; + $currentStmt = null; + $currentFunctionLike = null; + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $functionLike->stmts, - function (Node $node) use ($oldName, $expectedName, $assign, &$isRenamingActive): ?Variable { - if ($assign !== null && $node === $assign) { + (array) $functionLike->getStmts(), + function (Node $node) use ( + $oldName, + $expectedName, + $assign, + &$isRenamingActive, + &$hasRenamed, + &$currentStmt, + &$currentFunctionLike + ): int|null|Variable { + // skip param names + if ($node instanceof Param) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($assign instanceof Assign && $node === $assign) { $isRenamingActive = true; return null; } - if (! $node instanceof Variable) { - return null; + if ($node instanceof Stmt) { + $currentStmt = $node; } - // skip param names - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Param) { + if ($node instanceof FunctionLike) { + $currentFunctionLike = $node; + } + + if (! $node instanceof Variable) { return null; } - // TODO: Remove in next PR (with above param check?), // TODO: Should be implemented in BreakingVariableRenameGuard::shouldSkipParam() - if ($this->isParamInParentFunction($node)) { + if ($this->isParamInParentFunction($node, $currentFunctionLike)) { return null; } @@ -70,15 +87,21 @@ function (Node $node) use ($oldName, $expectedName, $assign, &$isRenamingActive) return null; } - return $this->renameVariableIfMatchesName($node, $oldName, $expectedName); + $variable = $this->renameVariableIfMatchesName($node, $oldName, $expectedName, $currentStmt); + if ($variable instanceof Variable) { + $hasRenamed = true; + } + + return $variable; } ); + + return $hasRenamed; } - private function isParamInParentFunction(Variable $variable): bool + private function isParamInParentFunction(Variable $variable, ?FunctionLike $functionLike): bool { - $closure = $this->betterNodeFinder->findParentType($variable, Closure::class); - if (! $closure instanceof Closure) { + if (! $functionLike instanceof FunctionLike) { return false; } @@ -87,7 +110,16 @@ private function isParamInParentFunction(Variable $variable): bool return false; } - foreach ($closure->params as $param) { + $scope = $variable->getAttribute(AttributeKey::SCOPE); + $functionLikeScope = $functionLike->getAttribute(AttributeKey::SCOPE); + + if ($scope instanceof MutatingScope && $functionLikeScope instanceof MutatingScope && $scope->equals( + $functionLikeScope + )) { + return false; + } + + foreach ($functionLike->getParams() as $param) { if ($this->nodeNameResolver->isName($param, $variableName)) { return true; } @@ -96,15 +128,19 @@ private function isParamInParentFunction(Variable $variable): bool return false; } - private function renameVariableIfMatchesName(Variable $variable, string $oldName, string $expectedName): ?Variable - { + private function renameVariableIfMatchesName( + Variable $variable, + string $oldName, + string $expectedName, + ?Stmt $currentStmt + ): ?Variable { if (! $this->nodeNameResolver->isName($variable, $oldName)) { return null; } $variable->name = $expectedName; - $variablePhpDocInfo = $this->resolvePhpDocInfo($variable); + $variablePhpDocInfo = $this->resolvePhpDocInfo($variable, $currentStmt); $this->varTagValueNodeRenamer->renameAssignVarTagVariableName($variablePhpDocInfo, $oldName, $expectedName); return $variable; @@ -113,11 +149,10 @@ private function renameVariableIfMatchesName(Variable $variable, string $oldName /** * Expression doc block has higher priority */ - private function resolvePhpDocInfo(Variable $variable): PhpDocInfo + private function resolvePhpDocInfo(Variable $variable, ?Stmt $currentStmt): PhpDocInfo { - $expression = $variable->getAttribute(AttributeKey::CURRENT_STATEMENT); - if ($expression instanceof Node) { - return $this->phpDocInfoFactory->createFromNodeOrEmpty($expression); + if ($currentStmt instanceof Stmt) { + return $this->phpDocInfoFactory->createFromNodeOrEmpty($currentStmt); } return $this->phpDocInfoFactory->createFromNodeOrEmpty($variable); diff --git a/rules/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector.php b/rules/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector.php new file mode 100644 index 00000000000..ef5aade25d6 --- /dev/null +++ b/rules/NetteUtils/Rector/StaticCall/UtilsJsonStaticCallNamedArgRector.php @@ -0,0 +1,86 @@ +isName($node->class, NetteClassName::JSON)) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + if (count($node->getArgs()) < 2) { + return null; + } + + if (! $this->isNames($node->name, ['encode', 'decode'])) { + return null; + } + + // flip 2nd arg from true/false to named arg + // check if 2nd arg is named arg already + $secondArg = $node->getArgs()[1]; + + // already set → skip + if ($secondArg->name instanceof Identifier) { + return null; + } + + if ($this->isName($node->name, 'encode')) { + $secondArg->name = new Identifier('pretty'); + return $node; + } + + $secondArg->name = new Identifier('forceArrays'); + return $node; + } +} diff --git a/rules/Order/Contract/RankeableInterface.php b/rules/Order/Contract/RankeableInterface.php deleted file mode 100644 index 194adf1b22f..00000000000 --- a/rules/Order/Contract/RankeableInterface.php +++ /dev/null @@ -1,15 +0,0 @@ - $oldToNewKeys - */ - public function hasOrderChanged(array $oldToNewKeys): bool - { - $keys = array_keys($oldToNewKeys); - $values = array_values($oldToNewKeys); - - return $keys !== $values; - } -} diff --git a/rules/Order/Rector/Class_/OrderPrivateMethodsByUseRector.php b/rules/Order/Rector/Class_/OrderPrivateMethodsByUseRector.php deleted file mode 100644 index 945baa8e34c..00000000000 --- a/rules/Order/Rector/Class_/OrderPrivateMethodsByUseRector.php +++ /dev/null @@ -1,198 +0,0 @@ -call1(); - $this->call2(); - } - - private function call2() - { - } - - private function call1() - { - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $this->call1(); - $this->call2(); - } - - private function call1() - { - } - - private function call2() - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class, Trait_::class]; - } - - /** - * @param Class_|Trait_ $node - */ - public function refactor(Node $node): ?Node - { - $sortedAndOriginalClassMethods = $this->getSortedAndOriginalClassMethods($node); - - // order is correct, nothing to change - if (! $sortedAndOriginalClassMethods->hasOrderChanged()) { - return null; - } - - // different private method count, one of them is dead probably - $attempt = 0; - while (! $sortedAndOriginalClassMethods->hasOrderSame()) { - ++$attempt; - if ($attempt >= self::MAX_ATTEMPTS) { - break; - } - - $oldToNewKeys = $this->stmtOrder->createOldToNewKeys( - $sortedAndOriginalClassMethods->getSortedClassMethods(), - $sortedAndOriginalClassMethods->getOriginalClassMethods() - ); - - $this->stmtOrder->reorderClassStmtsByOldToNewKeys($node, $oldToNewKeys); - $sortedAndOriginalClassMethods = $this->getSortedAndOriginalClassMethods($node); - } - - return $node; - } - - private function getSortedAndOriginalClassMethods( - Class_ | Trait_ $classLike - ): SortedClassMethodsAndOriginalClassMethods { - return new SortedClassMethodsAndOriginalClassMethods( - $this->getLocalPrivateMethodCallOrder($classLike), - $this->resolvePrivateClassMethods($classLike) - ); - } - - /** - * @return array - */ - private function getLocalPrivateMethodCallOrder(ClassLike $classLike): array - { - $localPrivateMethodCallInOrder = []; - - $this->traverseNodesWithCallable($classLike->getMethods(), function (Node $node) use ( - &$localPrivateMethodCallInOrder, - $classLike - ) { - if (! $node instanceof MethodCall) { - return null; - } - - if (! $node->var instanceof Variable) { - return null; - } - - if (! $this->nodeNameResolver->isName($node->var, 'this')) { - return null; - } - - $methodName = $this->getName($node->name); - if ($methodName === null) { - return null; - } - - $classMethod = $classLike->getMethod($methodName); - if (! $classMethod instanceof ClassMethod) { - return null; - } - - if ($classMethod->isPrivate()) { - $localPrivateMethodCallInOrder[] = $methodName; - } - - return null; - }); - - return array_unique($localPrivateMethodCallInOrder); - } - - /** - * @return array - */ - private function resolvePrivateClassMethods(ClassLike $classLike): array - { - $privateClassMethods = []; - - foreach ($classLike->stmts as $key => $classStmt) { - if (! $classStmt instanceof ClassMethod) { - continue; - } - - if (! $classStmt->isPrivate()) { - continue; - } - - /** @var string $classMethodName */ - $classMethodName = $this->getName($classStmt); - $privateClassMethods[$key] = $classMethodName; - } - - return $privateClassMethods; - } -} diff --git a/rules/Order/StmtOrder.php b/rules/Order/StmtOrder.php deleted file mode 100644 index 284bc11c00f..00000000000 --- a/rules/Order/StmtOrder.php +++ /dev/null @@ -1,98 +0,0 @@ - $desiredStmtOrder - * @param array $currentStmtOrder - * @return array - */ - public function createOldToNewKeys(array $desiredStmtOrder, array $currentStmtOrder): array - { - $newKeys = []; - foreach ($desiredStmtOrder as $singleDesiredStmtOrder) { - foreach ($currentStmtOrder as $currentKey => $classMethodName) { - if ($classMethodName === $singleDesiredStmtOrder) { - $newKeys[] = $currentKey; - } - } - } - - $oldKeys = array_values($newKeys); - sort($oldKeys); - - /** @var array $oldToNewKeys */ - $oldToNewKeys = array_combine($oldKeys, $newKeys); - - return $oldToNewKeys; - } - - /** - * @param array $oldToNewKeys - */ - public function reorderClassStmtsByOldToNewKeys(ClassLike $classLike, array $oldToNewKeys): void - { - $reorderedStmts = []; - - $stmtCount = count($classLike->stmts); - - foreach ($classLike->stmts as $key => $stmt) { - if (! array_key_exists($key, $oldToNewKeys)) { - $reorderedStmts[$key] = $stmt; - continue; - } - - // reorder here - $newKey = $oldToNewKeys[$key]; - - $reorderedStmts[$key] = $classLike->stmts[$newKey]; - } - - for ($i = 0; $i < $stmtCount; ++$i) { - if (! array_key_exists($i, $reorderedStmts)) { - continue; - } - - $classLike->stmts[$i] = $reorderedStmts[$i]; - } - } - - /** - * @param class-string $type - * @return array - */ - public function getStmtsOfTypeOrder(ClassLike $classLike, string $type): array - { - $stmtsByPosition = []; - foreach ($classLike->stmts as $position => $classStmt) { - if (! is_a($classStmt, $type)) { - continue; - } - - $name = $this->nodeNameResolver->getName($classStmt); - if ($name === null) { - continue; - } - - $stmtsByPosition[$position] = $name; - } - - return $stmtsByPosition; - } -} diff --git a/rules/Order/StmtVisibilitySorter.php b/rules/Order/StmtVisibilitySorter.php deleted file mode 100644 index aa6024796ea..00000000000 --- a/rules/Order/StmtVisibilitySorter.php +++ /dev/null @@ -1,134 +0,0 @@ -stmts as $position => $propertyStmt) { - if (! $propertyStmt instanceof Property) { - continue; - } - - /** @var string $propertyName */ - $propertyName = $this->nodeNameResolver->getName($propertyStmt); - - $propertyRankeables[] = new PropertyRankeable( - $propertyName, - $this->getVisibilityLevelOrder($propertyStmt), - $propertyStmt, - $position - ); - } - - return $this->sortByRanksAndGetNames($propertyRankeables); - } - - /** - * @return string[] - */ - public function sortMethods(ClassLike $classLike): array - { - $classMethodsRankeables = []; - - foreach ($classLike->stmts as $position => $classStmt) { - if (! $classStmt instanceof ClassMethod) { - continue; - } - - /** @var string $classMethodName */ - $classMethodName = $this->nodeNameResolver->getName($classStmt); - - $classMethodsRankeables[] = new ClassMethodRankeable( - $classMethodName, - $this->getVisibilityLevelOrder($classStmt), - $position, - $classStmt - ); - } - - return $this->sortByRanksAndGetNames($classMethodsRankeables); - } - - /** - * @return string[] - */ - public function sortConstants(Class_ | Interface_ $classLike): array - { - $classConstsRankeables = []; - foreach ($classLike->stmts as $position => $constantStmt) { - if (! $constantStmt instanceof ClassConst) { - continue; - } - - /** @var string $constantName */ - $constantName = $this->nodeNameResolver->getName($constantStmt); - - $classConstsRankeables[] = new ClassConstRankeable( - $constantName, - $this->getVisibilityLevelOrder($constantStmt), - $position - ); - } - - return $this->sortByRanksAndGetNames($classConstsRankeables); - } - - private function getVisibilityLevelOrder(ClassMethod | Property | ClassConst $stmt): int - { - if ($stmt->isPrivate()) { - return 2; - } - - if ($stmt->isProtected()) { - return 1; - } - - return 0; - } - - /** - * @param RankeableInterface[] $rankeables - * @return string[] - */ - private function sortByRanksAndGetNames(array $rankeables): array - { - uasort( - $rankeables, - fn (RankeableInterface $firstRankeable, RankeableInterface $secondRankeable): int => $firstRankeable->getRanks() <=> $secondRankeable->getRanks() - ); - - $names = []; - foreach ($rankeables as $rankeable) { - $names[] = $rankeable->getName(); - } - - return $names; - } -} diff --git a/rules/Order/ValueObject/ClassConstRankeable.php b/rules/Order/ValueObject/ClassConstRankeable.php deleted file mode 100644 index c994c69f0c1..00000000000 --- a/rules/Order/ValueObject/ClassConstRankeable.php +++ /dev/null @@ -1,31 +0,0 @@ -name; - } - - /** - * An array to sort the element order by - * @return int[] - */ - public function getRanks(): array - { - return [$this->visibility, $this->position]; - } -} diff --git a/rules/Order/ValueObject/ClassMethodRankeable.php b/rules/Order/ValueObject/ClassMethodRankeable.php deleted file mode 100644 index 52859e5c54d..00000000000 --- a/rules/Order/ValueObject/ClassMethodRankeable.php +++ /dev/null @@ -1,40 +0,0 @@ -name; - } - - /** - * An array to sort the element order by - * @return bool[]|int[] - */ - public function getRanks(): array - { - return [ - $this->visibility, - $this->classMethod->isStatic(), - // negated on purpose, to put abstract later - ! $this->classMethod->isAbstract(), - $this->classMethod->isFinal(), - $this->position, - ]; - } -} diff --git a/rules/Order/ValueObject/PropertyRankeable.php b/rules/Order/ValueObject/PropertyRankeable.php deleted file mode 100644 index 8d07e469091..00000000000 --- a/rules/Order/ValueObject/PropertyRankeable.php +++ /dev/null @@ -1,32 +0,0 @@ -name; - } - - /** - * @return bool[]|int[] - */ - public function getRanks(): array - { - return [$this->visibility, $this->property->isStatic(), $this->position]; - } -} diff --git a/rules/Order/ValueObject/SortedClassMethodsAndOriginalClassMethods.php b/rules/Order/ValueObject/SortedClassMethodsAndOriginalClassMethods.php deleted file mode 100644 index e078dc01d9b..00000000000 --- a/rules/Order/ValueObject/SortedClassMethodsAndOriginalClassMethods.php +++ /dev/null @@ -1,47 +0,0 @@ - $sortedClassMethods - * @param array $originalClassMethods - */ - public function __construct( - private array $sortedClassMethods, - private array $originalClassMethods - ) { - } - - /** - * @return array - */ - public function getSortedClassMethods(): array - { - return $this->sortedClassMethods; - } - - /** - * @return array - */ - public function getOriginalClassMethods(): array - { - return $this->originalClassMethods; - } - - public function hasOrderChanged(): bool - { - return $this->sortedClassMethods !== $this->originalClassMethods; - } - - public function hasOrderSame(): bool - { - $sortedClassMethodValues = array_values($this->sortedClassMethods); - $originalClassMethodValues = array_values($this->originalClassMethods); - - return $sortedClassMethodValues === $originalClassMethodValues; - } -} diff --git a/rules/PSR4/Composer/PSR4AutoloadPathsProvider.php b/rules/PSR4/Composer/PSR4AutoloadPathsProvider.php deleted file mode 100644 index 6509f5b19c5..00000000000 --- a/rules/PSR4/Composer/PSR4AutoloadPathsProvider.php +++ /dev/null @@ -1,60 +0,0 @@ -> - */ - private array $cachedComposerJsonPSR4AutoloadPaths = []; - - public function __construct( - private JsonFileSystem $jsonFileSystem - ) { - } - - /** - * @return array - */ - public function provide(): array - { - if ($this->cachedComposerJsonPSR4AutoloadPaths !== []) { - return $this->cachedComposerJsonPSR4AutoloadPaths; - } - - $composerJson = $this->jsonFileSystem->loadFilePathToJson($this->getComposerJsonPath()); - $psr4Autoloads = array_merge( - $composerJson[ComposerJsonSection::AUTOLOAD]['psr-4'] ?? [], - $composerJson[ComposerJsonSection::AUTOLOAD_DEV]['psr-4'] ?? [] - ); - - $this->cachedComposerJsonPSR4AutoloadPaths = $this->removeEmptyNamespaces($psr4Autoloads); - - return $this->cachedComposerJsonPSR4AutoloadPaths; - } - - private function getComposerJsonPath(): string - { - // assume the project has "composer.json" in root directory - return getcwd() . '/composer.json'; - } - - /** - * @param array> $psr4Autoloads - * @return array> - */ - private function removeEmptyNamespaces(array $psr4Autoloads): array - { - return array_filter( - $psr4Autoloads, - fn (string $psr4Autoload): bool => $psr4Autoload !== '', - ARRAY_FILTER_USE_KEY - ); - } -} diff --git a/rules/PSR4/Composer/PSR4NamespaceMatcher.php b/rules/PSR4/Composer/PSR4NamespaceMatcher.php deleted file mode 100644 index 9893e72052c..00000000000 --- a/rules/PSR4/Composer/PSR4NamespaceMatcher.php +++ /dev/null @@ -1,61 +0,0 @@ -getSmartFileInfo(); - $psr4Autoloads = $this->psr4AutoloadPathsProvider->provide(); - - foreach ($psr4Autoloads as $namespace => $path) { - // remove extra slash - $paths = is_array($path) ? $path : [$path]; - - foreach ($paths as $path) { - $path = rtrim($path, '/'); - if (! \str_starts_with($smartFileInfo->getRelativeDirectoryPath(), $path)) { - continue; - } - - $expectedNamespace = $namespace . $this->resolveExtraNamespace($smartFileInfo, $path); - - if (str_contains($expectedNamespace, '-')) { - return null; - } - - return rtrim($expectedNamespace, '\\'); - } - } - - return null; - } - - /** - * Get the extra path that is not included in root PSR-4 namespace - */ - private function resolveExtraNamespace(SmartFileInfo $smartFileInfo, string $path): string - { - $extraNamespace = Strings::substring($smartFileInfo->getRelativeDirectoryPath(), Strings::length($path) + 1); - $extraNamespace = Strings::replace($extraNamespace, '#/#', '\\'); - - return trim($extraNamespace); - } -} diff --git a/rules/PSR4/Contract/PSR4AutoloadNamespaceMatcherInterface.php b/rules/PSR4/Contract/PSR4AutoloadNamespaceMatcherInterface.php deleted file mode 100644 index d1d78496342..00000000000 --- a/rules/PSR4/Contract/PSR4AutoloadNamespaceMatcherInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -nodeNameResolver->getName($classLike); - if ($className === null) { - return false; - } - - $smartFileInfo = $file->getSmartFileInfo(); - - $baseFileName = $this->clearNameFromTestingPrefix($smartFileInfo->getBasenameWithoutSuffix()); - $classShortName = $this->classNaming->getShortName($className); - - return $baseFileName === $classShortName; - } - - public function clearNameFromTestingPrefix(string $name): string - { - return Strings::replace($name, self::TESTING_PREFIX_REGEX, ''); - } -} diff --git a/rules/PSR4/FileRelocationResolver.php b/rules/PSR4/FileRelocationResolver.php deleted file mode 100644 index 64081a73bf6..00000000000 --- a/rules/PSR4/FileRelocationResolver.php +++ /dev/null @@ -1,187 +0,0 @@ -resolveRootDirectory($smartFileInfo, $suffixName, $groupNames); - $filename = $this->fileInfoDeletionAnalyzer->clearNameFromTestingPrefix($smartFileInfo->getFilename()); - return $newDirectory . DIRECTORY_SEPARATOR . $filename; - } - - /** - * @param string[] $groupNames - */ - public function resolveNewNamespaceName(Namespace_ $namespace, string $suffixName, array $groupNames): string - { - /** @var Name $name */ - $name = $namespace->name; - $currentNamespaceParts = $name->parts; - - return $this->resolveNearestRootWithCategory( - $currentNamespaceParts, - $suffixName, - self::NAMESPACE_SEPARATOR, - $groupNames - ); - } - - public function resolveNewFileLocationFromOldClassToNewClass( - SmartFileInfo $smartFileInfo, - string $oldClass, - string $newClass - ): string { - $beforeToAfterPart = $this->resolveBeforeToAfterPartBetweenClassNames($oldClass, $newClass); - - return $this->replaceRelativeFilePathsWithBeforeAfter($smartFileInfo, $beforeToAfterPart); - } - - /** - * @param string[] $groupNames - */ - private function resolveRootDirectory(SmartFileInfo $smartFileInfo, string $suffixName, array $groupNames): string - { - if (\str_starts_with($smartFileInfo->getRealPathDirectory(), '/tmp')) { - $currentTraversePath = $smartFileInfo->getRealPathDirectory(); - } else { - $currentTraversePath = $smartFileInfo->getRelativeDirectoryPath(); - } - - $currentDirectoryParts = explode(DIRECTORY_SEPARATOR, $currentTraversePath); - - return $this->resolveNearestRootWithCategory( - $currentDirectoryParts, - $suffixName, - DIRECTORY_SEPARATOR, - $groupNames - ); - } - - /** - * @param string[] $groupNames - * @param string[] $nameParts - */ - private function resolveNearestRootWithCategory( - array $nameParts, - string $suffixName, - string $separator, - array $groupNames - ): string { - $reversedNameParts = array_reverse($nameParts); - - $removedParts = []; - $hasStopped = false; - - foreach ($reversedNameParts as $key => $reversedNamePart) { - unset($reversedNameParts[$key]); - - if (in_array($reversedNamePart, $groupNames, true)) { - $hasStopped = true; - break; - } - - $removedParts[] = $reversedNamePart; - } - - if (! $hasStopped) { - $rootNameParts = $nameParts; - $rootNameParts[] = $suffixName; - } else { - $rootNameParts = array_reverse($reversedNameParts); - $rootNameParts[] = $suffixName; - - if ($removedParts !== []) { - $rootNameParts = array_merge($rootNameParts, $removedParts); - } - } - - return implode($separator, $rootNameParts); - } - - /** - * @return string[] - */ - private function resolveBeforeToAfterPartBetweenClassNames(string $oldClass, string $newClass): array - { - $oldClassNameParts = explode(self::NAMESPACE_SEPARATOR, $oldClass); - $newClassNameParts = explode(self::NAMESPACE_SEPARATOR, $newClass); - - $beforeToAfterParts = []; - foreach ($oldClassNameParts as $key => $oldClassNamePart) { - if (! isset($newClassNameParts[$key])) { - continue; - } - - $newClassNamePart = $newClassNameParts[$key]; - if ($oldClassNamePart === $newClassNamePart) { - continue; - } - - $beforeToAfterParts[$oldClassNamePart] = $newClassNamePart; - } - - return $beforeToAfterParts; - } - - /** - * @param string[] $beforeToAfterPart - */ - private function replaceRelativeFilePathsWithBeforeAfter( - SmartFileInfo $oldSmartFileInfo, - array $beforeToAfterPart - ): string { - // A. first "dir has changed" dummy detection - $relativeFilePathParts = Strings::split( - $this->normalizeDirectorySeparator($oldSmartFileInfo->getRelativeFilePath()), - // the windows dir separator would be interpreted as a regex-escape char, therefore quote it. - '#' . preg_quote(DIRECTORY_SEPARATOR, '#') . '#' - ); - - foreach ($relativeFilePathParts as $key => $relativeFilePathPart) { - if (! isset($beforeToAfterPart[$relativeFilePathPart])) { - continue; - } - - $relativeFilePathParts[$key] = $beforeToAfterPart[$relativeFilePathPart]; - - // clear from further use - unset($beforeToAfterPart[$relativeFilePathPart]); - } - - return implode(DIRECTORY_SEPARATOR, $relativeFilePathParts); - } - - private function normalizeDirectorySeparator(string $path): string - { - return str_replace('/', DIRECTORY_SEPARATOR, $path); - } -} diff --git a/rules/PSR4/NodeManipulator/FullyQualifyStmtsAnalyzer.php b/rules/PSR4/NodeManipulator/FullyQualifyStmtsAnalyzer.php deleted file mode 100644 index 50568080346..00000000000 --- a/rules/PSR4/NodeManipulator/FullyQualifyStmtsAnalyzer.php +++ /dev/null @@ -1,74 +0,0 @@ -parameterProvider->provideBoolParameter(Option::AUTO_IMPORT_NAMES)) { - return; - } - - // FQNize all class names - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node): ?FullyQualified { - if (! $node instanceof Name) { - return null; - } - - $fullyQualifiedName = $this->nodeNameResolver->getName($node); - if (in_array($fullyQualifiedName, ['self', 'parent', 'static'], true)) { - return null; - } - - if ($this->isNativeConstant($node)) { - return null; - } - - return new FullyQualified($fullyQualifiedName); - }); - } - - private function isNativeConstant(Name $name): bool - { - $parent = $name->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof ConstFetch) { - return false; - } - - $scope = $name->getAttribute(AttributeKey::SCOPE); - if (! $this->reflectionProvider->hasConstant($name, $scope)) { - return false; - } - - $constantReflection = $this->reflectionProvider->getConstant($name, $scope); - return $constantReflection instanceof RuntimeConstantReflection; - } -} diff --git a/rules/PSR4/NodeManipulator/NamespaceManipulator.php b/rules/PSR4/NodeManipulator/NamespaceManipulator.php deleted file mode 100644 index 364f224bbfb..00000000000 --- a/rules/PSR4/NodeManipulator/NamespaceManipulator.php +++ /dev/null @@ -1,22 +0,0 @@ -stmts as $key => $namespaceStatement) { - if (! $namespaceStatement instanceof ClassLike) { - continue; - } - - unset($namespace->stmts[$key]); - } - } -} diff --git a/rules/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector.php b/rules/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector.php deleted file mode 100644 index d8c17113506..00000000000 --- a/rules/PSR4/Rector/FileWithoutNamespace/NormalizeNamespaceByPSR4ComposerAutoloadRector.php +++ /dev/null @@ -1,131 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Namespace_::class, FileWithoutNamespace::class]; - } - - /** - * @param FileWithoutNamespace|Namespace_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->inlineHTMLAnalyzer->hasInlineHTML($node)) { - return null; - } - - $expectedNamespace = $this->psr4AutoloadNamespaceMatcher->getExpectedNamespace($this->file, $node); - if ($expectedNamespace === null) { - return null; - } - - // is namespace and already correctly named? - if ($node instanceof Namespace_ && $this->nodeNameResolver->isCaseSensitiveName($node, $expectedNamespace)) { - return null; - } - - // to put declare_strict types on correct place - if ($node instanceof FileWithoutNamespace) { - return $this->refactorFileWithoutNamespace($node, $expectedNamespace); - } - - $node->name = new Name($expectedNamespace); - $this->fullyQualifyStmtsAnalyzer->process($node->stmts); - - return $node; - } - - private function refactorFileWithoutNamespace( - FileWithoutNamespace $fileWithoutNamespace, - string $expectedNamespace - ): Namespace_ { - $nodes = $fileWithoutNamespace->stmts; - - $nodesWithStrictTypesThenNamespace = []; - foreach ($nodes as $key => $fileWithoutNamespace) { - if ($fileWithoutNamespace instanceof Declare_) { - $nodesWithStrictTypesThenNamespace[] = $fileWithoutNamespace; - unset($nodes[$key]); - } - } - - $namespace = new Namespace_(new Name($expectedNamespace), $nodes); - $nodesWithStrictTypesThenNamespace[] = $namespace; - - $this->fullyQualifyStmtsAnalyzer->process($nodes); - - return $namespace; - } -} diff --git a/rules/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector.php b/rules/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector.php deleted file mode 100644 index 0366ac2733a..00000000000 --- a/rules/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector.php +++ /dev/null @@ -1,193 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Namespace_::class, FileWithoutNamespace::class]; - } - - /** - * @param Namespace_|FileWithoutNamespace $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->hasAtLeastTwoClassLikes($node)) { - return null; - } - - $nodeToReturn = null; - if ($node instanceof Namespace_) { - $nodeToReturn = $this->refactorNamespace($node); - } - - if ($node instanceof FileWithoutNamespace) { - $nodeToReturn = $this->refactorFileWithoutNamespace($node); - } - - // 1. remove this node - if ($nodeToReturn !== null) { - return $nodeToReturn; - } - - $smartFileInfo = $this->file->getSmartFileInfo(); - - // 2. nothing to return - remove the file - $this->removedAndAddedFilesCollector->removeFile($smartFileInfo); - - return null; - } - - private function hasAtLeastTwoClassLikes(Node $node): bool - { - $classes = $this->betterNodeFinder->findClassLikes($node); - return count($classes) > 1; - } - - private function refactorNamespace(Namespace_ $namespace): ?Namespace_ - { - /** @var ClassLike[] $classLikes */ - $classLikes = $this->betterNodeFinder->findClassLikes($namespace->stmts); - - $this->namespaceManipulator->removeClassLikes($namespace); - - $nodeToReturn = null; - foreach ($classLikes as $classLike) { - $newNamespace = clone $namespace; - $newNamespace->stmts[] = $classLike; - - // 1. is the class that will be kept in original file? - if ($this->fileInfoDeletionAnalyzer->isClassLikeAndFileInfoMatch($this->file, $classLike)) { - $nodeToReturn = $newNamespace; - continue; - } - - // 2. new file - $this->printNewNodes($classLike, $newNamespace); - } - - return $nodeToReturn; - } - - private function refactorFileWithoutNamespace(FileWithoutNamespace $fileWithoutNamespace): ?FileWithoutNamespace - { - /** @var ClassLike[] $classLikes */ - $classLikes = $this->betterNodeFinder->findClassLikes($fileWithoutNamespace->stmts); - - $nodeToReturn = null; - - foreach ($classLikes as $classLike) { - // 1. is the class that will be kept in original file? - if ($this->fileInfoDeletionAnalyzer->isClassLikeAndFileInfoMatch($this->file, $classLike)) { - $nodeToReturn = $fileWithoutNamespace; - continue; - } - - // 2. is new file - $this->printNewNodes($classLike, $fileWithoutNamespace); - } - - return $nodeToReturn; - } - - private function printNewNodes(ClassLike $classLike, Namespace_ | FileWithoutNamespace $mainNode): void - { - $smartFileInfo = $this->file->getSmartFileInfo(); - - $declares = []; - $declare = $this->betterNodeFinder->findFirstPreviousOfTypes($mainNode, [Declare_::class]); - if ($declare instanceof Declare_) { - $declares = [$declare]; - } - - if ($mainNode instanceof FileWithoutNamespace) { - $nodesToPrint = array_merge($declares, [$classLike]); - } else { - $nodesToPrint = array_merge($declares, [$mainNode]); - } - - $fileDestination = $this->createClassLikeFileDestination($classLike, $smartFileInfo); - - $addedFileWithNodes = new AddedFileWithNodes($fileDestination, $nodesToPrint); - $this->removedAndAddedFilesCollector->addAddedFile($addedFileWithNodes); - } - - private function createClassLikeFileDestination(ClassLike $classLike, SmartFileInfo $smartFileInfo): string - { - $currentDirectory = dirname($smartFileInfo->getRealPath()); - return $currentDirectory . DIRECTORY_SEPARATOR . $classLike->name . '.php'; - } -} diff --git a/rules/Php52/Rector/Property/VarToPublicPropertyRector.php b/rules/Php52/Rector/Property/VarToPublicPropertyRector.php index fb44f2a4e6d..be58c2a19cd 100644 --- a/rules/Php52/Rector/Property/VarToPublicPropertyRector.php +++ b/rules/Php52/Rector/Property/VarToPublicPropertyRector.php @@ -6,15 +6,28 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Property; -use Rector\Core\Rector\AbstractRector; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php52\Rector\Property\VarToPublicPropertyRector\VarToPublicPropertyRectorTest */ -final class VarToPublicPropertyRector extends AbstractRector +final class VarToPublicPropertyRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly VisibilityManipulator $visibilityManipulator, + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::PROPERTY_MODIFIER; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Change property modifier from `var` to `public`', [ diff --git a/rules/Php52/Rector/Switch_/ContinueToBreakInSwitchRector.php b/rules/Php52/Rector/Switch_/ContinueToBreakInSwitchRector.php index c12109cc6bb..97a831172c3 100644 --- a/rules/Php52/Rector/Switch_/ContinueToBreakInSwitchRector.php +++ b/rules/Php52/Rector/Switch_/ContinueToBreakInSwitchRector.php @@ -5,26 +5,49 @@ namespace Rector\Php52\Rector\Switch_; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Break_; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Continue_; +use PhpParser\Node\Stmt\Do_; +use PhpParser\Node\Stmt\For_; +use PhpParser\Node\Stmt\Foreach_; +use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Switch_; +use PhpParser\Node\Stmt\While_; +use PhpParser\NodeVisitor; use PHPStan\Type\Constant\ConstantIntegerType; -use PHPStan\Type\ConstantType; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/a/12349889/1348344 * @see \Rector\Tests\Php52\Rector\Switch_\ContinueToBreakInSwitchRector\ContinueToBreakInSwitchRectorTest */ -final class ContinueToBreakInSwitchRector extends AbstractRector +final class ContinueToBreakInSwitchRector extends AbstractRector implements MinPhpVersionInterface { + private bool $hasChanged = false; + + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::CONTINUE_TO_BREAK; + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Use break instead of continue in switch statements', [ + return new RuleDefinition('Use `break` instead of `continue` in switch statements', [ new CodeSample( <<<'CODE_SAMPLE' function some_run($value) @@ -68,51 +91,81 @@ public function getNodeTypes(): array /** * @param Switch_ $node */ - public function refactor(Node $node): Switch_ + public function refactor(Node $node): ?Switch_ { + $this->hasChanged = false; + foreach ($node->cases as $case) { - foreach ($case->stmts as $key => $caseStmt) { - if (! $caseStmt instanceof Continue_) { - continue; - } + $this->processContinueStatement($case); + } - $case->stmts[$key] = $this->processContinueStatement($caseStmt); - } + if (! $this->hasChanged) { + return null; } return $node; } - private function processContinueStatement(Continue_ $continue): Break_ | Continue_ + /** + * @param Stmt|StmtsAware $stmt + */ + private function processContinueStatement(Stmt|Node $stmt): void { - if ($continue->num === null) { - return new Break_(); - } + $this->traverseNodesWithCallable( + $stmt, + function (Node $subNode): null|int|Break_ { + if ($subNode instanceof Class_ || $subNode instanceof Function_ || $subNode instanceof Closure) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } - if ($continue->num instanceof LNumber) { - $continueNumber = $this->valueResolver->getValue($continue->num); - if ($continueNumber <= 1) { - return new Break_(); - } - } elseif ($continue->num instanceof Variable) { - return $this->processVariableNum($continue, $continue->num); - } + // continue is belong to loop + if ($subNode instanceof Foreach_ || $subNode instanceof While_ || $subNode instanceof Do_ || $subNode instanceof For_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $subNode instanceof Continue_) { + return null; + } + + if (! $subNode->num instanceof Expr) { + $this->hasChanged = true; + return new Break_(); + } - return $continue; + if ($subNode->num instanceof Int_) { + $continueNumber = $this->valueResolver->getValue($subNode->num); + if ($continueNumber <= 1) { + $this->hasChanged = true; + return new Break_(); + } + } elseif ($subNode->num instanceof Variable) { + $processVariableNum = $this->processVariableNum($subNode, $subNode->num); + if ($processVariableNum instanceof Break_) { + $this->hasChanged = true; + return $processVariableNum; + } + } + + return null; + } + ); } private function processVariableNum(Continue_ $continue, Variable $numVariable): Continue_ | Break_ { - $staticType = $this->getStaticType($numVariable); - if (! $staticType instanceof ConstantType) { + $staticType = $this->getType($numVariable); + if (! $staticType->isConstantValue()->yes()) { return $continue; } + if (! $staticType instanceof ConstantIntegerType) { return $continue; } + if ($staticType->getValue() > 1) { return $continue; } + return new Break_(); } } diff --git a/rules/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector.php b/rules/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector.php deleted file mode 100644 index 8e204f57f3a..00000000000 --- a/rules/Php53/Rector/AssignRef/ClearReturnNewByReferenceRector.php +++ /dev/null @@ -1,58 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [AssignRef::class]; - } - - /** - * @param AssignRef $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->expr instanceof New_) { - return null; - } - - return new Assign($node->var, $node->expr); - } -} diff --git a/rules/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector.php b/rules/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector.php index 67e2b1d38b6..858f9a78166 100644 --- a/rules/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector.php +++ b/rules/Php53/Rector/FuncCall/DirNameFileConstantToDirConstantRector.php @@ -8,8 +8,8 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Scalar\MagicConst\Dir; use PhpParser\Node\Scalar\MagicConst\File; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,7 +21,7 @@ final class DirNameFileConstantToDirConstantRector extends AbstractRector implem { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Convert dirname(__FILE__) to __DIR__', [ + return new RuleDefinition('Convert `dirname(__FILE__)` to `__DIR__`', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -63,12 +63,21 @@ public function refactor(Node $node): ?Node return null; } + if ($node->isFirstClassCallable()) { + return null; + } + if (count($node->args) !== 1) { return null; } - $firstArgValue = $node->args[0]->value; - if (! $firstArgValue instanceof File) { + if (! isset($node->getArgs()[0])) { + return null; + } + + $firstArg = $node->getArgs()[0]; + + if (! $firstArg->value instanceof File) { return null; } diff --git a/rules/Php53/Rector/Ternary/TernaryToElvisRector.php b/rules/Php53/Rector/Ternary/TernaryToElvisRector.php index d5e74c610a2..83b5ead1a5b 100644 --- a/rules/Php53/Rector/Ternary/TernaryToElvisRector.php +++ b/rules/Php53/Rector/Ternary/TernaryToElvisRector.php @@ -5,24 +5,23 @@ namespace Rector\Php53\Rector\Ternary; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Ternary; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog http://php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary https://stackoverflow.com/a/1993455/1348344 - * * @see \Rector\Tests\Php53\Rector\Ternary\TernaryToElvisRector\TernaryToElvisRectorTest */ final class TernaryToElvisRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Use ?: instead of ?, where useful', [ + return new RuleDefinition('Use `?:` instead of `?`, where useful', [ new CodeSample( <<<'CODE_SAMPLE' function elvis() @@ -59,8 +58,15 @@ public function refactor(Node $node): ?Node } $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); - $node->if = null; + /** @var Expr $nodeIf */ + $nodeIf = $node->if; + + if ($node->else instanceof Ternary && $this->isParenthesized($nodeIf, $node->else)) { + $node->else->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + } + + $node->if = null; return $node; } @@ -68,4 +74,31 @@ public function provideMinPhpVersion(): int { return PhpVersionFeature::ELVIS_OPERATOR; } + + private function isParenthesized(Expr $ifExpr, Expr $elseExpr): bool + { + $tokens = $this->getFile() + ->getOldTokens(); + + $ifExprTokenEnd = $ifExpr->getEndTokenPos(); + $elseExprTokenStart = $elseExpr->getStartTokenPos(); + + if ($ifExprTokenEnd < 0 || $elseExprTokenStart < 0 || $elseExprTokenStart <= $ifExprTokenEnd) { + return false; + } + + while (isset($tokens[$ifExprTokenEnd])) { + ++$ifExprTokenEnd; + + if ($elseExprTokenStart === $ifExprTokenEnd) { + break; + } + + if ((string) $tokens[$ifExprTokenEnd] === '(') { + return true; + } + } + + return false; + } } diff --git a/rules/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector.php b/rules/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector.php index 90a4c818e62..5e3eee9798a 100644 --- a/rules/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector.php +++ b/rules/Php53/Rector/Variable/ReplaceHttpServerVarsByServerRector.php @@ -6,20 +6,21 @@ use PhpParser\Node; use PhpParser\Node\Expr\Variable; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php53\Rector\Variable\ReplaceHttpServerVarsByServerRector\ReplaceHttpServerVarsByServerRectorTest - * @changelog https://blog.tigertech.net/posts/php-5-3-http-server-vars/ */ -final class ReplaceHttpServerVarsByServerRector extends AbstractRector +final class ReplaceHttpServerVarsByServerRector extends AbstractRector implements MinPhpVersionInterface { /** * @var array */ - private const VARIABLE_RENAME_MAP = [ + private const array VARIABLE_RENAME_MAP = [ 'HTTP_SERVER_VARS' => '_SERVER', 'HTTP_GET_VARS' => '_GET', 'HTTP_POST_VARS' => '_POST', @@ -29,6 +30,11 @@ final class ReplaceHttpServerVarsByServerRector extends AbstractRector 'HTTP_COOKIE_VARS' => '_COOKIE', ]; + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SERVER_VAR; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( diff --git a/rules/Php54/Rector/Array_/LongArrayToShortArrayRector.php b/rules/Php54/Rector/Array_/LongArrayToShortArrayRector.php new file mode 100644 index 00000000000..a0409e3af0a --- /dev/null +++ b/rules/Php54/Rector/Array_/LongArrayToShortArrayRector.php @@ -0,0 +1,115 @@ +> + */ + public function getNodeTypes(): array + { + return [Array_::class]; + } + + /** + * @param Array_ $node + */ + public function refactor(Node $node): ?Node + { + // no kind attribute yet, it means just created + // no need to reprint, it already will be short array by default + if (! $node->hasAttribute(AttributeKey::KIND)) { + return null; + } + + if ($node->getAttribute(AttributeKey::KIND) === Array_::KIND_SHORT) { + return null; + } + + $node->setAttribute(AttributeKey::KIND, Array_::KIND_SHORT); + + $tokens = $this->getFile() + ->getOldTokens(); + + $startTokenPos = $node->getStartTokenPos(); + $endTokenPos = $node->getEndTokenPos(); + + if (! isset($tokens[$startTokenPos], $tokens[$endTokenPos])) { + return null; + } + + // replace array opening + $tokens[$startTokenPos]->text = ''; + + $iteration = 1; + while (isset($tokens[$startTokenPos + $iteration])) { + if (trim($tokens[$startTokenPos + $iteration]->text) === '') { + ++$iteration; + continue; + } + + if (trim($tokens[$startTokenPos + $iteration]->text) !== '(') { + break; + } + + // replace ( parentheses opening + $tokens[$startTokenPos + $iteration]->text = '['; + // replace ) parentheses closing + $tokens[$endTokenPos]->text = ']'; + + break; + } + + return $node; + } +} diff --git a/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php b/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php index ae720304bd1..4b44538da1a 100644 --- a/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php +++ b/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php @@ -5,23 +5,34 @@ namespace Rector\Php54\Rector\Break_; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Stmt\Break_; use PhpParser\Node\Stmt\Continue_; use PHPStan\Type\Constant\ConstantIntegerType; -use PHPStan\Type\ConstantType; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://www.php.net/manual/en/control-structures.continue.php https://www.php.net/manual/en/control-structures.break.php - * * @see \Rector\Tests\Php54\Rector\Break_\RemoveZeroBreakContinueRector\RemoveZeroBreakContinueRectorTest */ -final class RemoveZeroBreakContinueRector extends AbstractRector +final class RemoveZeroBreakContinueRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_ZERO_BREAK; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Remove 0 from break and continue', [ @@ -74,11 +85,11 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($node->num === null) { + if (! $node->num instanceof Expr) { return null; } - if ($node->num instanceof LNumber) { + if ($node->num instanceof Int_) { $number = $this->valueResolver->getValue($node->num); if ($number > 1) { return null; @@ -101,9 +112,9 @@ public function refactor(Node $node): ?Node private function processVariableNum(Break_ | Continue_ $stmt, Variable $numVariable): ?Node { - $staticType = $this->getStaticType($numVariable); + $staticType = $this->getType($numVariable); - if ($staticType instanceof ConstantType) { + if ($staticType->isConstantValue()->yes()) { if ($staticType instanceof ConstantIntegerType) { if ($staticType->getValue() === 0) { $stmt->num = null; @@ -111,7 +122,7 @@ private function processVariableNum(Break_ | Continue_ $stmt, Variable $numVaria } if ($staticType->getValue() > 0) { - $stmt->num = new LNumber($staticType->getValue()); + $stmt->num = new Int_($staticType->getValue()); return $stmt; } } diff --git a/rules/Php54/Rector/FuncCall/RemoveReferenceFromCallRector.php b/rules/Php54/Rector/FuncCall/RemoveReferenceFromCallRector.php index 148b9127bf2..4db7fe19c4b 100644 --- a/rules/Php54/Rector/FuncCall/RemoveReferenceFromCallRector.php +++ b/rules/Php54/Rector/FuncCall/RemoveReferenceFromCallRector.php @@ -5,19 +5,29 @@ namespace Rector\Php54\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\StaticCall; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php54\Rector\FuncCall\RemoveReferenceFromCallRector\RemoveReferenceFromCallRectorTest */ -final class RemoveReferenceFromCallRector extends AbstractRector +final class RemoveReferenceFromCallRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_REFERENCE_IN_ARG; + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Remove & from function and method calls', [ + return new RuleDefinition('Remove `&` from function and method calls', [ new CodeSample( <<<'CODE_SAMPLE' final class SomeClass @@ -47,20 +57,33 @@ public function run($one) */ public function getNodeTypes(): array { - return [FuncCall::class]; + return [FuncCall::class, MethodCall::class, StaticCall::class]; } /** - * @param FuncCall $node + * @param FuncCall|MethodCall|StaticCall $node */ - public function refactor(Node $node): FuncCall + public function refactor(Node $node): FuncCall|MethodCall|StaticCall|null { + $hasChanged = false; + foreach ($node->args as $nodeArg) { - if ($nodeArg->byRef) { - $nodeArg->byRef = false; + if (! $nodeArg instanceof Arg) { + continue; } + + if (! $nodeArg->byRef) { + continue; + } + + $nodeArg->byRef = false; + $hasChanged = true; + } + + if ($hasChanged) { + return $node; } - return $node; + return null; } } diff --git a/rules/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector.php b/rules/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector.php new file mode 100644 index 00000000000..5717a1de307 --- /dev/null +++ b/rules/Php55/Rector/ClassConstFetch/StaticToSelfOnFinalClassRector.php @@ -0,0 +1,96 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + if (! $node->isFinal()) { + return null; + } + + $hasChanged = false; + + $this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged): ?ClassConstFetch { + if (! $node instanceof ClassConstFetch) { + return null; + } + + if (! $this->isName($node->class, ObjectReference::STATIC)) { + return null; + } + + if (! $this->isName($node->name, 'class')) { + return null; + } + + $hasChanged = true; + return $this->nodeFactory->createSelfFetchConstant('class'); + }); + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::CLASSNAME_CONSTANT; + } +} diff --git a/rules/Php55/Rector/Class_/ClassConstantToSelfClassRector.php b/rules/Php55/Rector/Class_/ClassConstantToSelfClassRector.php index b5c103a74cc..17338274f78 100644 --- a/rules/Php55/Rector/Class_/ClassConstantToSelfClassRector.php +++ b/rules/Php55/Rector/Class_/ClassConstantToSelfClassRector.php @@ -7,22 +7,20 @@ use PhpParser\Node; use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Scalar\MagicConst\Class_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/deprecations_php_7_4 (not confirmed yet) - * @see https://3v4l.org/INd7o * @see \Rector\Tests\Php55\Rector\Class_\ClassConstantToSelfClassRector\ClassConstantToSelfClassRectorTest */ final class ClassConstantToSelfClassRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change `__CLASS__` to self::class', [ + return new RuleDefinition('Change `__CLASS__` to `self::class`', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -60,7 +58,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ClassConstFetch { - return $this->nodeFactory->createSelfFetchConstant('class', $node); + return $this->nodeFactory->createSelfFetchConstant('class'); } public function provideMinPhpVersion(): int diff --git a/rules/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector.php b/rules/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector.php new file mode 100644 index 00000000000..df4db919dab --- /dev/null +++ b/rules/Php55/Rector/FuncCall/GetCalledClassToSelfClassRector.php @@ -0,0 +1,97 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node, 'get_called_class')) { + return null; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + if (! $scope->isInClass()) { + return null; + } + + if ($this->classModifierChecker->isInsideFinalClass($node)) { + return $this->nodeFactory->createClassConstFetch(ObjectReference::SELF, 'class'); + } + + if ($scope->isInAnonymousFunction()) { + return $this->nodeFactory->createClassConstFetch(ObjectReference::SELF, 'class'); + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::CLASSNAME_CONSTANT; + } +} diff --git a/rules/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector.php b/rules/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector.php new file mode 100644 index 00000000000..e8c7b68a7cc --- /dev/null +++ b/rules/Php55/Rector/FuncCall/GetCalledClassToStaticClassRector.php @@ -0,0 +1,87 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node, 'get_called_class')) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + if (! $scope->isInClass()) { + return null; + } + + $classReflection = $scope->getClassReflection(); + if ($classReflection->isAnonymous()) { + return null; + } + + if (! $classReflection->isFinal()) { + return $this->nodeFactory->createClassConstFetch(ObjectReference::STATIC, 'class'); + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::CLASSNAME_CONSTANT; + } +} diff --git a/rules/Php55/Rector/FuncCall/PregReplaceEModifierRector.php b/rules/Php55/Rector/FuncCall/PregReplaceEModifierRector.php index 23fbf6d1db0..fbe3fa28e8c 100644 --- a/rules/Php55/Rector/FuncCall/PregReplaceEModifierRector.php +++ b/rules/Php55/Rector/FuncCall/PregReplaceEModifierRector.php @@ -5,29 +5,35 @@ namespace Rector\Php55\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\Rector\AbstractRector; use Rector\Php55\RegexMatcher; use Rector\Php72\NodeFactory\AnonymousFunctionFactory; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/remove_preg_replace_eval_modifier https://stackoverflow.com/q/19245205/1348344 - * * @see \Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector\PregReplaceEModifierRectorTest */ -final class PregReplaceEModifierRector extends AbstractRector +final class PregReplaceEModifierRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private AnonymousFunctionFactory $anonymousFunctionFactory, - private RegexMatcher $regexMatcher + private readonly AnonymousFunctionFactory $anonymousFunctionFactory, + private readonly RegexMatcher $regexMatcher, ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::PREG_REPLACE_CALLBACK_E_MODIFIER; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -77,24 +83,38 @@ public function refactor(Node $node): ?Node return null; } - $firstArgumentValue = $node->args[0]->value; - $patternWithoutE = $this->regexMatcher->resolvePatternExpressionWithoutEIfFound($firstArgumentValue); - if (! $patternWithoutE instanceof Expr) { + if ($node->isFirstClassCallable()) { return null; } - $secondArgumentValue = $node->args[1]->value; - $anonymousFunction = $this->anonymousFunctionFactory->createAnonymousFunctionFromString( - $secondArgumentValue - ); + if (count($node->getArgs()) < 2) { + return null; + } + + $firstArgument = $node->getArgs()[0]; + $firstArgumentValue = $firstArgument->value; + + $patternWithoutEExpr = $this->regexMatcher->resolvePatternExpressionWithoutEIfFound($firstArgumentValue); + if (! $patternWithoutEExpr instanceof Expr) { + return null; + } + + $secondArgument = $node->getArgs()[1]; + + $anonymousFunction = $this->createAnonymousFunction($secondArgument); if (! $anonymousFunction instanceof Closure) { return null; } $node->name = new Name('preg_replace_callback'); - $node->args[0]->value = $patternWithoutE; - $node->args[1]->value = $anonymousFunction; + $firstArgument->value = $patternWithoutEExpr; + $secondArgument->value = $anonymousFunction; return $node; } + + private function createAnonymousFunction(Arg $arg): ?Closure + { + return $this->anonymousFunctionFactory->createAnonymousFunctionFromExpr($arg->value); + } } diff --git a/rules/Php55/Rector/String_/StringClassNameToClassConstantRector.php b/rules/Php55/Rector/String_/StringClassNameToClassConstantRector.php index 738379f18b0..267e07d6dcf 100644 --- a/rules/Php55/Rector/String_/StringClassNameToClassConstantRector.php +++ b/rules/Php55/Rector/String_/StringClassNameToClassConstantRector.php @@ -5,52 +5,42 @@ namespace Rector\Php55\Rector\String_; use PhpParser\Node; -use PhpParser\Node\Arg; use PhpParser\Node\Expr\ClassConstFetch; -use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Scalar\String_; -use PhpParser\Node\Stmt\ClassConst; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Util\StaticRectorStrings; -use Rector\Core\ValueObject\PhpVersionFeature; +use PHPStan\Reflection\ReflectionProvider; +use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; -use Symplify\PackageBuilder\Reflection\ClassLikeExistenceChecker; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** - * @changelog https://wiki.php.net/rfc/class_name_scalars https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.8.md#form - * * @see \Rector\Tests\Php55\Rector\String_\StringClassNameToClassConstantRector\StringClassNameToClassConstantRectorTest */ -final class StringClassNameToClassConstantRector extends AbstractRector implements ConfigurableRectorInterface, MinPhpVersionInterface +final class StringClassNameToClassConstantRector extends AbstractRector implements MinPhpVersionInterface, ConfigurableRectorInterface { /** - * @api - * @var string + * @deprecated since 2.2.12. Default behavior now. */ - public const CLASSES_TO_SKIP = 'classes_to_skip'; + public const string SHOULD_KEEP_PRE_SLASH = 'should_keep_pre_slash'; /** * @var string[] */ - private array $classesToSkip = [ - // can be string - 'Error', - 'Exception', - ]; + private array $classesToSkip = []; public function __construct( - private ClassLikeExistenceChecker $classLikeExistenceChecker + private readonly ReflectionProvider $reflectionProvider, ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Replace string class names by ::class constant', [ + return new RuleDefinition('Replace string class names by `::class` constant', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' class AnotherClass @@ -79,10 +69,8 @@ public function run() } } CODE_SAMPLE -, - [ - self::CLASSES_TO_SKIP => ['ClassName', 'AnotherClassName'], - ] + , + ['ClassName', 'AnotherClassName'], ), ]); } @@ -98,8 +86,12 @@ public function getNodeTypes(): array /** * @param String_ $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ClassConstFetch|null { + if ($this->shouldSkipIsA($node)) { + return null; + } + $classLikeName = $node->value; // remove leading slash @@ -108,7 +100,7 @@ public function refactor(Node $node): ?Node return null; } - if ($this->shouldSkip($classLikeName, $node)) { + if ($this->shouldSkip($classLikeName)) { return null; } @@ -117,15 +109,13 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param array $configuration */ public function configure(array $configuration): void { - if (! isset($configuration[self::CLASSES_TO_SKIP])) { - return; - } + Assert::allString($configuration); - $this->classesToSkip = $configuration[self::CLASSES_TO_SKIP]; + $this->classesToSkip = $configuration; } public function provideMinPhpVersion(): int @@ -133,37 +123,47 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::CLASSNAME_CONSTANT; } - private function isPartOfIsAFuncCall(String_ $string): bool + private function shouldSkip(string $classLikeName): bool { - $parent = $string->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Arg) { - return false; + // skip short class names, mostly invalid use of strings + if (! str_contains($classLikeName, '\\')) { + return true; } - $parentParent = $parent->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentParent instanceof FuncCall) { - return false; + // possibly string + if (ctype_lower($classLikeName[0])) { + return true; } - return $this->nodeNameResolver->isName($parentParent, 'is_a'); - } - - private function shouldSkip(string $classLikeName, String_ $string): bool - { - if (! $this->classLikeExistenceChecker->doesClassLikeInsensitiveExists($classLikeName)) { + if (! $this->reflectionProvider->hasClass($classLikeName)) { return true; } - if (StaticRectorStrings::isInArrayInsensitive($classLikeName, $this->classesToSkip)) { - return true; + foreach ($this->classesToSkip as $classToSkip) { + if (str_contains($classToSkip, '*')) { + if (fnmatch($classToSkip, $classLikeName, FNM_NOESCAPE)) { + return true; + } + + continue; + } + + if ($this->nodeNameResolver->isStringName($classLikeName, $classToSkip)) { + return true; + } } - if ($this->isPartOfIsAFuncCall($string)) { - return true; + return false; + } + + private function shouldSkipIsA(String_ $string): bool + { + if (! $string->getAttribute(AttributeKey::IS_ARG_VALUE, false)) { + return false; } - // allow class strings to be part of class const arrays, as probably on purpose - $parentClassConst = $this->betterNodeFinder->findParentType($string, ClassConst::class); - return $parentClassConst instanceof ClassConst; + $funcCallName = $string->getAttribute(AttributeKey::FROM_FUNC_CALL_NAME); + + return $funcCallName === 'is_a'; } } diff --git a/rules/Php55/RegexMatcher.php b/rules/Php55/RegexMatcher.php index 5865de05f7a..3a8b6d26125 100644 --- a/rules/Php55/RegexMatcher.php +++ b/rules/Php55/RegexMatcher.php @@ -8,50 +8,47 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Scalar\String_; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\PhpParser\Node\Value\ValueResolver; -final class RegexMatcher +final readonly class RegexMatcher { /** - * @var string * @see https://regex101.com/r/Ok4wuE/1 */ - private const LAST_E_REGEX = '#(\w+)?e(\w+)?$#'; + private const string LAST_E_REGEX = '#(\w+)?e(\w+)?$#'; /** - * @var string * @see https://regex101.com/r/2NWVwT/1 */ - private const LETTER_SUFFIX_REGEX = '#(?\w+)$#'; + private const string LETTER_SUFFIX_REGEX = '#(?\w+)$#'; - public function __construct( - private ValueResolver $valueResolver - ) { - } + /** + * @var string[] + * @see https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php + */ + private const array ALL_MODIFIERS_VALUES = ['i', 'm', 's', 'x', 'e', 'A', 'D', 'S', 'U', 'X', 'J', 'u']; - public function resolvePatternExpressionWithoutEIfFound(Expr $expr): ?Expr + public function resolvePatternExpressionWithoutEIfFound(Expr $expr): Concat|String_|null { if ($expr instanceof String_) { - $pattern = $this->valueResolver->getValue($expr); - - if (! is_string($pattern)) { - return null; - } + $pattern = $expr->value; $delimiter = $pattern[0]; - if (! is_string($delimiter)) { - throw new ShouldNotHappenException(); - } - - /** @var string $modifiers */ - $modifiers = Strings::after($pattern, $delimiter, -1); + $delimiter = match ($delimiter) { + '(' => ')', + '{' => '}', + '[' => ']', + '<' => '>', + default => $delimiter + }; + + $modifiers = $this->resolveModifiers((string) Strings::after($pattern, $delimiter, -1)); if (! \str_contains($modifiers, 'e')) { return null; } - $patternWithoutE = $this->createPatternWithoutE($pattern, $delimiter, $modifiers); - return new String_($patternWithoutE); + $expr->value = $this->createPatternWithoutE($pattern, $delimiter, $modifiers); + + return $expr; } if ($expr instanceof Concat) { @@ -61,15 +58,34 @@ public function resolvePatternExpressionWithoutEIfFound(Expr $expr): ?Expr return null; } - private function createPatternWithoutE(string $pattern, string $delimiter, string $modifiers): string + private function resolveModifiers(string $modifiersCandidate): string { - $modifiersWithoutE = Strings::replace($modifiers, '#e#', ''); + $modifiers = ''; + for ($modifierIndex = 0; $modifierIndex < strlen($modifiersCandidate); ++$modifierIndex) { + if (! in_array($modifiersCandidate[$modifierIndex], self::ALL_MODIFIERS_VALUES, true)) { + $modifiers = ''; + continue; + } + + $modifiers .= $modifiersCandidate[$modifierIndex]; + } + + return $modifiers; + } + private function createPatternWithoutE(string $pattern, string $delimiter, string $modifiers): string + { + $modifiersWithoutE = str_replace('e', '', $modifiers); return Strings::before($pattern, $delimiter, -1) . $delimiter . $modifiersWithoutE; } - private function matchConcat(Concat $concat): ?Expr + private function matchConcat(Concat $concat): ?Concat { + // cause parse error + if (! $concat->left instanceof Concat) { + return null; + } + $lastItem = $concat->right; if (! $lastItem instanceof String_) { return null; diff --git a/rules/Php56/NodeAnalyzer/UndefinedVariableResolver.php b/rules/Php56/NodeAnalyzer/UndefinedVariableResolver.php deleted file mode 100644 index ff070c04bb0..00000000000 --- a/rules/Php56/NodeAnalyzer/UndefinedVariableResolver.php +++ /dev/null @@ -1,150 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable((array) $node->stmts, function (Node $node) use ( - &$undefinedVariables - ): ?int { - // entering new scope - break! - if ($node instanceof FunctionLike && ! $node instanceof ArrowFunction) { - return NodeTraverser::STOP_TRAVERSAL; - } - - if ($node instanceof Foreach_) { - // handled above - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - if (! $node instanceof Variable) { - return null; - } - - if ($this->shouldSkipVariable($node)) { - return null; - } - - /** @var string $variableName */ - $variableName = $this->nodeNameResolver->getName($node); - - // defined 100 % - /** @var Scope $scope */ - $scope = $node->getAttribute(AttributeKey::SCOPE); - if ($scope->hasVariableType($variableName)->yes()) { - return null; - } - - $undefinedVariables[] = $variableName; - - return null; - }); - - return array_unique($undefinedVariables); - } - - private function shouldSkipVariable(Variable $variable): bool - { - $parentNode = $variable->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - return true; - } - - if ($parentNode instanceof Global_) { - return true; - } - - if ($parentNode instanceof Node && - ( - $parentNode instanceof Assign || $parentNode instanceof AssignRef || $this->isStaticVariable( - $parentNode - ) - )) { - return true; - } - - if ($parentNode instanceof Unset_ || $parentNode instanceof UnsetCast) { - return true; - } - - // list() = | [$values] = defines variables as null - if ($this->isListAssign($parentNode)) { - return true; - } - - $nodeScope = $variable->getAttribute(AttributeKey::SCOPE); - if (! $nodeScope instanceof Scope) { - return true; - } - - $variableName = $this->nodeNameResolver->getName($variable); - - // skip $this, as probably in outer scope - if ($variableName === 'this') { - return true; - } - - return $variableName === null; - } - - private function isStaticVariable(Node $parentNode): bool - { - // definition of static variable - if ($parentNode instanceof StaticVar) { - $parentParentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE); - if ($parentParentNode instanceof Static_) { - return true; - } - } - - return false; - } - - private function isListAssign(Node $node): bool - { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof List_) { - return true; - } - - return $parentNode instanceof Array_; - } -} diff --git a/rules/Php56/Rector/FuncCall/PowToExpRector.php b/rules/Php56/Rector/FuncCall/PowToExpRector.php index 2fd2e0b5fe2..de81227758e 100644 --- a/rules/Php56/Rector/FuncCall/PowToExpRector.php +++ b/rules/Php56/Rector/FuncCall/PowToExpRector.php @@ -7,8 +7,10 @@ use PhpParser\Node; use PhpParser\Node\Expr\BinaryOp\Pow; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use PhpParser\Node\Expr\UnaryMinus; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,7 +23,7 @@ final class PowToExpRector extends AbstractRector implements MinPhpVersionInterf public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Changes pow(val, val2) to ** (exp) parameter', + 'Changes `pow(val, val2)` to `**` (exp) parameter', [new CodeSample('pow(1, 2);', '1**2;')] ); } @@ -43,14 +45,20 @@ public function refactor(Node $node): ?Node return null; } - if (count($node->args) !== 2) { + if ($node->isFirstClassCallable()) { return null; } - $firstArgument = $node->args[0]->value; - $secondArgument = $node->args[1]->value; + $firstExpr = $node->getArgs()[0] + ->value; + $secondExpr = $node->getArgs()[1] + ->value; - return new Pow($firstArgument, $secondArgument); + if ($firstExpr instanceof UnaryMinus) { + $firstExpr->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + + return new Pow($firstExpr, $secondExpr); } public function provideMinPhpVersion(): int diff --git a/rules/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector.php b/rules/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector.php deleted file mode 100644 index b3923ed0c51..00000000000 --- a/rules/Php56/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector.php +++ /dev/null @@ -1,135 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, Function_::class, Closure::class]; - } - - /** - * @param ClassMethod|Function_|Closure $node - */ - public function refactor(Node $node): ?Node - { - if ($this->inlineHTMLAnalyzer->hasInlineHTML($node)) { - return null; - } - - $undefinedVariableNames = $this->undefinedVariableResolver->resolve($node); - - // avoids adding same variable multiple tiemes - $alreadyAddedVariableNames = (array) $node->getAttribute(self::ALREADY_ADDED_VARIABLE_NAMES); - $undefinedVariableNames = array_diff($undefinedVariableNames, $alreadyAddedVariableNames); - - if ($undefinedVariableNames === []) { - return null; - } - - $variablesInitiation = []; - foreach ($undefinedVariableNames as $undefinedVariableName) { - $value = $this->isArray($undefinedVariableName, (array) $node->stmts) - ? new Array_([]) - : $this->nodeFactory->createNull(); - - $assign = new Assign(new Variable($undefinedVariableName), $value); - $variablesInitiation[] = new Expression($assign); - } - - $node->setAttribute(self::ALREADY_ADDED_VARIABLE_NAMES, $undefinedVariableNames); - - $node->stmts = array_merge($variablesInitiation, (array) $node->stmts); - - return $node; - } - - /** - * @param Stmt[] $stmts - */ - private function isArray(string $undefinedVariable, array $stmts): bool - { - return (bool) $this->betterNodeFinder->findFirst($stmts, function (Node $node) use ( - $undefinedVariable - ): bool { - if (! $node instanceof ArrayDimFetch) { - return false; - } - - return $this->isName($node->var, $undefinedVariable); - }); - } -} diff --git a/rules/Php70/Enum/BattleshipCompareOrder.php b/rules/Php70/Enum/BattleshipCompareOrder.php new file mode 100644 index 00000000000..46e31cd8fc7 --- /dev/null +++ b/rules/Php70/Enum/BattleshipCompareOrder.php @@ -0,0 +1,12 @@ + */ - private const CHARACTER_CLASS_MAP = [ + private const array CHARACTER_CLASS_MAP = [ ':alnum:' => '[:alnum:]', ':alpha:' => '[:alpha:]', ':blank:' => '[:blank:]', @@ -34,56 +34,38 @@ final class EregToPcreTransformer ]; /** - * @var string * @see https://regex101.com/r/htpXFg/1 */ - private const BOUND_REGEX = '/^(?<' . self::MINIMAL_NUMBER_PART . '>\d|[1-9]\d|1\d\d| + private const string BOUND_REGEX = '/^(?<' . self::MINIMAL_NUMBER_PART . '>\d|[1-9]\d|1\d\d| 2[0-4]\d|25[0-5]) (?,(?<' . self::MAXIMAL_NUMBER_PART . '>\d|[1-9]\d|1\d\d| 2[0-4]\d|25[0-5])?)?$/x'; - /** - * @var string - */ - private const MINIMAL_NUMBER_PART = 'minimal_number'; + private const string MINIMAL_NUMBER_PART = 'minimal_number'; - /** - * @var string - */ - private const MAXIMAL_NUMBER_PART = 'maximal_number'; + private const string MAXIMAL_NUMBER_PART = 'maximal_number'; /** - * @var string[] + * @var array */ private array $icache = []; /** - * @var string[] + * @var array */ private array $cache = []; /** - * Change this via services configuratoin in rector.php if you need it + * Change this via services configuration in rector.php if you need it * Single type is chosen to prevent every regular with different delimiter. */ public function __construct( - private string $pcreDelimiter = '#' + private readonly string $pcreDelimiter = '#' ) { } - public function transform(string $ereg, bool $isCaseInsensitive): string - { - if (! \str_contains($ereg, $this->pcreDelimiter)) { - return $this->ere2pcre($ereg, $isCaseInsensitive); - } - - // fallback - $quotedEreg = preg_quote($ereg, '#'); - return $this->ere2pcre($quotedEreg, $isCaseInsensitive); - } - // converts the ERE $s into the PCRE $r. triggers error on any invalid input. - private function ere2pcre(string $content, bool $ignorecase): string + public function transform(string $content, bool $ignorecase): string { if ($ignorecase) { if (isset($this->icache[$content])) { @@ -92,28 +74,31 @@ private function ere2pcre(string $content, bool $ignorecase): string } elseif (isset($this->cache[$content])) { return $this->cache[$content]; } + [$r, $i] = $this->_ere2pcre($content, 0); if ($i !== strlen($content)) { throw new InvalidEregException('unescaped metacharacter ")"'); } if ($ignorecase) { - return $this->icache[$content] = '#' . $r . '#mi'; + return $this->icache[$content] = $this->pcreDelimiter . $r . $this->pcreDelimiter . 'mi'; } - return $this->cache[$content] = '#' . $r . '#m'; + return $this->cache[$content] = $this->pcreDelimiter . $r . $this->pcreDelimiter . 'm'; } /** * Recursively converts ERE into PCRE, starting at the position $i. * - * @return mixed[] + * @return float[]|int[]|string[] */ private function _ere2pcre(string $content, int $i): array { $r = ['']; $rr = 0; $l = strlen($content); + $normalizeUnprintableChar = false; + while ($i < $l) { // atom $char = $content[$i]; @@ -127,9 +112,11 @@ private function _ere2pcre(string $content, int $i): array $cls .= '^'; ++$i; } + if ($i >= $l) { throw new InvalidEregException('"[" does not have a matching "]"'); } + $start = true; $i = (int) $i; @@ -138,10 +125,11 @@ private function _ere2pcre(string $content, int $i): array if ($i >= $l) { throw new InvalidEregException('"[" does not have a matching "]"'); } + $r[$rr] .= '[' . $cls . ']'; } elseif ($char === ')') { break; - } elseif ($char === '*' || $char === '+' || $char === '?') { + } elseif (in_array($char, ['*', '+', '?'], true)) { throw new InvalidEregException('unescaped metacharacter "' . $char . '"'); } elseif ($char === '{') { if ($i + 1 < $l && \str_contains('0123456789', $content[$i + 1])) { @@ -157,8 +145,9 @@ private function _ere2pcre(string $content, int $i): array continue; } elseif ($char === '|') { if ($r[$rr] === '') { - throw new InvalidEregException('empty branch'); + $normalizeUnprintableChar = true; } + $r[] = ''; ++$rr; ++$i; @@ -167,18 +156,21 @@ private function _ere2pcre(string $content, int $i): array if (++$i >= $l) { throw new InvalidEregException('an invalid escape sequence at the end'); } + $r[$rr] .= $this->_ere2pcre_escape($content[$i]); } else { // including ] and } which are allowed as a literal character $r[$rr] .= $this->_ere2pcre_escape($char); } + ++$i; if ($i >= $l) { break; } + // piece after the atom (only ONE of them is possible) $char = $content[$i]; - if ($char === '*' || $char === '+' || $char === '?') { + if (in_array($char, ['*', '+', '?'], true)) { $r[$rr] .= $char; ++$i; } elseif ($char === '{') { @@ -192,11 +184,20 @@ private function _ere2pcre(string $content, int $i): array throw new InvalidEregException('empty regular expression or branch'); } - return [implode('|', $r), $i]; + return [$this->normalize(implode('|', $r), $normalizeUnprintableChar), $i]; + } + + private function normalize(string $content, bool $normalizeUnprintableChar): string + { + if ($normalizeUnprintableChar) { + $content = str_replace("\x0C", '\\\f', $content); + } + + return str_replace($this->pcreDelimiter, '\\' . $this->pcreDelimiter, $content); } /** - * @param mixed[] $r + * @param array $r */ private function processBracket(string $content, int $i, int $l, array &$r, int $rr): int { @@ -210,15 +211,19 @@ private function processBracket(string $content, int $i, int $l, array &$r, int if ($ii >= $l || $content[$ii] !== ')') { throw new InvalidEregException('"(" does not have a matching ")"'); } + $r[$rr] .= '(' . $t . ')'; $i = $ii; } + // retype + $i = (int) $i; + return $i; } /** - * @return mixed[] + * @return float[]|int[]|string[] */ private function processSquareBracket(string $s, int $i, int $l, string $cls, bool $start): array { @@ -232,9 +237,10 @@ private function processSquareBracket(string $s, int $i, int $l, string $cls, bo if ($a === '-' && ! $start && ! ($i < $l && $s[$i] === ']')) { throw new InvalidEregException('"-" is invalid for the start character in the brackets'); } + if ($i < $l && $s[$i] === '-') { $b = $s[++$i]; - ++$i; + if ($b === ']') { $cls .= $this->_ere2pcre_escape($a) . '\-'; break; @@ -242,11 +248,15 @@ private function processSquareBracket(string $s, int $i, int $l, string $cls, bo $errorMessage = sprintf('an invalid character range %d-%d"', (int) $a, (int) $b); throw new InvalidEregException($errorMessage); } + $cls .= $this->_ere2pcre_escape($a) . '-' . $this->_ere2pcre_escape($b); + + ++$i; } else { $cls .= $this->_ere2pcre_escape($a); } } + $start = false; } while ($i < $l && $s[$i] !== ']'); @@ -267,7 +277,7 @@ private function _ere2pcre_escape(string $content): string } /** - * @param mixed[] $r + * @param array $r */ private function processCurlyBracket(string $s, int $i, array &$r, int $rr): int { @@ -318,6 +328,7 @@ private function processCharacterClass(string $content, int $i, string $cls): ar if (! isset(self::CHARACTER_CLASS_MAP[$ccls])) { throw new InvalidEregException('an invalid or unsupported character class [' . $ccls . ']'); } + $cls .= self::CHARACTER_CLASS_MAP[$ccls]; $i = $ii + 1; diff --git a/rules/Php70/NodeAnalyzer/BattleshipTernaryAnalyzer.php b/rules/Php70/NodeAnalyzer/BattleshipTernaryAnalyzer.php new file mode 100644 index 00000000000..1297b6c843b --- /dev/null +++ b/rules/Php70/NodeAnalyzer/BattleshipTernaryAnalyzer.php @@ -0,0 +1,150 @@ +cond instanceof Greater) { + return $this->evaluateGreater($ternary->cond, $ternary, $comparedExprs); + } + + if ($ternary->cond instanceof Smaller) { + return $this->evaluateSmaller($ternary->cond, $ternary, $comparedExprs); + } + + return null; + } + + /** + * We look for: + * + * $firstValue > $secondValue ? 1 : -1 + * + * @return BattleshipCompareOrder::*|null + */ + private function evaluateGreater(Greater $greater, Ternary $ternary, ComparedExprs $comparedExprs): ?string + { + if (! $ternary->if instanceof Expr) { + return null; + } + + if ( + $this->nodeComparator->areNodesEqual($greater->left, $comparedExprs->getFirstExpr()) && + $this->nodeComparator->areNodesEqual($greater->right, $comparedExprs->getSecondExpr()) + ) { + return $this->evaluateTernaryDesc($ternary); + } + + if (! $this->nodeComparator->areNodesEqual($greater->right, $comparedExprs->getFirstExpr())) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($greater->left, $comparedExprs->getSecondExpr())) { + return null; + } + + return $this->evaluateTernaryAsc($ternary); + } + + /** + * We look for: + * + * $firstValue < $secondValue ? -1 : 1 + * + * @return BattleshipCompareOrder::*|null + */ + private function evaluateSmaller(Smaller $smaller, Ternary $ternary, ComparedExprs $comparedExprs): ?string + { + if (! $ternary->if instanceof Expr) { + return null; + } + + if ( + $this->nodeComparator->areNodesEqual($smaller->left, $comparedExprs->getFirstExpr()) && + $this->nodeComparator->areNodesEqual($smaller->right, $comparedExprs->getSecondExpr()) + ) { + return $this->evaluateTernaryAsc($ternary); + } + + if (! $this->nodeComparator->areNodesEqual($smaller->right, $comparedExprs->getFirstExpr())) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($smaller->left, $comparedExprs->getSecondExpr())) { + return null; + } + + return $this->evaluateTernaryDesc($ternary); + } + + private function isValueOneAndMinusOne(Expr $firstExpr, Expr $secondExpr): bool + { + if (! $this->valueResolver->isValue($firstExpr, 1)) { + return false; + } + + return $this->valueResolver->isValue($secondExpr, -1); + } + + /** + * @return BattleshipCompareOrder::*|null + */ + private function evaluateTernaryAsc(Ternary $ternary): ?string + { + if (! $ternary->if instanceof Expr) { + return null; + } + + if ($this->isValueOneAndMinusOne($ternary->if, $ternary->else)) { + return BattleshipCompareOrder::ASC; + } + + if ($this->isValueOneAndMinusOne($ternary->else, $ternary->if)) { + return BattleshipCompareOrder::DESC; + } + + return null; + } + + /** + * @return BattleshipCompareOrder::*|null + */ + private function evaluateTernaryDesc(Ternary $ternary): ?string + { + if (! $ternary->if instanceof Expr) { + return null; + } + + if ($this->isValueOneAndMinusOne($ternary->if, $ternary->else)) { + return BattleshipCompareOrder::DESC; + } + + if ($this->isValueOneAndMinusOne($ternary->else, $ternary->if)) { + return BattleshipCompareOrder::ASC; + } + + return null; + } +} diff --git a/rules/Php70/NodeAnalyzer/MethodCallNameAnalyzer.php b/rules/Php70/NodeAnalyzer/MethodCallNameAnalyzer.php new file mode 100644 index 00000000000..f7b18bf2400 --- /dev/null +++ b/rules/Php70/NodeAnalyzer/MethodCallNameAnalyzer.php @@ -0,0 +1,51 @@ +var instanceof Variable) { + return false; + } + + if ($expr->var->name !== 'this') { + return false; + } + + if (! $expr->name instanceof Identifier) { + return false; + } + + return $expr->name->toString() === $desiredMethodName; + } + + public function isParentMethodCall(Class_ $class, Expr $expr): bool + { + if (! $class->extends instanceof Name) { + return false; + } + + $parentClassName = $class->extends->toString(); + if ($class->getMethod($parentClassName) instanceof ClassMethod) { + return false; + } + + return $this->isLocalMethodCallNamed($expr, $parentClassName); + } +} diff --git a/rules/Php70/NodeAnalyzer/Php4ConstructorClassMethodAnalyzer.php b/rules/Php70/NodeAnalyzer/Php4ConstructorClassMethodAnalyzer.php index cf5d0c1f172..98368425deb 100644 --- a/rules/Php70/NodeAnalyzer/Php4ConstructorClassMethodAnalyzer.php +++ b/rules/Php70/NodeAnalyzer/Php4ConstructorClassMethodAnalyzer.php @@ -5,24 +5,12 @@ namespace Rector\Php70\NodeAnalyzer; use PhpParser\Node\Stmt\ClassMethod; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\NodeTypeResolver\Node\AttributeKey; final class Php4ConstructorClassMethodAnalyzer { - public function detect(ClassMethod $classMethod): bool + public function detect(ClassMethod $classMethod, ClassReflection $classReflection): bool { - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - // catch only classes without namespace - if ($scope->getNamespace() !== null) { - return false; - } - if ($classMethod->isAbstract()) { return false; } @@ -31,11 +19,11 @@ public function detect(ClassMethod $classMethod): bool return false; } - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { + if ($classReflection->isAnonymous()) { return false; } - return ! $classReflection->isAnonymous(); + $possiblePhp4MethodNames = [$classReflection->getName(), lcfirst($classReflection->getName())]; + return in_array($classMethod->name->toString(), $possiblePhp4MethodNames, true); } } diff --git a/rules/Php70/Rector/Assign/ListSplitStringRector.php b/rules/Php70/Rector/Assign/ListSplitStringRector.php index b7bb0238150..a0a5b5532cd 100644 --- a/rules/Php70/Rector/Assign/ListSplitStringRector.php +++ b/rules/Php70/Rector/Assign/ListSplitStringRector.php @@ -7,18 +7,16 @@ use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\List_; -use PHPStan\Type\StringType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog http://php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.list - * - * @changelog https://stackoverflow.com/a/47965344/1348344 * @see \Rector\Tests\Php70\Rector\Assign\ListSplitStringRector\ListSplitStringRectorTest */ -final class ListSplitStringRector extends AbstractRector +final class ListSplitStringRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition(): RuleDefinition { @@ -28,6 +26,11 @@ public function getRuleDefinition(): RuleDefinition ); } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_LIST_SPLIT_STRING; + } + /** * @return array> */ @@ -45,7 +48,8 @@ public function refactor(Node $node): ?Node return null; } - if (! $this->nodeTypeResolver->isStaticType($node->expr, StringType::class)) { + $exprType = $this->getType($node->expr); + if (! $exprType->isString()->yes()) { return null; } diff --git a/rules/Php70/Rector/Assign/ListSwapArrayOrderRector.php b/rules/Php70/Rector/Assign/ListSwapArrayOrderRector.php index b4f29f4d7d7..4e58d56d31b 100644 --- a/rules/Php70/Rector/Assign/ListSwapArrayOrderRector.php +++ b/rules/Php70/Rector/Assign/ListSwapArrayOrderRector.php @@ -5,23 +5,29 @@ namespace Rector\Php70\Rector\Assign; use PhpParser\Node; +use PhpParser\Node\ArrayItem; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\List_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog http://php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.list * @see \Rector\Tests\Php70\Rector\Assign\ListSwapArrayOrderRector\ListSwapArrayOrderRectorTest */ final class ListSwapArrayOrderRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly BetterStandardPrinter $betterStandardPrinter + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -56,8 +62,8 @@ public function refactor(Node $node): ?Node continue; } - if ($arrayItem->value instanceof ArrayDimFetch && $arrayItem->value->dim === null) { - $printedVariables[] = $this->print($arrayItem->value->var); + if ($arrayItem->value instanceof ArrayDimFetch && ! $arrayItem->value->dim instanceof Expr) { + $printedVariables[] = $this->betterStandardPrinter->print($arrayItem->value->var); } else { return null; } diff --git a/rules/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector.php b/rules/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector.php index a0932aa581b..af5ebb098d5 100644 --- a/rules/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector.php +++ b/rules/Php70/Rector/Break_/BreakNotInLoopOrSwitchToReturnRector.php @@ -7,24 +7,29 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Break_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; use Rector\NodeNestingScope\ContextAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/questions/3618030/php-fatal-error-cannot-break-continue https://stackoverflow.com/questions/11988281/why-does-cannot-break-continue-1-level-comes-in-php - * - * @see https://3v4l.org/Qtelt * @see \Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector\BreakNotInLoopOrSwitchToReturnRectorTest */ -final class BreakNotInLoopOrSwitchToReturnRector extends AbstractRector +final class BreakNotInLoopOrSwitchToReturnRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private ContextAnalyzer $contextAnalyzer + private readonly ContextAnalyzer $contextAnalyzer ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_BREAK_OUTSIDE_LOOP; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -73,23 +78,18 @@ public function getNodeTypes(): array /** * @param Break_ $node + * @return Return_|null|NodeVisitor::REMOVE_NODE */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): Return_|null|int { if ($this->contextAnalyzer->isInLoop($node)) { return null; } - if ($this->contextAnalyzer->isInSwitch($node)) { - return null; - } - if ($this->contextAnalyzer->isInIf($node)) { return new Return_(); } - $this->removeNode($node); - - return $node; + return NodeVisitor::REMOVE_NODE; } } diff --git a/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php b/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php index 938b2cb0496..976454d3423 100644 --- a/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php +++ b/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php @@ -5,38 +5,49 @@ namespace Rector\Php70\Rector\ClassMethod; use PhpParser\Node; -use PhpParser\Node\Expr; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Enum\ObjectReference; +use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver; +use Rector\Php70\NodeAnalyzer\MethodCallNameAnalyzer; use Rector\Php70\NodeAnalyzer\Php4ConstructorClassMethodAnalyzer; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/remove_php4_constructors * @see \Rector\Tests\Php70\Rector\ClassMethod\Php4ConstructorRector\Php4ConstructorRectorTest */ -final class Php4ConstructorRector extends AbstractRector +final class Php4ConstructorRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private Php4ConstructorClassMethodAnalyzer $php4ConstructorClassMethodAnalyzer + private readonly Php4ConstructorClassMethodAnalyzer $php4ConstructorClassMethodAnalyzer, + private readonly ParentClassScopeResolver $parentClassScopeResolver, + private readonly MethodCallNameAnalyzer $methodCallNameAnalyzer, ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_PHP4_CONSTRUCTOR; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Changes PHP 4 style constructor to __construct.', + 'Change PHP 4 style constructor to `__construct`', [ new CodeSample( <<<'CODE_SAMPLE' @@ -66,66 +77,85 @@ public function __construct() */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): Class_|null { - if (! $this->php4ConstructorClassMethodAnalyzer->detect($node)) { + $scope = ScopeFetcher::fetch($node); + + // catch only classes without namespace + if ($scope->getNamespace() !== null) { return null; } - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { return null; } - // process parent call references first - $this->processClassMethodStatementsForParentConstructorCalls($node); - - // not PSR-4 constructor - if (! $this->nodeNameResolver->areNamesEqual($classLike, $node)) { + $className = $this->getName($node); + if (! is_string($className)) { return null; } - $classMethod = $classLike->getMethod(MethodName::CONSTRUCT); + foreach ($node->stmts as $classStmtKey => $classStmt) { + if (! $classStmt instanceof ClassMethod) { + continue; + } - // does it already have a __construct method? - if (! $classMethod instanceof ClassMethod) { - $node->name = new Identifier(MethodName::CONSTRUCT); - } + if (! $this->php4ConstructorClassMethodAnalyzer->detect($classStmt, $classReflection)) { + continue; + } - if ($node->stmts === null) { - return null; - } + $psr4ConstructorMethod = $classStmt; - if (count($node->stmts) === 1) { - /** @var Expression|Expr $stmt */ - $stmt = $node->stmts[0]; - if (! $stmt instanceof Expression) { - return null; - } + // process parent call references first + $this->processClassMethodStatementsForParentConstructorCalls($psr4ConstructorMethod, $scope); - if ($this->isLocalMethodCallNamed($stmt->expr, MethodName::CONSTRUCT)) { - $this->removeNode($node); + // does it already have a __construct method? + if (! $node->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) { + $psr4ConstructorMethod->name = new Identifier(MethodName::CONSTRUCT); + } - return null; + foreach ((array) $psr4ConstructorMethod->stmts as $classMethodStmt) { + if (! $classMethodStmt instanceof Expression) { + continue; + } + + // remove delegating method + if ($this->methodCallNameAnalyzer->isLocalMethodCallNamed( + $classMethodStmt->expr, + MethodName::CONSTRUCT + )) { + unset($node->stmts[$classStmtKey]); + } + + if ($this->methodCallNameAnalyzer->isParentMethodCall($node, $classMethodStmt->expr)) { + /** @var MethodCall $expr */ + $expr = $classMethodStmt->expr; + + /** @var string $parentClassName */ + $parentClassName = $this->getParentClassName($node); + + $classMethodStmt->expr = new StaticCall(new FullyQualified($parentClassName), new Identifier( + MethodName::CONSTRUCT + ), $expr->args); + } } + + return $node; } - return $node; + return null; } - private function processClassMethodStatementsForParentConstructorCalls(ClassMethod $classMethod): void + private function processClassMethodStatementsForParentConstructorCalls(ClassMethod $classMethod, Scope $scope): void { - if (! is_iterable($classMethod->stmts)) { - return; - } - - foreach ($classMethod->stmts as $methodStmt) { + foreach ((array) $classMethod->stmts as $methodStmt) { if (! $methodStmt instanceof Expression) { continue; } @@ -135,16 +165,16 @@ private function processClassMethodStatementsForParentConstructorCalls(ClassMeth continue; } - $this->processParentPhp4ConstructCall($methodStmt); + $this->processParentPhp4ConstructCall($methodStmt, $scope); } } - private function processParentPhp4ConstructCall(StaticCall $staticCall): void + private function processParentPhp4ConstructCall(StaticCall $staticCall, Scope $scope): void { - $parentClassName = $this->resolveParentClassName($staticCall); + $parentClassReflection = $this->parentClassScopeResolver->resolveParentClassReflection($scope); // no parent class - if ($parentClassName === null) { + if (! $parentClassReflection instanceof ClassReflection) { return; } @@ -153,60 +183,28 @@ private function processParentPhp4ConstructCall(StaticCall $staticCall): void } // rename ParentClass - if ($this->isName($staticCall->class, $parentClassName)) { - $staticCall->class = new Name('parent'); + if ($this->isName($staticCall->class, $parentClassReflection->getName())) { + $staticCall->class = new Name(ObjectReference::PARENT); } - if (! $this->isName($staticCall->class, 'parent')) { + if (! $this->isName($staticCall->class, ObjectReference::PARENT)) { return; } // it's not a parent PHP 4 constructor call - if (! $this->isName($staticCall->name, $parentClassName)) { + if (! $this->isName($staticCall->name, $parentClassReflection->getName())) { return; } $staticCall->name = new Identifier(MethodName::CONSTRUCT); } - private function resolveParentClassName(StaticCall $staticCall): ?string + private function getParentClassName(Class_ $class): ?string { - $scope = $staticCall->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { + if (! $class->extends instanceof Node) { return null; } - $classReflection = $scope->getClassReflection(); - - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $parentClassReflection = $classReflection->getParentClass(); - if (! $parentClassReflection instanceof ClassReflection) { - return null; - } - - return $parentClassReflection->getName(); - } - - private function isLocalMethodCallNamed(Expr $expr, string $name): bool - { - if (! $expr instanceof MethodCall) { - return false; - } - - if ($expr->var instanceof StaticCall) { - return false; - } - - if ($expr->var instanceof MethodCall) { - return false; - } - - if (! $this->isName($expr->var, 'this')) { - return false; - } - return $this->isName($expr->name, $name); + return $class->extends->toString(); } } diff --git a/rules/Php70/Rector/FuncCall/CallUserMethodRector.php b/rules/Php70/Rector/FuncCall/CallUserMethodRector.php index 02bf8a7de50..606414df951 100644 --- a/rules/Php70/Rector/FuncCall/CallUserMethodRector.php +++ b/rules/Php70/Rector/FuncCall/CallUserMethodRector.php @@ -5,25 +5,33 @@ namespace Rector\Php70\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\FuncCall\CallUserMethodRector\CallUserMethodRectorTest */ -final class CallUserMethodRector extends AbstractRector +final class CallUserMethodRector extends AbstractRector implements MinPhpVersionInterface { /** * @var array */ - private const OLD_TO_NEW_FUNCTIONS = [ + private const array OLD_TO_NEW_FUNCTIONS = [ 'call_user_method' => 'call_user_func', 'call_user_method_array' => 'call_user_func_array', ]; + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_CALL_USER_METHOD; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -55,9 +63,18 @@ public function refactor(Node $node): ?Node return null; } + if ($node->isFirstClassCallable()) { + return null; + } + + if (! isset(self::OLD_TO_NEW_FUNCTIONS[$this->getName($node)])) { + return null; + } + $newName = self::OLD_TO_NEW_FUNCTIONS[$this->getName($node)]; $node->name = new Name($newName); + /** @var Arg[] $oldArgs */ $oldArgs = $node->args; unset($node->args[1]); @@ -67,7 +84,7 @@ public function refactor(Node $node): ?Node unset($oldArgs[0]); unset($oldArgs[1]); - $node->args = $this->appendArgs($newArgs, $oldArgs); + $node->args = array_merge($newArgs, $oldArgs); return $node; } diff --git a/rules/Php70/Rector/FuncCall/EregToPregMatchRector.php b/rules/Php70/Rector/FuncCall/EregToPregMatchRector.php index aa6cea8084b..1313b823d47 100644 --- a/rules/Php70/Rector/FuncCall/EregToPregMatchRector.php +++ b/rules/Php70/Rector/FuncCall/EregToPregMatchRector.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Arg; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp\Concat; @@ -13,25 +14,25 @@ use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Php70\EregToPcreTransformer; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** - * @changelog http://php.net/reference.pcre.pattern.posix https://stackoverflow.com/a/17033826/1348344 https://docstore.mik.ua/orelly/webprog/pcook/ch13_02.htm - * * @see \Rector\Tests\Php70\Rector\FuncCall\EregToPregMatchRector\EregToPregMatchRectorTest */ -final class EregToPregMatchRector extends AbstractRector +final class EregToPregMatchRector extends AbstractRector implements MinPhpVersionInterface { /** * @var array */ - private const OLD_NAMES_TO_NEW_ONES = [ + private const array OLD_NAMES_TO_NEW_ONES = [ 'ereg' => 'preg_match', 'eregi' => 'preg_match', 'ereg_replace' => 'preg_replace', @@ -41,10 +42,15 @@ final class EregToPregMatchRector extends AbstractRector ]; public function __construct( - private EregToPcreTransformer $eregToPcreTransformer + private readonly EregToPcreTransformer $eregToPcreTransformer, ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_EREG_FUNCTION; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -58,43 +64,46 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [FuncCall::class]; + return [FuncCall::class, Assign::class]; } /** - * @param FuncCall $node + * @param FuncCall|Assign $node */ public function refactor(Node $node): ?Node { - $functionName = $this->getName($node); - if ($functionName === null) { - return null; + if ($node instanceof FuncCall) { + return $this->refactorFuncCall($node); } - if (! isset(self::OLD_NAMES_TO_NEW_ONES[$functionName])) { + if (! $this->isEregFuncCallWithThreeArgs($node->expr)) { return null; } - $patternNode = $node->args[0]->value; - if ($patternNode instanceof String_) { - $this->processStringPattern($node, $patternNode, $functionName); - } elseif ($patternNode instanceof Variable) { - $this->processVariablePattern($node, $patternNode, $functionName); - } + /** @var FuncCall $funcCall */ + $funcCall = $node->expr; - $this->processSplitLimitArgument($node, $functionName); + $node->expr = $this->createTernaryWithStrlenOfFirstMatch($funcCall); - $node->name = new Name(self::OLD_NAMES_TO_NEW_ONES[$functionName]); + return $node; + } - // ereg|eregi 3rd argument return value fix - if (in_array($functionName, ['ereg', 'eregi'], true) && isset($node->args[2])) { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Assign) { - return $this->createTernaryWithStrlenOfFirstMatch($node); - } + private function shouldSkipFuncCall(FuncCall $funcCall): bool + { + $functionName = $this->getName($funcCall); + if ($functionName === null) { + return true; } - return $node; + if (! isset(self::OLD_NAMES_TO_NEW_ONES[$functionName])) { + return true; + } + + if ($funcCall->isFirstClassCallable()) { + return true; + } + + return ! isset($funcCall->getArgs()[0]); } private function processStringPattern(FuncCall $funcCall, String_ $string, string $functionName): void @@ -102,7 +111,9 @@ private function processStringPattern(FuncCall $funcCall, String_ $string, strin $pattern = $string->value; $pattern = $this->eregToPcreTransformer->transform($pattern, $this->isCaseInsensitiveFunction($functionName)); - $funcCall->args[0]->value = new String_($pattern); + $firstArg = $funcCall->getArgs()[0]; + Assert::isInstanceOf($firstArg->value, String_::class); + $firstArg->value->value = $pattern; } private function processVariablePattern(FuncCall $funcCall, Variable $variable, string $functionName): void @@ -117,7 +128,9 @@ private function processVariablePattern(FuncCall $funcCall, Variable $variable, $endDelimiter = $this->isCaseInsensitiveFunction($functionName) ? '#mi' : '#m'; $concat = new Concat($startConcat, new String_($endDelimiter)); - $funcCall->args[0]->value = $concat; + /** @var Arg $arg */ + $arg = $funcCall->args[0]; + $arg->value = $concat; } /** @@ -128,20 +141,23 @@ private function processVariablePattern(FuncCall $funcCall, Variable $variable, */ private function processSplitLimitArgument(FuncCall $funcCall, string $functionName): void { - if (! \str_starts_with($functionName, 'split')) { + if (! isset($funcCall->args[2])) { return; } - // 3rd argument - $limit, 0 → 1 - if (! isset($funcCall->args[2])) { + if (! $funcCall->args[2] instanceof Arg) { return; } - if (! $funcCall->args[2]->value instanceof LNumber) { + if (! \str_starts_with($functionName, 'split')) { + return; + } + + // 3rd argument - $limit, 0 → 1 + if (! $funcCall->args[2]->value instanceof Int_) { return; } - /** @var LNumber $limitNumberNode */ $limitNumberNode = $funcCall->args[2]->value; if ($limitNumberNode->value !== 0) { return; @@ -152,7 +168,9 @@ private function processSplitLimitArgument(FuncCall $funcCall, string $functionN private function createTernaryWithStrlenOfFirstMatch(FuncCall $funcCall): Ternary { - $arrayDimFetch = new ArrayDimFetch($funcCall->args[2]->value, new LNumber(0)); + $thirdArg = $funcCall->getArgs()[2]; + + $arrayDimFetch = new ArrayDimFetch($thirdArg->value, new Int_(0)); $strlenFuncCall = $this->nodeFactory->createFuncCall('strlen', [$arrayDimFetch]); return new Ternary($funcCall, $strlenFuncCall, $this->nodeFactory->createFalse()); @@ -163,6 +181,49 @@ private function isCaseInsensitiveFunction(string $functionName): bool if (\str_contains($functionName, 'eregi')) { return true; } + return \str_contains($functionName, 'spliti'); } + + private function isEregFuncCallWithThreeArgs(Expr $expr): bool + { + if (! $expr instanceof FuncCall) { + return false; + } + + $functionName = $this->getName($expr); + if (! is_string($functionName)) { + return false; + } + + if (! in_array($functionName, ['ereg', 'eregi'], true)) { + return false; + } + + return isset($expr->getArgs()[2]); + } + + private function refactorFuncCall(FuncCall $funcCall): ?FuncCall + { + if ($this->shouldSkipFuncCall($funcCall)) { + return null; + } + + /** @var string $functionName */ + $functionName = $this->getName($funcCall); + + $firstArg = $funcCall->getArgs()[0]; + $patternExpr = $firstArg->value; + + if ($patternExpr instanceof String_) { + $this->processStringPattern($funcCall, $patternExpr, $functionName); + } elseif ($patternExpr instanceof Variable) { + $this->processVariablePattern($funcCall, $patternExpr, $functionName); + } + + $this->processSplitLimitArgument($funcCall, $functionName); + + $funcCall->name = new Name(self::OLD_NAMES_TO_NEW_ONES[$functionName]); + return $funcCall; + } } diff --git a/rules/Php70/Rector/FuncCall/MultiDirnameRector.php b/rules/Php70/Rector/FuncCall/MultiDirnameRector.php index 100e7b55a33..9cc84c7376e 100644 --- a/rules/Php70/Rector/FuncCall/MultiDirnameRector.php +++ b/rules/Php70/Rector/FuncCall/MultiDirnameRector.php @@ -7,9 +7,9 @@ use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Scalar\LNumber; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use PhpParser\Node\Scalar\Int_; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,10 +19,7 @@ */ final class MultiDirnameRector extends AbstractRector implements MinPhpVersionInterface { - /** - * @var string - */ - private const DIRNAME = 'dirname'; + private const string DIRNAME = 'dirname'; private int $nestingLevel = 0; @@ -56,17 +53,19 @@ public function refactor(Node $node): ?Node $activeFuncCallNode = $node; $lastFuncCallNode = $node; - while ($activeFuncCallNode = $this->matchNestedDirnameFuncCall($activeFuncCallNode)) { + $shouldUpdate = false; + while (($activeFuncCallNode = $this->matchNestedDirnameFuncCall($activeFuncCallNode)) instanceof FuncCall) { $lastFuncCallNode = $activeFuncCallNode; + $shouldUpdate = true; } // nothing to improve - if ($this->nestingLevel < 2) { - return $activeFuncCallNode; + if (! $shouldUpdate || $this->shouldSkip()) { + return null; } $node->args[0] = $lastFuncCallNode->args[0]; - $node->args[1] = new Arg(new LNumber($this->nestingLevel)); + $node->args[1] = new Arg(new Int_($this->nestingLevel)); return $node; } @@ -76,31 +75,40 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::DIRNAME_LEVELS; } + private function shouldSkip(): bool + { + return $this->nestingLevel < 2; + } + private function matchNestedDirnameFuncCall(FuncCall $funcCall): ?FuncCall { if (! $this->isName($funcCall, self::DIRNAME)) { return null; } - if (count($funcCall->args) >= 3) { + if ($funcCall->isFirstClassCallable()) { + return null; + } + + $args = $funcCall->getArgs(); + if (count($args) >= 3) { return null; } // dirname($path, ); - if (count($funcCall->args) === 2) { - if (! $funcCall->args[1]->value instanceof LNumber) { + if (count($args) === 2) { + if (! $args[1]->value instanceof Int_) { return null; } - /** @var LNumber $levelNumber */ - $levelNumber = $funcCall->args[1]->value; + $levelNumber = $args[1]->value; $this->nestingLevel += $levelNumber->value; } else { ++$this->nestingLevel; } - $nestedFuncCallNode = $funcCall->args[0]->value; + $nestedFuncCallNode = $args[0]->value; if (! $nestedFuncCallNode instanceof FuncCall) { return null; } diff --git a/rules/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php b/rules/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php deleted file mode 100644 index eabac6e4917..00000000000 --- a/rules/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php +++ /dev/null @@ -1,186 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class, MethodCall::class, StaticCall::class]; - } - - /** - * @param FuncCall|MethodCall|StaticCall $node - */ - public function refactor(Node $node): ?Node - { - $arguments = $this->getNonVariableArguments($node); - if ($arguments === []) { - return null; - } - - $scopeNode = $this->parentScopeFinder->find($node); - if ($scopeNode === null) { - return null; - } - - $currentScope = $scopeNode->getAttribute(AttributeKey::SCOPE); - if (! $currentScope instanceof Scope) { - return null; - } - - foreach ($arguments as $key => $argument) { - $replacements = $this->getReplacementsFor($argument, $currentScope, $scopeNode); - - $current = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - - $currentStatement = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - $this->addNodeBeforeNode( - $replacements->getAssign(), - $current instanceof Return_ ? $current : $currentStatement - ); - - $node->args[$key]->value = $replacements->getVariable(); - - // add variable name to scope, so we prevent duplication of new variable of the same name - $currentScope = $currentScope->assignExpression( - $replacements->getVariable(), - $currentScope->getType($replacements->getVariable()) - ); - } - - $scopeNode->setAttribute(AttributeKey::SCOPE, $currentScope); - - return $node; - } - - /** - * @return Expr[] - */ - private function getNonVariableArguments(FuncCall | MethodCall | StaticCall $call): array - { - $arguments = []; - - $functionLikeReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($call); - - if ($functionLikeReflection === null) { - return []; - } - - foreach ($functionLikeReflection->getVariants() as $parametersAcceptor) { - /** @var ParameterReflection $parameterReflection */ - foreach ($parametersAcceptor->getParameters() as $key => $parameterReflection) { - // omitted optional parameter - if (! isset($call->args[$key])) { - continue; - } - - if ($parameterReflection->passedByReference()->no()) { - continue; - } - - $argument = $call->args[$key]->value; - - if ($this->isVariableLikeNode($argument)) { - continue; - } - - $arguments[$key] = $argument; - } - } - - return $arguments; - } - - private function getReplacementsFor(Expr $expr, Scope $scope, Node $scopeNode): VariableAssignPair - { - /** @var Assign|AssignOp|AssignRef $expr */ - if ($this->isAssign($expr) && $this->isVariableLikeNode($expr->var)) { - return new VariableAssignPair($expr->var, $expr); - } - - $variableName = $this->variableNaming->resolveFromNodeWithScopeCountAndFallbackName($expr, $scope, 'tmp'); - - $variable = new Variable($variableName); - - // add a new scope with this variable - if ($scope instanceof MutatingScope) { - $mutatingScope = $scope->assignExpression($variable, new MixedType()); - $scopeNode->setAttribute(AttributeKey::SCOPE, $mutatingScope); - } - - return new VariableAssignPair($variable, new Assign($variable, $expr)); - } - - private function isVariableLikeNode(Node $node): bool - { - return $node instanceof Variable - || $node instanceof ArrayDimFetch - || $node instanceof PropertyFetch - || $node instanceof StaticPropertyFetch; - } - - private function isAssign(Expr $expr): bool - { - if ($expr instanceof Assign) { - return true; - } - - if ($expr instanceof AssignRef) { - return true; - } - - return $expr instanceof AssignOp; - } -} diff --git a/rules/Php70/Rector/FuncCall/RandomFunctionRector.php b/rules/Php70/Rector/FuncCall/RandomFunctionRector.php index 0fb1894dae8..4d7d5800153 100644 --- a/rules/Php70/Rector/FuncCall/RandomFunctionRector.php +++ b/rules/Php70/Rector/FuncCall/RandomFunctionRector.php @@ -8,9 +8,10 @@ use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use PhpParser\Node\Scalar\LNumber; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use PhpParser\Node\Scalar\Int_; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,18 +24,22 @@ final class RandomFunctionRector extends AbstractRector implements MinPhpVersion /** * @var array */ - private const OLD_TO_NEW_FUNCTION_NAMES = [ + private const array OLD_TO_NEW_FUNCTION_NAMES = [ 'getrandmax' => 'mt_getrandmax', 'srand' => 'mt_srand', - 'mt_rand' => 'random_int', 'rand' => 'random_int', ]; + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Changes rand, srand and getrandmax by new mt_* alternatives.', - [new CodeSample('rand();', 'mt_rand();')] + 'Changes rand, srand, and getrandmax to newer alternatives', + [new CodeSample('rand();', 'random_int();')] ); } @@ -49,23 +54,39 @@ public function getNodeTypes(): array /** * @param FuncCall $node */ - public function refactor(Node $node): FuncCall + public function refactor(Node $node): FuncCall|null { + if ($node->isFirstClassCallable()) { + return null; + } + foreach (self::OLD_TO_NEW_FUNCTION_NAMES as $oldFunctionName => $newFunctionName) { if ($this->isName($node, $oldFunctionName)) { $node->name = new Name($newFunctionName); // special case: random_int(); → random_int(0, getrandmax()); - if ($newFunctionName === 'random_int' && $node->args === []) { - $node->args[0] = new Arg(new LNumber(0)); - $node->args[1] = new Arg($this->nodeFactory->createFuncCall('mt_getrandmax')); + if ($newFunctionName === 'random_int') { + $args = $node->getArgs(); + if ($args === []) { + $node->args[0] = new Arg(new Int_(0)); + $node->args[1] = new Arg($this->nodeFactory->createFuncCall('mt_getrandmax')); + } elseif (count($args) === 2) { + $minValue = $this->valueResolver->getValue($args[0]->value); + $maxValue = $this->valueResolver->getValue($args[1]->value); + + if (is_int($minValue) && is_int($maxValue) && $minValue > $maxValue) { + $temp = $node->args[0]; + $node->args[0] = $node->args[1]; + $node->args[1] = $temp; + } + } } return $node; } } - return $node; + return null; } public function provideMinPhpVersion(): int diff --git a/rules/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector.php b/rules/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector.php index a800b3dda4b..c8fed4bb9de 100644 --- a/rules/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector.php +++ b/rules/Php70/Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector.php @@ -7,15 +7,16 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/F5GE8 * @see \Rector\Tests\Php70\Rector\FuncCall\RenameMktimeWithoutArgsToTimeRector\RenameMktimeWithoutArgsToTimeRectorTest */ -final class RenameMktimeWithoutArgsToTimeRector extends AbstractRector +final class RenameMktimeWithoutArgsToTimeRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition(): RuleDefinition { @@ -49,6 +50,11 @@ public function run() ); } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_MKTIME_WITHOUT_ARG; + } + /** * @return array> */ diff --git a/rules/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector.php b/rules/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector.php index f1e5d7eb851..0e1aef8950d 100644 --- a/rules/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector.php +++ b/rules/Php70/Rector/FunctionLike/ExceptionHandlerTypehintRector.php @@ -4,35 +4,32 @@ namespace Rector\Php70\Rector\FunctionLike; -use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\NullableType; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Rector\AbstractRector; +use Rector\Util\StringUtils; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/typed_properties_v2#proposal - * * @see \Rector\Tests\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector\ExceptionHandlerTypehintRectorTest */ final class ExceptionHandlerTypehintRector extends AbstractRector implements MinPhpVersionInterface { /** - * @var string * @see https://regex101.com/r/VBFXCR/1 */ - private const HANDLE_INSENSITIVE_REGEX = '#handle#i'; + private const string HANDLE_INSENSITIVE_REGEX = '#handle#i'; public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Change typehint from `Exception` to `Throwable`.', + 'Change typehint from `Exception` to `Throwable`', [ new CodeSample( <<<'CODE_SAMPLE' @@ -81,7 +78,7 @@ public function refactor(Node $node): ?Node } // is probably handling exceptions - if (! Strings::match((string) $node->name, self::HANDLE_INSENSITIVE_REGEX)) { + if (! StringUtils::isMatch((string) $node->name, self::HANDLE_INSENSITIVE_REGEX)) { return null; } diff --git a/rules/Php70/Rector/If_/IfToSpaceshipRector.php b/rules/Php70/Rector/If_/IfToSpaceshipRector.php index 1ed1c39b3fe..4275beb338b 100644 --- a/rules/Php70/Rector/If_/IfToSpaceshipRector.php +++ b/rules/Php70/Rector/If_/IfToSpaceshipRector.php @@ -6,53 +6,34 @@ use PhpParser\Node; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\Equal; -use PhpParser\Node\Expr\BinaryOp\Greater; use PhpParser\Node\Expr\BinaryOp\Identical; -use PhpParser\Node\Expr\BinaryOp\Smaller; use PhpParser\Node\Expr\BinaryOp\Spaceship; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Php70\Enum\BattleshipCompareOrder; +use Rector\Php70\NodeAnalyzer\BattleshipTernaryAnalyzer; +use Rector\Php70\ValueObject\ComparedExprs; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/combined-comparison-operator https://3v4l.org/LPbA0 - * * @see \Rector\Tests\Php70\Rector\If_\IfToSpaceshipRector\IfToSpaceshipRectorTest */ final class IfToSpaceshipRector extends AbstractRector implements MinPhpVersionInterface { - /** - * @var int|null - */ - private $onEqual; - - /** - * @var int|null - */ - private $onSmaller; - - /** - * @var int|null - */ - private $onGreater; - - private ?Expr $firstValue = null; - - private ?Expr $secondValue = null; - - /** - * @var Node|null - */ - private $nextNode; + public function __construct( + private readonly BattleshipTernaryAnalyzer $battleshipTernaryAnalyzer, + private readonly ValueResolver $valueResolver, + ) { + } public function getRuleDefinition(): RuleDefinition { @@ -61,34 +42,21 @@ public function getRuleDefinition(): RuleDefinition [ new CodeSample( <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - usort($languages, function ($a, $b) { - if ($a[0] === $b[0]) { - return 0; - } - - return ($a[0] < $b[0]) ? 1 : -1; - }); - } +usort($languages, function ($first, $second) { +if ($first[0] === $second[0]) { + return 0; } + +return ($first[0] < $second[0]) ? 1 : -1; +}); CODE_SAMPLE , <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - usort($languages, function ($a, $b) { - return $b[0] <=> $a[0]; - }); - } -} +usort($languages, function ($first, $second) { +return $second[0] <=> $first[0]; +}); CODE_SAMPLE - ), - ] + )] ); } @@ -97,48 +65,60 @@ public function run() */ public function getNodeTypes(): array { - return [If_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param If_ $node + * @param StmtsAware $node + * @return StmtsAware|Return_|null */ public function refactor(Node $node): ?Node { - if (! $node->cond instanceof Equal && ! $node->cond instanceof Identical) { - return null; + if ($node instanceof If_) { + return $this->refactorIf($node); } - $this->reset(); - - $this->matchOnEqualFirstValueAndSecondValue($node); - if ($this->firstValue === null) { - return null; - } - if ($this->secondValue === null) { + if ($node->stmts === null) { return null; } - /** @var Equal|Identical $condition */ - $condition = $node->cond; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Return_) { + continue; + } - if (! $this->areVariablesEqual($condition, $this->firstValue, $this->secondValue)) { - return null; - } + if (! $stmt->expr instanceof Ternary) { + continue; + } - // is spaceship return values? - if ([$this->onGreater, $this->onEqual, $this->onSmaller] !== [-1, 0, 1]) { - return null; - } + // preceded by if + $prevStmt = $node->stmts[$key - 1] ?? null; + if (! $prevStmt instanceof If_) { + continue; + } - if ($this->nextNode !== null) { - $this->removeNode($this->nextNode); - } + $comparedExprs = $this->matchExprComparedExprsReturnZero($prevStmt); + if (! $comparedExprs instanceof ComparedExprs) { + continue; + } - // spaceship ready! - $spaceship = new Spaceship($this->secondValue, $this->firstValue); + $battleshipCompareOrder = $this->battleshipTernaryAnalyzer->isGreaterLowerCompareReturnOneAndMinusOne( + $stmt->expr, + $comparedExprs + ); - return new Return_($spaceship); + $returnSpaceship = $this->createReturnSpaceship($battleshipCompareOrder, $comparedExprs); + if (! $returnSpaceship instanceof Return_) { + continue; + } + + unset($node->stmts[$key - 1]); + + $node->stmts[$key] = $returnSpaceship; + return $node; + } + + return null; } public function provideMinPhpVersion(): int @@ -146,100 +126,101 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::SPACESHIP; } - private function reset(): void + private function refactorIf(If_ $if): ?Return_ { - $this->onEqual = null; - $this->onSmaller = null; - $this->onGreater = null; + if ($if->elseifs !== []) { + return null; + } - $this->firstValue = null; - $this->secondValue = null; - } + if (! $if->else instanceof Else_) { + return null; + } - private function matchOnEqualFirstValueAndSecondValue(If_ $if): void - { - $this->matchOnEqual($if); + $comparedExprs = $this->matchExprComparedExprsReturnZero($if); + if (! $comparedExprs instanceof ComparedExprs) { + return null; + } - if ($if->else !== null) { - $this->processElse($if->else); - } else { - $this->nextNode = $if->getAttribute(AttributeKey::NEXT_NODE); - if ($this->nextNode instanceof Return_ && $this->nextNode->expr instanceof Ternary) { - /** @var Ternary $ternary */ - $ternary = $this->nextNode->expr; - $this->processTernary($ternary); - } + $ternary = $this->matchElseOnlyStmtTernary($if->else); + if (! $ternary instanceof Ternary) { + return null; } + + $battleshipCompareOrder = $this->battleshipTernaryAnalyzer->isGreaterLowerCompareReturnOneAndMinusOne( + $ternary, + $comparedExprs + ); + + return $this->createReturnSpaceship($battleshipCompareOrder, $comparedExprs); } - private function areVariablesEqual(BinaryOp $binaryOp, Expr $firstValue, Expr $secondValue): bool + /** + * We look for: + * + * if ($firstValue === $secondValue) { + * return 0; + * } + */ + private function matchExprComparedExprsReturnZero(If_ $if): ?ComparedExprs { - if ($this->nodeComparator->areNodesEqual($binaryOp->left, $firstValue) && $this->nodeComparator->areNodesEqual( - $binaryOp->right, - $secondValue - )) { - return true; - } - if (! $this->nodeComparator->areNodesEqual($binaryOp->right, $firstValue)) { - return false; + if (! $if->cond instanceof Equal && ! $if->cond instanceof Identical) { + return null; } - return $this->nodeComparator->areNodesEqual($binaryOp->left, $secondValue); - } - private function matchOnEqual(If_ $if): void - { + $binaryOp = $if->cond; if (count($if->stmts) !== 1) { - return; + return null; } - $onlyIfStmt = $if->stmts[0]; + $onlyStmt = $if->stmts[0]; + if (! $onlyStmt instanceof Return_) { + return null; + } - if ($onlyIfStmt instanceof Return_) { - if ($onlyIfStmt->expr === null) { - return; - } + if (! $onlyStmt->expr instanceof Expr) { + return null; + } - $this->onEqual = $this->valueResolver->getValue($onlyIfStmt->expr); + if (! $this->valueResolver->isValue($onlyStmt->expr, 0)) { + return null; } + + return new ComparedExprs($binaryOp->left, $binaryOp->right); } - private function processElse(Else_ $else): void + /** + * @param BattleshipCompareOrder::*|null $battleshipCompareOrder + */ + private function createReturnSpaceship(?string $battleshipCompareOrder, ComparedExprs $comparedExprs): ?Return_ { - if (count($else->stmts) !== 1) { - return; + if ($battleshipCompareOrder === null) { + return null; } - if (! $else->stmts[0] instanceof Return_) { - return; + if ($battleshipCompareOrder === BattleshipCompareOrder::DESC) { + $spaceship = new Spaceship($comparedExprs->getFirstExpr(), $comparedExprs->getSecondExpr()); + } else { + $spaceship = new Spaceship($comparedExprs->getSecondExpr(), $comparedExprs->getFirstExpr()); } - /** @var Return_ $returnNode */ - $returnNode = $else->stmts[0]; - if ($returnNode->expr instanceof Ternary) { - $this->processTernary($returnNode->expr); - } + return new Return_($spaceship); } - private function processTernary(Ternary $ternary): void + private function matchElseOnlyStmtTernary(Else_ $else): Ternary|null { - if ($ternary->cond instanceof Smaller) { - $this->firstValue = $ternary->cond->left; - $this->secondValue = $ternary->cond->right; - - if ($ternary->if !== null) { - $this->onSmaller = $this->valueResolver->getValue($ternary->if); - } - - $this->onGreater = $this->valueResolver->getValue($ternary->else); - } elseif ($ternary->cond instanceof Greater) { - $this->firstValue = $ternary->cond->right; - $this->secondValue = $ternary->cond->left; + if (count($else->stmts) !== 1) { + return null; + } - if ($ternary->if !== null) { - $this->onGreater = $this->valueResolver->getValue($ternary->if); - } + $onlyElseStmt = $else->stmts[0]; + if (! $onlyElseStmt instanceof Return_) { + return null; + } - $this->onSmaller = $this->valueResolver->getValue($ternary->else); + if (! $onlyElseStmt->expr instanceof Ternary) { + return null; } + + return $onlyElseStmt->expr; } } diff --git a/rules/Php70/Rector/List_/EmptyListRector.php b/rules/Php70/Rector/List_/EmptyListRector.php index f4f56bb554c..4eda277099d 100644 --- a/rules/Php70/Rector/List_/EmptyListRector.php +++ b/rules/Php70/Rector/List_/EmptyListRector.php @@ -5,18 +5,19 @@ namespace Rector\Php70\Rector\List_; use PhpParser\Node; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\List_; use PhpParser\Node\Expr\Variable; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog http://php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.list * @see \Rector\Tests\Php70\Rector\List_\EmptyListRector\EmptyListRectorTest */ -final class EmptyListRector extends AbstractRector +final class EmptyListRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition(): RuleDefinition { @@ -34,6 +35,11 @@ public function getRuleDefinition(): RuleDefinition ); } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_EMPTY_LIST; + } + /** * @return array> */ @@ -48,7 +54,7 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { foreach ($node->items as $item) { - if ($item !== null) { + if ($item instanceof ArrayItem) { return null; } } diff --git a/rules/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector.php b/rules/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector.php index f0a63d766dc..5f99652cd7f 100644 --- a/rules/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector.php +++ b/rules/Php70/Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector.php @@ -6,29 +6,42 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; +use PhpParser\Node\Scalar\InterpolatedString; use PhpParser\Node\Stmt\Class_; +use PhpParser\NodeVisitor; +use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\Php\PhpMethodReflection; -use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Reflection\ReflectionResolver; +use Rector\Enum\ObjectReference; use Rector\NodeCollector\StaticAnalyzer; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/rkiSC * @see \Rector\Tests\Php70\Rector\MethodCall\ThisCallOnStaticMethodToStaticCallRector\ThisCallOnStaticMethodToStaticCallRectorTest */ -final class ThisCallOnStaticMethodToStaticCallRector extends AbstractRector +final class ThisCallOnStaticMethodToStaticCallRector extends AbstractRector implements MinPhpVersionInterface { + private bool $hasChanged = false; + public function __construct( - private StaticAnalyzer $staticAnalyzer, - private ReflectionResolver $reflectionResolver + private readonly StaticAnalyzer $staticAnalyzer, + private readonly ReflectionResolver $reflectionResolver, ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::STATIC_CALL_ON_NON_STATIC; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -72,67 +85,102 @@ public static function eat() */ public function getNodeTypes(): array { - return [MethodCall::class]; + return [Class_::class]; } /** - * @param MethodCall $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if (! $node->var instanceof Variable) { + $scope = ScopeFetcher::fetch($node); + if (! $scope->isInClass()) { return null; } - if (! $this->nodeNameResolver->isName($node->var, 'this')) { - return null; - } - - $methodName = $this->getName($node->name); - if ($methodName === null) { - return null; - } + $classReflection = $scope->getClassReflection(); // skip PHPUnit calls, as they accept both self:: and $this-> formats - if ($this->isObjectType($node->var, new ObjectType('PHPUnit\Framework\TestCase'))) { + if ($classReflection->is('PHPUnit\Framework\TestCase')) { return null; } - /** @var class-string $className */ - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - if (! is_string($className)) { - return null; - } + $this->hasChanged = false; - $isStaticMethod = $this->staticAnalyzer->isStaticMethod($methodName, $className); - if (! $isStaticMethod) { - return null; + $this->processThisToStatic($node, $classReflection); + + if ($this->hasChanged) { + return $node; } - $classReference = $this->resolveClassSelf($node); - return $this->nodeFactory->createStaticCall($classReference, $methodName, $node->args); + return null; } - private function resolveClassSelf(MethodCall $methodCall): string + private function processThisToStatic(Class_ $class, ClassReflection $classReflection): void { - $classLike = $methodCall->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return 'static'; - } + $this->traverseNodesWithCallable($class, function (Node $subNode) use ( + $class, + $classReflection + ): null|StaticCall|int { + if ($subNode instanceof InterpolatedString) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $subNode instanceof MethodCall) { + return null; + } + + if (! $subNode->var instanceof Variable) { + return null; + } + + if (! $this->isName($subNode->var, 'this')) { + return null; + } + + if (! $subNode->name instanceof Identifier) { + return null; + } + + $methodName = $this->getName($subNode->name); + if ($methodName === null) { + return null; + } + + $isStaticMethod = $this->staticAnalyzer->isStaticMethod($classReflection, $methodName, $class); + if (! $isStaticMethod) { + return null; + } + + if ($subNode->isFirstClassCallable()) { + return null; + } + + $this->hasChanged = true; + + $objectReference = $this->resolveClassSelf($classReflection, $subNode); + return $this->nodeFactory->createStaticCall($objectReference, $methodName, $subNode->args); + }); + } - if ($classLike->isFinal()) { - return 'self'; + /** + * @return ObjectReference::STATIC|ObjectReference::SELF + */ + private function resolveClassSelf(ClassReflection $classReflection, MethodCall $methodCall): string + { + if ($classReflection->isFinalByKeyword()) { + return ObjectReference::SELF; } $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromMethodCall($methodCall); if (! $methodReflection instanceof PhpMethodReflection) { - return 'static'; + return ObjectReference::STATIC; } if (! $methodReflection->isPrivate()) { - return 'static'; + return ObjectReference::STATIC; } - return 'self'; + return ObjectReference::SELF; } } diff --git a/rules/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector.php b/rules/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector.php index 6ff53c3ffa9..23d3004f249 100644 --- a/rules/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector.php +++ b/rules/Php70/Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector.php @@ -10,32 +10,43 @@ use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\Variable; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Reflection\ReflectionResolver; +use Rector\CodingStyle\ValueObject\ObjectMagicMethods; +use Rector\Enum\ObjectReference; use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver; use Rector\NodeCollector\StaticAnalyzer; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use ReflectionMethod; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://thephp.cc/news/2017/07/dont-call-instance-methods-statically https://3v4l.org/tQ32f https://3v4l.org/jB9jn - * * @see \Rector\Tests\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector\StaticCallOnNonStaticToInstanceCallRectorTest */ -final class StaticCallOnNonStaticToInstanceCallRector extends AbstractRector +final class StaticCallOnNonStaticToInstanceCallRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private StaticAnalyzer $staticAnalyzer, - private ReflectionProvider $reflectionProvider, - private ReflectionResolver $reflectionResolver, - private ParentClassScopeResolver $parentClassScopeResolver + private readonly StaticAnalyzer $staticAnalyzer, + private readonly ReflectionProvider $reflectionProvider, + private readonly ReflectionResolver $reflectionResolver, + private readonly ParentClassScopeResolver $parentClassScopeResolver ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::INSTANCE_CALL; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -103,15 +114,22 @@ public function refactor(Node $node): ?Node if ($methodName === null) { return null; } + if ($className === null) { return null; } - if ($this->shouldSkip($methodName, $className, $node)) { + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkip($methodName, $className, $node, $scope)) { return null; } - if ($this->isInstantiable($className)) { + $classReflection = $scope->getClassReflection(); + if ($classReflection instanceof ClassReflection && $classReflection->getName() === $className) { + return new MethodCall(new Variable('this'), $node->name, $node->args); + } + + if ($this->isInstantiable($className, $scope)) { $new = new New_($node->class); return new MethodCall($new, $node->name, $node->args); } @@ -122,7 +140,7 @@ public function refactor(Node $node): ?Node private function resolveStaticCallClassName(StaticCall $staticCall): ?string { if ($staticCall->class instanceof PropertyFetch) { - $objectType = $this->getObjectType($staticCall->class); + $objectType = $this->getType($staticCall->class); if ($objectType instanceof ObjectType) { return $objectType->getClassName(); } @@ -131,36 +149,67 @@ private function resolveStaticCallClassName(StaticCall $staticCall): ?string return $this->getName($staticCall->class); } - private function shouldSkip(string $methodName, string $className, StaticCall $staticCall): bool + private function shouldSkip(string $methodName, string $className, StaticCall $staticCall, Scope $scope): bool { - $isStaticMethod = $this->staticAnalyzer->isStaticMethod($methodName, $className); + if (in_array($methodName, ObjectMagicMethods::METHOD_NAMES, true)) { + return true; + } + + if (! $this->reflectionProvider->hasClass($className)) { + return true; + } + + $classReflection = $this->reflectionProvider->getClass($className); + if ($classReflection->isAbstract()) { + return true; + } + + // does the method even exist? + if (! $classReflection->hasMethod($methodName)) { + return true; + } + + $isStaticMethod = $this->staticAnalyzer->isStaticMethod($classReflection, $methodName); if ($isStaticMethod) { return true; } - if ($this->isNames($staticCall->class, ['self', 'parent', 'static', 'class'])) { + $currentClassReflection = $scope->getClassReflection(); + if ($currentClassReflection instanceof ClassReflection + && $this->reflectionProvider->hasClass($className) + && $currentClassReflection->isSubclassOfClass($this->reflectionProvider->getClass($className)) + ) { + return true; + } + + $className = $this->getName($staticCall->class); + if (in_array($className, [ObjectReference::PARENT, ObjectReference::SELF, ObjectReference::STATIC], true)) { + return true; + } + + if ($className === 'class') { return true; } - $parentClassName = $this->parentClassScopeResolver->resolveParentClassName($staticCall); + $parentClassName = $this->parentClassScopeResolver->resolveParentClassName($scope); return $className === $parentClassName; } - private function isInstantiable(string $className): bool + private function isInstantiable(string $className, Scope $scope): bool { if (! $this->reflectionProvider->hasClass($className)) { return false; } - $methodReflection = $this->reflectionResolver->resolveMethodReflection($className, '__callStatic', null); + $methodReflection = $this->reflectionResolver->resolveMethodReflection($className, '__callStatic', $scope); if ($methodReflection instanceof MethodReflection) { return false; } $classReflection = $this->reflectionProvider->getClass($className); - $reflectionClass = $classReflection->getNativeReflection(); + $nativeReflection = $classReflection->getNativeReflection(); - $reflectionMethod = $reflectionClass->getConstructor(); + $reflectionMethod = $nativeReflection->getConstructor(); if (! $reflectionMethod instanceof ReflectionMethod) { return true; } diff --git a/rules/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector.php b/rules/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector.php new file mode 100644 index 00000000000..0a1e76a39ee --- /dev/null +++ b/rules/Php70/Rector/StmtsAwareInterface/IfIssetToCoalescingRector.php @@ -0,0 +1,150 @@ +items[$key])) { + return $this->items[$key]; + } + + return 'fallback value'; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + private $items = []; + + public function resolve($key) + { + return $this->items[$key] ?? 'fallback value'; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Return_) { + continue; + } + + if (! $stmt->expr instanceof Expr) { + continue; + } + + $previousStmt = $node->stmts[$key - 1] ?? null; + + if (! $previousStmt instanceof If_) { + continue; + } + + if (! $previousStmt->cond instanceof Isset_) { + continue; + } + + $ifOnlyStmt = $this->matchBareIfOnlyStmt($previousStmt); + + if (! $ifOnlyStmt instanceof Return_) { + continue; + } + + if (! $ifOnlyStmt->expr instanceof Expr) { + continue; + } + + $ifIsset = $previousStmt->cond; + if (! $this->nodeComparator->areNodesEqual($ifOnlyStmt->expr, $ifIsset->vars[0])) { + continue; + } + + unset($node->stmts[$key - 1]); + + if ($stmt->expr instanceof Ternary) { + $stmt->expr->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + } + + $stmt->expr = new Coalesce($ifOnlyStmt->expr, $stmt->expr); + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NULL_COALESCE; + } + + private function matchBareIfOnlyStmt(If_ $if): ?Stmt + { + if ($if->else instanceof Else_) { + return null; + } + + if ($if->elseifs !== []) { + return null; + } + + if (count($if->stmts) !== 1) { + return null; + } + + return $if->stmts[0]; + } +} diff --git a/rules/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector.php b/rules/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector.php index bb0aaee03d3..493e95b230d 100644 --- a/rules/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector.php +++ b/rules/Php70/Rector/Switch_/ReduceMultipleDefaultSwitchRector.php @@ -5,22 +5,24 @@ namespace Rector\Php70\Rector\Switch_; use PhpParser\Node; -use PhpParser\Node\Stmt\Case_; +use PhpParser\Node\Expr; use PhpParser\Node\Stmt\Switch_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/iGDVW - * - * @changelog https://wiki.php.net/rfc/switch.default.multiple https://stackoverflow.com/a/44000794/1348344 https://github.com/franzliedke/wp-mpdf/commit/9dc489215fbd1adcb514810653a73dea71db8e99#diff-2f1f4a51a2dd3a73ca034a48a67a2320L1373 - * * @see \Rector\Tests\Php70\Rector\Switch_\ReduceMultipleDefaultSwitchRector\ReduceMultipleDefaultSwitchRectorTest */ -final class ReduceMultipleDefaultSwitchRector extends AbstractRector +final class ReduceMultipleDefaultSwitchRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_MULTIPLE_DEFAULT_SWITCH; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -64,46 +66,31 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { $defaultCases = []; - foreach ($node->cases as $case) { - if ($case->cond !== null) { + foreach ($node->cases as $key => $case) { + if ($case->cond instanceof Expr) { continue; } - $defaultCases[] = $case; + $defaultCases[$key] = $case; } - if (count($defaultCases) < 2) { + $defaultCaseCount = count($defaultCases); + if ($defaultCaseCount < 2) { return null; } - $this->removeExtraDefaultCases($defaultCases); - - return $node; - } - - /** - * @param Case_[] $defaultCases - */ - private function removeExtraDefaultCases(array $defaultCases): void - { - // keep only last - array_pop($defaultCases); - foreach ($defaultCases as $defaultCase) { - $this->keepStatementsToParentCase($defaultCase); - $this->removeNode($defaultCase); - } - } + foreach ($node->cases as $key => $case) { + if ($case->cond instanceof Expr) { + continue; + } - private function keepStatementsToParentCase(Case_ $case): void - { - $previousNode = $case->getAttribute(AttributeKey::PREVIOUS_NODE); - if (! $previousNode instanceof Case_) { - return; + // remove previous default cases + if ($defaultCaseCount > 1) { + unset($node->cases[$key]); + --$defaultCaseCount; + } } - if ($previousNode->stmts === []) { - $previousNode->stmts = $case->stmts; - $case->stmts = []; - } + return $node; } } diff --git a/rules/Php70/Rector/Ternary/TernaryToNullCoalescingRector.php b/rules/Php70/Rector/Ternary/TernaryToNullCoalescingRector.php index a01746fb562..681fc708e99 100644 --- a/rules/Php70/Rector/Ternary/TernaryToNullCoalescingRector.php +++ b/rules/Php70/Rector/Ternary/TernaryToNullCoalescingRector.php @@ -6,13 +6,17 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\Coalesce; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\Isset_; use PhpParser\Node\Expr\Ternary; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\Application\File; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,6 +26,11 @@ */ final class TernaryToNullCoalescingRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -47,7 +56,7 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { if ($node->cond instanceof Isset_) { - return $this->processTernaryWithIsset($node); + return $this->processTernaryWithIsset($node, $node->cond); } if ($node->cond instanceof Identical) { @@ -60,18 +69,20 @@ public function refactor(Node $node): ?Node // not a match return null; } - if ($checkedNode === null) { + + if (! $checkedNode instanceof Expr) { return null; } + if (! $fallbackNode instanceof Expr) { return null; } - /** @var Identical|NotIdentical $ternaryCompareNode */ $ternaryCompareNode = $node->cond; if ($this->isNullMatch($ternaryCompareNode->left, $ternaryCompareNode->right, $checkedNode)) { return new Coalesce($checkedNode, $fallbackNode); } + if ($this->isNullMatch($ternaryCompareNode->right, $ternaryCompareNode->left, $checkedNode)) { return new Coalesce($checkedNode, $fallbackNode); } @@ -84,30 +95,67 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::NULL_COALESCE; } - private function processTernaryWithIsset(Ternary $ternary): ?Coalesce + private function processTernaryWithIsset(Ternary $ternary, Isset_ $isset): ?Coalesce { - if ($ternary->if === null) { + if (! $ternary->if instanceof Expr) { + return null; + } + + if ($isset->vars === []) { return null; } - /** @var Isset_ $issetNode */ - $issetNode = $ternary->cond; // none or multiple isset values cannot be handled here - if (! isset($issetNode->vars[0])) { + if (count($isset->vars) > 1) { return null; } - if (count($issetNode->vars) > 1) { + + if (! $this->nodeComparator->areNodesEqual($ternary->if, $isset->vars[0])) { return null; } - if ($this->nodeComparator->areNodesEqual($ternary->if, $issetNode->vars[0])) { - return new Coalesce($ternary->if, $ternary->else); + if (($ternary->else instanceof Ternary || $ternary->else instanceof BinaryOp) && $this->isTernaryParenthesized( + $this->getFile(), + $ternary->cond, + $ternary + )) { + $ternary->else->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); } - return null; + return new Coalesce($ternary->if, $ternary->else); + } + + private function isTernaryParenthesized(File $file, Expr $expr, Ternary $ternary): bool + { + $oldTokens = $file->getOldTokens(); + $endTokenPost = $ternary->getEndTokenPos(); + + if (isset($oldTokens[$endTokenPost]) && (string) $oldTokens[$endTokenPost] === ')') { + $startTokenPos = $ternary->else->getStartTokenPos(); + $previousEndTokenPost = $expr->getEndTokenPos(); + + while ($startTokenPos > $previousEndTokenPost) { + --$startTokenPos; + + if (! isset($oldTokens[$startTokenPos])) { + return false; + } + + // handle space before open parentheses + if (trim((string) $oldTokens[$startTokenPos]) === '') { + continue; + } + + return (string) $oldTokens[$startTokenPos] === '('; + } + + return false; + } + + return false; } - private function isNullMatch(Expr $possibleNullExpr, Node $firstNode, Node $secondNode): bool + private function isNullMatch(Expr $possibleNullExpr, Expr $firstNode, Expr $secondNode): bool { if (! $this->valueResolver->isNull($possibleNullExpr)) { return false; diff --git a/rules/Php70/Rector/Ternary/TernaryToSpaceshipRector.php b/rules/Php70/Rector/Ternary/TernaryToSpaceshipRector.php index ff3a0650ab4..753cb646f7b 100644 --- a/rules/Php70/Rector/Ternary/TernaryToSpaceshipRector.php +++ b/rules/Php70/Rector/Ternary/TernaryToSpaceshipRector.php @@ -10,18 +10,23 @@ use PhpParser\Node\Expr\BinaryOp\Smaller; use PhpParser\Node\Expr\BinaryOp\Spaceship; use PhpParser\Node\Expr\Ternary; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/combined-comparison-operator * @see \Rector\Tests\Php70\Rector\Ternary\TernaryToSpaceshipRector\TernaryToSpaceshipRectorTest */ final class TernaryToSpaceshipRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -65,7 +70,7 @@ public function refactor(Node $node): ?Node $nestedTernary = $node->else; $spaceshipNode = $this->processSmallerThanTernary($node, $nestedTernary); - if ($spaceshipNode !== null) { + if ($spaceshipNode instanceof Spaceship) { return $spaceshipNode; } @@ -97,6 +102,7 @@ private function shouldSkip(Ternary $ternary): bool if (! $this->nodeComparator->areNodesEqual($ternary->cond->left, $nestedTernary->cond->left)) { return true; } + // $a X $b ? . : ($a X $b ? . : .) return ! $this->nodeComparator->areNodesEqual($ternary->cond->right, $nestedTernary->cond->right); } diff --git a/rules/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector.php b/rules/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector.php index 9594a6cef33..db673c7950c 100644 --- a/rules/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector.php +++ b/rules/Php70/Rector/Variable/WrapVariableVariableNameInCurlyBracesRector.php @@ -7,16 +7,22 @@ use PhpParser\Node; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\Variable\WrapVariableVariableNameInCurlyBracesRector\WrapVariableVariableNameInCurlyBracesRectorTest - * @changelog https://www.php.net/manual/en/language.variables.variable.php */ -final class WrapVariableVariableNameInCurlyBracesRector extends AbstractRector +final class WrapVariableVariableNameInCurlyBracesRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::WRAP_VARIABLE_VARIABLE; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( diff --git a/rules/Php70/ValueObject/ComparedExprs.php b/rules/Php70/ValueObject/ComparedExprs.php new file mode 100644 index 00000000000..3e9c866f806 --- /dev/null +++ b/rules/Php70/ValueObject/ComparedExprs.php @@ -0,0 +1,26 @@ +firstExpr; + } + + public function getSecondExpr(): Expr + { + return $this->secondExpr; + } +} diff --git a/rules/Php70/ValueObject/VariableAssignPair.php b/rules/Php70/ValueObject/VariableAssignPair.php deleted file mode 100644 index 80690ce1672..00000000000 --- a/rules/Php70/ValueObject/VariableAssignPair.php +++ /dev/null @@ -1,37 +0,0 @@ -variable; - } - - public function getAssign(): Assign | AssignOp | AssignRef - { - return $this->assign; - } -} diff --git a/rules/Php71/IsArrayAndDualCheckToAble.php b/rules/Php71/IsArrayAndDualCheckToAble.php index 93b97a59dd0..22b8ba20f07 100644 --- a/rules/Php71/IsArrayAndDualCheckToAble.php +++ b/rules/Php71/IsArrayAndDualCheckToAble.php @@ -9,17 +9,18 @@ use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Instanceof_; -use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name; -use Rector\Core\NodeManipulator\BinaryOpManipulator; +use Rector\NodeManipulator\BinaryOpManipulator; use Rector\NodeNameResolver\NodeNameResolver; use Rector\Php71\ValueObject\TwoNodeMatch; +use Rector\PhpParser\Comparing\NodeComparator; -final class IsArrayAndDualCheckToAble +final readonly class IsArrayAndDualCheckToAble { public function __construct( private BinaryOpManipulator $binaryOpManipulator, - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, + private NodeComparator $nodeComparator ) { } @@ -35,13 +36,13 @@ public function processBooleanOr(BooleanOr $booleanOr, string $type, string $new return null; } - /** @var Instanceof_ $instanceOf */ - $instanceOf = $twoNodeMatch->getFirstExpr(); + /** @var Instanceof_ $instanceofExpr */ + $instanceofExpr = $twoNodeMatch->getFirstExpr(); - /** @var FuncCall $funcCall */ - $funcCall = $twoNodeMatch->getSecondExpr(); + /** @var FuncCall $funcCallExpr */ + $funcCallExpr = $twoNodeMatch->getSecondExpr(); - $instanceOfClass = $instanceOf->class; + $instanceOfClass = $instanceofExpr->class; if ($instanceOfClass instanceof Expr) { return null; } @@ -50,30 +51,26 @@ public function processBooleanOr(BooleanOr $booleanOr, string $type, string $new return null; } - if (! $this->nodeNameResolver->isName($funcCall, 'is_array')) { + if (! $this->nodeNameResolver->isName($funcCallExpr, 'is_array')) { return null; } - // both use same var - if (! $funcCall->args[0]->value instanceof Variable) { + if ($funcCallExpr->isFirstClassCallable()) { return null; } - /** @var Variable $firstVarNode */ - $firstVarNode = $funcCall->args[0]->value; - - if (! $instanceOf->expr instanceof Variable) { + if (! isset($funcCallExpr->getArgs()[0])) { return null; } - /** @var Variable $secondVarNode */ - $secondVarNode = $instanceOf->expr; + $firstArg = $funcCallExpr->getArgs()[0]; - // are they same variables - if ($firstVarNode->name !== $secondVarNode->name) { + $firstExprNode = $firstArg->value; + if (! $this->nodeComparator->areNodesEqual($instanceofExpr->expr, $firstExprNode)) { return null; } - return new FuncCall(new Name($newMethodName), [new Arg($firstVarNode)]); + // both use same Expr + return new FuncCall(new Name($newMethodName), [new Arg($firstExprNode)]); } } diff --git a/rules/Php71/NodeAnalyzer/CountableAnalyzer.php b/rules/Php71/NodeAnalyzer/CountableAnalyzer.php deleted file mode 100644 index adba77dbd86..00000000000 --- a/rules/Php71/NodeAnalyzer/CountableAnalyzer.php +++ /dev/null @@ -1,69 +0,0 @@ -nodeTypeResolver->resolve($expr->var); - - $propertyName = $this->nodeNameResolver->getName($expr->name); - if (! is_string($propertyName)) { - return false; - } - - if ($callerObjectType instanceof UnionType) { - $callerObjectType = $callerObjectType->getTypes()[0]; - } - - if (! $callerObjectType instanceof TypeWithClassName) { - return false; - } - - if (is_a($callerObjectType->getClassName(), Stmt::class, true)) { - return false; - } - - if (is_a($callerObjectType->getClassName(), Array_::class, true)) { - return false; - } - - // this must be handled reflection, as PHPStan ReflectionProvider does not provide default values for properties in any way - - $classReflection = $this->reflectionProvider->getClass($callerObjectType->getClassName()); - - $nativeReflectionClass = $classReflection->getNativeReflection(); - $propertiesDefaults = $nativeReflectionClass->getDefaultProperties(); - - if (! array_key_exists($propertyName, $propertiesDefaults)) { - return false; - } - - $propertyDefaultValue = $propertiesDefaults[$propertyName]; - return $propertyDefaultValue === null; - } -} diff --git a/rules/Php71/Rector/Assign/AssignArrayToStringRector.php b/rules/Php71/Rector/Assign/AssignArrayToStringRector.php index 6a2c1bf6a89..7e69acbbb2d 100644 --- a/rules/Php71/Rector/Assign/AssignArrayToStringRector.php +++ b/rules/Php71/Rector/Assign/AssignArrayToStringRector.php @@ -9,23 +9,39 @@ use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\StaticPropertyFetch; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Scalar\String_; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\Property; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\NodeVisitor; +use PHPStan\Type\UnionType; +use Rector\PhpParser\Node\FileNode; +use Rector\PhpParser\NodeFinder\PropertyFetchFinder; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/a/41000866/1348344 https://3v4l.org/ABDNv - * * @see \Rector\Tests\Php71\Rector\Assign\AssignArrayToStringRector\AssignArrayToStringRectorTest */ -final class AssignArrayToStringRector extends AbstractRector +final class AssignArrayToStringRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly PropertyFetchFinder $propertyFetchFinder, + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_ASSIGN_ARRAY_TO_STRING; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -49,89 +65,218 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Assign::class, Property::class]; + return [ + Namespace_::class, + FileNode::class, + Class_::class, + ClassMethod::class, + Function_::class, + Closure::class, + ]; } /** - * @param Assign|Property $node + * @param Namespace_|FileNode|Class_|ClassMethod|Function_|Closure $node */ public function refactor(Node $node): ?Node { - $defaultExpr = $this->resolveDefaultValueExpr($node); - if (! $defaultExpr instanceof Expr) { + if ($node instanceof Class_) { + return $this->refactorClass($node); + } + + if ($node->stmts === null) { return null; } - if (! $this->isEmptyString($defaultExpr)) { + if ($node instanceof FileNode && $node->isNamespaced()) { + // handled in Namespace_ return null; } - $assignedVar = $this->resolveAssignedVar($node); + $hasChanged = false; + $this->traverseNodesWithCallable( + $node->stmts, + function (Node $subNode) use (&$hasChanged, $node): ?int { + if ($subNode instanceof Class_ || $subNode instanceof Function_ || $subNode instanceof Closure) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } - // 1. variable! - $shouldRetype = false; + if ($subNode instanceof Assign) { + $assign = $this->refactorAssign($subNode, $node); - /** @var array $exprUsages */ - $exprUsages = $this->betterNodeFinder->findSameNamedExprs($assignedVar); + if ($assign instanceof Assign) { + $hasChanged = true; + return null; + } + } - // detect if is part of variable assign? - foreach ($exprUsages as $exprUsage) { - $parent = $exprUsage->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof ArrayDimFetch) { - continue; + return null; } + ); - $firstAssign = $this->betterNodeFinder->findParentType($parent, Assign::class); - if (! $firstAssign instanceof Assign) { - continue; - } + if ($hasChanged) { + return $node; + } + + return null; + } - // skip explicit assigns - if ($parent->dim !== null) { + private function isEmptyString(Expr $expr): bool + { + if (! $expr instanceof String_) { + return false; + } + + return $expr->value === ''; + } + + private function refactorClass(Class_ $class): ?Class_ + { + $hasChanged = false; + + foreach ($class->getProperties() as $property) { + if (! $this->hasPropertyDefaultEmptyString($property)) { continue; } - $shouldRetype = true; - break; - } + $arrayDimFetches = $this->propertyFetchFinder->findLocalPropertyArrayDimFetchesAssignsByName( + $class, + $property + ); - if (! $shouldRetype) { - return null; + foreach ($arrayDimFetches as $arrayDimFetch) { + if ($arrayDimFetch->dim instanceof Expr) { + continue; + } + + $property->props[0]->default = new Array_(); + $hasChanged = true; + } } - if ($node instanceof Property) { - $node->props[0]->default = new Array_(); - return $node; + if ($hasChanged) { + return $class; } - $node->expr = new Array_(); - return $node; + return null; } - private function resolveDefaultValueExpr(Assign | Property $node): ?Expr + private function hasPropertyDefaultEmptyString(Property $property): bool { - if ($node instanceof Property) { - return $node->props[0]->default; + $defaultExpr = $property->props[0]->default; + if (! $defaultExpr instanceof Expr) { + return false; } - return $node->expr; + return $this->isEmptyString($defaultExpr); } - private function isEmptyString(Expr $expr): bool - { - if (! $expr instanceof String_) { - return false; + /** + * @return ArrayDimFetch[] + */ + private function findSameNamedVariableAssigns( + Variable $variable, + Namespace_|FileNode|ClassMethod|Function_|Closure $node + ): array { + if ($node->stmts === null) { + return []; } - return $expr->value === ''; + $variableName = $this->getName($variable); + if ($variableName === null) { + return []; + } + + $assignedArrayDimFetches = []; + + $this->traverseNodesWithCallable($node->stmts, function (Node $node) use ( + $variable, + $variableName, + &$assignedArrayDimFetches + ) { + if (! $node instanceof Assign) { + return null; + } + + if ($this->isReAssignedAsArray($node, $variableName, $variable)) { + $assignedArrayDimFetches = []; + return NodeVisitor::STOP_TRAVERSAL; + } + + if (! $node->var instanceof ArrayDimFetch) { + return null; + } + + $arrayDimFetch = $node->var; + if (! $arrayDimFetch->var instanceof Variable) { + return null; + } + + if (! $this->isName($arrayDimFetch->var, $variableName)) { + return null; + } + + $assignedArrayDimFetches[] = $arrayDimFetch; + }); + + return $assignedArrayDimFetches; } - private function resolveAssignedVar(Assign | Property $node): Expr | Property + private function isReAssignedAsArray(Assign $assign, string $variableName, Variable $variable): bool { - if ($node instanceof Assign) { - return $node->var; + if ($assign->var instanceof Variable && $this->isName( + $assign->var, + $variableName + ) && $assign->var->getStartTokenPos() > $variable->getStartTokenPos()) { + $exprType = $this->nodeTypeResolver->getNativeType($assign->expr); + if ($exprType->isArray()->yes()) { + return true; + } + } + + return false; + } + + private function refactorAssign( + Assign $assign, + Namespace_|FileNode|ClassMethod|Function_|Closure $node + ): ?Assign { + if (! $assign->var instanceof Variable) { + return null; + } + + if (! $this->isEmptyString($assign->expr)) { + return null; + } + + $type = $this->nodeTypeResolver->getNativeType($assign->var); + if ($type->isArray()->yes()) { + return null; + } + + if ($type instanceof UnionType) { + return null; + } + + $variableAssignArrayDimFetches = $this->findSameNamedVariableAssigns($assign->var, $node); + + $shouldRetype = false; + + // detect if is part of variable assign? + foreach ($variableAssignArrayDimFetches as $variableAssignArrayDimFetch) { + if ($variableAssignArrayDimFetch->dim instanceof Expr) { + continue; + } + + $shouldRetype = true; + break; + } + + if (! $shouldRetype) { + return null; } - return $node; + $assign->expr = new Array_(); + return $assign; } } diff --git a/rules/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector.php b/rules/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector.php index 57cbdb522ab..9172b0f2dfc 100644 --- a/rules/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector.php +++ b/rules/Php71/Rector/BinaryOp/BinaryOpBetweenNumberAndStringRector.php @@ -11,21 +11,33 @@ use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Scalar; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Float_; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\MagicConst\Line; use PhpParser\Node\Scalar\String_; use PHPStan\Type\Constant\ConstantStringType; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeAnalyzer\ExprAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/XPEEl - * @see https://3v4l.org/ObNQZ * @see \Rector\Tests\Php71\Rector\BinaryOp\BinaryOpBetweenNumberAndStringRector\BinaryOpBetweenNumberAndStringRectorTest */ -final class BinaryOpBetweenNumberAndStringRector extends AbstractRector +final class BinaryOpBetweenNumberAndStringRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly ExprAnalyzer $exprAnalyzer + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::BINARY_OP_NUMBER_STRING; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -49,7 +61,7 @@ class SomeClass public function run() { $value = 5 + 0; - $value = 5.0 + 0; + $value = 5.0 + 0.0; } } CODE_SAMPLE @@ -74,21 +86,39 @@ public function refactor(Node $node): ?Node if ($node instanceof Concat) { return null; } + if ($node instanceof Coalesce) { return null; } - if ($this->isStringOrStaticNonNumbericString($node->left) && $this->nodeTypeResolver->isNumberType( + + if ($this->exprAnalyzer->isNonTypedFromParam($node->left)) { + return null; + } + + if ($this->exprAnalyzer->isNonTypedFromParam($node->right)) { + return null; + } + + if ($this->isStringOrStaticNonNumericString($node->left) && $this->nodeTypeResolver->isNumberType( $node->right )) { - $node->left = new LNumber(0); + $node->left = $this->nodeTypeResolver->getNativeType($node->right) + ->isInteger() + ->yes() + ? new Int_(0) + : new Float_(0); return $node; } - if ($this->isStringOrStaticNonNumbericString($node->right) && $this->nodeTypeResolver->isNumberType( + if ($this->isStringOrStaticNonNumericString($node->right) && $this->nodeTypeResolver->isNumberType( $node->left )) { - $node->right = new LNumber(0); + $node->right = $this->nodeTypeResolver->getNativeType($node->left) + ->isInteger() + ->yes() + ? new Int_(0) + : new Float_(0); return $node; } @@ -96,7 +126,7 @@ public function refactor(Node $node): ?Node return null; } - private function isStringOrStaticNonNumbericString(Expr $expr): bool + private function isStringOrStaticNonNumericString(Expr $expr): bool { // replace only scalar values, not variables/constants/etc. if (! $expr instanceof Scalar && ! $expr instanceof Variable) { @@ -107,17 +137,15 @@ private function isStringOrStaticNonNumbericString(Expr $expr): bool return false; } - $value = null; - $exprStaticType = $this->getStaticType($expr); - if ($expr instanceof String_) { - $value = $expr->value; - } elseif ($exprStaticType instanceof ConstantStringType) { - $value = $exprStaticType->getValue(); - } else { - return false; + return ! is_numeric($expr->value); + } + + $exprStaticType = $this->getType($expr); + if ($exprStaticType instanceof ConstantStringType) { + return ! is_numeric($exprStaticType->getValue()); } - return ! is_numeric($value); + return false; } } diff --git a/rules/Php71/Rector/BooleanOr/IsIterableRector.php b/rules/Php71/Rector/BooleanOr/IsIterableRector.php index f3232c6345f..360ca062a4e 100644 --- a/rules/Php71/Rector/BooleanOr/IsIterableRector.php +++ b/rules/Php71/Rector/BooleanOr/IsIterableRector.php @@ -8,23 +8,31 @@ use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Name; use PHPStan\Reflection\ReflectionProvider; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Php\PhpVersionProvider; use Rector\Php71\IsArrayAndDualCheckToAble; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php71\Rector\BooleanOr\IsIterableRector\IsIterableRectorTest */ -final class IsIterableRector extends AbstractRector +final class IsIterableRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private IsArrayAndDualCheckToAble $isArrayAndDualCheckToAble, - private ReflectionProvider $reflectionProvider + private readonly IsArrayAndDualCheckToAble $isArrayAndDualCheckToAble, + private readonly ReflectionProvider $reflectionProvider, + private readonly PhpVersionProvider $phpVersionProvider, ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::IS_ITERABLE; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -50,7 +58,7 @@ public function refactor(Node $node): ?Node return null; } - return $this->isArrayAndDualCheckToAble->processBooleanOr($node, 'Traversable', 'is_iterable') ?: $node; + return $this->isArrayAndDualCheckToAble->processBooleanOr($node, 'Traversable', 'is_iterable'); } private function shouldSkip(): bool diff --git a/rules/Php71/Rector/ClassConst/PublicConstantVisibilityRector.php b/rules/Php71/Rector/ClassConst/PublicConstantVisibilityRector.php deleted file mode 100644 index 4b99e1e5acd..00000000000 --- a/rules/Php71/Rector/ClassConst/PublicConstantVisibilityRector.php +++ /dev/null @@ -1,78 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassConst::class]; - } - - /** - * @param ClassConst $node - */ - public function refactor(Node $node): ?Node - { - // already non-public - if (! $node->isPublic()) { - return null; - } - - // explicitly public - if ($node->flags !== 0) { - return null; - } - - $this->visibilityManipulator->makePublic($node); - - return $node; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::CONSTANT_VISIBILITY; - } -} diff --git a/rules/Php71/Rector/FuncCall/CountOnNullRector.php b/rules/Php71/Rector/FuncCall/CountOnNullRector.php deleted file mode 100644 index 446c145b226..00000000000 --- a/rules/Php71/Rector/FuncCall/CountOnNullRector.php +++ /dev/null @@ -1,168 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - $countedNode = $node->args[0]->value; - if ($this->countableTypeAnalyzer->isCountableType($countedNode)) { - return null; - } - - // this can lead to false positive by phpstan, but that's best we can do - $onlyValueType = $this->getStaticType($countedNode); - if ($onlyValueType instanceof ArrayType) { - if (! $this->countableAnalyzer->isCastableArrayType($countedNode)) { - return null; - } - - return $this->castToArray($countedNode, $node); - } - - if ($this->nodeTypeResolver->isNullableTypeOfSpecificType($countedNode, ArrayType::class)) { - return $this->castToArray($countedNode, $node); - } - - if ($this->nodeTypeResolver->isNullableType($countedNode) || $this->nodeTypeResolver->isStaticType( - $countedNode, - NullType::class - )) { - $identical = new Identical($countedNode, $this->nodeFactory->createNull()); - $ternary = new Ternary($identical, new LNumber(0), $node); - // prevent infinity loop re-resolution - $node->setAttribute(self::ALREADY_CHANGED_ON_COUNT, true); - return $ternary; - } - - if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::IS_COUNTABLE)) { - $conditionNode = new FuncCall(new Name('is_countable'), [new Arg($countedNode)]); - } else { - $instanceof = new Instanceof_($countedNode, new FullyQualified('Countable')); - $conditionNode = new BooleanOr($this->nodeFactory->createFuncCall( - 'is_array', - [new Arg($countedNode)] - ), $instanceof); - } - - // prevent infinity loop re-resolution - $node->setAttribute(self::ALREADY_CHANGED_ON_COUNT, true); - - return new Ternary($conditionNode, $node, new LNumber(0)); - } - - private function shouldSkip(FuncCall $funcCall): bool - { - if (! $this->isName($funcCall, 'count')) { - return true; - } - - if (! isset($funcCall->args[0])) { - return true; - } - - if ($funcCall->args[0]->value instanceof ClassConstFetch) { - return true; - } - - $alreadyChangedOnCount = $funcCall->getAttribute(self::ALREADY_CHANGED_ON_COUNT); - - // check if it has some condition before already, if so, probably it's already handled - if ($alreadyChangedOnCount) { - return true; - } - - $parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Ternary) { - return true; - } - - // skip node in trait, as impossible to analyse - $classLike = $funcCall->getAttribute(AttributeKey::CLASS_NODE); - return $classLike instanceof Trait_; - } - - private function castToArray(Expr $countedExpr, FuncCall $funcCall): FuncCall - { - $castArray = new Array_($countedExpr); - $funcCall->args = [new Arg($castArray)]; - - return $funcCall; - } -} diff --git a/rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php b/rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php index 9391fcc5d58..dd17169e1f5 100644 --- a/rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php +++ b/rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php @@ -13,26 +13,31 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\Php\PhpMethodReflection; use PHPStan\Reflection\Type\UnionTypeMethodReflection; -use Rector\Core\NodeAnalyzer\VariadicAnalyzer; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Reflection\ReflectionResolver; +use Rector\Enum\ObjectReference; +use Rector\NodeAnalyzer\VariadicAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://www.reddit.com/r/PHP/comments/a1ie7g/is_there_a_linter_for_argumentcounterror_for_php/ - * @changelog http://php.net/manual/en/class.argumentcounterror.php - * * @see \Rector\Tests\Php71\Rector\FuncCall\RemoveExtraParametersRector\RemoveExtraParametersRectorTest */ -final class RemoveExtraParametersRector extends AbstractRector +final class RemoveExtraParametersRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private VariadicAnalyzer $variadicAnalyzer, - private ReflectionResolver $reflectionResolver + private readonly VariadicAnalyzer $variadicAnalyzer, + private readonly ReflectionResolver $reflectionResolver ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_EXTRA_PARAMETERS; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Remove extra parameters', [ @@ -68,6 +73,10 @@ public function refactor(Node $node): ?Node } if ($functionLikeReflection instanceof PhpMethodReflection) { + if ($functionLikeReflection->isAbstract()) { + return null; + } + $classReflection = $functionLikeReflection->getDeclaringClass(); if ($classReflection->isInterface()) { return null; @@ -75,8 +84,15 @@ public function refactor(Node $node): ?Node } $maximumAllowedParameterCount = $this->resolveMaximumAllowedParameterCount($functionLikeReflection); + if ($node->isFirstClassCallable()) { + return null; + } + + if ($this->shouldSkipFunctionReflection($functionLikeReflection)) { + return null; + } - $numberOfArguments = count($node->args); + $numberOfArguments = count($node->getRawArgs()); if ($numberOfArguments <= $maximumAllowedParameterCount) { return null; } @@ -88,6 +104,26 @@ public function refactor(Node $node): ?Node return $node; } + private function shouldSkipFunctionReflection(MethodReflection|FunctionReflection $reflection): bool + { + if ($reflection instanceof FunctionReflection) { + $fileName = (string) $reflection->getFileName(); + if (str_contains($fileName, 'phpstan.phar')) { + return true; + } + } + + if ($reflection instanceof MethodReflection) { + $classReflection = $reflection->getDeclaringClass(); + $fileName = (string) $classReflection->getFileName(); + if (str_contains($fileName, 'phpstan.phar')) { + return \true; + } + } + + return false; + } + private function shouldSkip(FuncCall | MethodCall | StaticCall $call): bool { if ($call->args === []) { @@ -99,7 +135,7 @@ private function shouldSkip(FuncCall | MethodCall | StaticCall $call): bool return true; } - if ($this->isName($call->class, 'parent')) { + if ($this->isName($call->class, ObjectReference::PARENT)) { return true; } } @@ -115,6 +151,11 @@ private function resolveMaximumAllowedParameterCount( $parameterCounts[] = count($parametersAcceptor->getParameters()); } - return (int) max($parameterCounts); + // empty variants -> use max value possibly has to prevent removing arguments incorrectly + if ($parameterCounts === [0]) { + return PHP_INT_MAX; + } + + return max($parameterCounts); } } diff --git a/rules/Php71/Rector/List_/ListToArrayDestructRector.php b/rules/Php71/Rector/List_/ListToArrayDestructRector.php index 27ab9c88290..eaafa1ea313 100644 --- a/rules/Php71/Rector/List_/ListToArrayDestructRector.php +++ b/rules/Php71/Rector/List_/ListToArrayDestructRector.php @@ -5,27 +5,26 @@ namespace Rector\Php71\Rector\List_; use PhpParser\Node; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\List_; use PhpParser\Node\Stmt\Foreach_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/short_list_syntax https://www.php.net/manual/en/migration71.new-features.php#migration71.new-features.symmetric-array-destructuring - * * @see \Rector\Tests\Php71\Rector\List_\ListToArrayDestructRector\ListToArrayDestructRectorTest */ final class ListToArrayDestructRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change list() to array destruct', [ + return new RuleDefinition('Change `list()` to array destruct', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -61,30 +60,67 @@ public function run() */ public function getNodeTypes(): array { - return [List_::class]; + return [Assign::class, Foreach_::class]; } /** - * @param List_ $node + * @param Assign|Foreach_ $node */ public function refactor(Node $node): ?Node { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); + if ($node instanceof Assign) { + if (! $node->var instanceof List_) { + return null; + } - if ($parentNode instanceof Assign && $parentNode->var === $node) { - return new Array_($node->items); + if ($node->var->getAttribute(AttributeKey::KIND) === List_::KIND_ARRAY) { + return null; + } + + $list = $node->var; + + // all list items must be set + if ($this->hasPartialDestruct($list)) { + return null; + } + + $node->var = new Array_($list->items); + return $node; + } + + if (! $node->valueVar instanceof List_) { + return null; } - if (! $parentNode instanceof Foreach_) { + + if ($node->valueVar->getAttribute(AttributeKey::KIND) === List_::KIND_ARRAY) { return null; } - if ($parentNode->valueVar !== $node) { + + $list = $node->valueVar; + + // all list items must be set + if ($this->hasPartialDestruct($list)) { return null; } - return new Array_($node->items); + + $node->valueVar = new Array_($list->items); + + return $node; } public function provideMinPhpVersion(): int { return PhpVersionFeature::ARRAY_DESTRUCT; } + + private function hasPartialDestruct(List_ $list): bool + { + foreach ($list->items as $listItem) { + if (! $listItem instanceof ArrayItem) { + return true; + } + } + + return false; + } } diff --git a/rules/Php71/Rector/Name/ReservedObjectRector.php b/rules/Php71/Rector/Name/ReservedObjectRector.php deleted file mode 100644 index fb2f1f17c26..00000000000 --- a/rules/Php71/Rector/Name/ReservedObjectRector.php +++ /dev/null @@ -1,126 +0,0 @@ - - */ - private array $reservedKeywordsToReplacements = []; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Changes reserved "Object" name to "Object" where can be configured', - [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -class Object -{ -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SmartObject -{ -} -CODE_SAMPLE - , - [ - self::RESERVED_KEYWORDS_TO_REPLACEMENTS => [ - 'ReservedObject' => 'SmartObject', - 'Object' => 'AnotherSmartObject', - ], - ] - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Identifier::class, Name::class]; - } - - /** - * @param Identifier|Name $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof Identifier) { - return $this->processIdentifier($node); - } - - return $this->processName($node); - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->reservedKeywordsToReplacements = $configuration[self::RESERVED_KEYWORDS_TO_REPLACEMENTS] ?? []; - } - - private function processIdentifier(Identifier $identifier): Identifier - { - foreach ($this->reservedKeywordsToReplacements as $reservedKeyword => $replacement) { - if (! $this->isName($identifier, $reservedKeyword)) { - continue; - } - - $identifier->name = $replacement; - return $identifier; - } - - return $identifier; - } - - private function processName(Name $name): Name - { - // we look for "extends " - $parentNode = $name->getAttribute(AttributeKey::PARENT_NODE); - // "Object" can part of namespace name - if ($parentNode instanceof Namespace_) { - return $name; - } - - // process lass part - foreach ($this->reservedKeywordsToReplacements as $reservedKeyword => $replacement) { - if (strtolower($name->getLast()) === strtolower($reservedKeyword)) { - $name->parts[count($name->parts) - 1] = $replacement; - - // invoke override - $name->setAttribute(AttributeKey::ORIGINAL_NODE, null); - } - } - - return $name; - } -} diff --git a/rules/Php71/Rector/TryCatch/MultiExceptionCatchRector.php b/rules/Php71/Rector/TryCatch/MultiExceptionCatchRector.php index 55266800f43..0fb994cd3e2 100644 --- a/rules/Php71/Rector/TryCatch/MultiExceptionCatchRector.php +++ b/rules/Php71/Rector/TryCatch/MultiExceptionCatchRector.php @@ -5,26 +5,28 @@ namespace Rector\Php71\Rector\TryCatch; use PhpParser\Node; -use PhpParser\Node\Name; -use PhpParser\Node\Stmt\Catch_; use PhpParser\Node\Stmt\TryCatch; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/multiple-catch - * * @see \Rector\Tests\Php71\Rector\TryCatch\MultiExceptionCatchRector\MultiExceptionCatchRectorTest */ final class MultiExceptionCatchRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly BetterStandardPrinter $betterStandardPrinter + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Changes multi catch of same exception to single one | separated.', + 'Change multiple catch statements of the same exception to a single one `|` separated', [ new CodeSample( <<<'CODE_SAMPLE' @@ -66,60 +68,40 @@ public function refactor(Node $node): ?Node return null; } - $catchKeysByContent = $this->collectCatchKeysByContent($node); - /** @var Catch_[] $catchKeys */ - foreach ($catchKeysByContent as $catchKeys) { - // no duplicates - $count = count($catchKeys); - if ($count < 2) { - continue; - } - - $collectedTypes = $this->collectTypesFromCatchedByIds($node, $catchKeys); + $hasChanged = false; - /** @var Catch_ $firstCatch */ - $firstCatch = array_shift($catchKeys); - $firstCatch->types = $collectedTypes; + foreach ($node->catches as $key => $catch) { + if (! isset($node->catches[$key + 1])) { + break; + } - foreach ($catchKeys as $catchKey) { - $this->removeNode($catchKey); + $currentPrintedCatch = $this->betterStandardPrinter->print($catch->stmts); + $nextPrintedCatch = $this->betterStandardPrinter->print($node->catches[$key + 1]->stmts); + + // already duplicated catch → remove it and join the type + if ($currentPrintedCatch === $nextPrintedCatch) { + // use current var as next var + $node->catches[$key + 1]->var = $node->catches[$key]->var; + // merge next types as current merge to next types + $node->catches[$key + 1]->types = array_merge( + $node->catches[$key]->types, + $node->catches[$key + 1]->types + ); + + unset($node->catches[$key]); + $hasChanged = true; } } - return $node; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::MULTI_EXCEPTION_CATCH; - } - - /** - * @return array - */ - private function collectCatchKeysByContent(TryCatch $tryCatch): array - { - $catchKeysByContent = []; - foreach ($tryCatch->catches as $catch) { - $catchContent = $this->print($catch->stmts); - $catchKeysByContent[$catchContent][] = $catch; + if ($hasChanged) { + return $node; } - return $catchKeysByContent; + return null; } - /** - * @param Catch_[] $catches - * @return Name[] - */ - private function collectTypesFromCatchedByIds(TryCatch $tryCatch, array $catches): array + public function provideMinPhpVersion(): int { - $collectedTypes = []; - - foreach ($catches as $catch) { - $collectedTypes = array_merge($collectedTypes, $catch->types); - } - - return $collectedTypes; + return PhpVersionFeature::MULTI_EXCEPTION_CATCH; } } diff --git a/rules/Php71/ValueObject/TwoNodeMatch.php b/rules/Php71/ValueObject/TwoNodeMatch.php index 49ac3c906ee..0795c99ed69 100644 --- a/rules/Php71/ValueObject/TwoNodeMatch.php +++ b/rules/Php71/ValueObject/TwoNodeMatch.php @@ -6,7 +6,7 @@ use PhpParser\Node\Expr; -final class TwoNodeMatch +final readonly class TwoNodeMatch { public function __construct( private Expr $firstExpr, diff --git a/rules/Php72/NodeFactory/AnonymousFunctionFactory.php b/rules/Php72/NodeFactory/AnonymousFunctionFactory.php index 3f5240c07d3..be6c9ba511b 100644 --- a/rules/Php72/NodeFactory/AnonymousFunctionFactory.php +++ b/rules/Php72/NodeFactory/AnonymousFunctionFactory.php @@ -6,188 +6,162 @@ use Nette\Utils\Strings; use PhpParser\Node; +use PhpParser\Node\ClosureUse; +use PhpParser\Node\ComplexType; use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; -use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\Closure; -use PhpParser\Node\Expr\ClosureUse; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\New_; -use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; use PhpParser\Node\Name; -use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\NullableType; use PhpParser\Node\Param; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\UnionType; -use PhpParser\Parser; -use PHPStan\Reflection\FunctionVariantWithPhpDocs; -use PHPStan\Reflection\ParameterReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; -use PHPStan\Reflection\Php\PhpMethodReflection; -use PHPStan\Type\MixedType; -use PHPStan\Type\VoidType; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\Node\NodeFactory; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; -use Rector\StaticTypeMapper\StaticTypeMapper; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\Php\ReservedKeywordAnalyzer; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Parser\InlineCodeParser; +use Rector\PhpParser\Parser\SimplePhpParser; -final class AnonymousFunctionFactory +final readonly class AnonymousFunctionFactory { /** - * @var string * @see https://regex101.com/r/jkLLlM/2 */ - private const DIM_FETCH_REGEX = '#(\\$|\\\\|\\x0)(?\d+)#'; + private const string DIM_FETCH_REGEX = '#(\\$|\\\\|\\x0)(?\d+)#'; public function __construct( private NodeNameResolver $nodeNameResolver, private BetterNodeFinder $betterNodeFinder, - private NodeFactory $nodeFactory, - private StaticTypeMapper $staticTypeMapper, private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private Parser $parser + private SimplePhpParser $simplePhpParser, + private InlineCodeParser $inlineCodeParser, + private ReservedKeywordAnalyzer $reservedKeywordAnalyzer, ) { } /** + * @api * @param Param[] $params * @param Stmt[] $stmts */ public function create( array $params, array $stmts, - Identifier | Name | NullableType | UnionType | null $returnTypeNode + Identifier | Name | NullableType | UnionType | ComplexType | null $returnTypeNode, + bool $static = false ): Closure { $useVariables = $this->createUseVariablesFromParams($stmts, $params); - $anonymousFunctionNode = new Closure(); - $anonymousFunctionNode->params = $params; + $anonymousFunctionClosure = new Closure(); + $anonymousFunctionClosure->params = $params; - foreach ($useVariables as $useVariable) { - $anonymousFunctionNode->uses[] = new ClosureUse($useVariable); - } - - if ($returnTypeNode instanceof Node) { - $anonymousFunctionNode->returnType = $returnTypeNode; - } - - $anonymousFunctionNode->stmts = $stmts; - return $anonymousFunctionNode; - } - - public function createFromPhpMethodReflection(PhpMethodReflection $phpMethodReflection, Expr $expr): ?Closure - { - /** @var FunctionVariantWithPhpDocs $functionVariantWithPhpDoc */ - $functionVariantWithPhpDoc = ParametersAcceptorSelector::selectSingle($phpMethodReflection->getVariants()); - - $anonymousFunction = new Closure(); - $newParams = $this->createParams($functionVariantWithPhpDoc->getParameters()); - - $anonymousFunction->params = $newParams; - - $innerMethodCall = $this->createInnerMethodCall($phpMethodReflection, $expr, $newParams); - if ($innerMethodCall === null) { - return null; - } - - if (! $functionVariantWithPhpDoc->getReturnType() instanceof MixedType) { - $returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $functionVariantWithPhpDoc->getReturnType(), - TypeKind::RETURN() - ); - $anonymousFunction->returnType = $returnType; + if ($static) { + $anonymousFunctionClosure->static = $static; } - // does method return something? - if (! $functionVariantWithPhpDoc->getReturnType() instanceof VoidType) { - $anonymousFunction->stmts[] = new Return_($innerMethodCall); - } else { - $anonymousFunction->stmts[] = new Expression($innerMethodCall); + foreach ($useVariables as $useVariable) { + $anonymousFunctionClosure->uses[] = new ClosureUse($useVariable); } - if ($expr instanceof Variable && ! $this->nodeNameResolver->isName($expr, 'this')) { - $anonymousFunction->uses[] = new ClosureUse($expr); + if ($returnTypeNode instanceof Node) { + $anonymousFunctionClosure->returnType = $returnTypeNode; } - return $anonymousFunction; + $anonymousFunctionClosure->stmts = $stmts; + return $anonymousFunctionClosure; } - public function createAnonymousFunctionFromString(Expr $expr): ?Closure + public function createAnonymousFunctionFromExpr(Expr $expr): ?Closure { - if (! $expr instanceof String_) { - // not supported yet - throw new ShouldNotHappenException(); - } + $stringValue = $this->inlineCodeParser->stringify($expr); - $phpCode = 'value . ';'; - $contentNodes = (array) $this->parser->parse($phpCode); + $phpCode = 'simplePhpParser->parseString($phpCode); $anonymousFunction = new Closure(); - $firstNode = $contentNodes[0] ?? null; + $firstNode = $contentStmts[0] ?? null; if (! $firstNode instanceof Expression) { return null; } $stmt = $firstNode->expr; - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmt, function (Node $node): Node { + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmt, static function (Node $node): Node { if (! $node instanceof String_) { return $node; } $match = Strings::match($node->value, self::DIM_FETCH_REGEX); - if (! $match) { + if ($match === null) { return $node; } $matchesVariable = new Variable('matches'); - return new ArrayDimFetch($matchesVariable, new LNumber((int) $match['number'])); + return new ArrayDimFetch($matchesVariable, new Int_((int) $match['number'])); }); $anonymousFunction->stmts[] = new Return_($stmt); $anonymousFunction->params[] = new Param(new Variable('matches')); + $variables = $expr instanceof Variable + ? [] + : $this->betterNodeFinder->findInstanceOf($expr, Variable::class); + + $anonymousFunction->uses = array_map( + static fn (Variable $variable): ClosureUse => new ClosureUse($variable), + $variables + ); + return $anonymousFunction; } /** - * @param Node[] $nodes - * @param Param[] $paramNodes - * @return Variable[] + * @param Param[] $params + * @return string[] */ - private function createUseVariablesFromParams(array $nodes, array $paramNodes): array + private function collectParamNames(array $params): array { $paramNames = []; - foreach ($paramNodes as $paramNode) { - $paramNames[] = $this->nodeNameResolver->getName($paramNode); + foreach ($params as $param) { + $paramNames[] = $this->nodeNameResolver->getName($param); } - $variableNodes = $this->betterNodeFinder->findInstanceOf($nodes, Variable::class); + return $paramNames; + } - /** @var Variable[] $filteredVariables */ + /** + * @param Node[] $nodes + * @param Param[] $params + * @return array + */ + private function createUseVariablesFromParams(array $nodes, array $params): array + { + $paramNames = $this->collectParamNames($params); + + /** @var Variable[] $variables */ + $variables = $this->betterNodeFinder->findInstanceOf($nodes, Variable::class); + + /** @var array $filteredVariables */ $filteredVariables = []; + $alreadyAssignedVariables = []; - foreach ($variableNodes as $variableNode) { + foreach ($variables as $variable) { // "$this" is allowed - if ($this->nodeNameResolver-> isName($variableNode, 'this')) { + if ($this->nodeNameResolver->isName($variable, 'this')) { continue; } - $variableName = $this->nodeNameResolver->getName($variableNode); + $variableName = $this->nodeNameResolver->getName($variable); if ($variableName === null) { continue; } @@ -196,108 +170,24 @@ private function createUseVariablesFromParams(array $nodes, array $paramNodes): continue; } - $parentNode = $variableNode->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Assign) { - $alreadyAssignedVariables[] = $variableName; - } - - if ($this->nodeNameResolver->isNames($variableNode, $alreadyAssignedVariables)) { + // Superglobal variables cannot be in a use statement + if ($this->reservedKeywordAnalyzer->isNativeVariable($variableName)) { continue; } - $filteredVariables[$variableName] = $variableNode; - } - - return $filteredVariables; - } - - /** - * @param ParameterReflection[] $parameterReflections - * @return Param[] - */ - private function createParams(array $parameterReflections): array - { - $params = []; - foreach ($parameterReflections as $parameterReflection) { - $param = new Param(new Variable($parameterReflection->getName())); - - if (! $parameterReflection->getType() instanceof MixedType) { - $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $parameterReflection->getType(), - TypeKind::PARAM() - ); - } - - $params[] = $param; - } - - return $params; - } - - /** - * @param Param[] $params - */ - private function createInnerMethodCall( - PhpMethodReflection $phpMethodReflection, - Expr $expr, - array $params - ): MethodCall | StaticCall | null { - if ($phpMethodReflection->isStatic()) { - $expr = $this->normalizeClassConstFetchForStatic($expr); - if ($expr === null) { - return null; + if ( + $variable->getAttribute(AttributeKey::IS_BEING_ASSIGNED) === true + || $variable->getAttribute(AttributeKey::IS_PARAM_VAR) === true + || $variable->getAttribute(AttributeKey::IS_VARIABLE_LOOP) === true + ) { + $alreadyAssignedVariables[] = $variableName; } - $innerMethodCall = new StaticCall($expr, $phpMethodReflection->getName()); - } else { - $expr = $this->resolveExpr($expr); - if (! $expr instanceof Expr) { - return null; + if (! $this->nodeNameResolver->isNames($variable, $alreadyAssignedVariables)) { + $filteredVariables[$variableName] = $variable; } - - $innerMethodCall = new MethodCall($expr, $phpMethodReflection->getName()); - } - - $innerMethodCall->args = $this->nodeFactory->createArgsFromParams($params); - - return $innerMethodCall; - } - - private function normalizeClassConstFetchForStatic(Expr $expr): null | FullyQualified | Expr - { - if (! $expr instanceof ClassConstFetch) { - return $expr; - } - - if (! $this->nodeNameResolver->isName($expr->name, 'class')) { - return $expr; } - // dynamic name, nothing we can do - $className = $this->nodeNameResolver->getName($expr->class); - if ($className === null) { - return null; - } - - return new FullyQualified($className); - } - - private function resolveExpr(Expr $expr): New_ | Expr | null - { - if (! $expr instanceof ClassConstFetch) { - return $expr; - } - - if (! $this->nodeNameResolver->isName($expr->name, 'class')) { - return $expr; - } - - // dynamic name, nothing we can do - $className = $this->nodeNameResolver->getName($expr->class); - if ($className === null) { - return null; - } - - return new New_(new FullyQualified($className)); + return $filteredVariables; } } diff --git a/rules/Php72/Rector/Assign/ListEachRector.php b/rules/Php72/Rector/Assign/ListEachRector.php index b6bfe11ab4a..50192f9e8a2 100644 --- a/rules/Php72/Rector/Assign/ListEachRector.php +++ b/rules/Php72/Rector/Assign/ListEachRector.php @@ -5,30 +5,34 @@ namespace Rector\Php72\Rector\Assign; use PhpParser\Node; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\List_; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\NodeManipulator\AssignManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Exception\ShouldNotHappenException; +use Rector\NodeManipulator\AssignManipulator; +use Rector\Php72\ValueObject\ListAndEach; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/deprecations_php_7_2#each - * * @see \Rector\Tests\Php72\Rector\Assign\ListEachRector\ListEachRectorTest */ -final class ListEachRector extends AbstractRector +final class ListEachRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private AssignManipulator $assignManipulator + private readonly AssignManipulator $assignManipulator, ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_EACH; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -54,87 +58,85 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Assign::class]; + return [Expression::class]; } /** - * @param Assign $node + * @param Expression $node + * @return null|Expression|Stmt[] */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): null|Expression|array { - if ($this->shouldSkip($node)) { + if (! $node->expr instanceof Assign) { + return null; + } + + $listAndEach = $this->assignManipulator->matchListAndEach($node->expr); + if (! $listAndEach instanceof ListAndEach) { return null; } - /** @var List_ $listNode */ - $listNode = $node->var; + if ($this->shouldSkipAssign($listAndEach)) { + return null; + } - /** @var FuncCall $eachFuncCall */ - $eachFuncCall = $node->expr; + $list = $listAndEach->getList(); + $eachFuncCall = $listAndEach->getEachFuncCall(); // only key: list($key, ) = each($values); - if ($listNode->items[0] && $listNode->items[1] === null) { + if ($list->items[0] instanceof ArrayItem && ! $list->items[1] instanceof ArrayItem) { $keyFuncCall = $this->nodeFactory->createFuncCall('key', $eachFuncCall->args); - return new Assign($listNode->items[0]->value, $keyFuncCall); + $keyFuncCallAssign = new Assign($list->items[0]->value, $keyFuncCall); + + return new Expression($keyFuncCallAssign); } // only value: list(, $value) = each($values); - if ($listNode->items[1] && $listNode->items[0] === null) { + if ($list->items[1] instanceof ArrayItem && ! $list->items[0] instanceof ArrayItem) { $nextFuncCall = $this->nodeFactory->createFuncCall('next', $eachFuncCall->args); - $this->addNodeAfterNode($nextFuncCall, $node); - $currentFuncCall = $this->nodeFactory->createFuncCall('current', $eachFuncCall->args); - $secondArrayItem = $listNode->items[1]; - return new Assign($secondArrayItem->value, $currentFuncCall); + $secondArrayItem = $list->items[1]; + $currentAssign = new Assign($secondArrayItem->value, $currentFuncCall); + + return [new Expression($currentAssign), new Expression($nextFuncCall)]; } // both: list($key, $value) = each($values); $currentFuncCall = $this->nodeFactory->createFuncCall('current', $eachFuncCall->args); - $secondArrayItem = $listNode->items[1]; + $secondArrayItem = $list->items[1]; if (! $secondArrayItem instanceof ArrayItem) { throw new ShouldNotHappenException(); } - $assign = new Assign($secondArrayItem->value, $currentFuncCall); - $this->addNodeAfterNode($assign, $node); + $currentAssign = new Assign($secondArrayItem->value, $currentFuncCall); $nextFuncCall = $this->nodeFactory->createFuncCall('next', $eachFuncCall->args); - $this->addNodeAfterNode($nextFuncCall, $node); - $keyFuncCall = $this->nodeFactory->createFuncCall('key', $eachFuncCall->args); - $firstArrayItem = $listNode->items[0]; + $firstArrayItem = $list->items[0]; if (! $firstArrayItem instanceof ArrayItem) { throw new ShouldNotHappenException(); } - return new Assign($firstArrayItem->value, $keyFuncCall); + $keyAssign = new Assign($firstArrayItem->value, $keyFuncCall); + + return [new Expression($keyAssign), new Expression($currentAssign), new Expression($nextFuncCall)]; } - private function shouldSkip(Assign $assign): bool + private function shouldSkipAssign(ListAndEach $listAndEach): bool { - if (! $this->assignManipulator->isListToEachAssign($assign)) { - return true; - } - - // assign should be top level, e.g. not in a while loop - $parentNode = $assign->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Expression) { + $list = $listAndEach->getList(); + if (count($list->items) !== 2) { return true; } - /** @var List_ $listNode */ - $listNode = $assign->var; - - if (count($listNode->items) !== 2) { - return true; - } // empty list → cannot handle - if ($listNode->items[0] !== null) { + if ($list->items[0] instanceof ArrayItem) { return false; } - return $listNode->items[1] === null; + + return ! $list->items[1] instanceof ArrayItem; } } diff --git a/rules/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector.php b/rules/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector.php index 189e08b7ff8..938d8633698 100644 --- a/rules/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector.php +++ b/rules/Php72/Rector/Assign/ReplaceEachAssignmentWithKeyCurrentRector.php @@ -12,37 +12,44 @@ use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\List_; -use PhpParser\Node\Stmt\While_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Expression; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector\ReplaceEachAssignmentWithKeyCurrentRectorTest */ -final class ReplaceEachAssignmentWithKeyCurrentRector extends AbstractRector +final class ReplaceEachAssignmentWithKeyCurrentRector extends AbstractRector implements MinPhpVersionInterface { - /** - * @var string - */ - private const KEY = 'key'; + private const string KEY = 'key'; + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_EACH_OUTSIDE_LOOP; + } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Replace each() assign outside loop', [ + return new RuleDefinition('Replace `each()` assign outside loop', [ new CodeSample( <<<'CODE_SAMPLE' $array = ['b' => 1, 'a' => 2]; + $eachedArray = each($array); CODE_SAMPLE , <<<'CODE_SAMPLE' $array = ['b' => 1, 'a' => 2]; + $eachedArray[1] = current($array); $eachedArray['value'] = current($array); $eachedArray[0] = key($array); $eachedArray['key'] = key($array); + next($array); CODE_SAMPLE ), @@ -54,29 +61,40 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Assign::class]; + return [Expression::class]; } /** - * @param Assign $node + * @param Expression $node + * @return Stmt[]|null */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - if ($this->shouldSkip($node)) { + if (! $node->expr instanceof Assign) { + return null; + } + + $assign = $node->expr; + if ($this->shouldSkip($assign)) { return null; } /** @var FuncCall $eachFuncCall */ - $eachFuncCall = $node->expr; - $eachedVariable = $eachFuncCall->args[0]->value; + $eachFuncCall = $assign->expr; - $assignVariable = $node->var; + if ($eachFuncCall->isFirstClassCallable()) { + return null; + } + + if (! isset($eachFuncCall->getArgs()[0])) { + return null; + } - $newNodes = $this->createNewNodes($assignVariable, $eachedVariable); - $this->addNodesAfterNode($newNodes, $node); - $this->removeNode($node); + $assignVariable = $assign->var; + $eachedVariable = $eachFuncCall->getArgs()[0] + ->value; - return null; + return $this->createNewStmts($assignVariable, $eachedVariable); } private function shouldSkip(Assign $assign): bool @@ -85,37 +103,27 @@ private function shouldSkip(Assign $assign): bool return true; } - if (! $this->nodeNameResolver->isName($assign->expr, 'each')) { + if (! $this->isName($assign->expr, 'each')) { return true; } - $parentNode = $assign->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof While_) { - return true; - } - // skip assign to List - if (! $parentNode instanceof Assign) { - return false; - } - return $parentNode->var instanceof List_; + return $assign->var instanceof List_; } /** - * @return array + * @return Stmt[] */ - private function createNewNodes(Expr $assignVariable, Expr $eachedVariable): array + private function createNewStmts(Expr $assignVariable, Expr $eachedVariable): array { - $newNodes = []; - - $newNodes[] = $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 1, 'current'); - $newNodes[] = $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 'value', 'current'); - - $newNodes[] = $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 0, self::KEY); - $newNodes[] = $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, self::KEY, self::KEY); - - $newNodes[] = $this->nodeFactory->createFuncCall('next', [new Arg($eachedVariable)]); - - return $newNodes; + $exprs = [ + $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 1, 'current'), + $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 'value', 'current'), + $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 0, self::KEY), + $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, self::KEY, self::KEY), + $this->nodeFactory->createFuncCall('next', [new Arg($eachedVariable)]), + ]; + + return array_map(static fn (Expr $expr): Expression => new Expression($expr), $exprs); } private function createDimFetchAssignWithFuncCall( @@ -124,8 +132,8 @@ private function createDimFetchAssignWithFuncCall( string | int $dimValue, string $functionName ): Assign { - $dim = BuilderHelpers::normalizeValue($dimValue); - $arrayDimFetch = new ArrayDimFetch($assignVariable, $dim); + $dimExpr = BuilderHelpers::normalizeValue($dimValue); + $arrayDimFetch = new ArrayDimFetch($assignVariable, $dimExpr); return new Assign($arrayDimFetch, $this->nodeFactory->createFuncCall( $functionName, diff --git a/rules/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector.php b/rules/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector.php index 83edf84e3e2..356d49237b0 100644 --- a/rules/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector.php +++ b/rules/Php72/Rector/FuncCall/CreateFunctionToAnonymousFunctionRector.php @@ -13,32 +13,37 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; use PhpParser\Node\Param; -use PhpParser\Node\Scalar\Encapsed; +use PhpParser\Node\Scalar\InterpolatedString; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\Php\ReservedKeywordAnalyzer; -use Rector\Core\PhpParser\Parser\InlineCodeParser; -use Rector\Core\Rector\AbstractRector; +use Rector\Exception\ShouldNotHappenException; +use Rector\Php\ReservedKeywordAnalyzer; use Rector\Php72\NodeFactory\AnonymousFunctionFactory; +use Rector\PhpParser\Parser\InlineCodeParser; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://stackoverflow.com/q/48161526/1348344 http://php.net/manual/en/migration72.deprecated.php#migration72.deprecated.create_function-function - * * @see \Rector\Tests\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector\CreateFunctionToAnonymousFunctionRectorTest */ -final class CreateFunctionToAnonymousFunctionRector extends AbstractRector +final class CreateFunctionToAnonymousFunctionRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private InlineCodeParser $inlineCodeParser, - private AnonymousFunctionFactory $anonymousFunctionFactory, - private ReservedKeywordAnalyzer $reservedKeywordAnalyzer + private readonly InlineCodeParser $inlineCodeParser, + private readonly AnonymousFunctionFactory $anonymousFunctionFactory, + private readonly ReservedKeywordAnalyzer $reservedKeywordAnalyzer, ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_CREATE_FUNCTION; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -89,8 +94,21 @@ public function refactor(Node $node): ?Node return null; } - $params = $this->createParamsFromString($node->args[0]->value); - $stmts = $this->parseStringToBody($node->args[1]->value); + if ($node->isFirstClassCallable()) { + return null; + } + + if (count($node->getArgs()) < 2) { + return null; + } + + $firstExpr = $node->getArgs()[0] + ->value; + $secondExpr = $node->getArgs()[1] + ->value; + + $params = $this->createParamsFromString($firstExpr); + $stmts = $this->parseStringToBody($secondExpr); $refactored = $this->anonymousFunctionFactory->create($params, $stmts, null); foreach ($refactored->uses as $key => $use) { @@ -115,7 +133,7 @@ private function createParamsFromString(Expr $expr): array $content = $this->inlineCodeParser->stringify($expr); $content = 'inlineCodeParser->parse($content); + $nodes = $this->inlineCodeParser->parseString($content); /** @var Expression $expression */ $expression = $nodes[0]; @@ -132,17 +150,17 @@ private function createParamsFromString(Expr $expr): array } /** - * @return Expression[]|Stmt[] + * @return Stmt[] */ private function parseStringToBody(Expr $expr): array { - if (! $expr instanceof String_ && ! $expr instanceof Encapsed && ! $expr instanceof Concat) { + if (! $expr instanceof String_ && ! $expr instanceof InterpolatedString && ! $expr instanceof Concat) { // special case of code elsewhere return [$this->createEval($expr)]; } - $expr = $this->inlineCodeParser->stringify($expr); - return $this->inlineCodeParser->parse($expr); + $content = $this->inlineCodeParser->stringify($expr); + return $this->inlineCodeParser->parseString($content); } private function createEval(Expr $expr): Expression diff --git a/rules/Php72/Rector/FuncCall/GetClassOnNullRector.php b/rules/Php72/Rector/FuncCall/GetClassOnNullRector.php index a8a72572ab8..b4e1fd9d04d 100644 --- a/rules/Php72/Rector/FuncCall/GetClassOnNullRector.php +++ b/rules/Php72/Rector/FuncCall/GetClassOnNullRector.php @@ -5,28 +5,32 @@ namespace Rector\Php72\Rector\FuncCall; use PhpParser\Node; -use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Stmt\Class_; -use PHPStan\Analyser\Scope; -use PHPStan\Type\NullType; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog http://php.net/manual/en/migration72.incompatible.php#migration72.incompatible.no-null-to-get_class https://3v4l.org/sk0fp - * * @see \Rector\Tests\Php72\Rector\FuncCall\GetClassOnNullRector\GetClassOnNullRectorTest */ -final class GetClassOnNullRector extends AbstractRector +final class GetClassOnNullRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_NULL_ON_GET_CLASS; + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Null is no more allowed in get_class()', [ + return new RuleDefinition('Null is no more allowed in `get_class()`', [ new CodeSample( <<<'CODE_SAMPLE' final class SomeClass @@ -58,120 +62,68 @@ public function getItem() */ public function getNodeTypes(): array { - return [FuncCall::class]; + return [Class_::class]; } /** - * @param FuncCall $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if (! $this->isName($node, 'get_class')) { - return null; - } - - $firstArgValue = $node->args[0]->value; + $hasChanged = false; - // only relevant inside the class - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - if (! $scope->isInClass()) { - return null; - } + $this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged): int|null|Ternary { + if ($node instanceof Ternary) { + return NodeVisitor::STOP_TRAVERSAL; + } - // possibly already changed - if ($this->shouldSkip($node)) { - return null; - } + if (! $node instanceof FuncCall) { + return null; + } - if (! $this->nodeTypeResolver->isNullableType($firstArgValue) && ! $this->nodeTypeResolver->isStaticType( - $firstArgValue, - NullType::class - )) { - return null; - } + // just created func call + if ($node->getAttribute(AttributeKey::ORIGINAL_NODE) === null) { + return null; + } - $notIdentical = new NotIdentical($firstArgValue, $this->nodeFactory->createNull()); - $funcCall = $this->createGetClassFuncCall($node); - $selfClassConstFetch = $this->nodeFactory->createClassConstReference('self'); + if (! $this->isName($node, 'get_class')) { + return null; + } - return new Ternary($notIdentical, $funcCall, $selfClassConstFetch); - } + if ($node->isFirstClassCallable()) { + return null; + } - private function shouldSkip(FuncCall $funcCall): bool - { - $isJustAdded = (bool) $funcCall->getAttribute(AttributeKey::DO_NOT_CHANGE); - if ($isJustAdded) { - return true; - } + $firstArg = $node->getArgs()[0] ?? null; + if (! $firstArg instanceof Arg) { + return null; + } - $classLike = $funcCall->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return true; - } + $firstArgValue = $firstArg->value; - $parent = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Ternary) { - if ($this->isIdenticalToNotNull($funcCall, $parent)) { - return true; + $firstArgType = $this->getType($firstArgValue); + if (! $this->nodeTypeResolver->isNullableType($firstArgValue) && ! $firstArgType->isNull()->yes()) { + return null; } - return $this->isNotIdenticalToNull($funcCall, $parent); - } + $notIdentical = new NotIdentical($firstArgValue, $this->nodeFactory->createNull()); + $funcCall = $this->createGetClassFuncCall($node); + $selfClassConstFetch = $this->nodeFactory->createClassConstReference('self'); - return false; - } - - private function createGetClassFuncCall(FuncCall $oldFuncCall): FuncCall - { - $funcCall = new FuncCall($oldFuncCall->name, $oldFuncCall->args); - $funcCall->setAttribute(AttributeKey::DO_NOT_CHANGE, true); + $hasChanged = true; - return $funcCall; - } + return new Ternary($notIdentical, $funcCall, $selfClassConstFetch); + }); - /** - * E.g. "$value === [!null] ? get_class($value)" - */ - private function isIdenticalToNotNull(FuncCall $funcCall, Ternary $ternary): bool - { - if (! $ternary->cond instanceof Identical) { - return false; + if ($hasChanged) { + return $node; } - if ($this->nodeComparator->areNodesEqual( - $ternary->cond->left, - $funcCall->args[0]->value - ) && ! $this->valueResolver->isNull($ternary->cond->right)) { - return true; - } - if (! $this->nodeComparator->areNodesEqual($ternary->cond->right, $funcCall->args[0]->value)) { - return false; - } - return ! $this->valueResolver->isNull($ternary->cond->left); + return null; } - /** - * E.g. "$value !== null ? get_class($value)" - */ - private function isNotIdenticalToNull(FuncCall $funcCall, Ternary $ternary): bool + private function createGetClassFuncCall(FuncCall $oldFuncCall): FuncCall { - if (! $ternary->cond instanceof NotIdentical) { - return false; - } - - if ($this->nodeComparator->areNodesEqual( - $ternary->cond->left, - $funcCall->args[0]->value - ) && $this->valueResolver->isNull($ternary->cond->right)) { - return true; - } - if (! $this->nodeComparator->areNodesEqual($ternary->cond->right, $funcCall->args[0]->value)) { - return false; - } - return $this->valueResolver->isNull($ternary->cond->left); + return new FuncCall($oldFuncCall->name, $oldFuncCall->args); } } diff --git a/rules/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector.php b/rules/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector.php deleted file mode 100644 index a16c24f3827..00000000000 --- a/rules/Php72/Rector/FuncCall/IsObjectOnIncompleteClassRector.php +++ /dev/null @@ -1,77 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'is_object')) { - return null; - } - - $incompleteClassObjectType = new ObjectType('__PHP_Incomplete_Class'); - if (! $this->isObjectType($node->args[0]->value, $incompleteClassObjectType)) { - return null; - } - - if ($this->shouldSkip($node)) { - return null; - } - - return new BooleanNot($node); - } - - private function shouldSkip(FuncCall $funcCall): bool - { - $parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - return $parentNode instanceof BooleanNot; - } -} diff --git a/rules/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector.php b/rules/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector.php index 602ad12fd68..e69b992ad64 100644 --- a/rules/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector.php +++ b/rules/Php72/Rector/FuncCall/ParseStrWithResultArgumentRector.php @@ -8,18 +8,25 @@ use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Variable; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Expression; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://github.com/gueff/blogimus/commit/04086a10320595470efe446c7ddd90e602aa7228 https://github.com/pxgamer/youtube-dl-php/commit/83cb32b8b36844f2e39f82a862a5ab73da77b608 - * * @see \Rector\Tests\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector\ParseStrWithResultArgumentRectorTest */ -final class ParseStrWithResultArgumentRector extends AbstractRector +final class ParseStrWithResultArgumentRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::RESULT_ARG_IN_PARSE_STR; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -45,47 +52,86 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [FuncCall::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param FuncCall $node + * @param StmtsAware $node + * @return StmtsAware */ public function refactor(Node $node): ?Node { - if (! $this->isName($node, 'parse_str')) { - return null; - } + return $this->processStrWithResult($node, false); + } - if (isset($node->args[1])) { + /** + * @param StmtsAware $stmtsAware + * @return StmtsAware|null + */ + private function processStrWithResult(Node $stmtsAware, bool $hasChanged, int $jumpToKey = 0): null|Node + { + if ($stmtsAware->stmts === null) { return null; } - $resultVariable = new Variable('result'); - $node->args[1] = new Arg($resultVariable); + $totalKeys = array_key_last($stmtsAware->stmts); + for ($key = $jumpToKey; $key < $totalKeys; ++$key) { + if (! isset($stmtsAware->stmts[$key], $stmtsAware->stmts[$key + 1])) { + break; + } + + $stmt = $stmtsAware->stmts[$key]; + if ($this->shouldSkip($stmt)) { + continue; + } - $expression = $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - if ($expression === null) { - return null; + /** @var Expression $stmt */ + /** @var FuncCall $expr */ + $expr = $stmt->expr; + $resultVariable = new Variable('result'); + $expr->args[1] = new Arg($resultVariable); + + $nextExpression = $stmtsAware->stmts[$key + 1]; + $this->traverseNodesWithCallable($nextExpression, function (Node $node) use ( + $resultVariable, + &$hasChanged + ): ?Variable { + if (! $node instanceof FuncCall) { + return null; + } + + if (! $this->isName($node, 'get_defined_vars')) { + return null; + } + + $hasChanged = true; + return $resultVariable; + }); + + return $this->processStrWithResult($stmtsAware, $hasChanged, $key + 2); } - $nextExpression = $expression->getAttribute(AttributeKey::NEXT_NODE); - if ($nextExpression === null) { - return null; + if ($hasChanged) { + return $stmtsAware; } - $this->traverseNodesWithCallable($nextExpression, function (Node $node) use ($resultVariable): ?Variable { - if (! $node instanceof FuncCall) { - return null; - } + return null; + } - if (! $this->isName($node, 'get_defined_vars')) { - return null; - } + private function shouldSkip(Stmt $stmt): bool + { + if (! $stmt instanceof Expression) { + return true; + } - return $resultVariable; - }); + if (! $stmt->expr instanceof FuncCall) { + return true; + } + + if (! $this->isName($stmt->expr, 'parse_str')) { + return true; + } - return $node; + return isset($stmt->expr->args[1]); } } diff --git a/rules/Php72/Rector/FuncCall/StringifyDefineRector.php b/rules/Php72/Rector/FuncCall/StringifyDefineRector.php index 6e02c1477e8..85ed2a829fe 100644 --- a/rules/Php72/Rector/FuncCall/StringifyDefineRector.php +++ b/rules/Php72/Rector/FuncCall/StringifyDefineRector.php @@ -8,22 +8,28 @@ use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; use Rector\NodeTypeResolver\TypeAnalyzer\StringTypeAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/YiTeP * @see \Rector\Tests\Php72\Rector\FuncCall\StringifyDefineRector\StringifyDefineRectorTest */ -final class StringifyDefineRector extends AbstractRector +final class StringifyDefineRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private StringTypeAnalyzer $stringTypeAnalyzer + private readonly StringTypeAnalyzer $stringTypeAnalyzer ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::STRING_IN_FIRST_DEFINE_ARG; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Make first argument of define() string', [ @@ -70,17 +76,27 @@ public function refactor(Node $node): ?Node return null; } - if ($this->stringTypeAnalyzer->isStringOrUnionStringOnlyType($node->args[0]->value)) { + if ($node->isFirstClassCallable()) { + return null; + } + + if (! isset($node->getArgs()[0])) { + return null; + } + + $firstArg = $node->getArgs()[0]; + + if ($this->stringTypeAnalyzer->isStringOrUnionStringOnlyType($firstArg->value)) { return null; } - if ($node->args[0]->value instanceof ConstFetch) { - $nodeName = $this->getName($node->args[0]->value); + if ($firstArg->value instanceof ConstFetch) { + $nodeName = $this->getName($firstArg->value); if ($nodeName === null) { return null; } - $node->args[0]->value = new String_($nodeName); + $firstArg->value = new String_($nodeName); } return $node; diff --git a/rules/Php72/Rector/FuncCall/StringsAssertNakedRector.php b/rules/Php72/Rector/FuncCall/StringsAssertNakedRector.php index e2d8e13c930..4fd77b1bd8c 100644 --- a/rules/Php72/Rector/FuncCall/StringsAssertNakedRector.php +++ b/rules/Php72/Rector/FuncCall/StringsAssertNakedRector.php @@ -9,23 +9,28 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Expression; -use PhpParser\Parser; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Parser\SimplePhpParser; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://github.com/simplesamlphp/simplesamlphp/pull/708/files - * * @see \Rector\Tests\Php72\Rector\FuncCall\StringsAssertNakedRector\StringsAssertNakedRectorTest */ -final class StringsAssertNakedRector extends AbstractRector +final class StringsAssertNakedRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private Parser $parser + private readonly SimplePhpParser $simplePhpParser ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::STRING_IN_ASSERT_ARG; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -69,25 +74,29 @@ public function refactor(Node $node): ?Node return null; } - if (! $node->args[0]->value instanceof String_) { + if ($node->isFirstClassCallable()) { return null; } - /** @var String_ $stringNode */ - $stringNode = $node->args[0]->value; + $firstArg = $node->getArgs()[0]; + + $firstArgValue = $firstArg->value; + if (! $firstArgValue instanceof String_) { + return null; + } - $phpCode = 'value . ';'; - $contentNodes = $this->parser->parse($phpCode); + $phpCode = 'value . ';'; + $contentStmts = $this->simplePhpParser->parseString($phpCode); - if (! isset($contentNodes[0])) { + if (! isset($contentStmts[0])) { return null; } - if (! $contentNodes[0] instanceof Expression) { + if (! $contentStmts[0] instanceof Expression) { return null; } - $node->args[0] = new Arg($contentNodes[0]->expr); + $node->args[0] = new Arg($contentStmts[0]->expr); return $node; } diff --git a/rules/Php72/Rector/Unset_/UnsetCastRector.php b/rules/Php72/Rector/Unset_/UnsetCastRector.php index 5901e4e5e4a..d53c8477cd5 100644 --- a/rules/Php72/Rector/Unset_/UnsetCastRector.php +++ b/rules/Php72/Rector/Unset_/UnsetCastRector.php @@ -7,20 +7,28 @@ use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Cast\Unset_; +use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Stmt\Expression; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\NodeVisitor; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php72\Rector\Unset_\UnsetCastRector\UnsetCastRectorTest */ -final class UnsetCastRector extends AbstractRector +final class UnsetCastRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_UNSET_CAST; + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Removes (unset) cast', [ + return new RuleDefinition('Remove `(unset)` cast', [ new CodeSample( <<<'CODE_SAMPLE' $different = (unset) $value; @@ -42,33 +50,41 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Unset_::class, Assign::class]; + return [Unset_::class, Assign::class, Expression::class]; } /** - * @param Unset_|Assign $node + * @param Unset_|Assign|Expression $node + * @return NodeVisitor::REMOVE_NODE|Node|null */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): int|null|Node { if ($node instanceof Assign) { - if ($node->expr instanceof Unset_) { - $unset = $node->expr; + return $this->refactorAssign($node); + } - if ($this->nodeComparator->areNodesEqual($node->var, $unset->expr)) { - return $this->nodeFactory->createFuncCall('unset', [$node->var]); - } + if ($node instanceof Expression) { + if (! $node->expr instanceof Unset_) { + return null; } - return null; + return NodeVisitor::REMOVE_NODE; } - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Expression) { - $this->removeNode($node); + return $this->nodeFactory->createNull(); + } + + private function refactorAssign(Assign $assign): ?FuncCall + { + if (! $assign->expr instanceof Unset_) { + return null; + } + $unset = $assign->expr; + if (! $this->nodeComparator->areNodesEqual($assign->var, $unset->expr)) { return null; } - return $this->nodeFactory->createNull(); + return $this->nodeFactory->createFuncCall('unset', [$assign->var]); } } diff --git a/rules/Php72/Rector/While_/WhileEachToForeachRector.php b/rules/Php72/Rector/While_/WhileEachToForeachRector.php index 3c4efbd98c0..ef400d36b9a 100644 --- a/rules/Php72/Rector/While_/WhileEachToForeachRector.php +++ b/rules/Php72/Rector/While_/WhileEachToForeachRector.php @@ -5,33 +5,37 @@ namespace Rector\Php72\Rector\While_; use PhpParser\Node; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\List_; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\While_; -use Rector\Core\NodeManipulator\AssignManipulator; -use Rector\Core\Rector\AbstractRector; +use Rector\NodeManipulator\AssignManipulator; +use Rector\Php72\ValueObject\ListAndEach; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/deprecations_php_7_2#each - * * @see \Rector\Tests\Php72\Rector\While_\WhileEachToForeachRector\WhileEachToForeachRectorTest */ -final class WhileEachToForeachRector extends AbstractRector +final class WhileEachToForeachRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private AssignManipulator $assignManipulator + private readonly AssignManipulator $assignManipulator ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_EACH; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'each() function is deprecated, use foreach() instead.', + 'Use `foreach()` instead of deprecated `each()`', [ new CodeSample( <<<'CODE_SAMPLE' @@ -80,37 +84,48 @@ public function refactor(Node $node): ?Node return null; } - /** @var Assign $assignNode */ - $assignNode = $node->cond; - if (! $this->assignManipulator->isListToEachAssign($assignNode)) { + $listAndEach = $this->assignManipulator->matchListAndEach($node->cond); + if (! $listAndEach instanceof ListAndEach) { return null; } - /** @var FuncCall $eachFuncCall */ - $eachFuncCall = $assignNode->expr; + $eachFuncCall = $listAndEach->getEachFuncCall(); + + $list = $listAndEach->getList(); + if (! isset($eachFuncCall->getArgs()[0])) { + return null; + } - /** @var List_ $listNode */ - $listNode = $assignNode->var; + $firstArg = $eachFuncCall->getArgs()[0]; - $foreachedExpr = count($listNode->items) === 1 ? $this->nodeFactory->createFuncCall( + $foreachedExpr = count($list->items) === 1 ? $this->nodeFactory->createFuncCall( 'array_keys', - [$eachFuncCall->args[0]] - ) : $eachFuncCall->args[0]->value; + [$firstArg] + ) : $firstArg->value; + + $arrayItem = array_pop($list->items); + + $isTrailingCommaLast = false; + + if (! $arrayItem instanceof ArrayItem) { + $foreachedExpr = $this->nodeFactory->createFuncCall('array_keys', [$eachFuncCall->args[0]]); + /** @var ArrayItem $arrayItem */ + $arrayItem = current($list->items); + $isTrailingCommaLast = true; + } - /** @var ArrayItem $arrayItem */ - $arrayItem = array_pop($listNode->items); - $foreach = new Foreach_($foreachedExpr, $arrayItem, [ + $foreach = new Foreach_($foreachedExpr, $arrayItem->value, [ 'stmts' => $node->stmts, ]); $this->mirrorComments($foreach, $node); // is key included? add it to foreach - if ($listNode->items !== []) { + if ($list->items !== []) { /** @var ArrayItem|null $keyItem */ - $keyItem = array_pop($listNode->items); + $keyItem = array_pop($list->items); - if ($keyItem !== null) { + if ($keyItem instanceof ArrayItem && ! $isTrailingCommaLast) { $foreach->keyVar = $keyItem->value; } } diff --git a/rules/Php72/ValueObject/ListAndEach.php b/rules/Php72/ValueObject/ListAndEach.php new file mode 100644 index 00000000000..496fb52c464 --- /dev/null +++ b/rules/Php72/ValueObject/ListAndEach.php @@ -0,0 +1,27 @@ +list; + } + + public function getEachFuncCall(): FuncCall + { + return $this->eachFuncCall; + } +} diff --git a/rules/Php73/NodeTypeAnalyzer/NodeTypeAnalyzer.php b/rules/Php73/NodeTypeAnalyzer/NodeTypeAnalyzer.php deleted file mode 100644 index 4f2881ed67b..00000000000 --- a/rules/Php73/NodeTypeAnalyzer/NodeTypeAnalyzer.php +++ /dev/null @@ -1,50 +0,0 @@ -nodeTypeResolver->getStaticType($expr); - return $this->isStringType($staticType); - } - - private function isStringType(Type $type): bool - { - if ($type instanceof StringType) { - return true; - } - - if ($type instanceof AccessoryNumericStringType) { - return true; - } - - if ($type instanceof IntersectionType || $type instanceof UnionType) { - foreach ($type->getTypes() as $innerType) { - if (! $this->isStringType($innerType)) { - return false; - } - } - - return true; - } - - return false; - } -} diff --git a/rules/Php73/Rector/BooleanOr/IsCountableRector.php b/rules/Php73/Rector/BooleanOr/IsCountableRector.php index 2402fa107fb..b05570c96da 100644 --- a/rules/Php73/Rector/BooleanOr/IsCountableRector.php +++ b/rules/Php73/Rector/BooleanOr/IsCountableRector.php @@ -8,20 +8,23 @@ use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Name; use PHPStan\Reflection\ReflectionProvider; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; use Rector\Php71\IsArrayAndDualCheckToAble; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\ValueObject\PolyfillPackage; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; +use Rector\VersionBonding\Contract\RelatedPolyfillInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php73\Rector\BinaryOr\IsCountableRector\IsCountableRectorTest */ -final class IsCountableRector extends AbstractRector +final class IsCountableRector extends AbstractRector implements MinPhpVersionInterface, RelatedPolyfillInterface { public function __construct( - private IsArrayAndDualCheckToAble $isArrayAndDualCheckToAble, - private ReflectionProvider $reflectionProvider + private readonly IsArrayAndDualCheckToAble $isArrayAndDualCheckToAble, + private readonly ReflectionProvider $reflectionProvider ) { } @@ -60,7 +63,7 @@ public function refactor(Node $node): ?Node return null; } - return $this->isArrayAndDualCheckToAble->processBooleanOr($node, 'Countable', 'is_countable') ?: $node; + return $this->isArrayAndDualCheckToAble->processBooleanOr($node, 'Countable', 'is_countable'); } public function provideMinPhpVersion(): int @@ -68,6 +71,11 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::IS_COUNTABLE; } + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_73; + } + private function shouldSkip(): bool { return ! $this->reflectionProvider->hasFunction(new Name('is_countable'), null); diff --git a/rules/Php73/Rector/ConstFetch/SensitiveConstantNameRector.php b/rules/Php73/Rector/ConstFetch/SensitiveConstantNameRector.php index 3f1da60b7ab..eb7f86a01aa 100644 --- a/rules/Php73/Rector/ConstFetch/SensitiveConstantNameRector.php +++ b/rules/Php73/Rector/ConstFetch/SensitiveConstantNameRector.php @@ -9,23 +9,22 @@ use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PHPStan\Reflection\ReflectionProvider; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/case_insensitive_constant_deprecation - * * @see \Rector\Tests\Php73\Rector\ConstFetch\SensitiveConstantNameRector\SensitiveConstantNameRectorTest */ -final class SensitiveConstantNameRector extends AbstractRector +final class SensitiveConstantNameRector extends AbstractRector implements MinPhpVersionInterface { /** * @see http://php.net/manual/en/reserved.constants.php * @var string[] */ - private const PHP_RESERVED_CONSTANTS = [ + private const array PHP_RESERVED_CONSTANTS = [ 'PHP_VERSION', 'PHP_MAJOR_VERSION', 'PHP_MINOR_VERSION', @@ -85,14 +84,19 @@ final class SensitiveConstantNameRector extends AbstractRector ]; public function __construct( - private ReflectionProvider $reflectionProvider + private readonly ReflectionProvider $reflectionProvider ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_INSENSITIVE_CONSTANT_NAME; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Changes case insensitive constants to sensitive ones.', + 'Change case insensitive constants to sensitive ones', [ new CodeSample( <<<'CODE_SAMPLE' @@ -137,8 +141,7 @@ public function refactor(Node $node): ?Node } // constant is defined in current lower/upper case - $scope = $node->getAttribute(AttributeKey::SCOPE); - if ($this->reflectionProvider->hasConstant(new Name($constantName), $scope)) { + if ($this->reflectionProvider->hasConstant(new Name($constantName), null)) { return null; } @@ -147,6 +150,14 @@ public function refactor(Node $node): ?Node return null; } + if ( + str_contains($uppercasedConstantName, '\\') + || str_contains($uppercasedConstantName, '(') + || str_contains($uppercasedConstantName, "'") + ) { + return null; + } + $node->name = new FullyQualified($uppercasedConstantName); return $node; diff --git a/rules/Php73/Rector/FuncCall/ArrayKeyFirstLastRector.php b/rules/Php73/Rector/FuncCall/ArrayKeyFirstLastRector.php index c1bf81b1a76..32329d96894 100644 --- a/rules/Php73/Rector/FuncCall/ArrayKeyFirstLastRector.php +++ b/rules/Php73/Rector/FuncCall/ArrayKeyFirstLastRector.php @@ -7,45 +7,42 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; use PHPStan\Reflection\ReflectionProvider; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\ValueObject\PolyfillPackage; use Rector\VersionBonding\Contract\MinPhpVersionInterface; +use Rector\VersionBonding\Contract\RelatedPolyfillInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://tomasvotruba.com/blog/2018/08/16/whats-new-in-php-73-in-30-seconds-in-diffs/#2-first-and-last-array-key - * * This needs to removed 1 floor above, because only nodes in arrays can be removed why traversing, * see https://github.com/nikic/PHP-Parser/issues/389 * * @see \Rector\Tests\Php73\Rector\FuncCall\ArrayKeyFirstLastRector\ArrayKeyFirstLastRectorTest */ -final class ArrayKeyFirstLastRector extends AbstractRector implements MinPhpVersionInterface +final class ArrayKeyFirstLastRector extends AbstractRector implements MinPhpVersionInterface, RelatedPolyfillInterface { - /** - * @var string - */ - private const ARRAY_KEY_FIRST = 'array_key_first'; + private const string ARRAY_KEY_FIRST = 'array_key_first'; - /** - * @var string - */ - private const ARRAY_KEY_LAST = 'array_key_last'; + private const string ARRAY_KEY_LAST = 'array_key_last'; /** * @var array */ - private const PREVIOUS_TO_NEW_FUNCTIONS = [ + private const array PREVIOUS_TO_NEW_FUNCTIONS = [ 'reset' => self::ARRAY_KEY_FIRST, 'end' => self::ARRAY_KEY_LAST, ]; public function __construct( - private ReflectionProvider $reflectionProvider + private readonly ReflectionProvider $reflectionProvider, + private readonly BetterNodeFinder $betterNodeFinder ) { } @@ -84,76 +81,206 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [FuncCall::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param FuncCall $node + * @param StmtsAware $node + * @return ?StmtsAware */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + return $this->processArrayKeyFirstLast($node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_KEY_FIRST_LAST; + } + + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_73; + } + + /** + * @param StmtsAware $stmtsAware + * @return StmtsAware|null + */ + private function processArrayKeyFirstLast(Node $stmtsAware, int $jumpToKey = 0): ?Node + { + if ($stmtsAware->stmts === null) { return null; } - $nextExpression = $this->getNextExpression($node); - if (! $nextExpression instanceof Node) { - return null; + /** @var int $totalKeys */ + $totalKeys = array_key_last($stmtsAware->stmts); + for ($key = $jumpToKey; $key < $totalKeys; ++$key) { + if (! isset($stmtsAware->stmts[$key], $stmtsAware->stmts[$key + 1])) { + break; + } + + if (! $stmtsAware->stmts[$key] instanceof Expression) { + continue; + } + + $stmt = $stmtsAware->stmts[$key]; + if ($this->shouldSkip($stmt)) { + continue; + } + + $nextStmt = $stmtsAware->stmts[$key + 1]; + + /** @var FuncCall $resetOrEndFuncCall */ + $resetOrEndFuncCall = $stmt->expr; + + $keyFuncCall = $this->resolveKeyFuncCall($nextStmt, $resetOrEndFuncCall); + if (! $keyFuncCall instanceof FuncCall) { + continue; + } + + if ($this->hasInternalPointerChangeNext($stmtsAware, $key + 1, $totalKeys, $keyFuncCall)) { + continue; + } + + if (! isset(self::PREVIOUS_TO_NEW_FUNCTIONS[$this->getName($stmt->expr)])) { + continue; + } + + $newName = self::PREVIOUS_TO_NEW_FUNCTIONS[$this->getName($stmt->expr)]; + $keyFuncCall->name = new Name($newName); + + $this->changeNextKeyCall($stmtsAware, $key + 2, $resetOrEndFuncCall, $keyFuncCall->name); + + unset($stmtsAware->stmts[$key]); + + return $stmtsAware; + } + + return null; + } + + /** + * @param StmtsAware $stmtsAware + */ + private function changeNextKeyCall( + Node $stmtsAware, + int $key, + FuncCall $resetOrEndFuncCall, + Name $newName + ): void { + if ($stmtsAware->stmts === null) { + return; + } + + $counter = count($stmtsAware->stmts); + for ($nextKey = $key; $nextKey < $counter; ++$nextKey) { + if (! isset($stmtsAware->stmts[$nextKey])) { + break; + } + + if ($stmtsAware->stmts[$nextKey] instanceof Expression && ! $this->shouldSkip( + $stmtsAware->stmts[$nextKey] + )) { + $this->processArrayKeyFirstLast($stmtsAware, $nextKey); + break; + } + + $keyFuncCall = $this->resolveKeyFuncCall($stmtsAware->stmts[$nextKey], $resetOrEndFuncCall); + if (! $keyFuncCall instanceof FuncCall) { + continue; + } + + $keyFuncCall->name = $newName; } + } - $resetOrEndFuncCall = $node; + private function resolveKeyFuncCall(Stmt $nextStmt, FuncCall $resetOrEndFuncCall): ?FuncCall + { + if ($resetOrEndFuncCall->isFirstClassCallable()) { + return null; + } - $keyFuncCall = $this->betterNodeFinder->findFirst($nextExpression, function (Node $node) use ( + /** @var FuncCall|null */ + return $this->betterNodeFinder->findFirst($nextStmt, function (Node $subNode) use ( $resetOrEndFuncCall ): bool { - if (! $node instanceof FuncCall) { + if (! $subNode instanceof FuncCall) { + return false; + } + + if (! $this->isName($subNode, 'key')) { return false; } - if (! $this->isName($node, 'key')) { + if ($subNode->isFirstClassCallable()) { return false; } - return $this->nodeComparator->areNodesEqual($resetOrEndFuncCall->args[0], $node->args[0]); + return $this->nodeComparator->areNodesEqual($resetOrEndFuncCall->getArgs()[0], $subNode->getArgs()[0]); }); + } - if (! $keyFuncCall instanceof FuncCall) { - return null; - } + /** + * @param StmtsAware $stmtsAware + */ + private function hasInternalPointerChangeNext( + Node $stmtsAware, + int $nextKey, + int $totalKeys, + FuncCall $funcCall + ): bool { + for ($key = $nextKey; $key <= $totalKeys; ++$key) { + if (! isset($stmtsAware->stmts[$key])) { + continue; + } - $newName = self::PREVIOUS_TO_NEW_FUNCTIONS[$this->getName($node)]; - $keyFuncCall->name = new Name($newName); + $hasPrevCallNext = (bool) $this->betterNodeFinder->findFirst( + $stmtsAware->stmts[$key], + function (Node $subNode) use ($funcCall): bool { + if (! $subNode instanceof FuncCall) { + return false; + } - $this->removeNode($node); + if (! $this->isNames($subNode, ['prev', 'next'])) { + return false; + } - return $node; - } + if ($subNode->isFirstClassCallable()) { + return true; + } - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::ARRAY_KEY_FIRST_LAST; + return $this->nodeComparator->areNodesEqual( + $subNode->getArgs()[0] + ->value, + $funcCall->getArgs()[0] + ->value + ); + } + ); + + if ($hasPrevCallNext) { + return true; + } + } + + return false; } - private function shouldSkip(FuncCall $funcCall): bool + private function shouldSkip(Expression $expression): bool { - if (! $this->isNames($funcCall, ['reset', 'end'])) { + if (! $expression->expr instanceof FuncCall) { return true; } - if (! $this->reflectionProvider->hasFunction(new Name(self::ARRAY_KEY_FIRST), null)) { + if (! $this->isNames($expression->expr, ['reset', 'end'])) { return true; } - return ! $this->reflectionProvider->hasFunction(new Name(self::ARRAY_KEY_LAST), null); - } - - private function getNextExpression(FuncCall $funcCall): ?Node - { - $currentExpression = $funcCall->getAttribute(AttributeKey::CURRENT_STATEMENT); - if (! $currentExpression instanceof Expression) { - return null; + if (! $this->reflectionProvider->hasFunction(new Name(self::ARRAY_KEY_FIRST), null)) { + return true; } - return $currentExpression->getAttribute(AttributeKey::NEXT_NODE); + return ! $this->reflectionProvider->hasFunction(new Name(self::ARRAY_KEY_LAST), null); } } diff --git a/rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php b/rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php index 7b4e213624a..32eafaacdc7 100644 --- a/rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php +++ b/rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php @@ -6,23 +6,35 @@ use PhpParser\Node; use PhpParser\Node\Arg; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\BinaryOp\BitwiseOr; use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Identifier; use PhpParser\Node\Name; -use PhpParser\Node\Scalar\LNumber; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use PhpParser\Node\Scalar\Int_; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog http://wiki.php.net/rfc/json_throw_on_error - * @see https://3v4l.org/5HMVE * @see \Rector\Tests\Php73\Rector\FuncCall\JsonThrowOnErrorRector\JsonThrowOnErrorRectorTest */ final class JsonThrowOnErrorRector extends AbstractRector implements MinPhpVersionInterface { + private const array FLAGS = ['JSON_THROW_ON_ERROR']; + + public function __construct( + private readonly ValueResolver $valueResolver, + private readonly BetterNodeFinder $betterNodeFinder + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -48,24 +60,45 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [FuncCall::class]; + return NodeGroup::STMTS_AWARE; } - /** - * @param FuncCall $node - */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + // if found, skip it :) + $hasJsonErrorFuncCall = (bool) $this->betterNodeFinder->findFirst( + $node, + fn (Node $node): bool => $this->isNames($node, ['json_last_error', 'json_last_error_msg']) + ); + + if ($hasJsonErrorFuncCall) { return null; } - if ($this->isName($node, 'json_encode')) { - return $this->processJsonEncode($node); - } + $hasChanged = false; + + $this->traverseNodesWithCallable($node, function (Node $currentNode) use (&$hasChanged): ?FuncCall { + if (! $currentNode instanceof FuncCall) { + return null; + } + + if ($this->shouldSkipFuncCall($currentNode)) { + return null; + } + + if ($this->isName($currentNode, 'json_encode')) { + return $this->processJsonEncode($currentNode, $hasChanged); + } + + if ($this->isName($currentNode, 'json_decode')) { + return $this->processJsonDecode($currentNode, $hasChanged); + } - if ($this->isName($node, 'json_decode')) { - return $this->processJsonDecode($node); + return null; + }); + + if ($hasChanged) { + return $node; } return null; @@ -76,36 +109,54 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::JSON_EXCEPTION; } - private function shouldSkip(FuncCall $funcCall): bool + private function shouldSkipFuncCall(FuncCall $funcCall): bool { - if (! $this->isNames($funcCall, ['json_encode', 'json_decode'])) { + if ($funcCall->isFirstClassCallable()) { + return true; + } + + if ($funcCall->args === []) { return true; } - return (bool) $this->betterNodeFinder->findFirstNext($funcCall, function (Node $node): bool { - if (! $node instanceof FuncCall) { - return false; + foreach ($funcCall->args as $arg) { + if (! $arg instanceof Arg) { + continue; } - return $this->isNames($node, ['json_last_error', 'json_last_error_msg']); - }); + if ($arg->name instanceof Identifier) { + return true; + } + } + + return $this->isFirstValueStringOrArray($funcCall); } - private function processJsonEncode(FuncCall $funcCall): ?FuncCall + private function processJsonEncode(FuncCall $funcCall, bool &$hasChanged): FuncCall { + $flags = []; if (isset($funcCall->args[1])) { - return null; + /** @var Arg $arg */ + $arg = $funcCall->args[1]; + $flags = $this->getFlags($arg); } - $funcCall->args[1] = new Arg($this->createConstFetch('JSON_THROW_ON_ERROR')); + $newArg = $this->getArgWithFlags($flags); + if ($newArg instanceof Arg) { + $hasChanged = true; + $funcCall->args[1] = $newArg; + } return $funcCall; } - private function processJsonDecode(FuncCall $funcCall): ?FuncCall + private function processJsonDecode(FuncCall $funcCall, bool &$hasChanged): FuncCall { + $flags = []; if (isset($funcCall->args[3])) { - return null; + /** @var Arg $arg */ + $arg = $funcCall->args[3]; + $flags = $this->getFlags($arg); } // set default to inter-args @@ -114,10 +165,14 @@ private function processJsonDecode(FuncCall $funcCall): ?FuncCall } if (! isset($funcCall->args[2])) { - $funcCall->args[2] = new Arg(new LNumber(512)); + $funcCall->args[2] = new Arg(new Int_(512)); } - $funcCall->args[3] = new Arg($this->createConstFetch('JSON_THROW_ON_ERROR')); + $newArg = $this->getArgWithFlags($flags); + if ($newArg instanceof Arg) { + $hasChanged = true; + $funcCall->args[3] = $newArg; + } return $funcCall; } @@ -126,4 +181,72 @@ private function createConstFetch(string $name): ConstFetch { return new ConstFetch(new Name($name)); } + + private function isFirstValueStringOrArray(FuncCall $funcCall): bool + { + if (! isset($funcCall->getArgs()[0])) { + return false; + } + + $firstArg = $funcCall->getArgs()[0]; + + $value = $this->valueResolver->getValue($firstArg->value); + if (is_string($value)) { + return true; + } + + return is_array($value); + } + + /** + * @param string[] $flags + * @return string[] + */ + private function getFlags(Expr|Arg $arg, array $flags = []): array + { + // Unwrap Arg + if ($arg instanceof Arg) { + $arg = $arg->value; + } + + // Single flag: SOME_CONST + if ($arg instanceof ConstFetch) { + $flags[] = $arg->name->getFirst(); + return $flags; + } + + // Multiple flags: FLAG_A | FLAG_B | FLAG_C + if ($arg instanceof BitwiseOr) { + $flags = $this->getFlags($arg->left, $flags); + $flags = $this->getFlags($arg->right, $flags); + } + + return array_values(array_unique($flags)); // array_unique in case the same flag is written multiple times + } + + /** + * @param string[] $flags + */ + private function getArgWithFlags(array $flags): ?Arg + { + $originalCount = count($flags); + $flags = array_values(array_unique(array_merge($flags, self::FLAGS))); + if ($originalCount === count($flags)) { + return null; + } + + // Single flag + if (count($flags) === 1) { + return new Arg($this->createConstFetch($flags[0])); + } + + // Build FLAG_A | FLAG_B | FLAG_C + $expr = $this->createConstFetch(array_shift($flags)); + + foreach ($flags as $flag) { + $expr = new BitwiseOr($expr, $this->createConstFetch($flag)); + } + + return new Arg($expr); + } } diff --git a/rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php b/rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php index 50b1ed7a1fd..f7b4b477664 100644 --- a/rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php +++ b/rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php @@ -6,44 +6,40 @@ use Nette\Utils\Strings; use PhpParser\Node; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Scalar\String_; -use Rector\Core\Php\Regex\RegexPatternArgumentManipulator; -use Rector\Core\Rector\AbstractRector; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\Util\StringUtils; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/dRG8U * @see \Rector\Tests\Php73\Rector\FuncCall\RegexDashEscapeRector\RegexDashEscapeRectorTest */ -final class RegexDashEscapeRector extends AbstractRector +final class RegexDashEscapeRector extends AbstractRector implements MinPhpVersionInterface { /** - * @var string * @see https://regex101.com/r/iQbGgZ/1 * * Use {2} as detected only 2 after $this->regexPatternArgumentManipulator->matchCallArgumentWithRegexPattern() call */ - private const THREE_BACKSLASH_FOR_ESCAPE_NEXT_REGEX = '#(?<=[^\\\\])\\\\{2}(?=[^\\\\])#'; + private const string THREE_BACKSLASH_FOR_ESCAPE_NEXT_REGEX = '#(?<=[^\\\\])\\\\{2}(?=[^\\\\])#'; /** - * @var string * @see https://regex101.com/r/YgVJFp/1 */ - private const LEFT_HAND_UNESCAPED_DASH_REGEX = '#(\[.*?\\\\(w|s|d))-(?!\])#i'; + private const string LEFT_HAND_UNESCAPED_DASH_REGEX = '#(\[.*?\\\\(w|s|d))-(?!\])#i'; /** - * @var string - * @see https://regex101.com/r/TBVme9/3 + * @see https://regex101.com/r/TBVme9/9 */ - private const RIGHT_HAND_UNESCAPED_DASH_REGEX = '#(?regexPatternArgumentManipulator->matchCallArgumentWithRegexPattern($node); - if ($regexArguments === []) { + $stringKind = $node->getAttribute(AttributeKey::KIND); + if (in_array($stringKind, [String_::KIND_HEREDOC, String_::KIND_NOWDOC], true)) { return null; } - foreach ($regexArguments as $regexArgument) { - if (Strings::match($regexArgument->value, self::THREE_BACKSLASH_FOR_ESCAPE_NEXT_REGEX)) { - continue; - } + if (StringUtils::isMatch($node->value, self::THREE_BACKSLASH_FOR_ESCAPE_NEXT_REGEX)) { + return null; + } - $this->escapeStringNode($regexArgument); + if ($node->getAttribute(AttributeKey::RAW_VALUE) !== null) { + $stringValue = substr($node->getAttribute(AttributeKey::RAW_VALUE), 1, -1); + } else { + $stringValue = $node->value; } - return $node; - } + if (StringUtils::isMatch($stringValue, self::LEFT_HAND_UNESCAPED_DASH_REGEX)) { + $node->value = Strings::replace($stringValue, self::LEFT_HAND_UNESCAPED_DASH_REGEX, '$1\-'); + $this->setRawValue($node); - private function escapeStringNode(String_ $string): void - { - $stringValue = $string->value; - - if (Strings::match($stringValue, self::LEFT_HAND_UNESCAPED_DASH_REGEX)) { - $string->value = Strings::replace($stringValue, self::LEFT_HAND_UNESCAPED_DASH_REGEX, '$1\-'); // helped needed to skip re-escaping regular expression - $string->setAttribute(AttributeKey::IS_REGULAR_PATTERN, true); - return; + $node->setAttribute(AttributeKey::IS_REGULAR_PATTERN, true); + + return $node; } - if (Strings::match($stringValue, self::RIGHT_HAND_UNESCAPED_DASH_REGEX)) { - $string->value = Strings::replace($stringValue, self::RIGHT_HAND_UNESCAPED_DASH_REGEX, '\-$1]'); + if (StringUtils::isMatch($stringValue, self::RIGHT_HAND_UNESCAPED_DASH_REGEX)) { + $node->value = Strings::replace($stringValue, self::RIGHT_HAND_UNESCAPED_DASH_REGEX, '\-$1]'); + $this->setRawValue($node); + // helped needed to skip re-escaping regular expression - $string->setAttribute(AttributeKey::IS_REGULAR_PATTERN, true); + $node->setAttribute(AttributeKey::IS_REGULAR_PATTERN, true); + + return $node; } + + return null; + } + + private function setRawValue(String_ $string): void + { + $rawValue = $string->getAttribute(AttributeKey::RAW_VALUE); + if ($rawValue === null) { + return; + } + + $rawValue = str_starts_with($rawValue, '"') + ? '"' . $string->value . '"' + : "'" . $string->value . "'"; + + $string->setAttribute(AttributeKey::RAW_VALUE, $rawValue); } } diff --git a/rules/Php73/Rector/FuncCall/SensitiveDefineRector.php b/rules/Php73/Rector/FuncCall/SensitiveDefineRector.php index 091bdf5bfaa..057bba1e99d 100644 --- a/rules/Php73/Rector/FuncCall/SensitiveDefineRector.php +++ b/rules/Php73/Rector/FuncCall/SensitiveDefineRector.php @@ -6,21 +6,26 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/case_insensitive_constant_deprecation - * * @see \Rector\Tests\Php73\Rector\FuncCall\SensitiveDefineRector\SensitiveDefineRectorTest */ -final class SensitiveDefineRector extends AbstractRector +final class SensitiveDefineRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_INSENSITIVE_CONSTANT_DEFINE; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Changes case insensitive constants to sensitive ones.', + 'Change case insensitive constant definition to sensitive one', [new CodeSample( <<<'CODE_SAMPLE' define('FOO', 42, true); diff --git a/rules/Php73/Rector/FuncCall/SetCookieRector.php b/rules/Php73/Rector/FuncCall/SetCookieRector.php index b010d0bd3e8..30cf8473ed7 100644 --- a/rules/Php73/Rector/FuncCall/SetCookieRector.php +++ b/rules/Php73/Rector/FuncCall/SetCookieRector.php @@ -6,13 +6,12 @@ use PhpParser\Node; use PhpParser\Node\Arg; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\Variable; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,8 +20,6 @@ * Convert legacy setcookie arguments to new array options * * @see \Rector\Tests\Php73\Rector\FuncCall\SetcookieRector\SetCookieRectorTest - * - * @changelog https://www.php.net/setcookie https://wiki.php.net/rfc/same-site-cookie */ final class SetCookieRector extends AbstractRector implements MinPhpVersionInterface { @@ -30,7 +27,7 @@ final class SetCookieRector extends AbstractRector implements MinPhpVersionInter * Conversion table from argument index to options name * @var array */ - private const KNOWN_OPTIONS = [ + private const array KNOWN_OPTIONS = [ 2 => 'expires', 3 => 'path', 4 => 'domain', @@ -82,7 +79,6 @@ public function refactor(Node $node): ?Node return null; } - /** @var FuncCall $node */ $node->args = $this->composeNewArgs($node); return $node; @@ -99,17 +95,25 @@ private function shouldSkip(FuncCall $funcCall): bool return true; } - $argsCount = count($funcCall->args); + if ($funcCall->isFirstClassCallable()) { + return true; + } + + $args = $funcCall->getArgs(); + $argsCount = count($args); if ($argsCount <= 2) { return true; } - if ($funcCall->args[2]->value instanceof Array_) { + if ($args[2]->value instanceof Array_) { return true; } if ($argsCount === 3) { - return $funcCall->args[2]->value instanceof Variable; + $type = $this->nodeTypeResolver->getNativeType($args[2]->value); + if (! $type->isInteger()->yes()) { + return true; + } } return false; @@ -120,17 +124,14 @@ private function shouldSkip(FuncCall $funcCall): bool */ private function composeNewArgs(FuncCall $funcCall): array { - $items = []; - - $args = $funcCall->args; + $args = $funcCall->getArgs(); - $newArgs = []; - $newArgs[] = $args[0]; - $newArgs[] = $args[1]; + $newArgs = [$args[0], $args[1]]; unset($args[0]); unset($args[1]); + $items = []; foreach ($args as $idx => $arg) { $newKey = new String_(self::KNOWN_OPTIONS[$idx]); $items[] = new ArrayItem($arg->value, $newKey); diff --git a/rules/Php73/Rector/FuncCall/StringifyStrNeedlesRector.php b/rules/Php73/Rector/FuncCall/StringifyStrNeedlesRector.php index 14d48107057..aa145b36db7 100644 --- a/rules/Php73/Rector/FuncCall/StringifyStrNeedlesRector.php +++ b/rules/Php73/Rector/FuncCall/StringifyStrNeedlesRector.php @@ -5,23 +5,25 @@ namespace Rector\Php73\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\Cast\String_; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Rector\AbstractRector; -use Rector\Php73\NodeTypeAnalyzer\NodeTypeAnalyzer; +use PhpParser\Node\Scalar\InterpolatedString; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/deprecations_php_7_3#string_search_functions_with_integer_needle * @see \Rector\Tests\Php73\Rector\FuncCall\StringifyStrNeedlesRector\StringifyStrNeedlesRectorTest */ -final class StringifyStrNeedlesRector extends AbstractRector +final class StringifyStrNeedlesRector extends AbstractRector implements MinPhpVersionInterface { /** * @var string[] */ - private const NEEDLE_STRING_SENSITIVE_FUNCTIONS = [ + private const array NEEDLE_STRING_SENSITIVE_FUNCTIONS = [ 'strpos', 'strrpos', 'stripos', @@ -34,14 +36,14 @@ final class StringifyStrNeedlesRector extends AbstractRector 'stristr', ]; - public function __construct( - private NodeTypeAnalyzer $nodeTypeAnalyzer - ) { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_INT_IN_STR_NEEDLES; } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Makes needles explicit strings', [ + return new RuleDefinition('Make needles explicit strings', [ new CodeSample( <<<'CODE_SAMPLE' $needle = 5; @@ -73,13 +75,23 @@ public function refactor(Node $node): ?Node return null; } + if (! isset($node->args[1])) { + return null; + } + + if (! $node->args[1] instanceof Arg) { + return null; + } + // is argument string? - $needleArgNode = $node->args[1]->value; - if ($this->nodeTypeAnalyzer->isStringTypeExpr($needleArgNode)) { + $needleArgValue = $node->args[1]->value; + + $needleType = $this->getType($needleArgValue); + if ($needleType->isString()->yes()) { return null; } - if ($needleArgNode instanceof String_) { + if ($needleArgValue instanceof InterpolatedString) { return null; } diff --git a/rules/Php73/Rector/String_/SensitiveHereNowDocRector.php b/rules/Php73/Rector/String_/SensitiveHereNowDocRector.php index 84d1faf60f6..b5c59dd5814 100644 --- a/rules/Php73/Rector/String_/SensitiveHereNowDocRector.php +++ b/rules/Php73/Rector/String_/SensitiveHereNowDocRector.php @@ -6,26 +6,24 @@ use PhpParser\Node; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/flexible_heredoc_nowdoc_syntaxes * @see \Rector\Tests\Php73\Rector\String_\SensitiveHereNowDocRector\SensitiveHereNowDocRectorTest */ -final class SensitiveHereNowDocRector extends AbstractRector +final class SensitiveHereNowDocRector extends AbstractRector implements MinPhpVersionInterface { - /** - * @var string - */ - private const WRAP_SUFFIX = '_WRAP'; + private const string WRAP_SUFFIX = '_WRAP'; - /** - * @var string - */ - private const ATTRIBUTE_DOC_LABEL = 'docLabel'; + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SENSITIVE_HERE_NOW_DOC; + } public function getRuleDefinition(): RuleDefinition { @@ -69,13 +67,13 @@ public function refactor(Node $node): ?Node // the doc label is not in the string → ok /** @var string $docLabel */ - $docLabel = $node->getAttribute(self::ATTRIBUTE_DOC_LABEL); + $docLabel = $node->getAttribute(AttributeKey::DOC_LABEL); if (! \str_contains($node->value, $docLabel)) { return null; } - $node->setAttribute(self::ATTRIBUTE_DOC_LABEL, $this->uniquateDocLabel($node->value, $docLabel)); + $node->setAttribute(AttributeKey::DOC_LABEL, $this->uniquateDocLabel($node->value, $docLabel)); // invoke redraw $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); diff --git a/rules/Php74/Guard/MakePropertyTypedGuard.php b/rules/Php74/Guard/MakePropertyTypedGuard.php new file mode 100644 index 00000000000..61ca8af9406 --- /dev/null +++ b/rules/Php74/Guard/MakePropertyTypedGuard.php @@ -0,0 +1,26 @@ +type instanceof Node) { + return false; + } + + return $this->propertyTypeChangeGuard->isLegal($property, $classReflection, $inlinePublic); + } +} diff --git a/rules/Php74/Guard/PropertyTypeChangeGuard.php b/rules/Php74/Guard/PropertyTypeChangeGuard.php new file mode 100644 index 00000000000..7ea21cb0cee --- /dev/null +++ b/rules/Php74/Guard/PropertyTypeChangeGuard.php @@ -0,0 +1,79 @@ +props) > 1) { + return false; + } + + /** + * - trait properties are unpredictable based on class context they appear in + * - on interface properties as well, as interface not allowed to have property + */ + if (! $classReflection->isClass()) { + return false; + } + + $propertyName = $this->nodeNameResolver->getName($property); + + if ($this->propertyManipulator->hasTraitWithSamePropertyOrWritten($classReflection, $propertyName)) { + return false; + } + + if ($this->propertyAnalyzer->hasForbiddenType($property)) { + return false; + } + + if ($inlinePublic) { + return true; + } + + if ($property->isPrivate()) { + return true; + } + + if ($isConstructorPromotion) { + return true; + } + + return $this->isSafeProtectedProperty($classReflection, $property); + } + + private function isSafeProtectedProperty(ClassReflection $classReflection, Property $property): bool + { + if (! $property->isProtected()) { + return false; + } + + if (! $classReflection->isFinalByKeyword()) { + return false; + } + + return $this->parentPropertyLookupGuard->isLegal($property, $classReflection); + } +} diff --git a/rules/Php74/NodeAnalyzer/ClosureArrowFunctionAnalyzer.php b/rules/Php74/NodeAnalyzer/ClosureArrowFunctionAnalyzer.php index 6974ee8c19c..b4c1021cdb8 100644 --- a/rules/Php74/NodeAnalyzer/ClosureArrowFunctionAnalyzer.php +++ b/rules/Php74/NodeAnalyzer/ClosureArrowFunctionAnalyzer.php @@ -5,19 +5,28 @@ namespace Rector\Php74\NodeAnalyzer; use PhpParser\Node; +use PhpParser\Node\ClosureUse; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Closure; -use PhpParser\Node\Expr\ClosureUse; +use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Return_; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\NodeAnalyzer\CompactFuncCallAnalyzer; +use Rector\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Util\ArrayChecker; -final class ClosureArrowFunctionAnalyzer +final readonly class ClosureArrowFunctionAnalyzer { public function __construct( private BetterNodeFinder $betterNodeFinder, - private NodeComparator $nodeComparator + private NodeComparator $nodeComparator, + private ArrayChecker $arrayChecker, + private PhpDocInfoFactory $phpDocInfoFactory, + private CompactFuncCallAnalyzer $compactFuncCallAnalyzer ) { } @@ -32,27 +41,77 @@ public function matchArrowFunctionExpr(Closure $closure): ?Expr return null; } - /** @var Return_ $return */ $return = $onlyStmt; - if ($return->expr === null) { + if (! $return->expr instanceof Expr) { return null; } - if ($this->shouldSkipForUsedReferencedValue($closure, $return->expr)) { + if ($this->shouldSkipForUsedReferencedValue($closure)) { + return null; + } + + if ($this->shouldSkipForUseVariableUsedByCompact($closure)) { + return null; + } + + if ($this->shouldSkipMoreSpecificTypeWithVarDoc($return)) { return null; } return $return->expr; } - private function shouldSkipForUsedReferencedValue(Closure $closure, Expr $expr): bool + private function shouldSkipForUseVariableUsedByCompact(Closure $closure): bool + { + $variables = array_map(fn (ClosureUse $use): Variable => $use->var, $closure->uses); + + if ($variables === []) { + return false; + } + + return (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped( + $closure, + function (Node $node) use ($variables): bool { + if (! $node instanceof FuncCall) { + return false; + } + + foreach ($variables as $variable) { + if ($this->compactFuncCallAnalyzer->isInCompact($node, $variable)) { + return true; + } + } + + return false; + } + ); + } + + /** + * Ensure @var doc usage to be skipped, as arrow functions do not support + * inline @var annotations for type narrowing (e.g. generic types like Builder) + */ + private function shouldSkipMoreSpecificTypeWithVarDoc(Return_ $return): bool + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($return); + + if (! $phpDocInfo instanceof PhpDocInfo) { + return false; + } + + $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + + return $varTagValueNode instanceof VarTagValueNode; + } + + private function shouldSkipForUsedReferencedValue(Closure $closure): bool { $referencedValues = $this->resolveReferencedUseVariablesFromClosure($closure); if ($referencedValues === []) { return false; } - return (bool) $this->betterNodeFinder->findFirst([$expr], function (Node $node) use ( + $isFoundInStmt = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped($closure, function (Node $node) use ( $referencedValues ): bool { foreach ($referencedValues as $referencedValue) { @@ -63,6 +122,42 @@ private function shouldSkipForUsedReferencedValue(Closure $closure, Expr $expr): return false; }); + + if ($isFoundInStmt) { + return true; + } + + return $this->isFoundInInnerUses($closure, $referencedValues); + } + + /** + * @param Variable[] $referencedValues + */ + private function isFoundInInnerUses(Closure $node, array $referencedValues): bool + { + return (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped( + $node, + function (Node $subNode) use ($referencedValues): bool { + if (! $subNode instanceof Closure) { + return false; + } + + foreach ($referencedValues as $referencedValue) { + $isFoundInInnerUses = $this->arrayChecker->doesExist( + $subNode->uses, + fn (ClosureUse $closureUse): bool => $closureUse->byRef && $this->nodeComparator->areNodesEqual( + $closureUse->var, + $referencedValue + ) + ); + if ($isFoundInInnerUses) { + return true; + } + } + + return false; + } + ); } /** diff --git a/rules/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector.php b/rules/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector.php new file mode 100644 index 00000000000..a83d0a2c69e --- /dev/null +++ b/rules/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector.php @@ -0,0 +1,87 @@ +> + */ + public function getNodeTypes(): array + { + return [ArrayDimFetch::class]; + } + + /** + * @param ArrayDimFetch $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isFollowedByCurlyBracket($this->getFile(), $node)) { + return null; + } + + // re-draw the ArrayDimFetch to use [] bracket + $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); + return $node; + } + + private function isFollowedByCurlyBracket(File $file, ArrayDimFetch $arrayDimFetch): bool + { + $oldTokens = $file->getOldTokens(); + $endTokenPost = $arrayDimFetch->getEndTokenPos(); + + if (isset($oldTokens[$endTokenPost]) && (string) $oldTokens[$endTokenPost] === '}') { + $startTokenPos = $arrayDimFetch->getStartTokenPos(); + return ! (isset($oldTokens[$startTokenPos]) && (string) $oldTokens[$startTokenPos] === '${'); + } + + return false; + } +} diff --git a/rules/Php74/Rector/Assign/NullCoalescingOperatorRector.php b/rules/Php74/Rector/Assign/NullCoalescingOperatorRector.php index c72fdc929e6..da9cdcebf27 100644 --- a/rules/Php74/Rector/Assign/NullCoalescingOperatorRector.php +++ b/rules/Php74/Rector/Assign/NullCoalescingOperatorRector.php @@ -8,21 +8,20 @@ use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\AssignOp\Coalesce as AssignCoalesce; use PhpParser\Node\Expr\BinaryOp\Coalesce; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/null_coalesce_equal_operator * @see \Rector\Tests\Php74\Rector\Assign\NullCoalescingOperatorRector\NullCoalescingOperatorRectorTest */ final class NullCoalescingOperatorRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Use null coalescing operator ??=', [ + return new RuleDefinition('Use null coalescing operator `??=`', [ new CodeSample( <<<'CODE_SAMPLE' $array = []; @@ -48,7 +47,7 @@ public function getNodeTypes(): array /** * @param Assign $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?AssignCoalesce { if (! $node->expr instanceof Coalesce) { return null; diff --git a/rules/Php74/Rector/Closure/ClosureToArrowFunctionRector.php b/rules/Php74/Rector/Closure/ClosureToArrowFunctionRector.php index 90f169dda02..416d7af0913 100644 --- a/rules/Php74/Rector/Closure/ClosureToArrowFunctionRector.php +++ b/rules/Php74/Rector/Closure/ClosureToArrowFunctionRector.php @@ -8,22 +8,21 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Closure; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Php74\NodeAnalyzer\ClosureArrowFunctionAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/arrow_functions_v2 - * * @see \Rector\Tests\Php74\Rector\Closure\ClosureToArrowFunctionRector\ClosureToArrowFunctionRectorTest */ final class ClosureToArrowFunctionRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private ClosureArrowFunctionAnalyzer $closureArrowFunctionAnalyzer + private readonly ClosureArrowFunctionAnalyzer $closureArrowFunctionAnalyzer ) { } @@ -74,17 +73,33 @@ public function refactor(Node $node): ?Node return null; } - $arrowFunction = new ArrowFunction(); - $arrowFunction->params = $node->params; - $arrowFunction->returnType = $node->returnType; - $arrowFunction->byRef = $node->byRef; + if ($node->getAttribute(AttributeKey::IS_CLOSURE_IN_ATTRIBUTE) === true) { + return null; + } + + $attributes = $node->getAttributes(); + unset($attributes[AttributeKey::ORIGINAL_NODE]); - $arrowFunction->expr = $returnExpr; + $arrowFunction = new ArrowFunction( + [ + 'params' => $node->params, + 'returnType' => $node->returnType, + 'byRef' => $node->byRef, + 'expr' => $returnExpr, + ], + $attributes + ); if ($node->static) { $arrowFunction->static = true; } + $comments = $node->stmts[0]->getAttribute(AttributeKey::COMMENTS) ?? []; + if ($comments !== []) { + $this->mirrorComments($arrowFunction->expr, $node->stmts[0]); + $arrowFunction->setAttribute(AttributeKey::COMMENTS, $node->stmts[0]->getComments()); + } + return $arrowFunction; } diff --git a/rules/Php74/Rector/Double/RealToFloatTypeCastRector.php b/rules/Php74/Rector/Double/RealToFloatTypeCastRector.php deleted file mode 100644 index 11523a6c4ff..00000000000 --- a/rules/Php74/Rector/Double/RealToFloatTypeCastRector.php +++ /dev/null @@ -1,74 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Double::class]; - } - - /** - * @param Double $node - */ - public function refactor(Node $node): ?Node - { - $kind = $node->getAttribute(AttributeKey::KIND); - if ($kind !== Double::KIND_REAL) { - return null; - } - - $node->setAttribute(AttributeKey::KIND, Double::KIND_FLOAT); - $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); - - return $node; - } -} diff --git a/rules/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector.php b/rules/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector.php index 08e0f6b4370..bb2c02d825d 100644 --- a/rules/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector.php +++ b/rules/Php74/Rector/FuncCall/ArrayKeyExistsOnPropertyRector.php @@ -5,20 +5,26 @@ namespace Rector\Php74\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/deprecations_php_7_4 (not confirmed yet) - * @see https://3v4l.org/69mpd * @see \Rector\Tests\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector\ArrayKeyExistsOnPropertyRectorTest */ -final class ArrayKeyExistsOnPropertyRector extends AbstractRector +final class ArrayKeyExistsOnPropertyRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_KEY_EXISTS_TO_PROPERTY_EXISTS; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -66,7 +72,15 @@ public function refactor(Node $node): ?Node return null; } - $firstArgStaticType = $this->getStaticType($node->args[1]->value); + if (! isset($node->args[1])) { + return null; + } + + if (! $node->args[1] instanceof Arg) { + return null; + } + + $firstArgStaticType = $this->getType($node->args[1]->value); if (! $firstArgStaticType instanceof ObjectType) { return null; } diff --git a/rules/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector.php b/rules/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector.php deleted file mode 100644 index ecb8e022879..00000000000 --- a/rules/Php74/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector.php +++ /dev/null @@ -1,199 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->isName($node, 'array_merge')) { - return $this->refactorArray($node); - } - - return null; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::ARRAY_SPREAD; - } - - private function refactorArray(FuncCall $funcCall): ?Array_ - { - $array = new Array_(); - - foreach ($funcCall->args as $arg) { - // cannot handle unpacked arguments - if ($arg->unpack) { - return null; - } - - $value = $arg->value; - if ($this->shouldSkipArrayForInvalidTypeOrKeys($value)) { - return null; - } - - $value = $this->resolveValue($value); - $array->items[] = $this->createUnpackedArrayItem($value); - } - - return $array; - } - - private function shouldSkipArrayForInvalidTypeOrKeys(Expr $expr): bool - { - // we have no idea what it is → cannot change it - if (! $this->arrayTypeAnalyzer->isArrayType($expr)) { - return true; - } - - $arrayStaticType = $this->getStaticType($expr); - if ($this->isConstantArrayTypeWithStringKeyType($arrayStaticType)) { - return true; - } - - if (! $arrayStaticType instanceof ArrayType) { - return true; - } - - // integer key type is required, @see https://twitter.com/nikita_ppv/status/1126470222838366209 - return ! $arrayStaticType->getKeyType() instanceof IntegerType; - } - - private function resolveValue(Expr $expr): Expr - { - if ($expr instanceof FuncCall && $this->isIteratorToArrayFuncCall($expr)) { - /** @var FuncCall $expr */ - $expr = $expr->args[0]->value; - } - - if (! $expr instanceof Ternary) { - return $expr; - } - - if (! $expr->cond instanceof FuncCall) { - return $expr; - } - - if (! $this->isName($expr->cond, 'is_array')) { - return $expr; - } - - if ($expr->if instanceof Variable && $this->isIteratorToArrayFuncCall($expr->else)) { - return $expr->if; - } - - return $expr; - } - - private function createUnpackedArrayItem(Expr $expr): ArrayItem - { - return new ArrayItem($expr, null, false, [], true); - } - - private function isConstantArrayTypeWithStringKeyType(Type $type): bool - { - if (! $type instanceof ConstantArrayType) { - return false; - } - - foreach ($type->getKeyTypes() as $keyType) { - // key cannot be string - if ($keyType instanceof ConstantStringType) { - return true; - } - } - - return false; - } - - private function isIteratorToArrayFuncCall(Expr $expr): bool - { - if (! $expr instanceof FuncCall) { - return false; - } - - return $this->nodeNameResolver->isName($expr, 'iterator_to_array'); - } -} diff --git a/rules/Php74/Rector/FuncCall/FilterVarToAddSlashesRector.php b/rules/Php74/Rector/FuncCall/FilterVarToAddSlashesRector.php index f46bb328da9..8c5929c9172 100644 --- a/rules/Php74/Rector/FuncCall/FilterVarToAddSlashesRector.php +++ b/rules/Php74/Rector/FuncCall/FilterVarToAddSlashesRector.php @@ -5,19 +5,25 @@ namespace Rector\Php74\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/deprecations_php_7_4 (not confirmed yet) - * @see https://3v4l.org/9rLjE * @see \Rector\Tests\Php74\Rector\FuncCall\FilterVarToAddSlashesRector\FilterVarToAddSlashesRectorTest */ -final class FilterVarToAddSlashesRector extends AbstractRector +final class FilterVarToAddSlashesRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::FILTER_VAR_TO_ADD_SLASHES; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -59,6 +65,10 @@ public function refactor(Node $node): ?Node return null; } + if (! $node->args[1] instanceof Arg) { + return null; + } + if (! $this->isName($node->args[1]->value, 'FILTER_SANITIZE_MAGIC_QUOTES')) { return null; } diff --git a/rules/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector.php b/rules/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector.php deleted file mode 100644 index e6c328f083f..00000000000 --- a/rules/Php74/Rector/FuncCall/GetCalledClassToStaticClassRector.php +++ /dev/null @@ -1,73 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node, 'get_called_class')) { - return null; - } - - return $this->nodeFactory->createClassConstFetch('static', 'class'); - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::CLASSNAME_CONSTANT; - } -} diff --git a/rules/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector.php b/rules/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector.php new file mode 100644 index 00000000000..a660db44dcd --- /dev/null +++ b/rules/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector.php @@ -0,0 +1,69 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?FuncCall + { + if (! $this->isName($node, 'hebrevc')) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $node->name = new Name('hebrev'); + return new FuncCall(new Name('nl2br'), [new Arg($node)]); + } +} diff --git a/rules/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector.php b/rules/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector.php index 989c30afef7..7eda8bde65f 100644 --- a/rules/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector.php +++ b/rules/Php74/Rector/FuncCall/MbStrrposEncodingArgumentPositionRector.php @@ -7,19 +7,23 @@ use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Scalar\LNumber; -use PHPStan\Type\IntegerType; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Scalar\Int_; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/deprecations_php_7_4 https://3v4l.org/kLdtB - * * @see \Rector\Tests\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector\MbStrrposEncodingArgumentPositionRectorTest */ -final class MbStrrposEncodingArgumentPositionRector extends AbstractRector +final class MbStrrposEncodingArgumentPositionRector extends AbstractRector implements MinPhpVersionInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::CHANGE_MB_STRPOS_ARG_POSITION; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -53,13 +57,17 @@ public function refactor(Node $node): ?Node return null; } - $secondArgType = $this->getStaticType($node->args[2]->value); - if ($secondArgType instanceof IntegerType) { + if (! $node->args[2] instanceof Arg) { + return null; + } + + $secondArgType = $this->getType($node->args[2]->value); + if ($secondArgType->isInteger()->yes()) { return null; } $node->args[3] = $node->args[2]; - $node->args[2] = new Arg(new LNumber(0)); + $node->args[2] = new Arg(new Int_(0)); return $node; } diff --git a/rules/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector.php b/rules/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector.php new file mode 100644 index 00000000000..1a18812fc50 --- /dev/null +++ b/rules/Php74/Rector/FuncCall/MoneyFormatToNumberFormatRector.php @@ -0,0 +1,108 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?FuncCall + { + if (! $this->isName($node, 'money_format')) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + if ($this->argsAnalyzer->hasNamedArg($args)) { + return null; + } + + $formatValue = $args[0]->value; + if (! $this->valueResolver->isValue($formatValue, '%i')) { + return null; + } + + return $this->warpInNumberFormatFuncCall($node, $args[1]->value); + } + + private function warpInNumberFormatFuncCall(FuncCall $funcCall, Expr $expr): FuncCall + { + $roundFuncCall = $this->nodeFactory->createFuncCall( + 'round', + [$expr, new Int_(2), new ConstFetch(new Name('PHP_ROUND_HALF_ODD'))] + ); + + $funcCall->name = new Name('number_format'); + $funcCall->args = [ + new Arg($roundFuncCall), + new Arg(new Int_(2)), + new Arg(new String_('.')), + new Arg(new String_('')), + ]; + + return $funcCall; + } +} diff --git a/rules/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector.php b/rules/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector.php new file mode 100644 index 00000000000..fe0ccac3113 --- /dev/null +++ b/rules/Php74/Rector/FuncCall/RestoreIncludePathToIniRestoreRector.php @@ -0,0 +1,73 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?FuncCall + { + if (! $this->isName($node, 'restore_include_path')) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $node->name = new Name('ini_restore'); + $node->args[0] = new Arg(new String_('include_path')); + + return $node; + } +} diff --git a/rules/Php74/Rector/Function_/ReservedFnFunctionRector.php b/rules/Php74/Rector/Function_/ReservedFnFunctionRector.php deleted file mode 100644 index e755678b2fd..00000000000 --- a/rules/Php74/Rector/Function_/ReservedFnFunctionRector.php +++ /dev/null @@ -1,114 +0,0 @@ - [ - 'fn' => 'someFunctionName', - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Function_::class, FuncCall::class]; - } - - /** - * @param Function_|FuncCall $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->reservedNamesToNewOnes as $reservedName => $newName) { - if (! $this->isName($node->name, $reservedName)) { - continue; - } - - if ($node instanceof FuncCall) { - $node->name = new Name($newName); - } else { - $node->name = new Identifier($newName); - } - - return $node; - } - - return null; - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->reservedNamesToNewOnes = $configuration[self::RESERVED_NAMES_TO_NEW_ONES] ?? []; - } -} diff --git a/rules/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector.php b/rules/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector.php index ede0f8f3bca..48261170523 100644 --- a/rules/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector.php +++ b/rules/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector.php @@ -4,53 +4,36 @@ namespace Rector\Php74\Rector\LNumber; -use Nette\Utils\Strings; use PhpParser\Node; -use PhpParser\Node\Scalar\DNumber; -use PhpParser\Node\Scalar\LNumber; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\Application\File; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\Node\Scalar\Float_; +use PhpParser\Node\Scalar\Int_; +use Rector\Configuration\Deprecation\Contract\DeprecatedInterface; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Exception\ShouldNotHappenException; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Webmozart\Assert\Assert; /** - * @changelog https://wiki.php.net/rfc/numeric_literal_separator - * @changelog https://github.com/nikic/PHP-Parser/pull/615 - * @see \Rector\Tests\Php74\Rector\LNumber\AddLiteralSeparatorToNumberRector\AddLiteralSeparatorToNumberRectorTest - * @changelog https://twitter.com/seldaek/status/1329064983120982022 - * * Taking the most generic use case to the account: https://wiki.php.net/rfc/numeric_literal_separator#should_it_be_the_role_of_an_ide_to_group_digits * The final check should be done manually + * + * @deprecated as opinionated and group size depends on context. Cannot be automated. Use manually where needed instead. */ -final class AddLiteralSeparatorToNumberRector extends AbstractRector implements ConfigurableRectorInterface, MinPhpVersionInterface +final class AddLiteralSeparatorToNumberRector extends AbstractRector implements MinPhpVersionInterface, ConfigurableRectorInterface, DeprecatedInterface { /** * @api - * @var string - */ - public const LIMIT_VALUE = 'limit_value'; - - /** - * @var int */ - private const GROUP_SIZE = 3; - - private int $limitValue = 1_000_000; + public const string LIMIT_VALUE = 'limit_value'; /** - * @param mixed[] $configuration + * @param array $configuration */ public function configure(array $configuration): void { - $limitValue = $configuration[self::LIMIT_VALUE] ?? 1_000_000; - Assert::integer($limitValue); - - $this->limitValue = $limitValue; } public function getRuleDefinition(): RuleDefinition @@ -94,106 +77,22 @@ public function run() */ public function getNodeTypes(): array { - return [LNumber::class, DNumber::class]; + return [Int_::class, Float_::class]; } /** - * @param LNumber|DNumber $node + * @param Int_|Float_ $node */ public function refactor(Node $node): ?Node { - $numericValueAsString = (string) $node->value; - if ($this->shouldSkip($node, $numericValueAsString)) { - return null; - } - - if (\str_contains($numericValueAsString, '.')) { - [$mainPart, $decimalPart] = explode('.', $numericValueAsString); - - $chunks = $this->strSplitNegative($mainPart, self::GROUP_SIZE); - $literalSeparatedNumber = implode('_', $chunks) . '.' . $decimalPart; - } else { - $chunks = $this->strSplitNegative($numericValueAsString, self::GROUP_SIZE); - $literalSeparatedNumber = implode('_', $chunks); - - // PHP converts: (string) 1000.0 -> "1000"! - if (is_float($node->value)) { - $literalSeparatedNumber .= '.0'; - } - } - - $node->value = $literalSeparatedNumber; - - return $node; + throw new ShouldNotHappenException(sprintf( + '"%s" is deprecated as opinionated and group size depends on context. Cannot be automated. Use manually where needed instead', + self::class + )); } public function provideMinPhpVersion(): int { return PhpVersionFeature::LITERAL_SEPARATOR; } - - private function shouldSkip(LNumber | DNumber $node, string $numericValueAsString): bool - { - /** @var int $startToken */ - $startToken = $node->getAttribute(AttributeKey::START_TOKEN_POSITION); - - $file = $node->getAttribute(AttributeKey::FILE); - - // new node - if (! $file instanceof File) { - return true; - } - - $oldTokens = $file->getOldTokens(); - - foreach ($oldTokens[$startToken] as $token) { - if (! is_string($token)) { - continue; - } - if (! str_contains($token, '_')) { - continue; - } - return true; - } - - if ($numericValueAsString < $this->limitValue) { - return true; - } - - // already separated - if (\str_contains($numericValueAsString, '_')) { - return true; - } - - $kind = $node->getAttribute(AttributeKey::KIND); - if (in_array($kind, [LNumber::KIND_BIN, LNumber::KIND_OCT, LNumber::KIND_HEX], true)) { - return true; - } - - // e+/e- - if (Strings::match($numericValueAsString, '#e#i')) { - return true; - } - - // too short - return Strings::length($numericValueAsString) <= self::GROUP_SIZE; - } - - /** - * @return string[] - */ - private function strSplitNegative(string $string, int $length): array - { - $inversed = strrev($string); - - /** @var string[] $chunks */ - $chunks = str_split($inversed, $length); - - $chunks = array_reverse($chunks); - foreach ($chunks as $key => $chunk) { - $chunks[$key] = strrev($chunk); - } - - return $chunks; - } } diff --git a/rules/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector.php b/rules/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector.php deleted file mode 100644 index d633b589922..00000000000 --- a/rules/Php74/Rector/MethodCall/ChangeReflectionTypeToStringToGetNameRector.php +++ /dev/null @@ -1,242 +0,0 @@ -getParameters()[0]; - - $paramType = (string) $parameterReflection->getType(); - - $stringValue = 'hey' . $reflectionFunction->getReturnType(); - - // keep - return $reflectionFunction->getReturnType(); - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function go(ReflectionFunction $reflectionFunction) - { - $parameterReflection = $reflectionFunction->getParameters()[0]; - - $paramType = (string) ($parameterReflection->getType() ? $parameterReflection->getType()->getName() : null); - - $stringValue = 'hey' . ($reflectionFunction->getReturnType() ? $reflectionFunction->getReturnType()->getName() : null); - - // keep - return $reflectionFunction->getReturnType(); - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class, String_::class]; - } - - /** - * @param MethodCall|String_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof MethodCall) { - return $this->refactorMethodCall($node); - } - - if ($node->expr instanceof MethodCall) { - return $this->refactorIfHasReturnTypeWasCalled($node->expr); - } - if (! $node->expr instanceof Variable) { - return null; - } - if (! $this->isObjectType($node->expr, new ObjectType('ReflectionType'))) { - return null; - } - - $type = $this->nodeTypeResolver->resolve($node->expr); - if (! $type instanceof UnionType) { - return $this->nodeFactory->createMethodCall($node->expr, self::GET_NAME); - } - - if (! $this->isWithReflectionType($type)) { - return $this->nodeFactory->createMethodCall($node->expr, self::GET_NAME); - } - - return null; - } - - private function isWithReflectionType(UnionType $unionType): bool - { - foreach ($unionType->getTypes() as $type) { - if (! $type instanceof ObjectType) { - continue; - } - if ($type->getClassName() !== 'ReflectionType') { - continue; - } - return true; - } - - return false; - } - - private function refactorMethodCall(MethodCall $methodCall): ?Node - { - $this->collectCallByVariable($methodCall); - - if ($this->shouldSkipMethodCall($methodCall)) { - return null; - } - - if ($this->isReflectionParameterGetTypeMethodCall($methodCall)) { - return $this->refactorReflectionParameterGetName($methodCall); - } - - if ($this->isReflectionFunctionAbstractGetReturnTypeMethodCall($methodCall)) { - return $this->refactorReflectionFunctionGetReturnType($methodCall); - } - - return null; - } - - private function refactorIfHasReturnTypeWasCalled(MethodCall $methodCall): ?Node - { - if (! $methodCall->var instanceof Variable) { - return null; - } - - $variableName = $this->getName($methodCall->var); - - $callsByVariable = $this->callsByVariable[$variableName] ?? []; - - // we already know it has return type - if (in_array('hasReturnType', $callsByVariable, true)) { - return $this->nodeFactory->createMethodCall($methodCall, self::GET_NAME); - } - - return null; - } - - private function collectCallByVariable(MethodCall $methodCall): void - { - // bit workaround for now - if ($methodCall->var instanceof Variable) { - $variableName = $this->getName($methodCall->var); - $methodName = $this->getName($methodCall->name); - if (! $variableName) { - return; - } - if (! $methodName) { - return; - } - $this->callsByVariable[$variableName][] = $methodName; - } - } - - private function shouldSkipMethodCall(MethodCall $methodCall): bool - { - $scope = $methodCall->getAttribute(AttributeKey::SCOPE); - // just added node → skip it - if (! $scope instanceof Scope) { - return true; - } - - // is to string retype? - $parentNode = $methodCall->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof String_) { - return false; - } - // probably already converted - return ! $parentNode instanceof Concat; - } - - private function isReflectionParameterGetTypeMethodCall(MethodCall $methodCall): bool - { - if (! $this->isObjectType($methodCall->var, new ObjectType('ReflectionParameter'))) { - return false; - } - - return $this->isName($methodCall->name, 'getType'); - } - - private function refactorReflectionParameterGetName(MethodCall $methodCall): Ternary - { - $getNameMethodCall = $this->nodeFactory->createMethodCall($methodCall, self::GET_NAME); - - return new Ternary($methodCall, $getNameMethodCall, $this->nodeFactory->createNull()); - } - - private function isReflectionFunctionAbstractGetReturnTypeMethodCall(MethodCall $methodCall): bool - { - if (! $this->isObjectType($methodCall->var, new ObjectType('ReflectionFunctionAbstract'))) { - return false; - } - - return $this->isName($methodCall->name, 'getReturnType'); - } - - private function refactorReflectionFunctionGetReturnType(MethodCall $methodCall): Node | Ternary - { - $refactoredMethodCall = $this->refactorIfHasReturnTypeWasCalled($methodCall); - if ($refactoredMethodCall !== null) { - return $refactoredMethodCall; - } - - $getNameMethodCall = $this->nodeFactory->createMethodCall($methodCall, self::GET_NAME); - return new Ternary($methodCall, $getNameMethodCall, $this->nodeFactory->createNull()); - } -} diff --git a/rules/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector.php b/rules/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector.php index dbb0a354c86..83f86675b13 100644 --- a/rules/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector.php +++ b/rules/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector.php @@ -5,16 +5,13 @@ namespace Rector\Php74\Rector\Property; use PhpParser\Node; -use PhpParser\Node\Expr\Assign; -use PhpParser\Node\NullableType; +use PhpParser\Node\Expr; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use PhpParser\NodeTraverser; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Rector\AbstractRector; +use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -24,6 +21,12 @@ */ final class RestoreDefaultNullToNullableTypePropertyRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly ConstructorAssignDetector $constructorAssignDetector, + private readonly PhpDocInfoFactory $phpDocInfoFactory + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -36,7 +39,7 @@ class SomeClass public ?string $name; } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -53,22 +56,36 @@ class SomeClass */ public function getNodeTypes(): array { - return [Property::class]; + return [Class_::class]; } /** - * @param Property $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + if ($this->isReadonlyClass($node)) { return null; } - $onlyProperty = $node->props[0]; - $onlyProperty->default = $this->nodeFactory->createNull(); + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + if ($this->shouldSkipProperty($property, $node)) { + continue; + } + + $onlyProperty = $property->props[0]; + $onlyProperty->default = $this->nodeFactory->createNull(); + + $hasChanged = true; + } - return $node; + if ($hasChanged) { + return $node; + } + + return null; } public function provideMinPhpVersion(): int @@ -76,9 +93,9 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::TYPED_PROPERTIES; } - private function shouldSkip(Property $property): bool + private function shouldSkipProperty(Property $property, Class_ $class): bool { - if (! $property->type instanceof NullableType) { + if (! $property->type instanceof Node) { return true; } @@ -86,47 +103,48 @@ private function shouldSkip(Property $property): bool return true; } - $onlyProperty = $property->props[0]; - if ($onlyProperty->default !== null) { + if ($property->props[0]->default instanceof Expr) { return true; } - // is variable assigned in constructor - $propertyName = $this->getName($property); - - return $this->isPropertyInitiatedInConstuctor($property, $propertyName); - } + if ($this->isReadonlyProperty($property)) { + return true; + } - private function isPropertyInitiatedInConstuctor(Property $property, string $propertyName): bool - { - $classLike = $property->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return false; + if (! $this->nodeTypeResolver->isNullableType($property)) { + return true; } - $constructClassMethod = $classLike->getMethod(MethodName::CONSTRUCT); - if (! $constructClassMethod instanceof ClassMethod) { - return false; + if ($property->hooks !== []) { + return true; } - $isPropertyInitiated = false; - $this->traverseNodesWithCallable((array) $constructClassMethod->stmts, function (Node $node) use ( - $propertyName, - &$isPropertyInitiated - ): ?int { - if (! $node instanceof Assign) { - return null; - } + // is variable assigned in constructor + $propertyName = $this->getName($property); + return $this->constructorAssignDetector->isPropertyAssignedConditionally($class, $propertyName); + } - if (! $this->nodeNameResolver->isLocalPropertyFetchNamed($node->var, $propertyName)) { - return null; - } + private function isReadonlyProperty(Property $property): bool + { + // native readonly + if ($property->isReadonly()) { + return true; + } - $isPropertyInitiated = true; + // @readonly annotation + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + return $phpDocInfo->hasByName('@readonly'); + } - return NodeTraverser::STOP_TRAVERSAL; - }); + private function isReadonlyClass(Class_ $class): bool + { + // native readonly + if ($class->isReadonly()) { + return true; + } - return $isPropertyInitiated; + // @immutable annotation + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($class); + return $phpDocInfo->hasByName('@immutable'); } } diff --git a/rules/Php74/Rector/Property/TypedPropertyRector.php b/rules/Php74/Rector/Property/TypedPropertyRector.php deleted file mode 100644 index 693ea7dec5f..00000000000 --- a/rules/Php74/Rector/Property/TypedPropertyRector.php +++ /dev/null @@ -1,264 +0,0 @@ - false, - ] - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Property::class]; - } - - /** - * @param Property $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkipProperty($node)) { - return null; - } - - $varType = $this->propertyTypeInferer->inferProperty($node); - if ($varType instanceof MixedType) { - return null; - } - - if ($varType instanceof UnionType) { - $types = $varType->getTypes(); - if (count($types) === 2 && $types[0] instanceof TemplateType) { - $node->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $types[0]->getBound(), - TypeKind::PROPERTY() - ); - - return $node; - } - } - - $propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($varType, TypeKind::PROPERTY()); - - if ($this->isNullOrNonClassLikeTypeOrMixedOrVendorLockedIn($propertyTypeNode, $node)) { - return null; - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - $propertyType = $this->familyRelationsAnalyzer->getPossibleUnionPropertyType( - $node, - $varType, - $scope, - $propertyTypeNode - ); - $varType = $propertyType->getVarType(); - $propertyTypeNode = $propertyType->getPropertyTypeNode(); - - $this->varTagRemover->removeVarPhpTagValueNodeIfNotComment($node, $varType); - $this->removeDefaultValueForDoctrineCollection($node, $varType); - $this->addDefaultValueNullForNullableType($node, $varType); - - $node->type = $propertyTypeNode; - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $this->classLikeTypeOnly = $configuration[self::CLASS_LIKE_TYPE_ONLY] ?? false; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::TYPED_PROPERTIES; - } - - private function isNullOrNonClassLikeTypeOrMixedOrVendorLockedIn( - Name | NullableType | PhpParserUnionType | null $node, - Property $property - ): bool { - if (! $node instanceof Node) { - return true; - } - - // is not class-type and should be skipped - if ($this->shouldSkipNonClassLikeType($node)) { - return true; - } - - // false positive - if (! $node instanceof Name) { - return $this->vendorLockResolver->isPropertyTypeChangeVendorLockedIn($property); - } - - if (! $this->isName($node, 'mixed')) { - return $this->vendorLockResolver->isPropertyTypeChangeVendorLockedIn($property); - } - - return true; - } - - private function shouldSkipNonClassLikeType(Name | NullableType | PhpParserUnionType $node): bool - { - // unwrap nullable type - if ($node instanceof NullableType) { - $node = $node->type; - } - - $typeName = $this->getName($node); - if ($typeName === null) { - return false; - } - - if ($typeName === 'null') { - return true; - } - - if ($typeName === 'callable') { - return true; - } - - if (! $this->classLikeTypeOnly) { - return false; - } - - return ! $this->reflectionProvider->hasClass($typeName); - } - - private function removeDefaultValueForDoctrineCollection(Property $property, Type $propertyType): void - { - if (! $this->doctrineTypeAnalyzer->isDoctrineCollectionWithIterableUnionType($propertyType)) { - return; - } - - $onlyProperty = $property->props[0]; - $onlyProperty->default = null; - } - - private function addDefaultValueNullForNullableType(Property $property, Type $propertyType): void - { - if (! $propertyType instanceof UnionType) { - return; - } - - if (! $propertyType->isSuperTypeOf(new NullType())->yes()) { - return; - } - - $onlyProperty = $property->props[0]; - - // skip is already has value - if ($onlyProperty->default !== null) { - return; - } - - if ($this->propertyFetchAnalyzer->isFilledByConstructParam($property)) { - return; - } - - $onlyProperty->default = $this->nodeFactory->createNull(); - } - - private function shouldSkipProperty(Property $property): bool - { - // type is already set → skip - if ($property->type !== null) { - return true; - } - - // skip multiple properties - return count($property->props) > 1; - } -} diff --git a/rules/Php74/Rector/StaticCall/ExportToReflectionFunctionRector.php b/rules/Php74/Rector/StaticCall/ExportToReflectionFunctionRector.php index 0de169c2560..06bf2182372 100644 --- a/rules/Php74/Rector/StaticCall/ExportToReflectionFunctionRector.php +++ b/rules/Php74/Rector/StaticCall/ExportToReflectionFunctionRector.php @@ -11,17 +11,28 @@ use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/deprecations_php_7_4 (not confirmed yet) - * @see https://3v4l.org/RTCUq * @see \Rector\Tests\Php74\Rector\StaticCall\ExportToReflectionFunctionRector\ExportToReflectionFunctionRectorTest */ -final class ExportToReflectionFunctionRector extends AbstractRector +final class ExportToReflectionFunctionRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::EXPORT_TO_REFLECTION_FUNCTION; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -59,7 +70,7 @@ public function refactor(Node $node): ?Node return null; } - $callerType = $this->nodeTypeResolver->resolve($node->class); + $callerType = $this->nodeTypeResolver->getType($node->class); if (! $callerType->isSuperTypeOf(new ObjectType('ReflectionFunction'))->yes()) { return null; } @@ -68,9 +79,23 @@ public function refactor(Node $node): ?Node return null; } - $new = new New_($node->class, [new Arg($node->args[0]->value)]); + if ($node->isFirstClassCallable()) { + return null; + } + + $firstArg = $node->getArgs()[0] ?? null; + if (! $firstArg instanceof Arg) { + return null; + } + + $new = new New_($node->class, [new Arg($firstArg->value)]); + + $secondArg = $node->getArgs()[1] ?? null; + if (! $secondArg instanceof Arg) { + return $new; + } - if (isset($node->args[1]) && $this->valueResolver->isTrue($node->args[1]->value)) { + if ($this->valueResolver->isTrue($secondArg->value)) { return new String_($new); } diff --git a/rules/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector.php b/rules/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector.php new file mode 100644 index 00000000000..f84e0e31f24 --- /dev/null +++ b/rules/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector.php @@ -0,0 +1,82 @@ +> + */ + public function getNodeTypes(): array + { + return [Ternary::class]; + } + + /** + * @param Ternary $node + */ + public function refactor(Node $node): ?Node + { + if ($node->cond instanceof Ternary || $node->else instanceof Ternary) { + // Chaining of short-ternaries (elvis operator) is stable and behaves reasonably + $isElvis = $node->cond instanceof Ternary && ! $node->cond->if instanceof Expr; + if ($isElvis) { + return null; + } + + if ($this->parenthesizedNestedTernaryAnalyzer->isParenthesized($this->getFile(), $node)) { + return null; + } + + // re-print with brackets + $node->cond->setAttribute(AttributeKey::ORIGINAL_NODE, null); + return $node; + } + + return null; + } +} diff --git a/rules/Php74/Tokenizer/ParenthesizedNestedTernaryAnalyzer.php b/rules/Php74/Tokenizer/ParenthesizedNestedTernaryAnalyzer.php new file mode 100644 index 00000000000..a96d9d530ca --- /dev/null +++ b/rules/Php74/Tokenizer/ParenthesizedNestedTernaryAnalyzer.php @@ -0,0 +1,23 @@ +getOldTokens(); + $startTokenPos = $ternary->getStartTokenPos(); + $endTokenPos = $ternary->getEndTokenPos(); + + $hasOpenParentheses = isset($oldTokens[$startTokenPos]) && (string) $oldTokens[$startTokenPos] === '('; + $hasCloseParentheses = isset($oldTokens[$endTokenPos]) && (string) $oldTokens[$endTokenPos] === ')'; + + return $hasOpenParentheses || $hasCloseParentheses; + } +} diff --git a/rules/Php80/AttributeDecorator/DoctrineConverterAttributeDecorator.php b/rules/Php80/AttributeDecorator/DoctrineConverterAttributeDecorator.php new file mode 100644 index 00000000000..65a22ed5ee8 --- /dev/null +++ b/rules/Php80/AttributeDecorator/DoctrineConverterAttributeDecorator.php @@ -0,0 +1,47 @@ +args as $arg) { + if (! $arg->name instanceof Identifier) { + continue; + } + + if ($arg->name->toString() !== 'nullable') { + continue; + } + + $value = $arg->value; + if (! $value instanceof String_) { + continue; + } + + if (! in_array($value->value, ['true', 'false'], true)) { + continue; + } + + $arg->value = $value->value === 'true' ? new ConstFetch(new Name('true')) : new ConstFetch(new Name( + 'false' + )); + break; + } + } +} diff --git a/rules/Php80/AttributeDecorator/SensioParamConverterAttributeDecorator.php b/rules/Php80/AttributeDecorator/SensioParamConverterAttributeDecorator.php new file mode 100644 index 00000000000..ad823ee1e4d --- /dev/null +++ b/rules/Php80/AttributeDecorator/SensioParamConverterAttributeDecorator.php @@ -0,0 +1,23 @@ +args[0]; + $firstArg->name = null; + } +} diff --git a/rules/Php80/Contract/ConverterAttributeDecoratorInterface.php b/rules/Php80/Contract/ConverterAttributeDecoratorInterface.php new file mode 100644 index 00000000000..c244d5ce263 --- /dev/null +++ b/rules/Php80/Contract/ConverterAttributeDecoratorInterface.php @@ -0,0 +1,14 @@ +getComments(); + + // already has @param tag → give it priority over @var and remove @var + if ($paramTagValueNode instanceof ParamTagValueNode) { + $propertyDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + if ($propertyDocInfo->hasByType(VarTagValueNode::class)) { + $propertyDocInfo->removeByType(VarTagValueNode::class); + + $propertyComments = $this->phpDocInfoPrinter->printToComments($propertyDocInfo); + + /** @var Comment[] $mergedComments */ + $mergedComments = [...$paramComments, ...$propertyComments]; + + $mergedComments = $this->removeEmptyComments($mergedComments); + + $param->setAttribute(AttributeKey::COMMENTS, $mergedComments); + } + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($param); + } + + public function decorateParamWithPropertyPhpDocInfo( + ClassMethod $classMethod, + Property $property, + Param $param, + string $paramName + ): void { + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + $param->setAttribute(AttributeKey::PHP_DOC_INFO, $propertyPhpDocInfo); + + // make sure the docblock is useful + if (! $param->type instanceof Node) { + $varTagValueNode = $propertyPhpDocInfo->getVarTagValueNode(); + if (! $varTagValueNode instanceof VarTagValueNode) { + return; + } + + $paramType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($varTagValueNode, $property); + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + $this->phpDocTypeChanger->changeParamType( + $classMethod, + $classMethodPhpDocInfo, + $paramType, + $param, + $paramName + ); + } else { + $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + } + + $this->varTagRemover->removeVarPhpTagValueNodeIfNotComment($param, $paramType); + } + + /** + * @param Comment[] $mergedComments + * @return Comment[] + */ + private function removeEmptyComments(array $mergedComments): array + { + return array_filter($mergedComments, static fn (Comment $comment): bool => $comment->getText() !== ''); + } +} diff --git a/rules/Php80/Enum/MatchKind.php b/rules/Php80/Enum/MatchKind.php index f4c9bcb02d9..19a522b78f6 100644 --- a/rules/Php80/Enum/MatchKind.php +++ b/rules/Php80/Enum/MatchKind.php @@ -4,33 +4,13 @@ namespace Rector\Php80\Enum; -use MyCLabs\Enum\Enum; - -/** - * @method static MatchKind NORMAL() - * @method static MatchKind ASSIGN() - * @method static MatchKind RETURN() - * @method static MatchKind THROW() - */ -final class MatchKind extends Enum +final class MatchKind { - /** - * @var string - */ - private const NORMAL = 'normal'; + public const string NORMAL = 'normal'; - /** - * @var string - */ - private const ASSIGN = 'assign'; + public const string ASSIGN = 'assign'; - /** - * @var string - */ - private const RETURN = 'return'; + public const string RETURN = 'return'; - /** - * @var string - */ - private const THROW = 'throw'; + public const string THROW = 'throw'; } diff --git a/rules/Php80/Guard/MakePropertyPromotionGuard.php b/rules/Php80/Guard/MakePropertyPromotionGuard.php new file mode 100644 index 00000000000..a0a9b10e397 --- /dev/null +++ b/rules/Php80/Guard/MakePropertyPromotionGuard.php @@ -0,0 +1,50 @@ +propertyTypeChangeGuard->isLegal($property, $classReflection, $inlinePublic, true)) { + return false; + } + + if ($class->isFinal()) { + return true; + } + + if ($inlinePublic) { + return true; + } + + if ($property->isPrivate()) { + return true; + } + + if (! $param->type instanceof Node) { + return true; + } + + return $property->type instanceof Node; + } +} diff --git a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrncmpMatchAndRefactor.php b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrncmpMatchAndRefactor.php index 6dd7164203b..52ccdefc4af 100644 --- a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrncmpMatchAndRefactor.php +++ b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrncmpMatchAndRefactor.php @@ -5,38 +5,35 @@ namespace Rector\Php80\MatchAndRefactor\StrStartsWithMatchAndRefactor; use PhpParser\Node; -use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\PhpParser\Comparing\NodeComparator; +use PhpParser\Node\Scalar\Int_; +use PhpParser\Node\Scalar\String_; use Rector\NodeNameResolver\NodeNameResolver; use Rector\Php80\Contract\StrStartWithMatchAndRefactorInterface; use Rector\Php80\NodeFactory\StrStartsWithFuncCallFactory; use Rector\Php80\ValueObject\StrStartsWith; use Rector\Php80\ValueObjectFactory\StrStartsWithFactory; +use Rector\PhpParser\Comparing\NodeComparator; -final class StrncmpMatchAndRefactor implements StrStartWithMatchAndRefactorInterface +final readonly class StrncmpMatchAndRefactor implements StrStartWithMatchAndRefactorInterface { - /** - * @var string - */ - private const FUNCTION_NAME = 'strncmp'; + private const string FUNCTION_NAME = 'strncmp'; public function __construct( private NodeNameResolver $nodeNameResolver, private StrStartsWithFactory $strStartsWithFactory, private NodeComparator $nodeComparator, - private StrStartsWithFuncCallFactory $strStartsWithFuncCallFactory + private StrStartsWithFuncCallFactory $strStartsWithFuncCallFactory, ) { } - /** - * @param Identical|NotIdentical $binaryOp - */ - public function match(BinaryOp $binaryOp): ?StrStartsWith + public function match(Identical|NotIdentical|Equal|NotEqual $binaryOp): ?StrStartsWith { - $isPositive = $binaryOp instanceof Identical; + $isPositive = $binaryOp instanceof Identical || $binaryOp instanceof Equal; if ($binaryOp->left instanceof FuncCall && $this->nodeNameResolver->isName( $binaryOp->left, @@ -44,37 +41,82 @@ public function match(BinaryOp $binaryOp): ?StrStartsWith )) { return $this->strStartsWithFactory->createFromFuncCall($binaryOp->left, $isPositive); } + if (! $binaryOp->right instanceof FuncCall) { return null; } + if (! $this->nodeNameResolver->isName($binaryOp->right, self::FUNCTION_NAME)) { return null; } + return $this->strStartsWithFactory->createFromFuncCall($binaryOp->right, $isPositive); } public function refactorStrStartsWith(StrStartsWith $strStartsWith): ?Node + { + if ($this->isNeedleExprWithStrlen($strStartsWith)) { + return $this->strStartsWithFuncCallFactory->createStrStartsWith($strStartsWith); + } + + if ($this->isHardcodedStringWithLNumberLength($strStartsWith)) { + return $this->strStartsWithFuncCallFactory->createStrStartsWith($strStartsWith); + } + + return null; + } + + private function isNeedleExprWithStrlen(StrStartsWith $strStartsWith): bool { $strncmpFuncCall = $strStartsWith->getFuncCall(); $needleExpr = $strStartsWith->getNeedleExpr(); - $secondArgumentValue = $strncmpFuncCall->args[2]->value; - if (! $secondArgumentValue instanceof FuncCall) { - return null; + if ($strncmpFuncCall->isFirstClassCallable()) { + return false; } - if (! $this->nodeNameResolver->isName($secondArgumentValue, 'strlen')) { - return null; + if (count($strncmpFuncCall->getArgs()) < 2) { + return false; } - /** @var FuncCall $strlenFuncCall */ - $strlenFuncCall = $strncmpFuncCall->args[2]->value; - $strlenArgumentValue = $strlenFuncCall->args[0]->value; + $thirdArg = $strncmpFuncCall->getArgs()[2]; - if (! $this->nodeComparator->areNodesEqual($needleExpr, $strlenArgumentValue)) { - return null; + $thirdArgExpr = $thirdArg->value; + if (! $thirdArgExpr instanceof FuncCall) { + return false; + } + + if (! $this->nodeNameResolver->isName($thirdArgExpr, 'strlen')) { + return false; + } + + $strlenFuncCall = $thirdArgExpr; + $strlenExpr = $strlenFuncCall->getArgs()[0] + ->value; + + return $this->nodeComparator->areNodesEqual($needleExpr, $strlenExpr); + } + + private function isHardcodedStringWithLNumberLength(StrStartsWith $strStartsWith): bool + { + $strncmpFuncCall = $strStartsWith->getFuncCall(); + + if (count($strncmpFuncCall->getArgs()) < 2) { + return false; + } + + $hardcodedStringNeedle = $strncmpFuncCall->getArgs()[1] + ->value; + if (! $hardcodedStringNeedle instanceof String_) { + return false; + } + + $lNumberLength = $strncmpFuncCall->getArgs()[2] + ->value; + if (! $lNumberLength instanceof Int_) { + return false; } - return $this->strStartsWithFuncCallFactory->createStrStartsWith($strStartsWith); + return $lNumberLength->value === strlen($hardcodedStringNeedle->value); } } diff --git a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrposMatchAndRefactor.php b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrposMatchAndRefactor.php index 0ab8edf4831..55912c58310 100644 --- a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrposMatchAndRefactor.php +++ b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrposMatchAndRefactor.php @@ -6,60 +6,45 @@ use PhpParser\Node; use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\PhpParser\Node\Value\ValueResolver; use Rector\NodeNameResolver\NodeNameResolver; use Rector\Php80\Contract\StrStartWithMatchAndRefactorInterface; use Rector\Php80\NodeFactory\StrStartsWithFuncCallFactory; use Rector\Php80\ValueObject\StrStartsWith; +use Rector\PhpParser\Node\Value\ValueResolver; -final class StrposMatchAndRefactor implements StrStartWithMatchAndRefactorInterface +final readonly class StrposMatchAndRefactor implements StrStartWithMatchAndRefactorInterface { public function __construct( private NodeNameResolver $nodeNameResolver, private ValueResolver $valueResolver, - private StrStartsWithFuncCallFactory $strStartsWithFuncCallFactory + private StrStartsWithFuncCallFactory $strStartsWithFuncCallFactory, ) { } - /** - * @param Identical|NotIdentical $binaryOp - */ - public function match(BinaryOp $binaryOp): ?StrStartsWith + public function match(Identical|NotIdentical|Equal|NotEqual $binaryOp): ?StrStartsWith { - $isPositive = $binaryOp instanceof Identical; + $isPositive = $binaryOp instanceof Identical || $binaryOp instanceof Equal; if ($binaryOp->left instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->left, 'strpos')) { - if (! $this->valueResolver->isValue($binaryOp->right, 0)) { - return null; - } - - /** @var FuncCall $funcCall */ - $funcCall = $binaryOp->left; - $haystack = $funcCall->args[0]->value; - $needle = $funcCall->args[1]->value; - - return new StrStartsWith($funcCall, $haystack, $needle, $isPositive); + return $this->processBinaryOpLeft($binaryOp, $isPositive); } - if ($binaryOp->right instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->right, 'strpos')) { - if (! $this->valueResolver->isValue($binaryOp->left, 0)) { - return null; - } - - /** @var FuncCall $funcCall */ - $funcCall = $binaryOp->right; - $haystack = $funcCall->args[0]->value; - $needle = $funcCall->args[1]->value; + if (! $binaryOp->right instanceof FuncCall) { + return null; + } - return new StrStartsWith($funcCall, $haystack, $needle, $isPositive); + if (! $this->nodeNameResolver->isName($binaryOp->right, 'strpos')) { + return null; } - return null; + return $this->processBinaryOpRight($binaryOp, $isPositive); } /** @@ -72,4 +57,49 @@ public function refactorStrStartsWith(StrStartsWith $strStartsWith): Node return $this->strStartsWithFuncCallFactory->createStrStartsWith($strStartsWith); } + + private function processBinaryOpLeft(BinaryOp $binaryOp, bool $isPositive): ?StrStartsWith + { + if (! $this->valueResolver->isValue($binaryOp->right, 0)) { + return null; + } + + /** @var FuncCall $funcCall */ + $funcCall = $binaryOp->left; + + if ($funcCall->isFirstClassCallable()) { + return null; + } + + if (count($funcCall->getArgs()) < 2) { + return null; + } + + $haystack = $funcCall->getArgs()[0] + ->value; + $needle = $funcCall->getArgs()[1] + ->value; + + return new StrStartsWith($funcCall, $haystack, $needle, $isPositive); + } + + private function processBinaryOpRight(BinaryOp $binaryOp, bool $isPositive): ?StrStartsWith + { + if (! $this->valueResolver->isValue($binaryOp->left, 0)) { + return null; + } + + /** @var FuncCall $funcCall */ + $funcCall = $binaryOp->right; + if (count($funcCall->getArgs()) < 2) { + return null; + } + + $haystack = $funcCall->getArgs()[0] + ->value; + $needle = $funcCall->getArgs()[1] + ->value; + + return new StrStartsWith($funcCall, $haystack, $needle, $isPositive); + } } diff --git a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php index af4b65537b6..344f24d4268 100644 --- a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php +++ b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php @@ -5,47 +5,48 @@ namespace Rector\Php80\MatchAndRefactor\StrStartsWithMatchAndRefactor; use PhpParser\Node; -use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\Value\ValueResolver; +use PhpParser\Node\Scalar\Int_; +use PhpParser\Node\Scalar\String_; use Rector\NodeNameResolver\NodeNameResolver; use Rector\Php80\Contract\StrStartWithMatchAndRefactorInterface; use Rector\Php80\NodeFactory\StrStartsWithFuncCallFactory; use Rector\Php80\ValueObject\StrStartsWith; +use Rector\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Node\Value\ValueResolver; -final class SubstrMatchAndRefactor implements StrStartWithMatchAndRefactorInterface +final readonly class SubstrMatchAndRefactor implements StrStartWithMatchAndRefactorInterface { public function __construct( private NodeNameResolver $nodeNameResolver, private ValueResolver $valueResolver, private NodeComparator $nodeComparator, - private StrStartsWithFuncCallFactory $strStartsWithFuncCallFactory + private StrStartsWithFuncCallFactory $strStartsWithFuncCallFactory, ) { } - /** - * @param Identical|NotIdentical $binaryOp - */ - public function match(BinaryOp $binaryOp): ?StrStartsWith + public function match(Identical|NotIdentical|Equal|NotEqual $binaryOp): ?StrStartsWith { - $isPositive = $binaryOp instanceof Identical; + $isPositive = $binaryOp instanceof Identical || $binaryOp instanceof Equal; if ($binaryOp->left instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->left, 'substr')) { - /** @var FuncCall $funcCall */ $funcCall = $binaryOp->left; - $haystack = $funcCall->args[0]->value; + + $haystack = $funcCall->getArgs()[0] + ->value; return new StrStartsWith($funcCall, $haystack, $binaryOp->right, $isPositive); } if ($binaryOp->right instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->right, 'substr')) { - /** @var FuncCall $funcCall */ $funcCall = $binaryOp->right; - $haystack = $funcCall->args[0]->value; + $haystack = $funcCall->getArgs()[0] + ->value; return new StrStartsWith($funcCall, $haystack, $binaryOp->left, $isPositive); } @@ -53,30 +54,77 @@ public function match(BinaryOp $binaryOp): ?StrStartsWith } public function refactorStrStartsWith(StrStartsWith $strStartsWith): ?Node + { + if ($this->isStrlenWithNeedleExpr($strStartsWith)) { + return $this->strStartsWithFuncCallFactory->createStrStartsWith($strStartsWith); + } + + if ($this->isHardcodedStringWithLNumberLength($strStartsWith)) { + return $this->strStartsWithFuncCallFactory->createStrStartsWith($strStartsWith); + } + + return null; + } + + private function isStrlenWithNeedleExpr(StrStartsWith $strStartsWith): bool { $substrFuncCall = $strStartsWith->getFuncCall(); - if (! $this->valueResolver->isValue($substrFuncCall->args[1]->value, 0)) { - return null; + + if ($substrFuncCall->isFirstClassCallable()) { + return false; } - $secondFuncCallArgValue = $substrFuncCall->args[2]->value; + $firstArg = $substrFuncCall->getArgs()[1]; + if (! $this->valueResolver->isValue($firstArg->value, 0)) { + return false; + } + + $secondFuncCallArgValue = $substrFuncCall->getArgs()[2] + ->value; if (! $secondFuncCallArgValue instanceof FuncCall) { - return null; + return false; } if (! $this->nodeNameResolver->isName($secondFuncCallArgValue, 'strlen')) { - return null; + return false; } - /** @var FuncCall $strlenFuncCall */ - $strlenFuncCall = $substrFuncCall->args[2]->value; - $needleExpr = $strlenFuncCall->args[0]->value; + $strlenFuncCall = $secondFuncCallArgValue; + $needleExpr = $strlenFuncCall->getArgs()[0] + ->value; $comparedNeedleExpr = $strStartsWith->getNeedleExpr(); - if (! $this->nodeComparator->areNodesEqual($needleExpr, $comparedNeedleExpr)) { - return null; + return $this->nodeComparator->areNodesEqual($needleExpr, $comparedNeedleExpr); + } + + private function isHardcodedStringWithLNumberLength(StrStartsWith $strStartsWith): bool + { + $substrFuncCall = $strStartsWith->getFuncCall(); + + if ($substrFuncCall->isFirstClassCallable()) { + return false; + } + + $secondArg = $substrFuncCall->getArgs()[1]; + if (! $this->valueResolver->isValue($secondArg->value, 0)) { + return false; + } + + $expr = $strStartsWith->getNeedleExpr(); + if (! $expr instanceof String_) { + return false; + } + + if (count($substrFuncCall->getArgs()) < 3) { + return false; + } + + $lNumberLength = $substrFuncCall->getArgs()[2] + ->value; + if (! $lNumberLength instanceof Int_) { + return false; } - return $this->strStartsWithFuncCallFactory->createStrStartsWith($strStartsWith); + return $lNumberLength->value === strlen($expr->value); } } diff --git a/rules/Php80/NodeAnalyzer/MatchSwitchAnalyzer.php b/rules/Php80/NodeAnalyzer/MatchSwitchAnalyzer.php index b3b88d2fde9..3f84b83fd5c 100644 --- a/rules/Php80/NodeAnalyzer/MatchSwitchAnalyzer.php +++ b/rules/Php80/NodeAnalyzer/MatchSwitchAnalyzer.php @@ -4,29 +4,50 @@ namespace Rector\Php80\NodeAnalyzer; +use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Match_; +use PhpParser\Node\Expr\Throw_; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\Stmt\Switch_; -use PhpParser\Node\Stmt\Throw_; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Php80\Enum\MatchKind; use Rector\Php80\ValueObject\CondAndExpr; +use Rector\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Printer\BetterStandardPrinter; -final class MatchSwitchAnalyzer +final readonly class MatchSwitchAnalyzer { public function __construct( private SwitchAnalyzer $switchAnalyzer, private NodeNameResolver $nodeNameResolver, + private NodeComparator $nodeComparator, + private BetterStandardPrinter $betterStandardPrinter ) { } /** * @param CondAndExpr[] $condAndExprs */ - public function shouldSkipSwitch(Switch_ $switch, array $condAndExprs): bool + public function isReturnCondsAndExprs(array $condAndExprs): bool + { + foreach ($condAndExprs as $condAndExpr) { + if ($condAndExpr->equalsMatchKind(MatchKind::RETURN)) { + return true; + } + } + + return false; + } + + /** + * @param CondAndExpr[] $condAndExprs + */ + public function shouldSkipSwitch(Switch_ $switch, array $condAndExprs, ?Stmt $nextStmt): bool { if ($condAndExprs === []) { return true; @@ -36,20 +57,24 @@ public function shouldSkipSwitch(Switch_ $switch, array $condAndExprs): bool return true; } + if ($this->switchAnalyzer->hasDifferentTypeCases($switch->cases, $switch->cond)) { + return true; + } + if (! $this->switchAnalyzer->hasEachCaseSingleStmt($switch)) { return false; } - if ($this->switchAnalyzer->hasDefault($switch)) { + if ($this->switchAnalyzer->hasDefaultSingleStmt($switch)) { return false; } // is followed by return? is considered implicit default - if ($this->isNextStmtReturnWithExpr($switch)) { + if ($this->isNextStmtReturnWithExpr($switch, $nextStmt)) { return false; } - return ! $this->isNextStmtThrows($switch); + return ! ($nextStmt instanceof Expression && $nextStmt->expr instanceof Throw_); } /** @@ -70,8 +95,7 @@ public function haveCondAndExprsMatchPotential(array $condAndExprs): bool } if ($expr->var instanceof ArrayDimFetch) { - $arrayDimFethName = $this->nodeNameResolver->getName($expr->var->var); - $assignVariableNames[] = $expr->var::class . $arrayDimFethName . '[]'; + $assignVariableNames[] = $this->betterStandardPrinter->print($expr->var); } else { $assignVariableNames[] = $expr->var::class . $this->nodeNameResolver->getName($expr->var); } @@ -81,12 +105,27 @@ public function haveCondAndExprsMatchPotential(array $condAndExprs): bool return count($assignVariableNames) <= 1; } + /** + * @param CondAndExpr[] $condAndExprs + */ + public function hasCondsAndExprDefaultValue(array $condAndExprs): bool + { + foreach ($condAndExprs as $condAndExpr) { + if ($condAndExpr->getCondExprs() === null) { + return true; + } + } + + return false; + } + public function hasDefaultValue(Match_ $match): bool { foreach ($match->arms as $matchArm) { if ($matchArm->conds === null) { return true; } + if ($matchArm->conds === []) { return true; } @@ -97,13 +136,13 @@ public function hasDefaultValue(Match_ $match): bool /** * @param CondAndExpr[] $condAndExprs - * @return MatchKind[] + * @return array */ private function resolveUniqueKindsWithoutThrows(array $condAndExprs): array { $condAndExprKinds = []; foreach ($condAndExprs as $condAndExpr) { - if ($condAndExpr->equalsMatchKind(MatchKind::THROW())) { + if ($condAndExpr->equalsMatchKind(MatchKind::THROW)) { continue; } @@ -113,19 +152,30 @@ private function resolveUniqueKindsWithoutThrows(array $condAndExprs): array return array_unique($condAndExprKinds); } - private function isNextStmtReturnWithExpr(Switch_ $switch): bool + private function isNextStmtReturnWithExpr(Switch_ $switch, ?Stmt $nextStmt): bool { - $parent = $switch->getAttribute(AttributeKey::NEXT_NODE); - if (! $parent instanceof Return_) { + if (! $nextStmt instanceof Return_) { return false; } - return $parent->expr !== null; - } + if (! $nextStmt->expr instanceof Expr) { + return false; + } - private function isNextStmtThrows(Switch_ $switch): bool - { - $parent = $switch->getAttribute(AttributeKey::NEXT_NODE); - return $parent instanceof Throw_; + foreach ($switch->cases as $case) { + /** @var Expression[] $expressions */ + $expressions = array_filter($case->stmts, static fn (Node $node): bool => $node instanceof Expression); + foreach ($expressions as $expression) { + if (! $expression->expr instanceof Assign) { + continue; + } + + if (! $this->nodeComparator->areNodesEqual($expression->expr->var, $nextStmt->expr)) { + return false; + } + } + } + + return true; } } diff --git a/rules/Php80/NodeAnalyzer/PhpAttributeAnalyzer.php b/rules/Php80/NodeAnalyzer/PhpAttributeAnalyzer.php index 2aaef53ee0b..44a4576129b 100644 --- a/rules/Php80/NodeAnalyzer/PhpAttributeAnalyzer.php +++ b/rules/Php80/NodeAnalyzer/PhpAttributeAnalyzer.php @@ -4,21 +4,30 @@ namespace Rector\Php80\NodeAnalyzer; +use PhpParser\Node\Arg; +use PhpParser\Node\ArrayItem; +use PhpParser\Node\AttributeGroup; +use PhpParser\Node\Expr\Array_; use PhpParser\Node\Param; +use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Property; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\PhpAttribute\Enum\DocTagNodeState; -final class PhpAttributeAnalyzer +final readonly class PhpAttributeAnalyzer { public function __construct( - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, ) { } - public function hasPhpAttribute(Property | ClassLike | ClassMethod | Param $node, string $attributeClass): bool - { + public function hasPhpAttribute( + Property | ClassLike | ClassMethod | Function_ | Param $node, + string $attributeClass + ): bool { foreach ($node->attrGroups as $attrGroup) { foreach ($attrGroup->attrs as $attribute) { if (! $this->nodeNameResolver->isName($attribute->name, $attributeClass)) { @@ -31,4 +40,62 @@ public function hasPhpAttribute(Property | ClassLike | ClassMethod | Param $node return false; } + + /** + * @param string[] $attributeClasses + */ + public function hasPhpAttributes( + Property | ClassLike | ClassMethod | Function_ | Param $node, + array $attributeClasses + ): bool { + foreach ($attributeClasses as $attributeClass) { + if ($this->hasPhpAttribute($node, $attributeClass)) { + return true; + } + } + + return false; + } + + /** + * @param AttributeGroup[] $attributeGroups + */ + public function hasRemoveArrayState(array $attributeGroups): bool + { + foreach ($attributeGroups as $attributeGroup) { + foreach ($attributeGroup->attrs as $attribute) { + $args = $attribute->args; + + if ($this->hasArgWithRemoveArrayValue($args)) { + return true; + } + } + } + + return false; + } + + /** + * @param Arg[] $args + */ + private function hasArgWithRemoveArrayValue(array $args): bool + { + foreach ($args as $arg) { + if (! $arg->value instanceof Array_) { + continue; + } + + foreach ($arg->value->items as $item) { + if (! $item instanceof ArrayItem) { + continue; + } + + if ($item->value instanceof String_ && $item->value->value === DocTagNodeState::REMOVE_ARRAY) { + return true; + } + } + } + + return false; + } } diff --git a/rules/Php80/NodeAnalyzer/PromotedPropertyCandidateResolver.php b/rules/Php80/NodeAnalyzer/PromotedPropertyCandidateResolver.php index 840df64cf33..5956e0a715f 100644 --- a/rules/Php80/NodeAnalyzer/PromotedPropertyCandidateResolver.php +++ b/rules/Php80/NodeAnalyzer/PromotedPropertyCandidateResolver.php @@ -6,88 +6,117 @@ use PhpParser\Node; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; -use PHPStan\Type\Generic\TemplateType; -use PHPStan\Type\MixedType; -use PHPStan\Type\Type; -use PHPStan\Type\UnionType; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\ValueObject\MethodName; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Enum\ClassName; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\NodeTypeResolver; -use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; -use Rector\NodeTypeResolver\TypeComparator\TypeComparator; use Rector\Php80\ValueObject\PropertyPromotionCandidate; -use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer; +use Rector\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Node\BetterNodeFinder; -final class PromotedPropertyCandidateResolver +final readonly class PromotedPropertyCandidateResolver { public function __construct( private NodeNameResolver $nodeNameResolver, private BetterNodeFinder $betterNodeFinder, private NodeComparator $nodeComparator, - private PropertyTypeInferer $propertyTypeInferer, - private NodeTypeResolver $nodeTypeResolver, - private TypeComparator $typeComparator, - private TypeFactory $typeFactory + private PropertyFetchAnalyzer $propertyFetchAnalyzer, + private PhpDocInfoFactory $phpDocInfoFactory, + private PhpAttributeAnalyzer $phpAttributeAnalyzer ) { } /** * @return PropertyPromotionCandidate[] */ - public function resolveFromClass(Class_ $class): array - { - $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); - if (! $constructClassMethod instanceof ClassMethod) { + public function resolveFromClass( + Class_ $class, + ClassMethod $constructClassMethod, + bool $allowModelBasedClasses + ): array { + if (! $allowModelBasedClasses && $this->hasModelTypeCheck($class, ClassName::DOCTRINE_ENTITY)) { return []; } $propertyPromotionCandidates = []; - foreach ($class->getProperties() as $property) { - $propertyCount = count($property->props); - if ($propertyCount !== 1) { + foreach ($class->stmts as $classStmtPosition => $classStmt) { + if (! $classStmt instanceof Property) { + continue; + } + + if (count($classStmt->props) !== 1) { continue; } - $propertyPromotionCandidate = $this->matchPropertyPromotionCandidate($property, $constructClassMethod); + $propertyPromotionCandidate = $this->matchPropertyPromotionCandidate( + $classStmt, + $constructClassMethod, + $classStmtPosition + ); if (! $propertyPromotionCandidate instanceof PropertyPromotionCandidate) { continue; } + if (! $allowModelBasedClasses && $this->hasModelTypeCheck($classStmt, ClassName::JMS_TYPE)) { + continue; + } + $propertyPromotionCandidates[] = $propertyPromotionCandidate; } return $propertyPromotionCandidates; } + private function hasModelTypeCheck(Class_|Property $node, string $modelType): bool + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if ($phpDocInfo instanceof PhpDocInfo && $phpDocInfo->hasByAnnotationClass($modelType)) { + return true; + } + + return $this->phpAttributeAnalyzer->hasPhpAttribute($node, $modelType); + } + private function matchPropertyPromotionCandidate( Property $property, - ClassMethod $constructClassMethod + ClassMethod $constructClassMethod, + int $propertyStmtPosition ): ?PropertyPromotionCandidate { + if ($property->flags === 0) { + return null; + } + $onlyProperty = $property->props[0]; $propertyName = $this->nodeNameResolver->getName($onlyProperty); $firstParamAsVariable = $this->resolveFirstParamUses($constructClassMethod); // match property name to assign in constructor - foreach ((array) $constructClassMethod->stmts as $stmt) { - if ($stmt instanceof Expression) { - $stmt = $stmt->expr; + foreach ((array) $constructClassMethod->stmts as $assignStmtPosition => $stmt) { + if (! $stmt instanceof Expression) { + continue; } - if (! $stmt instanceof Assign) { + if (! $stmt->expr instanceof Assign) { + continue; + } + + $assign = $stmt->expr; + + // promoted property must use non-static property only + if (! $assign->var instanceof PropertyFetch) { continue; } - $assign = $stmt; - if (! $this->nodeNameResolver->isLocalPropertyFetchNamed($assign->var, $propertyName)) { + if (! $this->propertyFetchAnalyzer->isLocalPropertyFetchName($assign->var, $propertyName)) { continue; } @@ -102,11 +131,16 @@ private function matchPropertyPromotionCandidate( continue; } - if ($this->shouldSkipParam($matchedParam, $property, $assignedExpr, $firstParamAsVariable)) { + if ($this->shouldSkipParam($matchedParam, $assignedExpr, $firstParamAsVariable)) { continue; } - return new PropertyPromotionCandidate($property, $assign, $matchedParam); + return new PropertyPromotionCandidate( + $property, + $matchedParam, + $propertyStmtPosition, + $assignStmtPosition + ); } return null; @@ -171,78 +205,19 @@ private function isParamUsedBeforeAssign(Variable $variable, array $firstParamAs return $firstVariablePosition < $variable->getStartTokenPos(); } - private function hasConflictingParamType(Param $param, Type $propertyType): bool - { - if ($param->type === null) { - return false; - } - - $matchedParamType = $this->nodeTypeResolver->resolve($param); - if ($param->default !== null) { - $defaultValueType = $this->nodeTypeResolver->getStaticType($param->default); - $matchedParamType = $this->typeFactory->createMixedPassedOrUnionType( - [$matchedParamType, $defaultValueType] - ); - } - - if (! $propertyType instanceof UnionType) { - return false; - } - - if ($this->typeComparator->areTypesEqual($propertyType, $matchedParamType)) { - return false; - } - - // different types, check not has mixed and not has templated generic types - if (! $this->hasMixedType($propertyType)) { - return false; - } - - return ! $this->hasTemplatedGenericType($propertyType); - } - - private function hasTemplatedGenericType(UnionType $unionType): bool - { - foreach ($unionType->getTypes() as $type) { - if ($type instanceof TemplateType) { - return true; - } - } - - return false; - } - - private function hasMixedType(UnionType $unionType): bool - { - foreach ($unionType->getTypes() as $type) { - if ($type instanceof MixedType) { - return true; - } - } - - return false; - } - /** * @param int[] $firstParamAsVariable */ private function shouldSkipParam( Param $matchedParam, - Property $property, Variable $assignedVariable, array $firstParamAsVariable ): bool { // already promoted - if ($matchedParam->flags !== 0) { - return true; - } - - if ($this->isParamUsedBeforeAssign($assignedVariable, $firstParamAsVariable)) { + if ($matchedParam->isPromoted()) { return true; } - // @todo unknown type, not suitable? - $propertyType = $this->propertyTypeInferer->inferProperty($property); - return $this->hasConflictingParamType($matchedParam, $propertyType); + return $this->isParamUsedBeforeAssign($assignedVariable, $firstParamAsVariable); } } diff --git a/rules/Php80/NodeAnalyzer/PromotedPropertyResolver.php b/rules/Php80/NodeAnalyzer/PromotedPropertyResolver.php index 14329c6d1e6..bf74aa82f22 100644 --- a/rules/Php80/NodeAnalyzer/PromotedPropertyResolver.php +++ b/rules/Php80/NodeAnalyzer/PromotedPropertyResolver.php @@ -7,7 +7,7 @@ use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\ValueObject\MethodName; +use Rector\ValueObject\MethodName; final class PromotedPropertyResolver { @@ -23,7 +23,7 @@ public function resolveFromClass(Class_ $class): array $promotedPropertyParams = []; foreach ($constructClassMethod->getParams() as $param) { - if ($param->flags === 0) { + if (! $param->isPromoted()) { continue; } diff --git a/rules/Php80/NodeAnalyzer/SwitchAnalyzer.php b/rules/Php80/NodeAnalyzer/SwitchAnalyzer.php index f63d79ea87f..db7ed01771d 100644 --- a/rules/Php80/NodeAnalyzer/SwitchAnalyzer.php +++ b/rules/Php80/NodeAnalyzer/SwitchAnalyzer.php @@ -5,13 +5,52 @@ namespace Rector\Php80\NodeAnalyzer; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Stmt\Break_; use PhpParser\Node\Stmt\Case_; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\Stmt\Switch_; +use PHPStan\Type\MixedType; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; -final class SwitchAnalyzer +final readonly class SwitchAnalyzer { + public function __construct( + private NodeTypeResolver $nodeTypeResolver, + private TypeFactory $typeFactory + ) { + } + + /** + * @param Case_[] $cases + */ + public function hasDifferentTypeCases(array $cases, Expr $expr): bool + { + $types = []; + foreach ($cases as $case) { + if ($case->cond instanceof Expr) { + $types[] = $this->nodeTypeResolver->getType($case->cond); + } + } + + if ($types === []) { + return false; + } + + $uniqueTypes = $this->typeFactory->uniquateTypes($types); + $countUniqueTypes = count($uniqueTypes); + + if ($countUniqueTypes === 1 && $uniqueTypes[0]->isInteger()->yes()) { + $switchCondType = $this->nodeTypeResolver->getType($expr); + if (! $switchCondType instanceof MixedType && $switchCondType->isString()->maybe()) { + return true; + } + } + + return $countUniqueTypes > 1; + } + public function hasEachCaseBreak(Switch_ $switch): bool { $totalCases = count($switch->cases); @@ -37,7 +76,11 @@ public function hasEachCaseBreak(Switch_ $switch): bool public function hasEachCaseSingleStmt(Switch_ $switch): bool { foreach ($switch->cases as $case) { - $stmtsWithoutBreak = array_filter($case->stmts, fn (Node $node): bool => ! $node instanceof Break_); + if (! $case->cond instanceof Expr) { + continue; + } + + $stmtsWithoutBreak = array_filter($case->stmts, static fn (Node $node): bool => ! $node instanceof Break_); if (count($stmtsWithoutBreak) !== 1) { return false; @@ -47,11 +90,15 @@ public function hasEachCaseSingleStmt(Switch_ $switch): bool return true; } - public function hasDefault(Switch_ $switch): bool + public function hasDefaultSingleStmt(Switch_ $switch): bool { foreach ($switch->cases as $case) { - if ($case->cond === null) { - return true; + if (! $case->cond instanceof Expr) { + $stmtsWithoutBreak = array_filter( + $case->stmts, + static fn (Node $node): bool => ! $node instanceof Break_ + ); + return count($stmtsWithoutBreak) === 1; } } diff --git a/rules/Php80/NodeFactory/AttrGroupsFactory.php b/rules/Php80/NodeFactory/AttrGroupsFactory.php index 55e9d568823..779702d4b84 100644 --- a/rules/Php80/NodeFactory/AttrGroupsFactory.php +++ b/rules/Php80/NodeFactory/AttrGroupsFactory.php @@ -5,10 +5,11 @@ namespace Rector\Php80\NodeFactory; use PhpParser\Node\AttributeGroup; +use PhpParser\Node\Stmt\Use_; use Rector\Php80\ValueObject\DoctrineTagAndAnnotationToAttribute; -use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory; +use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory; -final class AttrGroupsFactory +final readonly class AttrGroupsFactory { public function __construct( private PhpAttributeGroupFactory $phpAttributeGroupFactory @@ -17,9 +18,10 @@ public function __construct( /** * @param DoctrineTagAndAnnotationToAttribute[] $doctrineTagAndAnnotationToAttributes + * @param Use_[] $uses * @return AttributeGroup[] */ - public function create(array $doctrineTagAndAnnotationToAttributes): array + public function create(array $doctrineTagAndAnnotationToAttributes, array $uses): array { $attributeGroups = []; @@ -29,7 +31,8 @@ public function create(array $doctrineTagAndAnnotationToAttributes): array // add attributes $attributeGroups[] = $this->phpAttributeGroupFactory->create( $doctrineAnnotationTagValueNode, - $doctrineTagAndAnnotationToAttribute->getAnnotationToAttribute() + $doctrineTagAndAnnotationToAttribute->getAnnotationToAttribute(), + $uses ); } diff --git a/rules/Php80/NodeFactory/AttributeFlagFactory.php b/rules/Php80/NodeFactory/AttributeFlagFactory.php deleted file mode 100644 index c25d98c5397..00000000000 --- a/rules/Php80/NodeFactory/AttributeFlagFactory.php +++ /dev/null @@ -1,30 +0,0 @@ - $condAndExpr) { $expr = $condAndExpr->getExpr(); if ($expr instanceof Assign) { - // $this->assignExpr = $expr->var; $expr = $expr->expr; } + /** @var null|list $condExprs */ $condExprs = $condAndExpr->getCondExprs(); - $matchArms[] = new MatchArm($condExprs, $expr); + $matchArms[] = new MatchArm($condExprs, $expr, [ + AttributeKey::COMMENTS => $condAndExprs[$key]->getComments(), + ]); } return $matchArms; diff --git a/rules/Php80/NodeFactory/MatchFactory.php b/rules/Php80/NodeFactory/MatchFactory.php index 2627596d7f5..5417f8e8ddd 100644 --- a/rules/Php80/NodeFactory/MatchFactory.php +++ b/rules/Php80/NodeFactory/MatchFactory.php @@ -5,22 +5,85 @@ namespace Rector\Php80\NodeFactory; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ArrayDimFetch; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Match_; +use PhpParser\Node\Expr\Throw_; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Return_; +use Rector\Php80\Enum\MatchKind; +use Rector\Php80\NodeAnalyzer\MatchSwitchAnalyzer; use Rector\Php80\ValueObject\CondAndExpr; +use Rector\Php80\ValueObject\MatchResult; +use Rector\PhpParser\Comparing\NodeComparator; -final class MatchFactory +final readonly class MatchFactory { public function __construct( - private MatchArmsFactory $matchArmsFactory + private MatchArmsFactory $matchArmsFactory, + private MatchSwitchAnalyzer $matchSwitchAnalyzer, + private NodeComparator $nodeComparator ) { } /** * @param CondAndExpr[] $condAndExprs */ - public function createFromCondAndExprs(Expr $condExpr, array $condAndExprs): Match_ + public function createFromCondAndExprs(Expr $condExpr, array $condAndExprs, ?Stmt $nextStmt): ?MatchResult { + $shouldRemoteNextStmt = false; + + // is default value missing? maybe it can be found in next stmt + if (! $this->matchSwitchAnalyzer->hasCondsAndExprDefaultValue($condAndExprs)) { + // 1. is followed by throws stmts? + if ($nextStmt instanceof Expression && $nextStmt->expr instanceof Throw_) { + $throw = $nextStmt->expr; + $condAndExprs[] = new CondAndExpr([], $throw, MatchKind::RETURN); + + $shouldRemoteNextStmt = true; + } + + // 2. is followed by return expr + + // implicit return default after switch + if ($nextStmt instanceof Return_ && $nextStmt->expr instanceof Expr) { + // @todo this should be improved + $expr = $this->resolveAssignVar($condAndExprs); + if ($expr instanceof ArrayDimFetch) { + return null; + } + + if ($expr instanceof Expr && ! $this->nodeComparator->areNodesEqual($nextStmt->expr, $expr)) { + return null; + } + + $shouldRemoteNextStmt = ! $expr instanceof Expr; + + $condAndExprs[] = new CondAndExpr([], $nextStmt->expr, MatchKind::RETURN, $nextStmt->getComments()); + } + } + $matchArms = $this->matchArmsFactory->createFromCondAndExprs($condAndExprs); - return new Match_($condExpr, $matchArms); + $match = new Match_($condExpr, $matchArms); + + return new MatchResult($match, $shouldRemoteNextStmt); + } + + /** + * @param CondAndExpr[] $condAndExprs + */ + private function resolveAssignVar(array $condAndExprs): ?Expr + { + foreach ($condAndExprs as $condAndExpr) { + $expr = $condAndExpr->getExpr(); + if (! $expr instanceof Assign) { + continue; + } + + return $expr->var; + } + + return null; } } diff --git a/rules/Php80/NodeFactory/NestedAttrGroupsFactory.php b/rules/Php80/NodeFactory/NestedAttrGroupsFactory.php new file mode 100644 index 00000000000..50877e65121 --- /dev/null +++ b/rules/Php80/NodeFactory/NestedAttrGroupsFactory.php @@ -0,0 +1,53 @@ +getDoctrineAnnotationTagValueNode(); + + $nestedAnnotationToAttribute = $nestedDoctrineTagAndAnnotationToAttribute->getNestedAnnotationToAttribute(); + + // do not create alternative for the annotation, only unwrap + if (! $nestedAnnotationToAttribute->shouldRemoveOriginal()) { + // add attributes + $attributeGroups[] = $this->phpNestedAttributeGroupFactory->create( + $doctrineAnnotationTagValueNode, + $nestedDoctrineTagAndAnnotationToAttribute->getNestedAnnotationToAttribute(), + $uses + ); + } + + $nestedAttributeGroups = $this->phpNestedAttributeGroupFactory->createNested( + $doctrineAnnotationTagValueNode, + $nestedDoctrineTagAndAnnotationToAttribute->getNestedAnnotationToAttribute(), + ); + + $attributeGroups = [...$attributeGroups, ...$nestedAttributeGroups]; + } + + return array_unique($attributeGroups, SORT_REGULAR); + } +} diff --git a/rules/Php80/NodeManipulator/AttributeGroupNamedArgumentManipulator.php b/rules/Php80/NodeManipulator/AttributeGroupNamedArgumentManipulator.php new file mode 100644 index 00000000000..76911ed86ac --- /dev/null +++ b/rules/Php80/NodeManipulator/AttributeGroupNamedArgumentManipulator.php @@ -0,0 +1,40 @@ +attrs as $attr) { + $phpAttributeName = $attr->name->getAttribute(AttributeKey::PHP_ATTRIBUTE_NAME); + + foreach ($this->converterAttributeDecorators as $converterAttributeDecorator) { + if ($converterAttributeDecorator->getAttributeName() !== $phpAttributeName) { + continue; + } + + $converterAttributeDecorator->decorate($attr); + } + } + } + } +} diff --git a/rules/Php80/NodeManipulator/TokenManipulator.php b/rules/Php80/NodeManipulator/TokenManipulator.php deleted file mode 100644 index 867c42c6d46..00000000000 --- a/rules/Php80/NodeManipulator/TokenManipulator.php +++ /dev/null @@ -1,317 +0,0 @@ -replaceTokenDimFetchZeroWithGetTokenName($nodes, $singleTokenExpr); - - // replace "$token[1]"; with "$token->value" - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node): ?PropertyFetch { - if (! $node instanceof ArrayDimFetch) { - return null; - } - - if (! $this->isArrayDimFetchWithDimIntegerValue($node, 1)) { - return null; - } - - $tokenStaticType = $this->nodeTypeResolver->getStaticType($node->var); - if (! $tokenStaticType instanceof ArrayType) { - return null; - } - - return new PropertyFetch($node->var, 'text'); - }); - } - - /** - * @param Node[] $nodes - */ - public function refactorNonArrayToken(array $nodes, Expr $singleTokenExpr): void - { - // replace "$content = $token;" → "$content = $token->text;" - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node) use ( - $singleTokenExpr - ) { - if (! $node instanceof Assign) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($node->expr, $singleTokenExpr)) { - return null; - } - - $tokenStaticType = $this->nodeTypeResolver->getStaticType($node->expr); - if ($tokenStaticType instanceof ArrayType) { - return null; - } - - $node->expr = new PropertyFetch($singleTokenExpr, 'text'); - }); - - // replace "$name = null;" → "$name = $token->getTokenName();" - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node) use ( - $singleTokenExpr - ): ?Assign { - if (! $node instanceof Assign) { - return null; - } - - $tokenStaticType = $this->nodeTypeResolver->getStaticType($node->expr); - if ($tokenStaticType instanceof ArrayType) { - return null; - } - - if ($this->assignedNameExpr === null) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($node->var, $this->assignedNameExpr)) { - return null; - } - - if (! $this->valueResolver->isValue($node->expr, 'null')) { - return null; - } - - $node->expr = new MethodCall($singleTokenExpr, 'getTokenName'); - - return $node; - }); - } - - /** - * @param Node[] $nodes - */ - public function refactorTokenIsKind(array $nodes, Expr $singleTokenExpr): void - { - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node) use ( - $singleTokenExpr - ): ?MethodCall { - if (! $node instanceof Identical) { - return null; - } - - $arrayDimFetchAndConstFetch = $this->matchArrayDimFetchAndConstFetch($node); - if (! $arrayDimFetchAndConstFetch instanceof ArrayDimFetchAndConstFetch) { - return null; - } - - if (! $this->isArrayDimFetchWithDimIntegerValue($arrayDimFetchAndConstFetch->getArrayDimFetch(), 0)) { - return null; - } - - $arrayDimFetch = $arrayDimFetchAndConstFetch->getArrayDimFetch(); - $constFetch = $arrayDimFetchAndConstFetch->getConstFetch(); - - if (! $this->nodeComparator->areNodesEqual($arrayDimFetch->var, $singleTokenExpr)) { - return null; - } - - $constName = $this->nodeNameResolver->getName($constFetch); - if ($constName === null) { - return null; - } - - if (! Strings::match($constName, '#^T_#')) { - return null; - } - - return $this->createIsTConstTypeMethodCall( - $arrayDimFetch, - $arrayDimFetchAndConstFetch->getConstFetch() - ); - }); - } - - /** - * @param Node[] $nodes - */ - public function removeIsArray(array $nodes, Variable $singleTokenVariable): void - { - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node) use ( - $singleTokenVariable - ) { - if (! $node instanceof FuncCall) { - return null; - } - - if (! $this->nodeNameResolver->isName($node, 'is_array')) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($node->args[0]->value, $singleTokenVariable)) { - return null; - } - - if ($this->shouldSkipNodeRemovalForPartOfIf($node)) { - return null; - } - - // remove correct node - $nodeToRemove = $this->matchParentNodeInCaseOfIdenticalTrue($node); - - $this->nodesToRemoveCollector->addNodeToRemove($nodeToRemove); - }); - } - - /** - * Replace $token[0] with $token->getTokenName() call - * - * @param Node[] $nodes - */ - private function replaceTokenDimFetchZeroWithGetTokenName(array $nodes, Expr $singleTokenExpr): void - { - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node) use ( - $singleTokenExpr - ): ?MethodCall { - if (! $node instanceof FuncCall) { - return null; - } - - if (! $this->nodeNameResolver->isName($node, 'token_name')) { - return null; - } - - $possibleTokenArray = $node->args[0]->value; - if (! $possibleTokenArray instanceof ArrayDimFetch) { - return null; - } - - $tokenStaticType = $this->nodeTypeResolver->getStaticType($possibleTokenArray->var); - if (! $tokenStaticType instanceof ArrayType) { - return null; - } - - if ($possibleTokenArray->dim === null) { - return null; - } - - if (! $this->valueResolver->isValue($possibleTokenArray->dim, 0)) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($possibleTokenArray->var, $singleTokenExpr)) { - return null; - } - - // save token variable name for later - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Assign) { - $this->assignedNameExpr = $parentNode->var; - } - - return new MethodCall($singleTokenExpr, 'getTokenName'); - }); - } - - private function isArrayDimFetchWithDimIntegerValue(ArrayDimFetch $arrayDimFetch, int $value): bool - { - if ($arrayDimFetch->dim === null) { - return false; - } - - return $this->valueResolver->isValue($arrayDimFetch->dim, $value); - } - - private function matchArrayDimFetchAndConstFetch(Identical $identical): ?ArrayDimFetchAndConstFetch - { - if ($identical->left instanceof ArrayDimFetch && $identical->right instanceof ConstFetch) { - return new ArrayDimFetchAndConstFetch($identical->left, $identical->right); - } - if (! $identical->right instanceof ArrayDimFetch) { - return null; - } - if (! $identical->left instanceof ConstFetch) { - return null; - } - return new ArrayDimFetchAndConstFetch($identical->right, $identical->left); - } - - private function createIsTConstTypeMethodCall(ArrayDimFetch $arrayDimFetch, ConstFetch $constFetch): MethodCall - { - return new MethodCall($arrayDimFetch->var, 'is', [new Arg($constFetch)]); - } - - private function shouldSkipNodeRemovalForPartOfIf(FuncCall $funcCall): bool - { - $parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - - // cannot remove x from if(x) - if ($parentNode instanceof If_ && $parentNode->cond === $funcCall) { - return true; - } - - if ($parentNode instanceof BooleanNot) { - $parentParentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE); - if ($parentParentNode instanceof If_) { - $parentParentNode->cond = $parentNode; - return true; - } - } - - return false; - } - - private function matchParentNodeInCaseOfIdenticalTrue(FuncCall $funcCall): Identical | FuncCall - { - $parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Identical) { - $isRightValueTrue = $this->valueResolver->isValue($parentNode->right, true); - if ($parentNode->left === $funcCall && $isRightValueTrue) { - return $parentNode; - } - - $isLeftValueTrue = $this->valueResolver->isValue($parentNode->left, true); - if ($parentNode->right === $funcCall && $isLeftValueTrue) { - return $parentNode; - } - } - - return $funcCall; - } -} diff --git a/rules/Php80/NodeResolver/ArgumentSorter.php b/rules/Php80/NodeResolver/ArgumentSorter.php deleted file mode 100644 index 9899f173a60..00000000000 --- a/rules/Php80/NodeResolver/ArgumentSorter.php +++ /dev/null @@ -1,33 +0,0 @@ - $oldToNewPositions - * @param T[] $argOrParams - * @return T[] - */ - public function sortArgsByExpectedParamOrder(array $argOrParams, array $oldToNewPositions): array - { - $newArgsOrParams = []; - - foreach (array_keys($argOrParams) as $position) { - $newPosition = $oldToNewPositions[$position] ?? null; - if ($newPosition === null) { - continue; - } - - $newArgsOrParams[$position] = $argOrParams[$newPosition]; - } - - return $newArgsOrParams; - } -} diff --git a/rules/Php80/NodeResolver/RequireOptionalParamResolver.php b/rules/Php80/NodeResolver/RequireOptionalParamResolver.php deleted file mode 100644 index 48fde75a81b..00000000000 --- a/rules/Php80/NodeResolver/RequireOptionalParamResolver.php +++ /dev/null @@ -1,34 +0,0 @@ -getVariants()); - - $optionalParams = []; - $requireParams = []; - - foreach ($parametersAcceptor->getParameters() as $position => $parameterReflection) { - if ($parameterReflection->getDefaultValue() === null && ! $parameterReflection->isVariadic()) { - $requireParams[$position] = $parameterReflection; - } else { - $optionalParams[$position] = $parameterReflection; - } - } - - return $requireParams + $optionalParams; - } -} diff --git a/rules/Php80/NodeResolver/StrFalseComparisonResolver.php b/rules/Php80/NodeResolver/StrFalseComparisonResolver.php new file mode 100644 index 00000000000..4bd30579aed --- /dev/null +++ b/rules/Php80/NodeResolver/StrFalseComparisonResolver.php @@ -0,0 +1,54 @@ +valueResolver->isFalse($expr->left)) { + if (! $expr->right instanceof FuncCall) { + return null; + } + + if (! $this->nodeNameResolver->isNames($expr->right, $oldStrFuncNames)) { + return null; + } + + return $expr->right; + } + + if ($this->valueResolver->isFalse($expr->right)) { + if (! $expr->left instanceof FuncCall) { + return null; + } + + if (! $this->nodeNameResolver->isNames($expr->left, $oldStrFuncNames)) { + return null; + } + + return $expr->left; + } + + return null; + } +} diff --git a/rules/Php80/NodeResolver/SwitchExprsResolver.php b/rules/Php80/NodeResolver/SwitchExprsResolver.php index 582c25c7388..af6178fb640 100644 --- a/rules/Php80/NodeResolver/SwitchExprsResolver.php +++ b/rules/Php80/NodeResolver/SwitchExprsResolver.php @@ -6,12 +6,12 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\Throw_; use PhpParser\Node\Stmt\Break_; use PhpParser\Node\Stmt\Case_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\Stmt\Switch_; -use PhpParser\Node\Stmt\Throw_; use Rector\Php80\Enum\MatchKind; use Rector\Php80\ValueObject\CondAndExpr; @@ -22,32 +22,43 @@ final class SwitchExprsResolver */ public function resolve(Switch_ $switch): array { + $newSwitch = clone $switch; + $condAndExpr = []; $collectionEmptyCasesCond = []; - foreach ($switch->cases as $key => $case) { - if (! $this->isValidCase($case)) { - return []; + if (! $this->areCasesValid($newSwitch)) { + return []; + } + + $this->moveDefaultCaseToLast($newSwitch); + + foreach ($newSwitch->cases as $key => $case) { + if ($case->stmts !== []) { + continue; } - if ($case->stmts === [] && $case->cond instanceof Expr) { - $collectionEmptyCasesCond[$key] = $case->cond; + if (! $case->cond instanceof Expr) { + return []; } + + $collectionEmptyCasesCond[$key] = $case->cond; } - foreach ($switch->cases as $key => $case) { + foreach ($newSwitch->cases as $key => $case) { if ($case->stmts === []) { continue; } $expr = $case->stmts[0]; + $comments = $expr->getComments(); if ($expr instanceof Expression) { $expr = $expr->expr; } $condExprs = []; - if ($case->cond !== null) { + if ($case->cond instanceof Expr) { $emptyCasesCond = []; foreach ($collectionEmptyCasesCond as $i => $collectionEmptyCaseCond) { @@ -63,20 +74,19 @@ public function resolve(Switch_ $switch): array $condExprs[] = $case->cond; } - if ($expr instanceof Return_) { + if ($expr instanceof Throw_) { + $condAndExpr[] = new CondAndExpr($condExprs, $expr, MatchKind::THROW, $comments); + } elseif ($expr instanceof Return_) { $returnedExpr = $expr->expr; if (! $returnedExpr instanceof Expr) { return []; } - $condAndExpr[] = new CondAndExpr($condExprs, $returnedExpr, MatchKind::RETURN()); + $condAndExpr[] = new CondAndExpr($condExprs, $returnedExpr, MatchKind::RETURN, $comments); } elseif ($expr instanceof Assign) { - $condAndExpr[] = new CondAndExpr($condExprs, $expr, MatchKind::ASSIGN()); + $condAndExpr[] = new CondAndExpr($condExprs, $expr, MatchKind::ASSIGN, $comments); } elseif ($expr instanceof Expr) { - $condAndExpr[] = new CondAndExpr($condExprs, $expr, MatchKind::NORMAL()); - } elseif ($expr instanceof Throw_) { - $throwExpr = new Expr\Throw_($expr->expr); - $condAndExpr[] = new CondAndExpr($condExprs, $throwExpr, MatchKind::THROW()); + $condAndExpr[] = new CondAndExpr($condExprs, $expr, MatchKind::NORMAL, $comments); } else { return []; } @@ -85,6 +95,39 @@ public function resolve(Switch_ $switch): array return $condAndExpr; } + private function moveDefaultCaseToLast(Switch_ $switch): void + { + foreach ($switch->cases as $key => $case) { + if ($case->cond instanceof Expr) { + continue; + } + + // not has next? default is at the end, no need move + if (! isset($switch->cases[$key + 1])) { + return; + } + + // current default has no stmt? keep as is as rely to next case + if ($case->stmts === []) { + return; + } + + for ($loop = $key - 1; $loop >= 0; --$loop) { + if ($switch->cases[$loop]->stmts !== []) { + break; + } + + unset($switch->cases[$loop]); + } + + $caseToMove = $switch->cases[$key]; + unset($switch->cases[$key]); + $switch->cases[] = $caseToMove; + + break; + } + } + private function isValidCase(Case_ $case): bool { // prepend to previous one @@ -101,7 +144,7 @@ private function isValidCase(Case_ $case): bool return false; } - // throws expressoin + // throws expression if ($case->stmts[0] instanceof Throw_) { return true; } @@ -112,6 +155,17 @@ private function isValidCase(Case_ $case): bool } // default value - return $case->cond === null; + return ! $case->cond instanceof Expr; + } + + private function areCasesValid(Switch_ $newSwitch): bool + { + foreach ($newSwitch->cases as $case) { + if (! $this->isValidCase($case)) { + return false; + } + } + + return true; } } diff --git a/rules/Php80/PhpDocCleaner/ConvertedAnnotationToAttributeParentRemover.php b/rules/Php80/PhpDocCleaner/ConvertedAnnotationToAttributeParentRemover.php deleted file mode 100644 index 07a8cae0fe1..00000000000 --- a/rules/Php80/PhpDocCleaner/ConvertedAnnotationToAttributeParentRemover.php +++ /dev/null @@ -1,102 +0,0 @@ -traverseWithCallable($phpDocNode, '', function ($node) use ( - $annotationsToAttributes, - $skippedUnwrapAnnotations - ): ?int { - if (! $node instanceof SpacelessPhpDocTagNode) { - return null; - } - - if (! $node->value instanceof DoctrineAnnotationTagValueNode) { - return null; - } - - $doctrineAnnotationTagValueNode = $node->value; - if ($doctrineAnnotationTagValueNode->hasClassNames($skippedUnwrapAnnotations)) { - return PhpDocNodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - // has only children of annotation to attribute? it will be removed - if ($this->detect($node->value, $annotationsToAttributes)) { - return PhpDocNodeTraverser::NODE_REMOVE; - } - - return null; - }); - } - - /** - * @param AnnotationToAttribute[] $annotationsToAttributes - */ - private function detect( - DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, - array $annotationsToAttributes - ): bool { - $nodeValues = $doctrineAnnotationTagValueNode->getValues(); - if ($nodeValues === []) { - return false; - } - - foreach ($nodeValues as $nodeValue) { - if (! $nodeValue instanceof CurlyListNode) { - return false; - } - - if (! $this->isCurlyListOfDoctrineAnnotationTagValueNodes($nodeValue, $annotationsToAttributes)) { - return false; - } - } - - return true; - } - - /** - * @param AnnotationToAttribute[] $annotationsToAttributes - */ - private function isCurlyListOfDoctrineAnnotationTagValueNodes( - CurlyListNode $curlyListNode, - array $annotationsToAttributes - ): bool { - foreach ($curlyListNode->getOriginalValues() as $nodeValueValue) { - foreach ($annotationsToAttributes as $annotationToAttribute) { - if (! $nodeValueValue instanceof DoctrineAnnotationTagValueNode) { - return false; - } - - // found it - if ($nodeValueValue->hasClassName($annotationToAttribute->getTag())) { - continue 2; - } - } - - return false; - } - - return true; - } -} diff --git a/rules/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector.php b/rules/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector.php index 42d9edb5bc0..1a6fd8f6254 100644 --- a/rules/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector.php +++ b/rules/Php80/Rector/Catch_/RemoveUnusedVariableInCatchRector.php @@ -6,27 +6,33 @@ use PhpParser\Node; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Stmt\Catch_; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Stmt\Finally_; +use PhpParser\Node\Stmt\TryCatch; use Rector\DeadCode\NodeAnalyzer\ExprUsedInNodeAnalyzer; +use Rector\NodeManipulator\StmtsManipulator; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/non-capturing_catches - * * @see \Rector\Tests\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector\RemoveUnusedVariableInCatchRectorTest */ -final class RemoveUnusedVariableInCatchRector extends AbstractRector +final class RemoveUnusedVariableInCatchRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private ExprUsedInNodeAnalyzer $exprUsedInNodeAnalyzer + private readonly StmtsManipulator $stmtsManipulator, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly ExprUsedInNodeAnalyzer $exprUsedInNodeAnalyzer ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Remove unused variable in catch()', [ + return new RuleDefinition('Remove unused variable in `catch()`', [ new CodeSample( <<<'CODE_SAMPLE' final class SomeClass @@ -39,7 +45,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' final class SomeClass { @@ -60,48 +66,61 @@ public function run() */ public function getNodeTypes(): array { - return [Catch_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Catch_ $node + * @param StmtsAware $node */ public function refactor(Node $node): ?Node { - $caughtVar = $node->var; - if (! $caughtVar instanceof Variable) { + if ($node->stmts === null) { return null; } - if ($this->isVariableUsedInStmts($node->stmts, $caughtVar)) { - return null; - } + $hasChanged = false; - if ($this->isVariableUsedNext($node, $caughtVar)) { - return null; - } + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof TryCatch) { + continue; + } - $node->var = null; + foreach ($stmt->catches as $catch) { + $caughtVar = $catch->var; + if (! $caughtVar instanceof Variable) { + continue; + } - return $node; - } + /** @var string $variableName */ + $variableName = $this->getName($caughtVar); - /** - * @param Node[] $nodes - */ - private function isVariableUsedInStmts(array $nodes, Variable $variable): bool - { - return (bool) $this->betterNodeFinder->findFirst( - $nodes, - fn (Node $node): bool => $this->exprUsedInNodeAnalyzer->isUsed($node, $variable) - ); + $isFoundInCatchStmts = (bool) $this->betterNodeFinder->findFirst( + array_merge($catch->stmts, $stmt->finally instanceof Finally_ ? $stmt->finally->stmts : []), + fn (Node $subNode): bool => $this->exprUsedInNodeAnalyzer->isUsed($subNode, $caughtVar) + ); + + if ($isFoundInCatchStmts) { + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt($node, $key + 1, $variableName)) { + continue; + } + + $catch->var = null; + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; } - private function isVariableUsedNext(Catch_ $catch, Variable $variable): bool + public function provideMinPhpVersion(): int { - return (bool) $this->betterNodeFinder->findFirstNext( - $catch, - fn (Node $node): bool => $this->exprUsedInNodeAnalyzer->isUsed($node, $variable) - ); + return PhpVersionFeature::NON_CAPTURING_CATCH; } } diff --git a/rules/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector.php b/rules/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector.php new file mode 100644 index 00000000000..46483a99e8a --- /dev/null +++ b/rules/Php80/Rector/ClassConstFetch/ClassOnThisVariableObjectRector.php @@ -0,0 +1,119 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $className = $node->isFinal() ? 'self' : 'static'; + + $hasChanged = false; + $this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged, $className): ?ClassConstFetch { + if (! $node instanceof ClassConstFetch) { + return null; + } + + if ($this->shouldSkip($node)) { + return null; + } + + $node->class = new Name($className); + $hasChanged = true; + return $node; + }); + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::CLASS_ON_OBJECT; + } + + private function shouldSkip(ClassConstFetch $classConstFetch): bool + { + if (! $classConstFetch->class instanceof Variable) { + return true; + } + + if (! is_string($classConstFetch->class->name)) { + return true; + } + + if (! $this->isName($classConstFetch->class, 'this')) { + return true; + } + + if (! $classConstFetch->name instanceof Identifier) { + return true; + } + + return ! $this->isName($classConstFetch->name, 'class'); + } +} diff --git a/rules/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector.php b/rules/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector.php new file mode 100644 index 00000000000..7aecc2af9c0 --- /dev/null +++ b/rules/Php80/Rector/ClassMethod/AddParamBasedOnParentClassMethodRector.php @@ -0,0 +1,306 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->extends === null && $node->implements === []) { + return null; + } + + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($this->isName($classMethod, MethodName::CONSTRUCT)) { + continue; + } + + $parentMethodReflection = $this->parentClassMethodTypeOverrideGuard->getParentClassMethod($classMethod); + if (! $parentMethodReflection instanceof MethodReflection) { + continue; + } + + if ($parentMethodReflection->isPrivate()) { + continue; + } + + $scope = ScopeFetcher::fetch($node); + $currentClassReflection = $scope->getClassReflection(); + + $isPDO = $currentClassReflection instanceof ClassReflection && $currentClassReflection->is('PDO'); + + // It relies on phpstorm stubs that define 2 kind of query method for both php 7.4 and php 8.0 + // @see https://github.com/JetBrains/phpstorm-stubs/blob/e2e898a29929d2f520fe95bdb2109d8fa895ba4a/PDO/PDO.php#L1096-L1126 + if ($isPDO && $parentMethodReflection->getName() === 'query') { + continue; + } + + $parentClassMethod = $this->astResolver->resolveClassMethodFromMethodReflection($parentMethodReflection); + if (! $parentClassMethod instanceof ClassMethod) { + continue; + } + + $currentClassMethodParams = $classMethod->getParams(); + $parentClassMethodParams = $parentClassMethod->getParams(); + + $countCurrentClassMethodParams = count($currentClassMethodParams); + $countParentClassMethodParams = count($parentClassMethodParams); + + if ($countCurrentClassMethodParams === $countParentClassMethodParams) { + continue; + } + + if ($countCurrentClassMethodParams < $countParentClassMethodParams) { + $hasClassMethodChanged = $this->processReplaceClassMethodParams( + $classMethod, + $parentClassMethod, + $currentClassMethodParams, + $parentClassMethodParams + ); + + if ($hasClassMethodChanged) { + $hasChanged = true; + } + + continue; + } + + $hasClassMethodChanged = $this->processAddNullDefaultParam( + $currentClassMethodParams, + $parentClassMethodParams + ); + if ($hasClassMethodChanged) { + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + /** + * @param Param[] $currentClassMethodParams + * @param Param[] $parentClassMethodParams + */ + private function processAddNullDefaultParam( + array $currentClassMethodParams, + array $parentClassMethodParams + ): bool { + $hasChanged = false; + + foreach ($currentClassMethodParams as $key => $currentClassMethodParam) { + if (isset($parentClassMethodParams[$key])) { + continue; + } + + if ($currentClassMethodParam->default instanceof Expr) { + continue; + } + + if ($currentClassMethodParam->variadic) { + continue; + } + + $currentClassMethodParam->default = $this->nodeFactory->createNull(); + $hasChanged = true; + } + + return $hasChanged; + } + + /** + * @param array $currentClassMethodParams + * @param array $parentClassMethodParams + */ + private function processReplaceClassMethodParams( + ClassMethod $node, + ClassMethod $parentClassMethod, + array $currentClassMethodParams, + array $parentClassMethodParams + ): bool { + $originalParams = $node->params; + + $hasChanged = false; + + foreach ($parentClassMethodParams as $key => $parentClassMethodParam) { + if (isset($currentClassMethodParams[$key])) { + $currentParamName = $this->getName($currentClassMethodParams[$key]); + $collectParamNamesNextKey = $this->collectParamNamesNextKey($parentClassMethod, $key); + + if (in_array($currentParamName, $collectParamNamesNextKey, true)) { + $node->params = $originalParams; + return false; + } + + continue; + } + + $isUsedInStmts = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped( + $node, + function (Node $subNode) use ($parentClassMethodParam): bool { + if (! $subNode instanceof Variable) { + return false; + } + + return $this->nodeComparator->areNodesEqual($subNode, $parentClassMethodParam->var); + } + ); + + if ($isUsedInStmts) { + $node->params = $originalParams; + return false; + } + + $paramDefault = $parentClassMethodParam->default; + + if ($paramDefault instanceof Expr) { + $paramDefault = $this->nodeFactory->createReprintedNode($paramDefault); + } + + $paramName = $this->getName($parentClassMethodParam); + $paramType = $this->resolveParamType($parentClassMethodParam); + + $node->params[$key] = new Param( + new Variable($paramName), + $paramDefault, + $paramType, + $parentClassMethodParam->byRef, + $parentClassMethodParam->variadic, + [], + $parentClassMethodParam->flags + ); + + if ($parentClassMethodParam->attrGroups !== []) { + $attrGroupsAsComment = $this->betterStandardPrinter->print($parentClassMethodParam->attrGroups); + $node->params[$key]->setAttribute(AttributeKey::COMMENTS, [new Comment($attrGroupsAsComment)]); + } + + $hasChanged = true; + } + + return $hasChanged; + } + + private function resolveParamType(Param $param): null|Identifier|Name|ComplexType + { + if (! $param->type instanceof Node) { + return null; + } + + return $this->nodeFactory->createReprintedNode($param->type); + } + + /** + * @return string[] + */ + private function collectParamNamesNextKey(ClassMethod $classMethod, int $key): array + { + $paramNames = []; + + foreach ($classMethod->params as $paramKey => $param) { + if ($paramKey > $key) { + $paramNames[] = $this->getName($param); + } + } + + return $paramNames; + } +} diff --git a/rules/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector.php b/rules/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector.php index f5e656d3ba8..5f799c6d10e 100644 --- a/rules/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector.php +++ b/rules/Php80/Rector/ClassMethod/FinalPrivateToPrivateVisibilityRector.php @@ -6,18 +6,32 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Rector\AbstractRector; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector\FinalPrivateToPrivateVisibilityRectorTest */ -final class FinalPrivateToPrivateVisibilityRector extends AbstractRector +final class FinalPrivateToPrivateVisibilityRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly VisibilityManipulator $visibilityManipulator, + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NO_FINAL_PRIVATE; + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Changes method visibility from final private to only private', [ + return new RuleDefinition('Change method visibility from final private to only private', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -27,7 +41,7 @@ final private function getter() { } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -67,6 +81,11 @@ private function shouldSkip(ClassMethod $classMethod): bool if (! $classMethod->isFinal()) { return true; } + + if ($classMethod->name->toString() === MethodName::CONSTRUCT) { + return true; + } + return ! $classMethod->isPrivate(); } } diff --git a/rules/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php b/rules/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php deleted file mode 100644 index a9ec261df5d..00000000000 --- a/rules/Php80/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php +++ /dev/null @@ -1,205 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, New_::class, MethodCall::class]; - } - - /** - * @param ClassMethod|New_|MethodCall $node - */ - public function refactor(Node $node): ?Node - { - $isAlreadySorted = $node->getAttribute(self::ALREADY_SORTED); - if ($isAlreadySorted) { - return null; - } - - if ($node instanceof ClassMethod) { - return $this->refactorClassMethod($node); - } - - if ($node instanceof New_) { - return $this->refactorNew($node); - } - - return $this->refactorMethodCall($node); - } - - private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod - { - if ($classMethod->params === []) { - return null; - } - - $classMethod->getAttribute(AttributeKey::CLASS_NAME); - $classMethodReflection = $this->reflectionResolver->resolveMethodReflectionFromClassMethod($classMethod); - - if (! $classMethodReflection instanceof MethodReflection) { - return null; - } - - $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent( - $classMethodReflection, - $classMethod->params - ); - if ($expectedArgOrParamOrder === null) { - return null; - } - - $newParams = $this->argumentSorter->sortArgsByExpectedParamOrder( - $classMethod->params, - $expectedArgOrParamOrder - ); - - $classMethod->params = $newParams; - $classMethod->setAttribute(self::ALREADY_SORTED, true); - - return $classMethod; - } - - private function refactorNew(New_ $new): ?New_ - { - if ($new->args === []) { - return null; - } - - $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromNew($new); - if (! $methodReflection instanceof MethodReflection) { - return null; - } - - $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $new->args); - if ($expectedArgOrParamOrder === null) { - return null; - } - - $new->args = $this->argumentSorter->sortArgsByExpectedParamOrder($new->args, $expectedArgOrParamOrder); - $new->setAttribute(self::ALREADY_SORTED, true); - - return $new; - } - - private function refactorMethodCall(MethodCall $methodCall): ?MethodCall - { - $methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($methodCall); - if (! $methodReflection instanceof MethodReflection) { - return null; - } - - $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $methodCall->args); - if ($expectedArgOrParamOrder === null) { - return null; - } - - $newArgs = $this->argumentSorter->sortArgsByExpectedParamOrder( - $methodCall->args, - $expectedArgOrParamOrder - ); - - if ($methodCall->args === $newArgs) { - return null; - } - - $methodCall->args = $newArgs; - $methodCall->setAttribute(self::ALREADY_SORTED, true); - - return $methodCall; - } - - /** - * @param array $argsOrParams - * @return int[]|null - */ - private function resolveExpectedArgParamOrderIfDifferent( - MethodReflection $methodReflection, - array $argsOrParams - ): ?array { - if ($this->vendorLocationDetector->detectFunctionLikeReflection($methodReflection)) { - return null; - } - - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); - $expectedParameterReflections = $this->requireOptionalParamResolver->resolveFromReflection( - $methodReflection - ); - - if (count($argsOrParams) !== count($parametersAcceptor->getParameters())) { - return null; - } - - if ($expectedParameterReflections === $parametersAcceptor->getParameters()) { - return null; - } - - return array_keys($expectedParameterReflections); - } -} diff --git a/rules/Php80/Rector/ClassMethod/SetStateToStaticRector.php b/rules/Php80/Rector/ClassMethod/SetStateToStaticRector.php index 38838571be8..35e26cb4bc5 100644 --- a/rules/Php80/Rector/ClassMethod/SetStateToStaticRector.php +++ b/rules/Php80/Rector/ClassMethod/SetStateToStaticRector.php @@ -6,19 +6,32 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php80\Rector\ClassMethod\SetStateToStaticRector\SetStateToStaticRectorTest */ -final class SetStateToStaticRector extends AbstractRector +final class SetStateToStaticRector extends AbstractRector implements MinPhpVersionInterface { + public function __construct( + private readonly VisibilityManipulator $visibilityManipulator + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::STATIC_VISIBILITY_SET_STATE; + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Adds static visibility to __set_state() methods', [ + return new RuleDefinition('Add `static` visibility to `__set_state()` methods', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -28,7 +41,7 @@ public function __set_state($properties) { } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -68,6 +81,7 @@ private function shouldSkip(ClassMethod $classMethod): bool if (! $this->isName($classMethod, MethodName::SET_STATE)) { return true; } + return $classMethod->isStatic(); } } diff --git a/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php b/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php index ab9990e6640..823b8d1c514 100644 --- a/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php +++ b/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php @@ -12,59 +12,61 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Property; +use PhpParser\Node\Stmt\Use_; use PHPStan\PhpDocParser\Ast\Node as DocNode; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode; +use PHPStan\Reflection\ReflectionProvider; use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; -use Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Exception\Configuration\InvalidConfigurationException; +use Rector\Naming\Naming\UseImportsResolver; +use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; use Rector\Php80\NodeFactory\AttrGroupsFactory; -use Rector\Php80\PhpDocCleaner\ConvertedAnnotationToAttributeParentRemover; +use Rector\Php80\NodeManipulator\AttributeGroupNamedArgumentManipulator; use Rector\Php80\ValueObject\AnnotationToAttribute; +use Rector\Php80\ValueObject\AttributeValueAndDocComment; use Rector\Php80\ValueObject\DoctrineTagAndAnnotationToAttribute; -use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory; +use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory; +use Rector\PhpDocParser\PhpDocParser\PhpDocNodeTraverser; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Symplify\SimplePhpDocParser\PhpDocNodeTraverser; use Webmozart\Assert\Assert; /** - * @changelog https://wiki.php.net/rfc/attributes_v2 - * * @see \Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\AnnotationToAttributeRectorTest + * @see \Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Php81NestedAttributesRectorTest + * @see \Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\MultipleCallAnnotationToAttributeRectorTest */ final class AnnotationToAttributeRector extends AbstractRector implements ConfigurableRectorInterface, MinPhpVersionInterface { - /** - * @var string - */ - public const ANNOTATION_TO_ATTRIBUTE = 'annotation_to_attribute'; - - /** - * List of annotations that should not be unwrapped - * @var string[] - */ - private const SKIP_UNWRAP_ANNOTATIONS = [ - 'Symfony\Component\Validator\Constraints\All', - 'Symfony\Component\Validator\Constraints\AtLeastOneOf', - 'Symfony\Component\Validator\Constraints\Collection', - 'Symfony\Component\Validator\Constraints\Sequentially', - ]; - /** * @var AnnotationToAttribute[] */ private array $annotationsToAttributes = []; public function __construct( - private PhpAttributeGroupFactory $phpAttributeGroupFactory, - private ConvertedAnnotationToAttributeParentRemover $convertedAnnotationToAttributeParentRemover, - private AttrGroupsFactory $attrGroupsFactory + private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory, + private readonly AttrGroupsFactory $attrGroupsFactory, + private readonly PhpDocTagRemover $phpDocTagRemover, + private readonly AttributeGroupNamedArgumentManipulator $attributeGroupNamedArgumentManipulator, + private readonly UseImportsResolver $useImportsResolver, + private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly ReflectionProvider $reflectionProvider, + private readonly AttributeValueResolver $attributeValueResolver ) { } @@ -98,11 +100,7 @@ public function action() } CODE_SAMPLE , - [ - self::ANNOTATION_TO_ATTRIBUTE => [ - new AnnotationToAttribute('Symfony\Component\Routing\Annotation\Route'), - ], - ] + [new AnnotationToAttribute('Symfony\Component\Routing\Annotation\Route')] ), ]); } @@ -120,48 +118,54 @@ public function getNodeTypes(): array Function_::class, Closure::class, ArrowFunction::class, + Interface_::class, ]; } /** - * @param Class_|Property|Param|ClassMethod|Function_|Closure|ArrowFunction $node + * @param Class_|Property|Param|ClassMethod|Function_|Closure|ArrowFunction|Interface_ $node */ public function refactor(Node $node): ?Node { + if ($this->annotationsToAttributes === []) { + throw new InvalidConfigurationException(sprintf('The "%s" rule requires configuration.', self::class)); + } + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); if (! $phpDocInfo instanceof PhpDocInfo) { return null; } - // 1. generic tags + $uses = $this->useImportsResolver->resolveBareUses(); + + // 1. Doctrine annotation classes + $annotationAttributeGroups = $this->processDoctrineAnnotationClasses($phpDocInfo, $uses); + + // 2. bare tags without annotation class, e.g. "@require" $genericAttributeGroups = $this->processGenericTags($phpDocInfo); - // 2. Doctrine annotation classes - $annotationAttributeGroups = $this->processDoctrineAnnotationClasses($phpDocInfo); - $attributeGroups = array_merge($genericAttributeGroups, $annotationAttributeGroups); + $attributeGroups = [...$annotationAttributeGroups, ...$genericAttributeGroups]; if ($attributeGroups === []) { return null; } - $node->attrGroups = array_merge($node->attrGroups, $attributeGroups); + // 3. Reprint docblock + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); - $this->convertedAnnotationToAttributeParentRemover->processPhpDocNode( - $phpDocInfo->getPhpDocNode(), - $this->annotationsToAttributes, - self::SKIP_UNWRAP_ANNOTATIONS - ); + $this->attributeGroupNamedArgumentManipulator->decorate($attributeGroups); + $node->attrGroups = array_merge($node->attrGroups, $attributeGroups); return $node; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $annotationsToAttributes = $configuration[self::ANNOTATION_TO_ATTRIBUTE] ?? []; - Assert::allIsInstanceOf($annotationsToAttributes, AnnotationToAttribute::class); - $this->annotationsToAttributes = $annotationsToAttributes; + Assert::allIsAOf($configuration, AnnotationToAttribute::class); + + $this->annotationsToAttributes = $this->resolveWithChangedAttributesClass($configuration); } public function provideMinPhpVersion(): int @@ -169,6 +173,28 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::ATTRIBUTES; } + /** + * @param AnnotationToAttribute[] $configuration + * @return AnnotationToAttribute[] $configuration + */ + private function resolveWithChangedAttributesClass(array $configuration): array + { + foreach ($configuration as $config) { + /** @var AnnotationToAttribute $config */ + if ($config->getAttributeClass() !== $config->getTag()) { + // add to make sure apply after use statement changed + $configuration[] = new AnnotationToAttribute( + $config->getAttributeClass(), + $config->getAttributeClass(), + $config->getClassReferenceFields(), + $config->getUseValueAsAttributeArgument(), + ); + } + } + + return $configuration; + } + /** * @return AttributeGroup[] */ @@ -179,13 +205,13 @@ private function processGenericTags(PhpDocInfo $phpDocInfo): array $phpDocNodeTraverser = new PhpDocNodeTraverser(); $phpDocNodeTraverser->traverseWithCallable($phpDocInfo->getPhpDocNode(), '', function (DocNode $docNode) use ( &$attributeGroups, - $phpDocInfo - ): ?int { + ): int|PhpDocChildNode|null { + if (! $docNode instanceof PhpDocTagNode) { return null; } - if (! $docNode->value instanceof GenericTagValueNode) { + if (! $docNode->value instanceof GenericTagValueNode && ! $docNode->value instanceof DoctrineAnnotationTagValueNode) { return null; } @@ -198,14 +224,29 @@ private function processGenericTags(PhpDocInfo $phpDocInfo): array foreach ($this->annotationsToAttributes as $annotationToAttribute) { $desiredTag = $annotationToAttribute->getTag(); - if ($desiredTag !== $tag) { + if (strtolower($desiredTag) !== strtolower($tag)) { continue; } - $attributeGroups[] = $this->phpAttributeGroupFactory->createFromSimpleTag($annotationToAttribute); + // make sure the attribute class really exists to avoid error on early upgrade + if (! $this->reflectionProvider->hasClass($annotationToAttribute->getAttributeClass())) { + continue; + } + + $attributeValueAndDocComment = $this->attributeValueResolver->resolve($annotationToAttribute, $docNode); + + $attributeGroups[] = $this->phpAttributeGroupFactory->createFromSimpleTag( + $annotationToAttribute, + $attributeValueAndDocComment instanceof AttributeValueAndDocComment ? $attributeValueAndDocComment->attributeValue : null, + ); + + // keep partial original comment, if useful + if ($attributeValueAndDocComment instanceof AttributeValueAndDocComment && $attributeValueAndDocComment->docComment) { + return new PhpDocTextNode($attributeValueAndDocComment->docComment); + } - $phpDocInfo->markAsChanged(); return PhpDocNodeTraverser::NODE_REMOVE; + } return null; @@ -215,49 +256,87 @@ private function processGenericTags(PhpDocInfo $phpDocInfo): array } /** + * @param Use_[] $uses * @return AttributeGroup[] */ - private function processDoctrineAnnotationClasses(PhpDocInfo $phpDocInfo): array + private function processDoctrineAnnotationClasses(PhpDocInfo $phpDocInfo, array $uses): array { + if ($phpDocInfo->getPhpDocNode()->children === []) { + return []; + } + $doctrineTagAndAnnotationToAttributes = []; + $doctrineTagValueNodes = []; - $phpDocNodeTraverser = new PhpDocNodeTraverser(); + foreach ($phpDocInfo->getPhpDocNode()->children as $phpDocChildNode) { + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } - $phpDocNodeTraverser->traverseWithCallable($phpDocInfo->getPhpDocNode(), '', function ($node) use ( - &$doctrineTagAndAnnotationToAttributes, - $phpDocInfo - ): ?int { - if ($node instanceof SpacelessPhpDocTagNode && $node->value instanceof DoctrineAnnotationTagValueNode) { - $doctrineAnnotationTagValueNode = $node->value; - } elseif ($node instanceof DoctrineAnnotationTagValueNode) { - $doctrineAnnotationTagValueNode = $node; - } else { - return null; + if (! $phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) { + continue; } - if ($doctrineAnnotationTagValueNode->hasClassNames(self::SKIP_UNWRAP_ANNOTATIONS)) { - return PhpDocNodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + $doctrineTagValueNode = $phpDocChildNode->value; + + $annotationToAttribute = $this->matchAnnotationToAttribute($doctrineTagValueNode); + if (! $annotationToAttribute instanceof AnnotationToAttribute) { + continue; } - foreach ($this->annotationsToAttributes as $annotationToAttribute) { - if (! $doctrineAnnotationTagValueNode->hasClassName($annotationToAttribute->getTag())) { - continue; - } + if ($annotationToAttribute->getUseValueAsAttributeArgument()) { + /* Will be processed by processGenericTags instead */ + continue; + } - $doctrineTagAndAnnotationToAttributes[] = new DoctrineTagAndAnnotationToAttribute( - $doctrineAnnotationTagValueNode, - $annotationToAttribute - ); + if (! $this->isExistingAttributeClass($annotationToAttribute)) { + continue; + } - $phpDocInfo->markAsChanged(); + $doctrineTagAndAnnotationToAttributes[] = new DoctrineTagAndAnnotationToAttribute( + $doctrineTagValueNode, + $annotationToAttribute, + ); - // remove the original doctrine annotation, it becomes an attribute - return PhpDocNodeTraverser::NODE_REMOVE; + $doctrineTagValueNodes[] = $doctrineTagValueNode; + } + + $attributeGroups = $this->attrGroupsFactory->create($doctrineTagAndAnnotationToAttributes, $uses); + + if ($this->phpAttributeAnalyzer->hasRemoveArrayState($attributeGroups)) { + return []; + } + + foreach ($doctrineTagValueNodes as $doctrineTagValueNode) { + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineTagValueNode); + } + + return $attributeGroups; + } + + private function matchAnnotationToAttribute( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode + ): AnnotationToAttribute|null { + foreach ($this->annotationsToAttributes as $annotationToAttribute) { + if (! $doctrineAnnotationTagValueNode->hasClassName($annotationToAttribute->getTag())) { + continue; } - return null; - }); + return $annotationToAttribute; + } + + return null; + } + + private function isExistingAttributeClass(AnnotationToAttribute $annotationToAttribute): bool + { + // make sure the attribute class really exists to avoid error on early upgrade + if (! $this->reflectionProvider->hasClass($annotationToAttribute->getAttributeClass())) { + return false; + } - return $this->attrGroupsFactory->create($doctrineTagAndAnnotationToAttributes); + // make sure the class is marked as attribute + $classReflection = $this->reflectionProvider->getClass($annotationToAttribute->getAttributeClass()); + return $classReflection->isAttributeClass(); } } diff --git a/rules/Php80/Rector/Class_/AttributeValueResolver.php b/rules/Php80/Rector/Class_/AttributeValueResolver.php new file mode 100644 index 00000000000..7d9d46cc5ef --- /dev/null +++ b/rules/Php80/Rector/Class_/AttributeValueResolver.php @@ -0,0 +1,80 @@ +getUseValueAsAttributeArgument()) { + return null; + } + + $docValue = (string) $phpDocTagNode->value; + + if ($phpDocTagNode->value instanceof DoctrineAnnotationTagValueNode) { + $originalContent = (string) $phpDocTagNode->value->getOriginalContent(); + + if ($docValue === '') { + $docValue = $originalContent; + } else { + $attributeComment = ltrim($originalContent, $docValue); + if ($attributeComment !== '') { + $docValue .= "\n" . $attributeComment; + } + } + } + + $docComment = ''; + + // special case for newline + if (str_contains($docValue, "\n")) { + $keepJoining = true; + $docValueLines = NewLineSplitter::split($docValue); + + $joinDocValue = ''; + + $hasPreviousEndSlash = false; + + foreach ($docValueLines as $key => $docValueLine) { + if ($keepJoining) { + $joinDocValue .= rtrim($docValueLine, '\\\\'); + } + + if (Strings::match($docValueLine, self::END_SLASH_REGEX) === null) { + if ($hasPreviousEndSlash === false && $key > 0) { + if ($docComment === '') { + $docComment .= $docValueLine; + } else { + $docComment .= "\n * " . $docValueLine; + } + } + + $keepJoining = false; + } else { + $hasPreviousEndSlash = true; + } + } + + $docValue = $joinDocValue; + } + + return new AttributeValueAndDocComment($docValue, $docComment); + } +} diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index 597dc37ce09..7d897abebc3 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -5,41 +5,92 @@ namespace Rector\Php80\Rector\Class_; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\FunctionLike; use PhpParser\Node\Identifier; use PhpParser\Node\NullableType; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; +use PhpParser\Node\UnionType; +use PhpParser\NodeVisitor; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; -use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; -use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey; -use Rector\Core\NodeAnalyzer\ParamAnalyzer; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Type\MixedType; +use PHPStan\Type\TypeCombinator; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Naming\PropertyRenamer\PropertyPromotionRenamer; use Rector\Naming\VariableRenamer; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeAnalyzer\ParamAnalyzer; +use Rector\NodeTypeResolver\TypeComparator\TypeComparator; +use Rector\Php80\DocBlock\PropertyPromotionDocBlockMerger; +use Rector\Php80\Guard\MakePropertyPromotionGuard; use Rector\Php80\NodeAnalyzer\PromotedPropertyCandidateResolver; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/constructor_promotion https://github.com/php/php-src/pull/5291 - * * @see \Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\ClassPropertyAssignToConstructorPromotionRectorTest */ -final class ClassPropertyAssignToConstructorPromotionRector extends AbstractRector implements MinPhpVersionInterface +final class ClassPropertyAssignToConstructorPromotionRector extends AbstractRector implements MinPhpVersionInterface, ConfigurableRectorInterface { + /** + * @api + */ + public const string INLINE_PUBLIC = 'inline_public'; + + /** + * @api + */ + public const string RENAME_PROPERTY = 'rename_property'; + + /** + * @api + */ + public const string ALLOW_MODEL_BASED_CLASSES = 'allow_model_based_classes'; + + /** + * Default to false, which only apply changes: + * + * – private modifier property + * - protected/public modifier property when property typed + * + * Set to true will allow change whether property is typed or not as far as not forbidden, eg: callable type, null type, etc. + */ + private bool $inlinePublic = false; + + /** + * Set to false will skip property promotion when parameter and property have different names. + */ + private bool $renameProperty = true; + + /** + * Set to false will skip property promotion on model based classes + */ + private bool $allowModelBasedClasses = true; + public function __construct( - private PromotedPropertyCandidateResolver $promotedPropertyCandidateResolver, - private VariableRenamer $variableRenamer, - private VarTagRemover $varTagRemover, - private ParamAnalyzer $paramAnalyzer, - private PhpDocTypeChanger $phpDocTypeChanger + private readonly PromotedPropertyCandidateResolver $promotedPropertyCandidateResolver, + private readonly VariableRenamer $variableRenamer, + private readonly ParamAnalyzer $paramAnalyzer, + private readonly PropertyPromotionDocBlockMerger $propertyPromotionDocBlockMerger, + private readonly MakePropertyPromotionGuard $makePropertyPromotionGuard, + private readonly TypeComparator $typeComparator, + private readonly ReflectionResolver $reflectionResolver, + private readonly PropertyPromotionRenamer $propertyPromotionRenamer, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly StaticTypeMapper $staticTypeMapper ) { } @@ -48,32 +99,50 @@ public function getRuleDefinition(): RuleDefinition return new RuleDefinition( 'Change simple property init and assign to constructor promotion', [ - new CodeSample( + new ConfiguredCodeSample( <<<'CODE_SAMPLE' class SomeClass { - public float $someVariable; + public float $price; - public function __construct(float $someVariable = 0.0) - { - $this->someVariable = $someVariable; + public function __construct( + float $price = 0.0 + ) { + $this->price = $price; } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { - public function __construct(private float $someVariable = 0.0) - { + public function __construct( + public float $price = 0.0 + ) { } } CODE_SAMPLE + , + [ + self::INLINE_PUBLIC => false, + self::RENAME_PROPERTY => true, + self::ALLOW_MODEL_BASED_CLASSES => true, + ] ), ] ); } + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + $this->inlinePublic = $configuration[self::INLINE_PUBLIC] ?? false; + $this->renameProperty = $configuration[self::RENAME_PROPERTY] ?? true; + $this->allowModelBasedClasses = $configuration[self::ALLOW_MODEL_BASED_CLASSES] ?? true; + } + /** * @return array> */ @@ -87,55 +156,128 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $promotionCandidates = $this->promotedPropertyCandidateResolver->resolveFromClass($node); + $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { + return null; + } + + $promotionCandidates = $this->promotedPropertyCandidateResolver->resolveFromClass( + $node, + $constructClassMethod, + $this->allowModelBasedClasses + ); if ($promotionCandidates === []) { return null; } - /** @var ClassMethod $constructClassMethod */ - $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + $constructorPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($constructClassMethod); - $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($constructClassMethod); + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return null; + } + $hasChanged = false; foreach ($promotionCandidates as $promotionCandidate) { - // does property have some useful annotations? - $property = $promotionCandidate->getProperty(); $param = $promotionCandidate->getParam(); if ($this->shouldSkipParam($param)) { continue; } - $this->removeNode($property); - $this->removeNode($promotionCandidate->getAssign()); - $property = $promotionCandidate->getProperty(); + if (! $this->makePropertyPromotionGuard->isLegal( + $node, + $classReflection, + $property, + $param, + $this->inlinePublic + )) { + continue; + } + $paramName = $this->getName($param); // rename also following calls $propertyName = $this->getName($property->props[0]); - /** @var string $oldName */ - $oldName = $this->getName($param->var); - $this->variableRenamer->renameVariableInFunctionLike($constructClassMethod, $oldName, $propertyName, null); - $paramTagValueNode = $classMethodPhpDocInfo->getParamTagValueNodeByName($paramName); + if (! $this->renameProperty && $paramName !== $propertyName) { + continue; + } + + if ($this->shouldSkipPropertyOrParam($property, $param)) { + continue; + } + + $hasChanged = true; + + // remove property from class + unset($node->stmts[$promotionCandidate->getPropertyStmtPosition()]); + + // remove assign in constructor + unset($constructClassMethod->stmts[$promotionCandidate->getConstructorAssignStmtPosition()]); + + $oldParamName = $this->getName($param); + $this->variableRenamer->renameVariableInFunctionLike($constructClassMethod, $oldParamName, $propertyName); + + $paramTagValueNode = $constructorPhpDocInfo->getParamTagValueByName($paramName); if (! $paramTagValueNode instanceof ParamTagValueNode) { - $this->decorateParamWithPropertyPhpDocInfo($property, $param); + $this->propertyPromotionDocBlockMerger->decorateParamWithPropertyPhpDocInfo( + $constructClassMethod, + $property, + $param, + $paramName + ); } elseif ($paramTagValueNode->parameterName !== '$' . $propertyName) { - $paramTagValueNode->parameterName = '$' . $propertyName; - $paramTagValueNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); + $this->propertyPromotionRenamer->renameParamDoc( + $constructorPhpDocInfo, + $constructClassMethod, + $param, + $paramTagValueNode->parameterName, + $propertyName + ); } // property name has higher priority - $propertyName = $this->getName($property); - $param->var->name = $propertyName; + $paramName = $this->getName($property); + $param->var = new Variable($paramName); $param->flags = $property->flags; - // Copy over attributes of the "old" property - $param->attrGroups = $property->attrGroups; - $this->processNullableType($property, $param); + $param->hooks = $property->hooks; + + // copy attributes of the old property + $param->attrGroups = array_merge($param->attrGroups, $property->attrGroups); + $this->processUnionType($property, $param); + + $this->propertyPromotionDocBlockMerger->mergePropertyAndParamDocBlocks( + $property, + $param, + $paramTagValueNode + ); - $this->phpDocTypeChanger->copyPropertyDocToParam($property, $param); + // update variable to property fetch references + $this->traverseNodesWithCallable((array) $constructClassMethod->stmts, function (Node $node) use ( + $promotionCandidate, + $propertyName + ): null|int|PropertyFetch { + if ($node instanceof Class_ || $node instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $node instanceof Variable) { + return null; + } + + if (! $this->isName($node, $promotionCandidate->getParamName())) { + return null; + } + + return new PropertyFetch(new Variable('this'), $propertyName); + }); + } + + if (! $hasChanged) { + return null; } return $node; @@ -146,28 +288,39 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::PROPERTY_PROMOTION; } - private function processNullableType(Property $property, Param $param): void + private function processUnionType(Property $property, Param $param): void { - if ($this->nodeTypeResolver->isNullableType($property)) { - $objectType = $this->getObjectType($property); - $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($objectType, TypeKind::PARAM()); + if ($property->type instanceof Node) { + $param->type = $property->type; + return; } - } - private function decorateParamWithPropertyPhpDocInfo(Property $property, Param $param): void - { - $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - $propertyPhpDocInfo->markAsChanged(); + if (! $param->default instanceof Expr) { + return; + } + + if (! $param->type instanceof Node) { + return; + } + + $defaultType = $this->getType($param->default); + $paramType = $this->getType($param->type); - $param->setAttribute(AttributeKey::PHP_DOC_INFO, $propertyPhpDocInfo); + if ($this->typeComparator->isSubtype($defaultType, $paramType)) { + return; + } - // make sure the docblock is useful - if ($param->type === null) { + if ($this->typeComparator->areTypesEqual($defaultType, $paramType)) { return; } - $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - $this->varTagRemover->removeVarPhpTagValueNodeIfNotComment($param, $paramType); + if ($paramType instanceof MixedType) { + return; + } + + $paramType = TypeCombinator::union($paramType, $defaultType); + + $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramType, TypeKind::PARAM); } private function shouldSkipParam(Param $param): bool @@ -184,6 +337,43 @@ private function shouldSkipParam(Param $param): bool $type = $param->type; } - return $type instanceof Identifier && $this->isName($type, 'callable'); + if ($this->isCallableTypeIdentifier($type)) { + return true; + } + + if (! $type instanceof UnionType) { + return false; + } + + foreach ($type->types as $type) { + if ($this->isCallableTypeIdentifier($type)) { + return true; + } + } + + return false; + } + + private function isCallableTypeIdentifier(?Node $node): bool + { + return $node instanceof Identifier && $this->isName($node, 'callable'); + } + + private function shouldSkipPropertyOrParam(Property $property, Param $param): bool + { + foreach ($property->hooks as $hook) { + if (! is_array($hook->body)) { + continue; + } + + if (count($hook->body) > 1) { + return true; + } + } + + return $property->type instanceof Node + && $param->type instanceof Node + && $property->hooks !== [] + && ! $this->nodeComparator->areNodesEqual($property->type, $param->type); } } diff --git a/rules/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector.php b/rules/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector.php deleted file mode 100644 index 82c70eda421..00000000000 --- a/rules/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector.php +++ /dev/null @@ -1,241 +0,0 @@ - - */ - private const TARGET_TO_CONSTANT_MAP = [ - 'METHOD' => 'TARGET_METHOD', - 'PROPERTY' => 'TARGET_PROPERTY', - 'CLASS' => 'TARGET_CLASS', - 'FUNCTION' => 'TARGET_FUNCTION', - 'ALL' => 'TARGET_ALL', - // special case - 'ANNOTATION' => 'TARGET_CLASS', - ]; - - /** - * @var string - */ - private const ATTRIBUTE = 'Attribute'; - - private bool $shouldRemoveAnnotations = true; - - public function __construct( - private PhpDocTagRemover $phpDocTagRemover, - private AttributeFlagFactory $attributeFlagFactory, - private PhpAttributeGroupFactory $phpAttributeGroupFactory, - private PhpAttributeAnalyzer $phpAttributeAnalyzer, - private PropertyToAddCollector $propertyToAddCollector - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Refactor Doctrine @annotation annotated class to a PHP 8.0 attribute class', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -use Doctrine\Common\Annotations\Annotation\Target; - -/** - * @Annotation - * @Target({"METHOD"}) - */ -class SomeAnnotation -{ -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -use Attribute; - -#[Attribute(Attribute::TARGET_METHOD)] -class SomeAnnotation -{ -} -CODE_SAMPLE - , - [ - self::REMOVE_ANNOTATIONS => true, - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); - if (! $phpDocInfo instanceof PhpDocInfo) { - return null; - } - - if ($this->shouldSkipClass($phpDocInfo, $node)) { - return null; - } - - if ($this->shouldRemoveAnnotations) { - $this->phpDocTagRemover->removeByName($phpDocInfo, 'annotation'); - $this->phpDocTagRemover->removeByName($phpDocInfo, 'Annotation'); - } - - $attributeGroup = $this->phpAttributeGroupFactory->createFromClass(self::ATTRIBUTE); - $this->decorateTarget($phpDocInfo, $attributeGroup); - - foreach ($node->getProperties() as $property) { - $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNode($property); - if (! $propertyPhpDocInfo instanceof PhpDocInfo) { - continue; - } - - $requiredDoctrineAnnotationTagValueNode = $propertyPhpDocInfo->findOneByAnnotationClass( - 'Doctrine\Common\Annotations\Annotation\Required' - ); - if (! $requiredDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { - continue; - } - - if ($this->shouldRemoveAnnotations) { - $this->phpDocTagRemover->removeTagValueFromNode( - $propertyPhpDocInfo, - $requiredDoctrineAnnotationTagValueNode - ); - } - - // require in constructor - $propertyName = $this->getName($property); - - $propertyMetadata = new PropertyMetadata($propertyName, new MixedType(), Class_::MODIFIER_PUBLIC); - $this->propertyToAddCollector->addPropertyToClass($node, $propertyMetadata); - - if ($this->shouldRemoveAnnotations) { - $this->removeNode($property); - } - } - - $node->attrGroups[] = $attributeGroup; - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $shouldRemoveAnnotations = $configuration[self::REMOVE_ANNOTATIONS] ?? true; - Assert::boolean($shouldRemoveAnnotations); - $this->shouldRemoveAnnotations = $shouldRemoveAnnotations; - } - - /** - * @param array $targetValues - * @return ClassConstFetch[] - */ - private function resolveFlags(array $targetValues): array - { - $flags = []; - foreach (self::TARGET_TO_CONSTANT_MAP as $target => $constant) { - if (! in_array($target, $targetValues, true)) { - continue; - } - - $flags[] = $this->nodeFactory->createClassConstFetch(self::ATTRIBUTE, $constant); - } - - return $flags; - } - - private function decorateTarget(PhpDocInfo $phpDocInfo, AttributeGroup $attributeGroup): void - { - $targetDoctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass( - 'Doctrine\Common\Annotations\Annotation\Target' - ); - - if (! $targetDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { - return; - } - - if ($this->shouldRemoveAnnotations) { - $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $targetDoctrineAnnotationTagValueNode); - } - - $targets = $targetDoctrineAnnotationTagValueNode->getSilentValue(); - if ($targets instanceof CurlyListNode) { - $targetValues = $targets->getValuesWithExplicitSilentAndWithoutQuotes(); - } elseif (is_string($targets)) { - $targetValues = [$targets]; - } else { - return; - } - - $flags = $this->resolveFlags($targetValues); - $flagCollection = $this->attributeFlagFactory->createFlagCollection($flags); - if ($flagCollection === null) { - return; - } - - $attributeGroup->attrs[0]->args[] = new Arg($flagCollection); - } - - private function shouldSkipClass(PhpDocInfo $phpDocInfo, Class_ $class): bool - { - if (! $phpDocInfo->hasByNames(['Annotation', 'annotation'])) { - return true; - } - - // has attribute? skip it - return $this->phpAttributeAnalyzer->hasPhpAttribute($class, self::ATTRIBUTE); - } -} diff --git a/rules/Php80/Rector/Class_/StringableForToStringRector.php b/rules/Php80/Rector/Class_/StringableForToStringRector.php index 6bbc1f4268f..ea8c4618d19 100644 --- a/rules/Php80/Rector/Class_/StringableForToStringRector.php +++ b/rules/Php80/Rector/Class_/StringableForToStringRector.php @@ -5,33 +5,50 @@ namespace Rector\Php80\Rector\Class_; use PhpParser\Node; -use PhpParser\Node\Name; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Cast\String_ as CastString_; +use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Identifier; use PhpParser\Node\Name\FullyQualified; +use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\MethodName; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Return_; +use PhpParser\NodeVisitor; use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer; +use Rector\NodeAnalyzer\ClassAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer; +use Rector\TypeDeclaration\TypeInferer\SilentVoidResolver; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/stringable - * * @see \Rector\Tests\Php80\Rector\Class_\StringableForToStringRector\StringableForToStringRectorTest */ -final class StringableForToStringRector extends AbstractRector +final class StringableForToStringRector extends AbstractRector implements MinPhpVersionInterface { - /** - * @var string - */ - private const STRINGABLE = 'Stringable'; + private const string STRINGABLE = 'Stringable'; + + private bool $hasChanged = false; public function __construct( - private FamilyRelationsAnalyzer $familyRelationsAnalyzer + private readonly FamilyRelationsAnalyzer $familyRelationsAnalyzer, + private readonly ReturnTypeInferer $returnTypeInferer, + private readonly ClassAnalyzer $classAnalyzer, + private readonly SilentVoidResolver $silentVoidResolver ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::STRINGABLE; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -47,7 +64,7 @@ public function __toString() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass implements Stringable { @@ -75,28 +92,84 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { + if ($this->classAnalyzer->isAnonymousClass($node)) { + return null; + } + $toStringClassMethod = $node->getMethod(MethodName::TO_STRING); if (! $toStringClassMethod instanceof ClassMethod) { return null; } - // warning, classes that implements __toString() will return Stringable interface even if they don't implemen it + $this->hasChanged = false; + + // warning, classes that implements __toString() will return Stringable interface even if they don't implement it // reflection cannot be used for real detection $classLikeAncestorNames = $this->familyRelationsAnalyzer->getClassLikeAncestorNames($node); + $isAncestorHasStringable = in_array(self::STRINGABLE, $classLikeAncestorNames, true); - if (in_array(self::STRINGABLE, $classLikeAncestorNames, true)) { - return null; + $returnType = $this->returnTypeInferer->inferFunctionLike($toStringClassMethod); + if (! $returnType->isString()->yes()) { + $this->processNotStringType($toStringClassMethod); } - // add interface - $node->implements[] = new FullyQualified(self::STRINGABLE); + if (! $isAncestorHasStringable) { + // add interface + $node->implements[] = new FullyQualified(self::STRINGABLE); + + $this->hasChanged = true; + } // add return type + if (! $toStringClassMethod->returnType instanceof Node) { + $toStringClassMethod->returnType = new Identifier('string'); + $this->hasChanged = true; + } - if ($toStringClassMethod->returnType === null) { - $toStringClassMethod->returnType = new Name('string'); + if (! $this->hasChanged) { + return null; } return $node; } + + private function processNotStringType(ClassMethod $toStringClassMethod): void + { + if ($toStringClassMethod->isAbstract()) { + return; + } + + if ($this->silentVoidResolver->hasSilentVoid($toStringClassMethod)) { + $emptyStringReturn = new Return_(new String_('')); + $toStringClassMethod->stmts[] = $emptyStringReturn; + + $this->hasChanged = true; + + return; + } + + $this->traverseNodesWithCallable((array) $toStringClassMethod->stmts, function (Node $subNode): ?int { + if ($subNode instanceof Class_ || $subNode instanceof Function_ || $subNode instanceof Closure) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $subNode instanceof Return_) { + return null; + } + + if (! $subNode->expr instanceof Expr) { + $subNode->expr = new String_(''); + return null; + } + + $type = $this->nodeTypeResolver->getType($subNode->expr); + if ($type->isString()->yes()) { + return null; + } + + $subNode->expr = new CastString_($subNode->expr); + $this->hasChanged = true; + return null; + }); + } } diff --git a/rules/Php80/Rector/FuncCall/ClassOnObjectRector.php b/rules/Php80/Rector/FuncCall/ClassOnObjectRector.php index a25863222a2..818b6f88072 100644 --- a/rules/Php80/Rector/FuncCall/ClassOnObjectRector.php +++ b/rules/Php80/Rector/FuncCall/ClassOnObjectRector.php @@ -8,15 +8,13 @@ use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/class_name_literal_on_object - * * @see \Rector\Tests\Php80\Rector\FuncCall\ClassOnObjectRector\ClassOnObjectRectorTest */ final class ClassOnObjectRector extends AbstractRector implements MinPhpVersionInterface @@ -36,7 +34,7 @@ public function run($object) } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -64,16 +62,20 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if (! $this->nodeNameResolver->isName($node, 'get_class')) { + if (! $this->isName($node, 'get_class')) { return null; } - if (! isset($node->args[0])) { - return new ClassConstFetch(new Name('self'), 'class'); + if ($node->isFirstClassCallable()) { + return null; } - $object = $node->args[0]->value; + if (! isset($node->getArgs()[0])) { + return new ClassConstFetch(new Name('self'), 'class'); + } + $object = $node->getArgs()[0] + ->value; return new ClassConstFetch($object, 'class'); } diff --git a/rules/Php80/Rector/FuncCall/TokenGetAllToObjectRector.php b/rules/Php80/Rector/FuncCall/TokenGetAllToObjectRector.php deleted file mode 100644 index d9be8bd5b9e..00000000000 --- a/rules/Php80/Rector/FuncCall/TokenGetAllToObjectRector.php +++ /dev/null @@ -1,170 +0,0 @@ -getTokenName(); - $text = $phpToken->text; - } - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->nodeNameResolver->isName($node, 'token_get_all')) { - return null; - } - - $this->refactorTokensVariable($node); - - return $this->nodeFactory->createStaticCall('PhpToken', 'tokenize', $node->args); - } - - private function refactorTokensVariable(FuncCall $funcCall): void - { - $assign = $funcCall->getAttribute(AttributeKey::PARENT_NODE); - if (! $assign instanceof Assign) { - return; - } - - /** @var ClassMethod|Function_|null $classMethodOrFunction */ - $classMethodOrFunction = $this->parentFinder->findByTypes( - $funcCall, - [ClassMethod::class, Function_::class] - ); - if ($classMethodOrFunction === null) { - return; - } - - // dummy approach, improve when needed - $this->replaceGetNameOrGetValue($classMethodOrFunction, $assign->var); - } - - private function replaceGetNameOrGetValue(ClassMethod | Function_ $functionLike, Expr $assignedExpr): void - { - $tokensForeaches = $this->findForeachesOverTokenVariable($functionLike, $assignedExpr); - foreach ($tokensForeaches as $tokenForeach) { - $this->refactorTokenInForeach($tokenForeach); - } - } - - /** - * @return Foreach_[] - */ - private function findForeachesOverTokenVariable(ClassMethod | Function_ $functionLike, Expr $assignedExpr): array - { - return $this->betterNodeFinder->find((array) $functionLike->stmts, function (Node $node) use ( - $assignedExpr - ): bool { - if (! $node instanceof Foreach_) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node->expr, $assignedExpr); - }); - } - - private function refactorTokenInForeach(Foreach_ $tokensForeach): void - { - $singleToken = $tokensForeach->valueVar; - if (! $singleToken instanceof Variable) { - return; - } - - $this->traverseNodesWithCallable($tokensForeach, function (Node $node) use ($singleToken) { - $this->tokenManipulator->refactorArrayToken([$node], $singleToken); - $this->tokenManipulator->refactorNonArrayToken([$node], $singleToken); - $this->tokenManipulator->refactorTokenIsKind([$node], $singleToken); - - $this->tokenManipulator->removeIsArray([$node], $singleToken); - - // drop if "If_" node not needed - if ($node instanceof If_ && $node->else !== null) { - if (! $this->nodeComparator->areNodesEqual($node->stmts, $node->else->stmts)) { - return null; - } - - $this->unwrapStmts($node->stmts, $node); - $this->removeNode($node); - } - }); - } -} diff --git a/rules/Php80/Rector/FunctionLike/UnionTypesRector.php b/rules/Php80/Rector/FunctionLike/UnionTypesRector.php deleted file mode 100644 index fda4e791cb8..00000000000 --- a/rules/Php80/Rector/FunctionLike/UnionTypesRector.php +++ /dev/null @@ -1,218 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class]; - } - - /** - * @param ClassMethod | Function_ | Closure | ArrowFunction $node - */ - public function refactor(Node $node): ?Node - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - $this->refactorParamTypes($node, $phpDocInfo); - $this->refactorReturnType($node, $phpDocInfo); - - $this->paramTagRemover->removeParamTagsIfUseless($phpDocInfo, $node); - $this->returnTagRemover->removeReturnTagIfUseless($phpDocInfo, $node); - - return $node; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::UNION_TYPES; - } - - private function refactorParamTypes( - ClassMethod | Function_ | Closure | ArrowFunction $functionLike, - PhpDocInfo $phpDocInfo - ): void { - if ($functionLike instanceof ClassMethod && $this->classMethodParamVendorLockResolver->isVendorLocked( - $functionLike - )) { - return; - } - - foreach ($functionLike->getParams() as $param) { - /** @var string $paramName */ - $paramName = $this->getName($param->var); - $paramType = $phpDocInfo->getParamType($paramName); - if (! $paramType instanceof UnionType) { - continue; - } - - if ($this->unionTypeAnalyzer->hasObjectWithoutClassType($paramType)) { - $this->changeObjectWithoutClassType($param, $paramType); - continue; - } - - $uniqueatedParamType = $this->filterOutDuplicatedArrayTypes($paramType); - if (! $uniqueatedParamType instanceof UnionType) { - continue; - } - - $phpParserUnionType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $uniqueatedParamType, - TypeKind::PARAM() - ); - - if (! $phpParserUnionType instanceof PhpParserUnionType) { - continue; - } - - if ($param->type instanceof PhpParserUnionType) { - continue; - } - - $param->type = $phpParserUnionType; - } - } - - private function changeObjectWithoutClassType(Param $param, UnionType $unionType): void - { - if (! $this->unionTypeAnalyzer->hasObjectWithoutClassTypeWithOnlyFullyQualifiedObjectType($unionType)) { - return; - } - - $param->type = new Name('object'); - } - - private function refactorReturnType( - ClassMethod | Function_ | Closure | ArrowFunction $functionLike, - PhpDocInfo $phpDocInfo - ): void { - // do not override existing return type - if ($functionLike->getReturnType() !== null) { - return; - } - - $returnType = $phpDocInfo->getReturnType(); - if (! $returnType instanceof UnionType) { - return; - } - - $uniqueatedReturnType = $this->filterOutDuplicatedArrayTypes($returnType); - if (! $uniqueatedReturnType instanceof UnionType) { - return; - } - - $phpParserUnionType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $uniqueatedReturnType, - TypeKind::RETURN() - ); - - if (! $phpParserUnionType instanceof PhpParserUnionType) { - return; - } - - $functionLike->returnType = $phpParserUnionType; - } - - private function filterOutDuplicatedArrayTypes(UnionType $unionType): UnionType | Type - { - $hasArrayType = false; - $singleArrayTypes = []; - - $originalTypeCount = count($unionType->getTypes()); - - foreach ($unionType->getTypes() as $unionedType) { - if ($unionedType instanceof ArrayType) { - if ($hasArrayType) { - continue; - } - - $singleArrayTypes[] = $unionedType; - $hasArrayType = true; - continue; - } - - $singleArrayTypes[] = $unionedType; - } - - if ($originalTypeCount === count($singleArrayTypes)) { - return $unionType; - } - - return $this->typeFactory->createMixedPassedOrUnionType($singleArrayTypes); - } -} diff --git a/rules/Php80/Rector/Identical/StrEndsWithRector.php b/rules/Php80/Rector/Identical/StrEndsWithRector.php index 959fb5b7a3e..abca00117c1 100644 --- a/rules/Php80/Rector/Identical/StrEndsWithRector.php +++ b/rules/Php80/Rector/Identical/StrEndsWithRector.php @@ -5,36 +5,48 @@ namespace Rector\Php80\Rector\Identical; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\UnaryMinus; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; -use Rector\Core\Rector\AbstractRector; -use Rector\Nette\NodeAnalyzer\BinaryOpAnalyzer; -use Rector\Nette\ValueObject\FuncCallAndExpr; +use Rector\NodeAnalyzer\BinaryOpAnalyzer; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\FuncCallAndExpr; +use Rector\ValueObject\PhpVersionFeature; +use Rector\ValueObject\PolyfillPackage; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; +use Rector\VersionBonding\Contract\RelatedPolyfillInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/add_str_starts_with_and_ends_with_functions - * * @see \Rector\Tests\Php80\Rector\Identical\StrEndsWithRector\StrEndsWithRectorTest */ -final class StrEndsWithRector extends AbstractRector +final class StrEndsWithRector extends AbstractRector implements MinPhpVersionInterface, RelatedPolyfillInterface { public function __construct( - private BinaryOpAnalyzer $binaryOpAnalyzer + private readonly BinaryOpAnalyzer $binaryOpAnalyzer, + private readonly ValueResolver $valueResolver, ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::STR_ENDS_WITH; + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change helper functions to str_ends_with()', [ + return new RuleDefinition('Change helper functions to `str_ends_with()`', [ new CodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -47,7 +59,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -93,17 +105,22 @@ public function run() */ public function getNodeTypes(): array { - return [Identical::class, NotIdentical::class]; + return [Identical::class, NotIdentical::class, Equal::class, NotEqual::class]; } /** - * @param Identical|NotIdentical $node + * @param Identical|NotIdentical|Equal|NotEqual $node */ public function refactor(Node $node): ?Node { return $this->refactorSubstr($node) ?? $this->refactorSubstrCompare($node); } + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_80; + } + /** * Covers: * $isMatch = substr($haystack, -strlen($needle)) === $needle; @@ -121,21 +138,35 @@ private function refactorSubstr(BinaryOp $binaryOp): FuncCall | BooleanNot | nul return null; } - $haystack = $substrFuncCall->args[0]->value; + if ($substrFuncCall->isFirstClassCallable()) { + return null; + } + + if (count($substrFuncCall->getArgs()) < 2) { + return null; + } - if (! $this->isUnaryMinusStrlenFuncCallArgValue($substrFuncCall->args[1]->value, $comparedNeedleExpr) && - ! $this->isHardCodedLNumberAndString($substrFuncCall->args[1]->value, $comparedNeedleExpr) + $needle = $substrFuncCall->getArgs()[1] + ->value; + if ( + ! $this->isUnaryMinusStrlenFuncCallArgValue($needle, $comparedNeedleExpr) && + ! $this->isHardCodedLNumberAndString($needle, $comparedNeedleExpr) ) { return null; } - $isPositive = $binaryOp instanceof Identical; + $haystack = $substrFuncCall->getArgs()[0] + ->value; + + $isPositive = $binaryOp instanceof Identical || $binaryOp instanceof Equal; + return $this->buildReturnNode($haystack, $comparedNeedleExpr, $isPositive); } private function refactorSubstrCompare(BinaryOp $binaryOp): FuncCall | BooleanNot | null { $funcCallAndExpr = $this->binaryOpAnalyzer->matchFuncCallAndOtherExpr($binaryOp, 'substr_compare'); + if (! $funcCallAndExpr instanceof FuncCallAndExpr) { return null; } @@ -146,21 +177,36 @@ private function refactorSubstrCompare(BinaryOp $binaryOp): FuncCall | BooleanNo } $substrCompareFuncCall = $funcCallAndExpr->getFuncCall(); - $haystack = $substrCompareFuncCall->args[0]->value; - $needle = $substrCompareFuncCall->args[1]->value; - if (! $this->isUnaryMinusStrlenFuncCallArgValue($substrCompareFuncCall->args[2]->value, $needle) && - ! $this->isHardCodedLNumberAndString($substrCompareFuncCall->args[2]->value, $needle) + $args = $substrCompareFuncCall->getArgs(); + if (count($args) < 2) { + return null; + } + + $haystack = $args[0]->value; + $needle = $args[1]->value; + $thirdArgValue = $args[2]->value; + + $isCaseInsensitiveValue = isset($args[4]) ? $this->valueResolver->getValue($args[4]->value) : null; + + // is case insensitive → not valid replacement + if ($isCaseInsensitiveValue === true) { + return null; + } + + if ( + ! $this->isUnaryMinusStrlenFuncCallArgValue($thirdArgValue, $needle) && + ! $this->isHardCodedLNumberAndString($thirdArgValue, $needle) ) { return null; } - $isPositive = $binaryOp instanceof Identical; + $isPositive = $binaryOp instanceof Identical || $binaryOp instanceof Equal; return $this->buildReturnNode($haystack, $needle, $isPositive); } - private function isUnaryMinusStrlenFuncCallArgValue(Node $substrOffset, Node $needle): bool + private function isUnaryMinusStrlenFuncCallArgValue(Expr $substrOffset, Expr $needle): bool { if (! $substrOffset instanceof UnaryMinus) { return false; @@ -169,24 +215,34 @@ private function isUnaryMinusStrlenFuncCallArgValue(Node $substrOffset, Node $ne if (! $substrOffset->expr instanceof FuncCall) { return false; } + $funcCall = $substrOffset->expr; - if (! $this->nodeNameResolver->isName($funcCall, 'strlen')) { + if (! $this->isName($funcCall, 'strlen')) { + return false; + } + + if (! isset($funcCall->getArgs()[0])) { + return false; + } + + if (! $funcCall->args[0] instanceof Arg) { return false; } return $this->nodeComparator->areNodesEqual($funcCall->args[0]->value, $needle); } - private function isHardCodedLNumberAndString(Node $substrOffset, Node $needle): bool + private function isHardCodedLNumberAndString(Expr $substrOffset, Expr $needle): bool { if (! $substrOffset instanceof UnaryMinus) { return false; } - if (! $substrOffset->expr instanceof LNumber) { + if (! $substrOffset->expr instanceof Int_) { return false; } + $lNumber = $substrOffset->expr; if (! $needle instanceof String_) { @@ -196,7 +252,7 @@ private function isHardCodedLNumberAndString(Node $substrOffset, Node $needle): return $lNumber->value === strlen($needle->value); } - private function buildReturnNode(?Expr $haystack, ?Expr $needle, bool $isPositive): FuncCall | BooleanNot + private function buildReturnNode(Expr $haystack, Expr $needle, bool $isPositive): FuncCall | BooleanNot { $funcCall = $this->nodeFactory->createFuncCall('str_ends_with', [$haystack, $needle]); diff --git a/rules/Php80/Rector/Identical/StrStartsWithRector.php b/rules/Php80/Rector/Identical/StrStartsWithRector.php index 27b8bce1959..4fe11c5d166 100644 --- a/rules/Php80/Rector/Identical/StrStartsWithRector.php +++ b/rules/Php80/Rector/Identical/StrStartsWithRector.php @@ -5,30 +5,48 @@ namespace Rector\Php80\Rector\Identical; use PhpParser\Node; +use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; -use Rector\Core\Rector\AbstractRector; use Rector\Php80\Contract\StrStartWithMatchAndRefactorInterface; +use Rector\Php80\MatchAndRefactor\StrStartsWithMatchAndRefactor\StrncmpMatchAndRefactor; +use Rector\Php80\MatchAndRefactor\StrStartsWithMatchAndRefactor\StrposMatchAndRefactor; +use Rector\Php80\MatchAndRefactor\StrStartsWithMatchAndRefactor\SubstrMatchAndRefactor; use Rector\Php80\ValueObject\StrStartsWith; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\ValueObject\PolyfillPackage; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; +use Rector\VersionBonding\Contract\RelatedPolyfillInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/add_str_starts_with_and_ends_with_functions - * - * @see https://3v4l.org/RQHB5 for weak compare - * @see https://3v4l.org/AmLja for weak compare - * * @see \Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\StrStartsWithRectorTest */ -final class StrStartsWithRector extends AbstractRector +final class StrStartsWithRector extends AbstractRector implements MinPhpVersionInterface, RelatedPolyfillInterface { /** - * @param StrStartWithMatchAndRefactorInterface[] $strStartWithMatchAndRefactors + * @var StrStartWithMatchAndRefactorInterface[] */ + private array $strStartWithMatchAndRefactors = []; + public function __construct( - private array $strStartWithMatchAndRefactors + StrncmpMatchAndRefactor $strncmpMatchAndRefactor, + SubstrMatchAndRefactor $substrMatchAndRefactor, + StrposMatchAndRefactor $strposMatchAndRefactor, ) { + $this->strStartWithMatchAndRefactors = [ + $strncmpMatchAndRefactor, + $substrMatchAndRefactor, + $strposMatchAndRefactor, + ]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::STR_STARTS_WITH; } public function getRuleDefinition(): RuleDefinition @@ -48,7 +66,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -70,11 +88,11 @@ public function run() */ public function getNodeTypes(): array { - return [Identical::class, NotIdentical::class]; + return [Identical::class, NotIdentical::class, Equal::class, NotEqual::class]; } /** - * @param Identical|NotIdentical $node + * @param Identical|NotIdentical|Equal|NotEqual $node */ public function refactor(Node $node): ?Node { @@ -89,4 +107,9 @@ public function refactor(Node $node): ?Node return null; } + + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_80; + } } diff --git a/rules/Php80/Rector/If_/NullsafeOperatorRector.php b/rules/Php80/Rector/If_/NullsafeOperatorRector.php deleted file mode 100644 index cc8bbe9aa1a..00000000000 --- a/rules/Php80/Rector/If_/NullsafeOperatorRector.php +++ /dev/null @@ -1,460 +0,0 @@ - with full short circuiting', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($someObject) - { - $someObject2 = $someObject->mayFail1(); - if ($someObject2 === null) { - return null; - } - - return $someObject2->mayFail2(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run($someObject) - { - return $someObject->mayFail1()?->mayFail2(); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [If_::class, Ternary::class]; - } - - /** - * @param If_|Ternary $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof If_) { - return $this->handleIfNode($node); - } - - return $this->handleTernaryNode($node); - } - - private function handleIfNode(If_ $if): ?Node - { - $processNullSafeOperator = $this->processNullSafeOperatorIdentical($if); - if ($processNullSafeOperator !== null) { - /** @var Expression $prevNode */ - $prevNode = $if->getAttribute(AttributeKey::PREVIOUS_NODE); - $this->removeNode($prevNode); - - return $processNullSafeOperator; - } - - return $this->processNullSafeOperatorNotIdentical($if); - } - - private function processNullSafeOperatorIdentical(If_ $if, bool $isStartIf = true): ?Node - { - $comparedNode = $this->ifManipulator->matchIfValueReturnValue($if); - if (! $comparedNode instanceof Expr) { - return null; - } - - $prevNode = $if->getAttribute(AttributeKey::PREVIOUS_NODE); - $nextNode = $if->getAttribute(AttributeKey::NEXT_NODE); - - if (! $nextNode instanceof Node) { - return null; - } - - if (! $prevNode instanceof Expression) { - return null; - } - - $prevExpr = $prevNode->expr; - if (! $prevExpr instanceof Assign) { - return null; - } - - if (! $this->ifManipulator->isIfCondUsingAssignIdenticalVariable($if, $prevExpr)) { - return null; - } - - return $this->processAssign($prevExpr, $prevNode, $nextNode, $isStartIf); - } - - private function processNullSafeOperatorNotIdentical(If_ $if, ?Expr $expr = null): ?Node - { - $assign = $this->ifManipulator->matchIfNotNullNextAssignment($if); - if (! $assign instanceof Assign) { - return null; - } - - $assignExpr = $assign->expr; - if ($this->ifManipulator->isIfCondUsingAssignNotIdenticalVariable($if, $assignExpr)) { - return null; - } - - $expression = $assign->getAttribute(AttributeKey::PARENT_NODE); - if (! $expression instanceof Expression) { - return null; - } - - $nextNode = $expression->getAttribute(AttributeKey::NEXT_NODE); - $nullSafe = $this->nullsafeManipulator->processNullSafeExpr($assignExpr); - if ($nullSafe === null) { - return null; - } - - if ($expr !== null) { - /** @var Identifier $nullSafeIdentifier */ - $nullSafeIdentifier = $nullSafe->name; - /** @var NullsafeMethodCall|NullsafePropertyFetch $nullSafe */ - $nullSafe = $this->nullsafeManipulator->processNullSafeExprResult($expr, $nullSafeIdentifier); - } - - $nextOfNextNode = $this->processIfMayInNextNode($nextNode); - if ($nextOfNextNode !== null) { - return $nextOfNextNode; - } - - if (! $nextNode instanceof If_) { - $nullSafe = $this->verifyDefaultValueInElse($if, $nullSafe, $assign); - if ($nullSafe === null) { - return null; - } - - return new Assign($assign->var, $nullSafe); - } - - return $this->processNullSafeOperatorNotIdentical($nextNode, $nullSafe); - } - - private function verifyDefaultValueInElse( - If_ $if, - NullsafeMethodCall|NullsafePropertyFetch $nullSafe, - Assign $assign - ): NullsafeMethodCall|NullsafePropertyFetch|Coalesce|null { - if (! $if->else instanceof Else_) { - return $nullSafe; - } - - if (count($if->else->stmts) !== 1) { - return null; - } - - $expression = $if->else->stmts[0]; - if (! $expression instanceof Expression) { - return null; - } - - $expressionAssign = $expression->expr; - if (! $expressionAssign instanceof Assign) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($expressionAssign->var, $assign->var)) { - return null; - } - - if ($this->valueResolver->isNull($expressionAssign->expr)) { - return $nullSafe; - } - - return new Coalesce($nullSafe, $expressionAssign->expr); - } - - private function processAssign(Assign $assign, Expression $prevExpression, Node $nextNode, bool $isStartIf): ?Node - { - if ($this->shouldProcessAssignInCurrentNode($assign, $nextNode)) { - return $this->processAssignInCurrentNode($assign, $prevExpression, $nextNode, $isStartIf); - } - return $this->processAssignMayInNextNode($nextNode); - } - - private function shouldProcessAssignInCurrentNode(Assign $assign, Node $nextNode): bool - { - if (! property_exists($assign->expr, self::NAME)) { - return false; - } - if (! property_exists($nextNode, 'expr')) { - return false; - } - if (! property_exists($nextNode->expr, self::NAME)) { - return false; - } - return ! $this->valueResolver->isNull($nextNode->expr); - } - - private function processIfMayInNextNode(?Node $nextNode = null): ?Node - { - if (! $nextNode instanceof Node) { - return null; - } - - $nextOfNextNode = $nextNode->getAttribute(AttributeKey::NEXT_NODE); - while ($nextOfNextNode) { - if ($nextOfNextNode instanceof If_) { - /** @var If_ $beforeIf */ - $beforeIf = $nextOfNextNode->getAttribute(AttributeKey::PARENT_NODE); - $nullSafe = $this->processNullSafeOperatorNotIdentical($nextOfNextNode); - if (! $nullSafe instanceof NullsafeMethodCall && ! $nullSafe instanceof PropertyFetch) { - return $beforeIf; - } - - $beforeIf->stmts[count($beforeIf->stmts) - 1] = new Expression($nullSafe); - return $beforeIf; - } - - $nextOfNextNode = $nextOfNextNode->getAttribute(AttributeKey::NEXT_NODE); - } - - return null; - } - - private function processAssignInCurrentNode( - Assign $assign, - Expression $expression, - Node $nextNode, - bool $isStartIf - ): ?Node { - $assignNullSafe = $isStartIf - ? $assign->expr - : $this->nullsafeManipulator->processNullSafeExpr($assign->expr); - $nullSafe = $this->nullsafeManipulator->processNullSafeExprResult($assignNullSafe, $nextNode->expr->name); - - $prevAssign = $expression->getAttribute(AttributeKey::PREVIOUS_NODE); - if ($prevAssign instanceof If_) { - $nullSafe = $this->getNullSafeOnPrevAssignIsIf($prevAssign, $nextNode, $nullSafe); - } - - $this->removeNode($nextNode); - - if ($nextNode instanceof Return_) { - $nextNode->expr = $nullSafe; - return $nextNode; - } - - return $nullSafe; - } - - private function processAssignMayInNextNode(Node $nextNode): ?Node - { - if (! $nextNode instanceof Expression) { - return null; - } - if (! $nextNode->expr instanceof Assign) { - return null; - } - $mayNextIf = $nextNode->getAttribute(AttributeKey::NEXT_NODE); - if (! $mayNextIf instanceof If_) { - return null; - } - - if ($this->ifManipulator->isIfCondUsingAssignIdenticalVariable($mayNextIf, $nextNode->expr)) { - return $this->processNullSafeOperatorIdentical($mayNextIf, false); - } - - return null; - } - - private function getNullSafeOnPrevAssignIsIf(If_ $if, Node $nextNode, ?Expr $expr): ?Expr - { - $prevIf = $if->getAttribute(AttributeKey::PREVIOUS_NODE); - if (! $prevIf instanceof Expression) { - return $expr; - } - - if (! $this->ifManipulator->isIfCondUsingAssignIdenticalVariable($if, $prevIf->expr)) { - return $expr; - } - - $start = $prevIf; - - while ($prevIf instanceof Expression) { - $expressionNode = $prevIf->expr; - if (! $expressionNode instanceof Assign) { - return null; - } - - $expr = $this->nullsafeManipulator->processNullSafeExpr($expressionNode->expr); - - /** @var Node $prevPrevIf */ - $prevPrevIf = $prevIf->getAttribute(AttributeKey::PREVIOUS_NODE); - /** @var Node $prevPrevPrevIf */ - $prevPrevPrevIf = $prevPrevIf->getAttribute(AttributeKey::PREVIOUS_NODE); - - if (! $prevPrevPrevIf instanceof Expression && $prevPrevPrevIf !== null) { - $start = $this->getPreviousIf($prevPrevPrevIf); - break; - } - - $prevIf = $prevPrevPrevIf; - } - - if (! $expr instanceof NullsafeMethodCall && ! $expr instanceof NullsafePropertyFetch) { - return $expr; - } - - /** @var Expr $expr */ - $expr = $expr->var->getAttribute(AttributeKey::PARENT_NODE); - $expr = $this->getNullSafeAfterStartUntilBeforeEnd($start, $expr); - - return $this->nullsafeManipulator->processNullSafeExprResult($expr, $nextNode->expr->name); - } - - private function getPreviousIf(Node $node): ?Node - { - /** @var If_ $if */ - $if = $node->getAttribute(AttributeKey::NEXT_NODE); - - /** @var Expression $expression */ - $expression = $if->getAttribute(AttributeKey::NEXT_NODE); - - /** @var Expression $nextExpression */ - $nextExpression = $expression->getAttribute(AttributeKey::NEXT_NODE); - - return $nextExpression->getAttribute(AttributeKey::NEXT_NODE); - } - - private function getNullSafeAfterStartUntilBeforeEnd(?Node $node, ?Expr $expr): ?Expr - { - while ($node) { - $expr = $this->nullsafeManipulator->processNullSafeExprResult($expr, $node->expr->expr->name); - - $node = $node->getAttribute(AttributeKey::NEXT_NODE); - while ($node) { - /** @var If_ $if */ - $if = $node->getAttribute(AttributeKey::NEXT_NODE); - if ($node instanceof Expression && $this->ifManipulator->isIfCondUsingAssignIdenticalVariable( - $if, - $node->expr - )) { - break; - } - - $node = $node->getAttribute(AttributeKey::NEXT_NODE); - } - } - - return $expr; - } - - private function handleTernaryNode(Ternary $ternary): ?Node - { - if ($this->shouldSkipTernary($ternary)) { - return null; - } - - $nullSafeElse = $this->nullsafeManipulator->processNullSafeExpr($ternary->else); - - if ($nullSafeElse !== null) { - return $nullSafeElse; - } - - if ($ternary->if === null) { - return null; - } - - return $this->nullsafeManipulator->processNullSafeExpr($ternary->if); - } - - private function shouldSkipTernary(Ternary $ternary): bool - { - if (! $this->canTernaryReturnNull($ternary)) { - return true; - } - if ($ternary->cond instanceof Identical) { - return ! $this->hasNullComparison($ternary->cond); - } - - if ($ternary->cond instanceof NotIdentical) { - return ! $this->hasNullComparison($ternary->cond); - } - - return true; - } - - private function hasNullComparison(NotIdentical|Identical $check): bool - { - if ($this->valueResolver->isNull($check->left)) { - return true; - } - - return $this->valueResolver->isNull($check->right); - } - - private function canTernaryReturnNull(Ternary $ternary): bool - { - if ($this->valueResolver->isNull($ternary->else)) { - return true; - } - - if ($ternary->if === null) { - // $foo === null ?: 'xx' returns true if $foo is null - // therefore it does not return null in case of the elvis operator - return false; - } - - return $this->valueResolver->isNull($ternary->if); - } -} diff --git a/rules/Php80/Rector/NotIdentical/MbStrContainsRector.php b/rules/Php80/Rector/NotIdentical/MbStrContainsRector.php new file mode 100644 index 00000000000..31084285b5d --- /dev/null +++ b/rules/Php80/Rector/NotIdentical/MbStrContainsRector.php @@ -0,0 +1,135 @@ +> + */ + public function getNodeTypes(): array + { + return [Identical::class, NotIdentical::class, Equal::class, NotEqual::class]; + } + + /** + * @param Identical|NotIdentical|Equal|NotEqual $node + */ + public function refactor(Node $node): ?Node + { + $funcCall = $this->strFalseComparisonResolver->resolve($node, self::OLD_STR_NAMES); + + if (! $funcCall instanceof FuncCall) { + return null; + } + + if ($funcCall->isFirstClassCallable()) { + return null; + } + + if (isset($funcCall->getArgs()[2])) { + $secondArg = $funcCall->getArgs()[2]; + + if ($this->isName($funcCall->name, 'mb_strpos') && ! $this->isIntegerZero($secondArg->value)) { + $funcCall->args[0] = new Arg($this->nodeFactory->createFuncCall( + 'mb_substr', + [$funcCall->args[0], $secondArg] + )); + } + + unset($funcCall->args[2]); + } + + $funcCall->name = new Name('str_contains'); + + if ($node instanceof Identical || $node instanceof Equal) { + return new BooleanNot($funcCall); + } + + return $funcCall; + } + + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_80; + } + + private function isIntegerZero(Expr $expr): bool + { + if (! $expr instanceof Int_) { + return false; + } + + return $expr->value === 0; + } +} diff --git a/rules/Php80/Rector/NotIdentical/StrContainsRector.php b/rules/Php80/Rector/NotIdentical/StrContainsRector.php index 10b4948bc3e..11d2bb7b708 100644 --- a/rules/Php80/Rector/NotIdentical/StrContainsRector.php +++ b/rules/Php80/Rector/NotIdentical/StrContainsRector.php @@ -5,26 +5,44 @@ namespace Rector\Php80\Rector\NotIdentical; use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\NotEqual; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Scalar\Int_; +use Rector\Php80\NodeResolver\StrFalseComparisonResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\ValueObject\PolyfillPackage; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; +use Rector\VersionBonding\Contract\RelatedPolyfillInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://externals.io/message/108562 https://github.com/php/php-src/pull/5179 - * * @see \Rector\Tests\Php80\Rector\NotIdentical\StrContainsRector\StrContainsRectorTest */ -final class StrContainsRector extends AbstractRector +final class StrContainsRector extends AbstractRector implements MinPhpVersionInterface, RelatedPolyfillInterface { /** * @var string[] */ - private const OLD_STR_NAMES = ['strpos', 'strstr']; + private const array OLD_STR_NAMES = ['strpos', 'strstr']; + + public function __construct( + private readonly StrFalseComparisonResolver $strFalseComparisonResolver + ) { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::STR_CONTAINS; + } public function getRuleDefinition(): RuleDefinition { @@ -41,7 +59,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -61,59 +79,57 @@ public function run() */ public function getNodeTypes(): array { - return [Identical::class, NotIdentical::class]; + return [Identical::class, NotIdentical::class, Equal::class, NotEqual::class]; } /** - * @param Identical|NotIdentical $node + * @param Identical|NotIdentical|Equal|NotEqual $node */ public function refactor(Node $node): ?Node { - $funcCall = $this->matchIdenticalOrNotIdenticalToFalse($node); + $funcCall = $this->strFalseComparisonResolver->resolve($node, self::OLD_STR_NAMES); if (! $funcCall instanceof FuncCall) { return null; } - $funcCall->name = new Name('str_contains'); - - if ($node instanceof Identical) { - return new BooleanNot($funcCall); + if ($funcCall->isFirstClassCallable()) { + return null; } - return $funcCall; - } + if (isset($funcCall->getArgs()[2])) { + $secondArg = $funcCall->getArgs()[2]; - private function matchIdenticalOrNotIdenticalToFalse(Identical | NotIdentical $expr): ?FuncCall - { - if ($this->valueResolver->isFalse($expr->left)) { - if (! $expr->right instanceof FuncCall) { - return null; + if ($this->isName($funcCall->name, 'strpos') && ! $this->isIntegerZero($secondArg->value)) { + $funcCall->args[0] = new Arg($this->nodeFactory->createFuncCall( + 'substr', + [$funcCall->args[0], $secondArg] + )); } - if (! $this->isNames($expr->right, self::OLD_STR_NAMES)) { - return null; - } + unset($funcCall->args[2]); + } + + $funcCall->name = new Name('str_contains'); - /** @var FuncCall $funcCall */ - $funcCall = $expr->right; - return $funcCall; + if ($node instanceof Identical || $node instanceof Equal) { + return new BooleanNot($funcCall); } - if ($this->valueResolver->isFalse($expr->right)) { - if (! $expr->left instanceof FuncCall) { - return null; - } + return $funcCall; + } - if (! $this->isNames($expr->left, self::OLD_STR_NAMES)) { - return null; - } + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_80; + } - /** @var FuncCall $funcCall */ - $funcCall = $expr->left; - return $funcCall; + private function isIntegerZero(Expr $expr): bool + { + if (! $expr instanceof Int_) { + return false; } - return null; + return $expr->value === 0; } } diff --git a/rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php b/rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php new file mode 100644 index 00000000000..ab105675ea6 --- /dev/null +++ b/rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php @@ -0,0 +1,226 @@ +> + */ + public function getNodeTypes(): array + { + return [Property::class, Class_::class, Param::class]; + } + + /** + * @param Property|Class_|Param $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $phpDocInfo instanceof PhpDocInfo) { + return null; + } + + $uses = $this->useImportsResolver->resolveBareUses(); + + $attributeGroups = $this->transformDoctrineAnnotationClassesToAttributeGroups($phpDocInfo, $uses); + if ($attributeGroups === []) { + return null; + } + + // 3. Reprint docblock + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + $node->attrGroups = array_merge($node->attrGroups, $attributeGroups); + $this->completeExtraUseImports($attributeGroups); + + return $node; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsInstanceOf($configuration, NestedAnnotationToAttribute::class); + $this->nestedAnnotationsToAttributes = $configuration; + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_80; + } + + /** + * @param Use_[] $uses + * @return AttributeGroup[] + */ + private function transformDoctrineAnnotationClassesToAttributeGroups(PhpDocInfo $phpDocInfo, array $uses): array + { + if ($phpDocInfo->getPhpDocNode()->children === []) { + return []; + } + + $nestedDoctrineTagAndAnnotationToAttributes = []; + + foreach ($phpDocInfo->getPhpDocNode()->children as $phpDocChildNode) { + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if (! $phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) { + continue; + } + + $doctrineTagValueNode = $phpDocChildNode->value; + $nestedAnnotationToAttribute = $this->matchAnnotationToAttribute($doctrineTagValueNode); + if (! $nestedAnnotationToAttribute instanceof NestedAnnotationToAttribute) { + continue; + } + + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineTagValueNode); + + $nestedDoctrineTagAndAnnotationToAttributes[] = new NestedDoctrineTagAndAnnotationToAttribute( + $doctrineTagValueNode, + $nestedAnnotationToAttribute, + ); + } + + return $this->nestedAttrGroupsFactory->create($nestedDoctrineTagAndAnnotationToAttributes, $uses); + } + + private function matchAnnotationToAttribute( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode + ): NestedAnnotationToAttribute|null { + $doctrineResolvedClass = $doctrineAnnotationTagValueNode->identifierTypeNode->getAttribute( + PhpDocAttributeKey::RESOLVED_CLASS + ); + + foreach ($this->nestedAnnotationsToAttributes as $nestedAnnotationToAttribute) { + foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $annotationClass) { + if ($annotationClass->getAttributeClass() === $doctrineResolvedClass) { + return $nestedAnnotationToAttribute; + } + } + + if ($doctrineResolvedClass !== $nestedAnnotationToAttribute->getTag()) { + continue; + } + + return $nestedAnnotationToAttribute; + } + + return null; + } + + /** + * @param AttributeGroup[] $attributeGroups + */ + private function completeExtraUseImports(array $attributeGroups): void + { + foreach ($attributeGroups as $attributeGroup) { + foreach ($attributeGroup->attrs as $attr) { + $namespacedAttrName = $attr->name->getAttribute(AttributeKey::EXTRA_USE_IMPORT); + if (! is_string($namespacedAttrName)) { + continue; + } + + $this->useNodesToAddCollector->addUseImport(new FullyQualifiedObjectType($namespacedAttrName)); + } + } + } +} diff --git a/rules/Php80/Rector/Switch_/ChangeSwitchToMatchRector.php b/rules/Php80/Rector/Switch_/ChangeSwitchToMatchRector.php index 2ac2f8b1385..56d5e8283ad 100644 --- a/rules/Php80/Rector/Switch_/ChangeSwitchToMatchRector.php +++ b/rules/Php80/Rector/Switch_/ChangeSwitchToMatchRector.php @@ -7,40 +7,46 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\Cast; +use PhpParser\Node\Expr\Cast\Int_; +use PhpParser\Node\Expr\Cast\String_; use PhpParser\Node\Expr\Match_; -use PhpParser\Node\Expr\Throw_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\Stmt\Switch_; -use PhpParser\Node\Stmt\Throw_ as ThrowsStmt; -use Rector\Core\Rector\AbstractRector; +use PHPStan\Type\ObjectType; +use Rector\NodeAnalyzer\ExprAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\Php80\Enum\MatchKind; use Rector\Php80\NodeAnalyzer\MatchSwitchAnalyzer; use Rector\Php80\NodeFactory\MatchFactory; use Rector\Php80\NodeResolver\SwitchExprsResolver; use Rector\Php80\ValueObject\CondAndExpr; +use Rector\Php80\ValueObject\MatchResult; +use Rector\PhpParser\Enum\NodeGroup; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/match_expression_v2 - * @see https://3v4l.org/572T5 - * * @see \Rector\Tests\Php80\Rector\Switch_\ChangeSwitchToMatchRector\ChangeSwitchToMatchRectorTest */ -final class ChangeSwitchToMatchRector extends AbstractRector +final class ChangeSwitchToMatchRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private SwitchExprsResolver $switchExprsResolver, - private MatchSwitchAnalyzer $matchSwitchAnalyzer, - private MatchFactory $matchFactory, + private readonly SwitchExprsResolver $switchExprsResolver, + private readonly MatchSwitchAnalyzer $matchSwitchAnalyzer, + private readonly MatchFactory $matchFactory, + private readonly ValueResolver $valueResolver, + private readonly ExprAnalyzer $exprAnalyzer ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change switch() to match()', [ + return new RuleDefinition('Change `switch()` to `match()`', [ new CodeSample( <<<'CODE_SAMPLE' switch ($input) { @@ -71,155 +77,191 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Switch_::class]; + return NodeGroup::STMTS_AWARE; } /** - * @param Switch_ $node + * @param StmtsAware $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): null|Node { - $condAndExprs = $this->switchExprsResolver->resolve($node); - if ($this->matchSwitchAnalyzer->shouldSkipSwitch($node, $condAndExprs)) { + if (! is_array($node->stmts)) { return null; } - if (! $this->matchSwitchAnalyzer->haveCondAndExprsMatchPotential($condAndExprs)) { - return null; - } + $hasChanged = false; - $isReturn = false; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Switch_) { + continue; + } - foreach ($condAndExprs as $condAndExpr) { - if ($condAndExpr->equalsMatchKind(MatchKind::RETURN())) { - $isReturn = true; - break; + // possible reference override, where match + if ($stmt->getAttribute(AttributeKey::IS_INSIDE_BYREF_FUNCTION_LIKE)) { + continue; } - $expr = $condAndExpr->getExpr(); - if ($expr instanceof Throw_) { + $nextStmt = $node->stmts[$key + 1] ?? null; + + $condAndExprs = $this->switchExprsResolver->resolve($stmt); + if ($this->matchSwitchAnalyzer->shouldSkipSwitch($stmt, $condAndExprs, $nextStmt)) { continue; } - if (! $expr instanceof Assign) { - return null; + if (! $this->matchSwitchAnalyzer->haveCondAndExprsMatchPotential($condAndExprs)) { + continue; } - } - $match = $this->matchFactory->createFromCondAndExprs($node->cond, $condAndExprs); + $isReturn = $this->matchSwitchAnalyzer->isReturnCondsAndExprs($condAndExprs); - // implicit return default after switch - $match = $this->processImplicitReturnAfterSwitch($node, $match, $condAndExprs); + if ($this->nodeTypeResolver->getType($stmt->cond) instanceof ObjectType) { + continue; + } - $match = $this->processImplicitThrowsAfterSwitch($node, $match, $condAndExprs); + $matchResult = $this->matchFactory->createFromCondAndExprs($stmt->cond, $condAndExprs, $nextStmt); + if (! $matchResult instanceof MatchResult) { + continue; + } - if ($isReturn) { - return new Return_($match); - } + $match = $matchResult->getMatch(); + if ($matchResult->shouldRemoveNextStmt() && $isReturn) { + unset($node->stmts[$key + 1]); + } - $assignExpr = $this->resolveAssignExpr($condAndExprs); - if ($assignExpr instanceof Expr) { - return $this->changeToAssign($node, $match, $assignExpr); - } + $assignVar = $this->resolveAssignVar($condAndExprs); + $hasDefaultValue = $this->matchSwitchAnalyzer->hasDefaultValue($match); - return $match; - } + $this->castMatchCond($match); + $this->mirrorDynamicBoolExpr($match); - private function changeToAssign(Switch_ $switch, Match_ $match, Expr $assignExpr): Assign - { - $prevInitializedAssign = $this->betterNodeFinder->findFirstPreviousOfNode( - $switch, - fn (Node $node): bool => $node instanceof Assign && $this->nodeComparator->areNodesEqual( - $node->var, - $assignExpr - ) - ); - - $assign = new Assign($assignExpr, $match); - if (! $prevInitializedAssign instanceof Assign) { - return $assign; - } + if ($assignVar instanceof Expr) { + if (! $hasDefaultValue) { + continue; + } + + $assign = new Assign($assignVar, $match); + $node->stmts[$key] = new Expression($assign); + $this->mirrorComments($node->stmts[$key], $stmt); - if ($this->matchSwitchAnalyzer->hasDefaultValue($match)) { - $default = $match->arms[count($match->arms) - 1]->body; - if ($this->nodeComparator->areNodesEqual($default, $prevInitializedAssign->var)) { - return $assign; + $hasChanged = true; + + continue; } + + if (! $hasDefaultValue) { + continue; + } + + foreach ($match->arms as $arm) { + if ($arm->conds === null) { + continue; + } + + $arm->conds = array_values($arm->conds); + } + + $node->stmts[$key] = $isReturn ? new Return_($match) : new Expression($match); + $this->mirrorComments($node->stmts[$key], $stmt); + $hasChanged = true; } - $parentAssign = $prevInitializedAssign->getAttribute(AttributeKey::PARENT_NODE); - if ($parentAssign instanceof Expression) { - $this->removeNode($parentAssign); + if ($hasChanged) { + return $node; } - return $assign; + return null; } - /** - * @param CondAndExpr[] $condAndExprs - */ - private function resolveAssignExpr(array $condAndExprs): ?Expr + public function provideMinPhpVersion(): int { - foreach ($condAndExprs as $condAndExpr) { - $expr = $condAndExpr->getExpr(); - if (! $expr instanceof Assign) { + return PhpVersionFeature::MATCH_EXPRESSION; + } + + private function castMatchCond(Match_ $match): void + { + $type = $this->nodeTypeResolver->getNativeType($match->cond); + $isNativeCondString = $type->isString() + ->yes(); + $isNativeCondInt = $type->isInteger() + ->yes(); + + if (! $isNativeCondString && ! $isNativeCondInt) { + return; + } + + $armCondType = []; + $newMatchCond = null; + + foreach ($match->arms as $arm) { + if ($arm->conds === null) { continue; } - return $expr->var; + foreach ($arm->conds as $armCond) { + $armCondType = $this->nodeTypeResolver->getNativeType($armCond); + if ($armCondType->isInteger()->yes() && $isNativeCondString) { + $newMatchCond = new Int_($match->cond); + } elseif ($armCondType->isString()->yes() && $isNativeCondInt) { + $newMatchCond = new String_($match->cond); + } else { + $newMatchCond = null; + break; + } + } } - return null; + if ($newMatchCond instanceof Cast) { + $match->cond = $newMatchCond; + } } - /** - * @param CondAndExpr[] $condAndExprs - */ - private function processImplicitReturnAfterSwitch(Switch_ $switch, Match_ $match, array $condAndExprs): Match_ + private function mirrorDynamicBoolExpr(Match_ $match): void { - $nextNode = $switch->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof Return_) { - return $match; + // switch(true) already just use + // switch(false) is dead code that can be on purpose + if ($this->valueResolver->isTrueOrFalse($match->cond)) { + return; } - $returnedExpr = $nextNode->expr; - if (! $returnedExpr instanceof Expr) { - return $match; - } + $isChanged = false; + foreach ($match->arms as $arm) { + if ($arm->conds === null) { + continue; + } - if ($this->matchSwitchAnalyzer->hasDefaultValue($match)) { - return $match; + foreach ($arm->conds as $cond) { + if ($this->exprAnalyzer->isBoolExpr($cond) || $this->exprAnalyzer->isCallLikeReturnNativeBool($cond)) { + // dont' stop lookup for dynamic conditions + // continue verify other condition, in case of mixed condition + $isChanged = true; + continue; + } + + // return early here, as condition is mixed + // we need another real use case for mixed conditions of dynamic + non-dynamic case expr + return; + } } - $assignExpr = $this->resolveAssignExpr($condAndExprs); - - if (! $assignExpr instanceof Expr) { - $this->removeNode($nextNode); + if ($isChanged) { + $match->cond = $this->nodeFactory->createTrue(); } - - $condAndExprs[] = new CondAndExpr([], $returnedExpr, MatchKind::RETURN()); - return $this->matchFactory->createFromCondAndExprs($switch->cond, $condAndExprs); } /** * @param CondAndExpr[] $condAndExprs */ - private function processImplicitThrowsAfterSwitch(Switch_ $switch, Match_ $match, array $condAndExprs): Match_ + private function resolveAssignVar(array $condAndExprs): ?Expr { - $nextNode = $switch->getAttribute(AttributeKey::NEXT_NODE); - if (! $nextNode instanceof ThrowsStmt) { - return $match; - } + foreach ($condAndExprs as $condAndExpr) { + $expr = $condAndExpr->getExpr(); + if (! $expr instanceof Assign) { + continue; + } - if ($this->matchSwitchAnalyzer->hasDefaultValue($match)) { - return $match; + return $expr->var; } - $this->removeNode($nextNode); - - $throw = new Throw_($nextNode->expr); - - $condAndExprs[] = new CondAndExpr([], $throw, MatchKind::RETURN()); - return $this->matchFactory->createFromCondAndExprs($switch->cond, $condAndExprs); + return null; } } diff --git a/rules/Php80/Rector/Ternary/GetDebugTypeRector.php b/rules/Php80/Rector/Ternary/GetDebugTypeRector.php index 15f02122183..fbc98381295 100644 --- a/rules/Php80/Rector/Ternary/GetDebugTypeRector.php +++ b/rules/Php80/Rector/Ternary/GetDebugTypeRector.php @@ -5,19 +5,29 @@ namespace Rector\Php80\Rector\Ternary; use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Ternary; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Identifier; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\ValueObject\PolyfillPackage; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; +use Rector\VersionBonding\Contract\RelatedPolyfillInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/get_debug_type - * * @see \Rector\Tests\Php80\Rector\Ternary\GetDebugTypeRector\GetDebugTypeRectorTest */ -final class GetDebugTypeRector extends AbstractRector +final class GetDebugTypeRector extends AbstractRector implements MinPhpVersionInterface, RelatedPolyfillInterface { + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::GET_DEBUG_TYPE; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -33,7 +43,7 @@ public function run($value) } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -69,28 +79,49 @@ public function refactor(Node $node): ?Node return null; } - /** @var FuncCall $funcCall */ - $funcCall = $node->if; - $firstExpr = $funcCall->args[0]->value; + /** @var FuncCall|ClassConstFetch $getClassFuncCallOrClassConstFetchClass */ + $getClassFuncCallOrClassConstFetchClass = $node->if; + + $firstExpr = $getClassFuncCallOrClassConstFetchClass instanceof FuncCall + ? $getClassFuncCallOrClassConstFetchClass->getArgs()[0] + ->value + : $getClassFuncCallOrClassConstFetchClass->class; return $this->nodeFactory->createFuncCall('get_debug_type', [$firstExpr]); } + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_80; + } + private function shouldSkip(Ternary $ternary): bool { if (! $ternary->cond instanceof FuncCall) { return true; } - if (! $this->nodeNameResolver->isName($ternary->cond, 'is_object')) { + if ($ternary->cond->isFirstClassCallable()) { return true; } - if (! $ternary->if instanceof FuncCall) { + if (! isset($ternary->cond->getArgs()[0])) { + return true; + } + + if (! $this->isName($ternary->cond, 'is_object')) { return true; } - if (! $this->nodeNameResolver->isName($ternary->if, 'get_class')) { + if (! $ternary->if instanceof FuncCall) { + if (! $ternary->if instanceof ClassConstFetch) { + return true; + } + + return $this->shouldSkipClassConstFetch($ternary->if); + } + + if (! $this->isName($ternary->if, 'get_class')) { return true; } @@ -98,21 +129,56 @@ private function shouldSkip(Ternary $ternary): bool return true; } - return ! $this->nodeNameResolver->isName($ternary->else, 'gettype'); + if ($ternary->else->isFirstClassCallable()) { + return true; + } + + return ! $this->isName($ternary->else, 'gettype'); + } + + private function shouldSkipClassConstFetch(ClassConstFetch $classConstFetch): bool + { + if (! $classConstFetch->name instanceof Identifier) { + return true; + } + + return $classConstFetch->name->toString() !== 'class'; } private function areValuesIdentical(Ternary $ternary): bool { /** @var FuncCall $isObjectFuncCall */ $isObjectFuncCall = $ternary->cond; - $firstExpr = $isObjectFuncCall->args[0]->value; - /** @var FuncCall $getClassFuncCall */ - $getClassFuncCall = $ternary->if; - $secondExpr = $getClassFuncCall->args[0]->value; + if ($isObjectFuncCall->isFirstClassCallable()) { + return false; + } + + $firstExpr = $isObjectFuncCall->getArgs()[0] + ->value; + + /** @var FuncCall|ClassConstFetch $getClassFuncCallOrClassConstFetchClass */ + $getClassFuncCallOrClassConstFetchClass = $ternary->if; + + if ($getClassFuncCallOrClassConstFetchClass instanceof FuncCall && ! $getClassFuncCallOrClassConstFetchClass->args[0] instanceof Arg) { + return false; + } + + $secondExpr = $getClassFuncCallOrClassConstFetchClass instanceof FuncCall + ? $getClassFuncCallOrClassConstFetchClass->getArgs()[0] + ->value + : $getClassFuncCallOrClassConstFetchClass->class; - /** @var FuncCall $gettypeFuncCall */ $gettypeFuncCall = $ternary->else; + + if (! $gettypeFuncCall instanceof FuncCall) { + return false; + } + + if (! $gettypeFuncCall->args[0] instanceof Arg) { + return false; + } + $thirdExpr = $gettypeFuncCall->args[0]->value; if (! $this->nodeComparator->areNodesEqual($firstExpr, $secondExpr)) { diff --git a/rules/Php80/ValueObject/AnnotationPropertyToAttributeClass.php b/rules/Php80/ValueObject/AnnotationPropertyToAttributeClass.php new file mode 100644 index 00000000000..12b21b967fd --- /dev/null +++ b/rules/Php80/ValueObject/AnnotationPropertyToAttributeClass.php @@ -0,0 +1,33 @@ +annotationProperty; + } + + public function getAttributeClass(): string + { + return $this->attributeClass; + } + + public function doesNeedNewImport(): bool + { + return $this->doesNeedNewImport; + } +} diff --git a/rules/Php80/ValueObject/AnnotationToAttribute.php b/rules/Php80/ValueObject/AnnotationToAttribute.php index 30909d926e6..785fcfe80d8 100644 --- a/rules/Php80/ValueObject/AnnotationToAttribute.php +++ b/rules/Php80/ValueObject/AnnotationToAttribute.php @@ -4,29 +4,35 @@ namespace Rector\Php80\ValueObject; -final class AnnotationToAttribute +use Rector\Php80\Contract\ValueObject\AnnotationToAttributeInterface; +use Rector\Validation\RectorAssert; +use Webmozart\Assert\Assert; + +final readonly class AnnotationToAttribute implements AnnotationToAttributeInterface { /** - * @param class-string|string $tag - * @param class-string $attributeClass + * @param string[] $classReferenceFields */ public function __construct( private string $tag, - private ?string $attributeClass = null + private ?string $attributeClass = null, + private array $classReferenceFields = [], + private bool $useValueAsAttributeArgument = false, ) { + RectorAssert::className($tag); + + if (is_string($attributeClass)) { + RectorAssert::className($attributeClass); + } + + Assert::allString($classReferenceFields); } - /** - * @return class-string|string - */ public function getTag(): string { return $this->tag; } - /** - * @return class-string - */ public function getAttributeClass(): string { if ($this->attributeClass === null) { @@ -35,4 +41,17 @@ public function getAttributeClass(): string return $this->attributeClass; } + + /** + * @return string[] + */ + public function getClassReferenceFields(): array + { + return $this->classReferenceFields; + } + + public function getUseValueAsAttributeArgument(): bool + { + return $this->useValueAsAttributeArgument; + } } diff --git a/rules/Php80/ValueObject/ArrayDimFetchAndConstFetch.php b/rules/Php80/ValueObject/ArrayDimFetchAndConstFetch.php deleted file mode 100644 index a2f4769b76c..00000000000 --- a/rules/Php80/ValueObject/ArrayDimFetchAndConstFetch.php +++ /dev/null @@ -1,27 +0,0 @@ -arrayDimFetch; - } - - public function getConstFetch(): ConstFetch - { - return $this->constFetch; - } -} diff --git a/rules/Php80/ValueObject/AttributeValueAndDocComment.php b/rules/Php80/ValueObject/AttributeValueAndDocComment.php new file mode 100644 index 00000000000..90653b8f5ce --- /dev/null +++ b/rules/Php80/ValueObject/AttributeValueAndDocComment.php @@ -0,0 +1,14 @@ +condExprs; + // internally checked by PHPStan, cannot be empty array + if ($this->condExprs === []) { + return null; + } + + if ($this->condExprs === null) { + return null; + } + + return array_values($this->condExprs); } - public function getMatchKind(): MatchKind + /** + * @return MatchKind::* + */ + public function getMatchKind(): string { return $this->matchKind; } - public function equalsMatchKind(MatchKind $matchKind): bool + /** + * @param MatchKind::* $matchKind + */ + public function equalsMatchKind(string $matchKind): bool + { + return $this->matchKind === $matchKind; + } + + /** + * @return Comment[] + */ + public function getComments(): array { - return $this->matchKind->equals($matchKind); + return $this->comments; } } diff --git a/rules/Php80/ValueObject/DoctrineTagAndAnnotationToAttribute.php b/rules/Php80/ValueObject/DoctrineTagAndAnnotationToAttribute.php index 56e7a201e14..382c60b6e4d 100644 --- a/rules/Php80/ValueObject/DoctrineTagAndAnnotationToAttribute.php +++ b/rules/Php80/ValueObject/DoctrineTagAndAnnotationToAttribute.php @@ -6,11 +6,11 @@ use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; -final class DoctrineTagAndAnnotationToAttribute +final readonly class DoctrineTagAndAnnotationToAttribute { public function __construct( private DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, - private AnnotationToAttribute $annotationToAttribute + private AnnotationToAttribute $annotationToAttribute, ) { } diff --git a/rules/Php80/ValueObject/MatchResult.php b/rules/Php80/ValueObject/MatchResult.php new file mode 100644 index 00000000000..66baa4451ab --- /dev/null +++ b/rules/Php80/ValueObject/MatchResult.php @@ -0,0 +1,26 @@ +match; + } + + public function shouldRemoveNextStmt(): bool + { + return $this->shouldRemoveNextStmt; + } +} diff --git a/rules/Php80/ValueObject/NestedAnnotationToAttribute.php b/rules/Php80/ValueObject/NestedAnnotationToAttribute.php new file mode 100644 index 00000000000..1b2be75a3d8 --- /dev/null +++ b/rules/Php80/ValueObject/NestedAnnotationToAttribute.php @@ -0,0 +1,73 @@ +|string[]|AnnotationPropertyToAttributeClass[] $annotationPropertiesToAttributeClasses + */ + public function __construct( + private readonly string $tag, + array $annotationPropertiesToAttributeClasses, + private readonly bool $removeOriginal = false, + ) { + RectorAssert::className($tag); + + // back compatibility for raw scalar values + foreach ($annotationPropertiesToAttributeClasses as $annotationProperty => $attributeClass) { + if ($attributeClass instanceof AnnotationPropertyToAttributeClass) { + $this->annotationPropertiesToAttributeClasses[] = $attributeClass; + } else { + $this->annotationPropertiesToAttributeClasses[] = new AnnotationPropertyToAttributeClass( + $attributeClass, + $annotationProperty, + ); + } + } + } + + public function getTag(): string + { + return $this->tag; + } + + /** + * @return AnnotationPropertyToAttributeClass[] + */ + public function getAnnotationPropertiesToAttributeClasses(): array + { + return $this->annotationPropertiesToAttributeClasses; + } + + public function getAttributeClass(): string + { + return $this->tag; + } + + public function shouldRemoveOriginal(): bool + { + return $this->removeOriginal; + } + + public function hasExplicitParameters(): bool + { + foreach ($this->annotationPropertiesToAttributeClasses as $annotationPropertyToAttributeClass) { + if (is_string($annotationPropertyToAttributeClass->getAnnotationProperty())) { + return true; + } + } + + return false; + } +} diff --git a/rules/Php80/ValueObject/NestedDoctrineTagAndAnnotationToAttribute.php b/rules/Php80/ValueObject/NestedDoctrineTagAndAnnotationToAttribute.php new file mode 100644 index 00000000000..e2358962c0b --- /dev/null +++ b/rules/Php80/ValueObject/NestedDoctrineTagAndAnnotationToAttribute.php @@ -0,0 +1,26 @@ +doctrineAnnotationTagValueNode; + } + + public function getNestedAnnotationToAttribute(): NestedAnnotationToAttribute + { + return $this->nestedAnnotationToAttribute; + } +} diff --git a/rules/Php80/ValueObject/PropertyPromotionCandidate.php b/rules/Php80/ValueObject/PropertyPromotionCandidate.php index e7f6b2889fb..85e0e4dc026 100644 --- a/rules/Php80/ValueObject/PropertyPromotionCandidate.php +++ b/rules/Php80/ValueObject/PropertyPromotionCandidate.php @@ -4,16 +4,18 @@ namespace Rector\Php80\ValueObject; -use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Property; +use Rector\Exception\ShouldNotHappenException; -final class PropertyPromotionCandidate +final readonly class PropertyPromotionCandidate { public function __construct( private Property $property, - private Assign $assign, - private Param $param + private Param $param, + private int $propertyStmtPosition, + private int $constructorAssignStmtPosition, ) { } @@ -22,13 +24,33 @@ public function getProperty(): Property return $this->property; } - public function getAssign(): Assign + public function getParam(): Param { - return $this->assign; + return $this->param; } - public function getParam(): Param + public function getParamName(): string { - return $this->param; + $paramVar = $this->param->var; + + if (! $paramVar instanceof Variable) { + throw new ShouldNotHappenException(); + } + + if (! is_string($paramVar->name)) { + throw new ShouldNotHappenException(); + } + + return $paramVar->name; + } + + public function getPropertyStmtPosition(): int + { + return $this->propertyStmtPosition; + } + + public function getConstructorAssignStmtPosition(): int + { + return $this->constructorAssignStmtPosition; } } diff --git a/rules/Php80/ValueObject/StrStartsWith.php b/rules/Php80/ValueObject/StrStartsWith.php index 2d190a1764a..2a24bff62f1 100644 --- a/rules/Php80/ValueObject/StrStartsWith.php +++ b/rules/Php80/ValueObject/StrStartsWith.php @@ -7,7 +7,7 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\FuncCall; -final class StrStartsWith +final readonly class StrStartsWith { public function __construct( private FuncCall $funcCall, diff --git a/rules/Php80/ValueObjectFactory/StrStartsWithFactory.php b/rules/Php80/ValueObjectFactory/StrStartsWithFactory.php index 25da5fdfdd5..5bc825f14ce 100644 --- a/rules/Php80/ValueObjectFactory/StrStartsWithFactory.php +++ b/rules/Php80/ValueObjectFactory/StrStartsWithFactory.php @@ -9,10 +9,20 @@ final class StrStartsWithFactory { - public function createFromFuncCall(FuncCall $funcCall, bool $isPositive): StrStartsWith + public function createFromFuncCall(FuncCall $funcCall, bool $isPositive): ?StrStartsWith { - $haystack = $funcCall->args[0]->value; - $needle = $funcCall->args[1]->value; + if ($funcCall->isFirstClassCallable()) { + return null; + } + + if (count($funcCall->getArgs()) < 2) { + return null; + } + + $haystack = $funcCall->getArgs()[0] + ->value; + $needle = $funcCall->getArgs()[1] + ->value; return new StrStartsWith($funcCall, $haystack, $needle, $isPositive); } diff --git a/rules/Php81/Enum/AttributeName.php b/rules/Php81/Enum/AttributeName.php new file mode 100644 index 00000000000..9d46ad7b7c6 --- /dev/null +++ b/rules/Php81/Enum/AttributeName.php @@ -0,0 +1,16 @@ + + */ + public const array FUNCTION_TO_PARAM_NAMES = [ + 'preg_split' => ['subject'], + 'preg_match' => ['subject'], + 'preg_match_all' => ['subject'], + 'preg_filter' => ['replacement', 'subject'], + 'preg_replace' => ['replacement', 'subject'], + 'preg_replace_callback' => ['subject'], + 'preg_replace_callback_array' => ['subject'], + 'explode' => ['string'], + 'strlen' => ['string'], + 'str_contains' => ['haystack', 'needle'], + 'strtotime' => ['datetime'], + 'str_replace' => ['subject'], + 'substr_replace' => ['string', 'replace'], + 'str_ireplace' => ['search', 'replace', 'subject'], + 'substr' => ['string'], + 'str_starts_with' => ['haystack', 'needle'], + 'strtoupper' => ['string'], + 'strtolower' => ['string'], + 'strpos' => ['haystack', 'needle'], + 'stripos' => ['haystack', 'needle'], + 'json_decode' => ['json'], + 'urlencode' => ['string'], + 'urldecode' => ['string'], + 'rawurlencode' => ['string'], + 'rawurldecode' => ['string'], + 'base64_encode' => ['string'], + 'base64_decode' => ['string'], + 'utf8_encode' => ['string'], + 'utf8_decode' => ['string'], + 'bin2hex' => ['string'], + 'hex2bin' => ['string'], + 'hexdec' => ['hex_string'], + 'octdec' => ['octal_string'], + 'base_convert' => ['num'], + 'htmlspecialchars' => ['string'], + 'htmlspecialchars_decode' => ['string'], + 'html_entity_decode' => ['string'], + 'htmlentities' => ['string'], + 'addslashes' => ['string'], + 'addcslashes' => ['string', 'characters'], + 'stripslashes' => ['string'], + 'stripcslashes' => ['string'], + 'quotemeta' => ['string'], + 'quoted_printable_decode' => ['string'], + 'quoted_printable_encode' => ['string'], + 'escapeshellarg' => ['arg'], + 'curl_escape' => ['string'], + 'curl_unescape' => ['string'], + 'convert_uuencode' => ['string'], + 'setcookie' => ['value', 'path', 'domain'], + 'setrawcookie' => ['value', 'path', 'domain'], + 'zlib_encode' => ['data'], + 'gzdeflate' => ['data'], + 'gzencode' => ['data'], + 'gzcompress' => ['data'], + 'gzwrite' => ['data'], + 'gzputs' => ['data'], + 'deflate_add' => ['data'], + 'inflate_add' => ['data'], + 'unpack' => ['format', 'string'], + 'iconv_mime_encode' => ['field_name', 'field_value'], + 'iconv_mime_decode' => ['string'], + 'iconv' => ['from_encoding', 'to_encoding', 'string'], + 'sodium_bin2hex' => ['string'], + 'sodium_hex2bin' => ['string', 'ignore'], + 'sodium_bin2base64' => ['string'], + 'sodium_base642bin' => ['string', 'ignore'], + 'mb_detect_encoding' => ['string'], + 'mb_encode_mimeheader' => ['string'], + 'mb_decode_mimeheader' => ['string'], + 'mb_encode_numericentity' => ['string'], + 'mb_decode_numericentity' => ['string'], + 'transliterator_transliterate' => ['string'], + 'mysqli_real_escape_string' => ['string'], + 'mysqli_escape_string' => ['string'], + 'pg_escape_bytea' => ['string'], + 'pg_escape_literal' => ['string'], + 'pg_escape_string' => ['string'], + 'pg_unescape_bytea' => ['string'], + 'ucfirst' => ['string'], + 'lcfirst' => ['string'], + 'ucwords' => ['string'], + 'trim' => ['string'], + 'ltrim' => ['string'], + 'rtrim' => ['string'], + 'chop' => ['string'], + 'str_rot13' => ['string'], + 'str_shuffle' => ['string'], + 'substr_count' => ['haystack', 'needle'], + 'strcoll' => ['string1', 'string2'], + 'str_split' => ['string'], + 'chunk_split' => ['string'], + 'wordwrap' => ['string'], + 'strrev' => ['string'], + 'str_repeat' => ['string'], + 'str_pad' => ['string'], + 'nl2br' => ['string'], + 'strip_tags' => ['string'], + 'hebrev' => ['string'], + 'iconv_substr' => ['string'], + 'mb_strtoupper' => ['string'], + 'mb_strtolower' => ['string'], + 'mb_convert_case' => ['string'], + 'mb_convert_kana' => ['string'], + 'mb_convert_encoding' => ['string'], + 'mb_scrub' => ['string'], + 'mb_substr' => ['string'], + 'mb_substr_count' => ['haystack', 'needle'], + 'mb_str_split' => ['string'], + 'mb_split' => ['pattern', 'string'], + 'sodium_pad' => ['string'], + 'grapheme_substr' => ['string'], + 'strrpos' => ['haystack', 'needle'], + 'strripos' => ['haystack', 'needle'], + 'iconv_strpos' => ['haystack', 'needle'], + 'iconv_strrpos' => ['haystack', 'needle'], + 'mb_strpos' => ['haystack', 'needle'], + 'mb_strrpos' => ['haystack', 'needle'], + 'mb_stripos' => ['haystack', 'needle'], + 'mb_strripos' => ['haystack', 'needle'], + 'grapheme_extract' => ['haystack'], + 'grapheme_strpos' => ['haystack', 'needle'], + 'grapheme_strrpos' => ['haystack', 'needle'], + 'grapheme_stripos' => ['haystack', 'needle'], + 'grapheme_strripos' => ['haystack', 'needle'], + 'strcmp' => ['string1', 'string2'], + 'strncmp' => ['string1', 'string2'], + 'strcasecmp' => ['string1', 'string2'], + 'strncasecmp' => ['string1', 'string2'], + 'strnatcmp' => ['string1', 'string2'], + 'strnatcasecmp' => ['string1', 'string2'], + 'substr_compare' => ['haystack', 'needle'], + 'str_ends_with' => ['haystack', 'needle'], + 'collator_compare' => ['string1', 'string2'], + 'collator_get_sort_key' => ['string'], + 'metaphone' => ['string'], + 'soundex' => ['string'], + 'levenshtein' => ['string1', 'string2'], + 'similar_text' => ['string1', 'string2'], + 'sodium_compare' => ['string1', 'string2'], + 'sodium_memcmp' => ['string1', 'string2'], + 'strstr' => ['haystack', 'needle'], + 'strchr' => ['haystack', 'needle'], + 'stristr' => ['haystack', 'needle'], + 'strrchr' => ['haystack', 'needle'], + 'strpbrk' => ['string', 'characters'], + 'strspn' => ['string', 'characters'], + 'strcspn' => ['string', 'characters'], + 'strtr' => ['string'], + 'strtok' => ['string'], + 'str_word_count' => ['string'], + 'count_chars' => ['string'], + 'iconv_strlen' => ['string'], + 'mb_strlen' => ['string'], + 'mb_strstr' => ['haystack', 'needle'], + 'mb_strrchr' => ['haystack', 'needle'], + 'mb_stristr' => ['haystack', 'needle'], + 'mb_strrichr' => ['haystack', 'needle'], + 'mb_strcut' => ['string'], + 'mb_strwidth' => ['string'], + 'mb_strimwidth' => ['string', 'trim_marker'], + 'grapheme_strlen' => ['string'], + 'grapheme_strstr' => ['haystack', 'needle'], + 'grapheme_stristr' => ['haystack', 'needle'], + 'preg_quote' => ['str'], + 'mb_ereg' => ['pattern', 'string'], + 'mb_eregi' => ['pattern', 'string'], + 'mb_ereg_replace' => ['pattern', 'replacement', 'string'], + 'mb_eregi_replace' => ['pattern', 'replacement', 'string'], + 'mb_ereg_replace_callback' => ['pattern', 'string'], + 'mb_ereg_match' => ['pattern', 'string'], + 'mb_ereg_search_init' => ['string'], + 'normalizer_normalize' => ['string'], + 'normalizer_is_normalized' => ['string'], + 'normalizer_get_raw_decomposition' => ['string'], + 'numfmt_parse' => ['string'], + 'hash' => ['algo', 'data'], + 'hash_hmac' => ['algo', 'data', 'key'], + 'hash_update' => ['data'], + 'hash_pbkdf2' => ['algo', 'password', 'salt'], + 'crc32' => ['string'], + 'md5' => ['string'], + 'sha1' => ['string'], + 'crypt' => ['string', 'salt'], + 'basename' => ['path'], + 'dirname' => ['path'], + 'pathinfo' => ['path'], + 'sscanf' => ['string'], + 'fwrite' => ['data'], + 'fputs' => ['data'], + 'output_add_rewrite_var' => ['name', 'value'], + 'parse_url' => ['url'], + 'parse_str' => ['string'], + 'mb_parse_str' => ['string'], + 'parse_ini_string' => ['ini_string'], + 'locale_accept_from_http' => ['header'], + 'msgfmt_parse' => ['string'], + 'msgfmt_parse_message' => ['locale', 'pattern', 'message'], + 'str_getcsv' => ['string'], + 'fgetcsv' => ['escape'], + 'fputcsv' => ['escape'], + 'password_hash' => ['password'], + 'password_verify' => ['password', 'hash'], + 'bcadd' => ['num1', 'num2'], + 'bcsub' => ['num1', 'num2'], + 'bcmul' => ['num1', 'num2'], + 'bcdiv' => ['num1', 'num2'], + 'bcmod' => ['num1', 'num2'], + 'bcpow' => ['num', 'exponent'], + 'bcpowmod' => ['num', 'exponent', 'modulus'], + 'bcsqrt' => ['num'], + 'bccomp' => ['num1', 'num2'], + 'simplexml_load_string' => ['data'], + 'xml_parse' => ['data'], + 'xml_parse_into_struct' => ['data'], + 'xml_parser_create_ns' => ['separator'], + 'xmlwriter_set_indent_string' => ['indentation'], + 'xmlwriter_write_attribute' => ['name', 'value'], + 'xmlwriter_write_attribute_ns' => ['value'], + 'xmlwriter_write_pi' => ['target', 'content'], + 'xmlwriter_write_cdata' => ['content'], + 'xmlwriter_text' => ['content'], + 'xmlwriter_write_raw' => ['content'], + 'xmlwriter_write_comment' => ['content'], + 'xmlwriter_write_dtd' => ['name'], + 'xmlwriter_write_dtd_element' => ['name', 'content'], + 'xmlwriter_write_dtd_attlist' => ['name', 'content'], + 'xmlwriter_write_dtd_entity' => ['name', 'content'], + 'sodium_crypto_aead_aes256gcm_encrypt' => ['message', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_aes256gcm_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_chacha20poly1305_encrypt' => ['message', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_chacha20poly1305_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_chacha20poly1305_ietf_encrypt' => ['message', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_chacha20poly1305_ietf_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_xchacha20poly1305_ietf_encrypt' => ['message', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_xchacha20poly1305_ietf_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_auth' => ['message', 'key'], + 'sodium_crypto_auth_verify' => ['mac', 'message', 'key'], + 'sodium_crypto_box' => ['message', 'nonce', 'key_pair'], + 'sodium_crypto_box_seal' => ['message', 'public_key'], + 'sodium_crypto_generichash' => ['message'], + 'sodium_crypto_generichash_update' => ['message'], + 'sodium_crypto_secretbox' => ['message', 'nonce', 'key'], + 'sodium_crypto_secretstream_xchacha20poly1305_push' => ['message'], + 'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['ciphertext'], + 'sodium_crypto_shorthash' => ['message', 'key'], + 'sodium_crypto_sign' => ['message', 'secret_key'], + 'sodium_crypto_sign_detached' => ['message'], + 'sodium_crypto_sign_open' => ['signed_message', 'public_key'], + 'sodium_crypto_sign_verify_detached' => ['signature', 'message', 'public_key'], + 'sodium_crypto_stream_xor' => ['message', 'nonce', 'key'], + 'sodium_crypto_stream_xchacha20_xor' => ['message', 'nonce', 'key'], + 'imagechar' => ['char'], + 'imagecharup' => ['char'], + 'imageftbbox' => ['string'], + 'imagefttext' => ['text'], + 'imagestring' => ['string'], + 'imagestringup' => ['string'], + 'imagettfbbox' => ['string'], + 'imagettftext' => ['text'], + 'pspell_add_to_personal' => ['word'], + 'pspell_add_to_session' => ['word'], + 'pspell_check' => ['word'], + 'pspell_config_create' => ['language', 'spelling', 'jargon', 'encoding'], + 'pspell_new' => ['spelling', 'jargon', 'encoding'], + 'pspell_new_personal' => ['spelling', 'jargon', 'encoding'], + 'pspell_store_replacement' => ['correct'], + 'pspell_suggest' => ['word'], + 'stream_get_line' => ['ending'], + 'stream_socket_sendto' => ['data'], + 'socket_sendto' => ['data'], + 'socket_write' => ['data'], + 'socket_send' => ['data'], + 'mail' => ['to', 'subject', 'message'], + 'mb_send_mail' => ['to', 'subject', 'message'], + 'ctype_alnum' => ['text'], + 'ctype_alpha' => ['text'], + 'ctype_cntrl' => ['text'], + 'ctype_digit' => ['text'], + 'ctype_graph' => ['text'], + 'ctype_lower' => ['text'], + 'ctype_print' => ['text'], + 'ctype_punct' => ['text'], + 'ctype_space' => ['text'], + 'ctype_upper' => ['text'], + 'ctype_xdigit' => ['text'], + 'uniqid' => ['prefix'], + ]; +} diff --git a/rules/Php81/NodeFactory/EnumFactory.php b/rules/Php81/NodeFactory/EnumFactory.php index a140bd1d940..6a45a35d207 100644 --- a/rules/Php81/NodeFactory/EnumFactory.php +++ b/rules/Php81/NodeFactory/EnumFactory.php @@ -4,9 +4,16 @@ namespace Rector\Php81\NodeFactory; +use Nette\Utils\Strings; use PhpParser\BuilderFactory; +use PhpParser\Node\ArrayItem; +use PhpParser\Node\Expr\Array_; +use PhpParser\Node\Identifier; +use PhpParser\Node\Scalar\Int_; +use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassConst; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Enum_; use PhpParser\Node\Stmt\EnumCase; use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode; @@ -14,40 +21,88 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Node\Value\ValueResolver; -final class EnumFactory +final readonly class EnumFactory { + /** + * @see https://stackoverflow.com/a/2560017 + * @see https://regex101.com/r/2xEQVj/1 for changing iso9001 to iso_9001 + * @see https://regex101.com/r/Ykm6ub/1 for changing XMLParser to XML_Parser + * @see https://regex101.com/r/Zv4JhD/1 for changing needsReview to needs_Review + */ + private const string PASCAL_CASE_TO_UNDERSCORE_REGEX = '/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-Z])(?=[A-Z])|(?<=[A-Za-z])(?=[^A-Za-z])/'; + + /** + * @see https://regex101.com/r/FneU33/1 + */ + private const string MULTI_UNDERSCORES_REGEX = '#_{2,}#'; + public function __construct( private NodeNameResolver $nodeNameResolver, private PhpDocInfoFactory $phpDocInfoFactory, - private BuilderFactory $builderFactory + private BuilderFactory $builderFactory, + private ValueResolver $valueResolver, + private BetterNodeFinder $betterNodeFinder ) { } public function createFromClass(Class_ $class): Enum_ { $shortClassName = $this->nodeNameResolver->getShortName($class); - $enum = new Enum_($shortClassName); + $enum = new Enum_($shortClassName, [], [ + 'startLine' => $class->getStartLine(), + 'endLine' => $class->getEndLine(), + ]); + $enum->namespacedName = $class->namespacedName; - // constant to cases - foreach ($class->getConstants() as $classConst) { - $enum->stmts[] = $this->createEnumCaseFromConst($classConst); + $constants = $class->getConstants(); + + $enum->stmts = $class->getTraitUses(); + + if ($constants !== []) { + $value = $this->valueResolver->getValue($constants[0]->consts[0]->value); + $enum->scalarType = is_string($value) + ? new Identifier('string') + : new Identifier('int'); + + // constant to cases + foreach ($constants as $constant) { + $enum->stmts[] = $this->createEnumCaseFromConst($constant); + } } + $enum->stmts = [...$enum->stmts, ...$class->getMethods()]; + return $enum; } - public function createFromSpatieClass(Class_ $class): Enum_ + public function createFromSpatieClass(Class_ $class, bool $enumNameInSnakeCase = false): Enum_ { $shortClassName = $this->nodeNameResolver->getShortName($class); - $enum = new Enum_($shortClassName); + $enum = new Enum_($shortClassName, [], [ + 'startLine' => $class->getStartLine(), + 'endLine' => $class->getEndLine(), + ]); + $enum->namespacedName = $class->namespacedName; // constant to cases - $phpDocInfo = $this->phpDocInfoFactory->createFromNode($class); - $docBlockMethods = $phpDocInfo?->getTagsByName('@method'); - if ($docBlockMethods !== null) { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($class); + + $docBlockMethods = $phpDocInfo->getTagsByName('@method'); + if ($docBlockMethods !== []) { + $mapping = $this->generateMappingFromClass($class); + $identifierType = $this->getIdentifierTypeFromMappings($mapping); + $enum->scalarType = new Identifier($identifierType); + foreach ($docBlockMethods as $docBlockMethod) { - $enum->stmts[] = $this->createEnumCaseFromDocComment($docBlockMethod); + $enum->stmts[] = $this->createEnumCaseFromDocComment( + $docBlockMethod, + $class, + $mapping, + $enumNameInSnakeCase + ); } } @@ -57,7 +112,15 @@ public function createFromSpatieClass(Class_ $class): Enum_ private function createEnumCaseFromConst(ClassConst $classConst): EnumCase { $constConst = $classConst->consts[0]; - $enumCase = new EnumCase($constConst->name, $constConst->value); + $enumCase = new EnumCase( + $constConst->name, + $constConst->value, + [], + [ + 'startLine' => $constConst->getStartLine(), + 'endLine' => $constConst->getEndLine(), + ] + ); // mirror comments $enumCase->setAttribute(AttributeKey::PHP_DOC_INFO, $classConst->getAttribute(AttributeKey::PHP_DOC_INFO)); @@ -66,12 +129,108 @@ private function createEnumCaseFromConst(ClassConst $classConst): EnumCase return $enumCase; } - private function createEnumCaseFromDocComment(PhpDocTagNode $phpDocTagNode): EnumCase - { - /** - * @var MethodTagValueNode $nodeValue - */ + /** + * @param array $mapping + */ + private function createEnumCaseFromDocComment( + PhpDocTagNode $phpDocTagNode, + Class_ $class, + array $mapping = [], + bool $enumNameInSnakeCase = false, + ): EnumCase { + /** @var MethodTagValueNode $nodeValue */ $nodeValue = $phpDocTagNode->value; - return new EnumCase($nodeValue->methodName, $this->builderFactory->val($nodeValue->methodName)); + $enumValue = $mapping[$nodeValue->methodName] ?? $nodeValue->methodName; + if ($enumNameInSnakeCase) { + $enumName = strtoupper( + Strings::replace($nodeValue->methodName, self::PASCAL_CASE_TO_UNDERSCORE_REGEX, '_$0') + ); + $enumName = Strings::replace($enumName, self::MULTI_UNDERSCORES_REGEX, '_'); + } else { + $enumName = strtoupper($nodeValue->methodName); + } + + $enumExpr = $this->builderFactory->val($enumValue); + + return new EnumCase( + $enumName, + $enumExpr, + [], + [ + 'startLine' => $class->getStartLine(), + 'endLine' => $class->getEndLine(), + ] + ); + } + + /** + * @return array + */ + private function generateMappingFromClass(Class_ $class): array + { + $classMethod = $class->getMethod('values'); + if (! $classMethod instanceof ClassMethod) { + return []; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($classMethod); + + /** @var array $mapping */ + $mapping = []; + foreach ($returns as $return) { + if (! ($return->expr instanceof Array_)) { + continue; + } + + $mapping = $this->collectMappings($return->expr->items, $mapping); + } + + return $mapping; + } + + /** + * @param null[]|ArrayItem[] $items + * @param array $mapping + * @return array + */ + private function collectMappings(array $items, array $mapping): array + { + foreach ($items as $item) { + if (! $item instanceof ArrayItem) { + continue; + } + + if (! $item->key instanceof Int_ && ! $item->key instanceof String_) { + continue; + } + + if (! $item->value instanceof Int_ && ! $item->value instanceof String_) { + continue; + } + + $mapping[$item->key->value] = $item->value->value; + } + + return $mapping; + } + + /** + * @param array $mapping + */ + private function getIdentifierTypeFromMappings(array $mapping): string + { + $callableGetType = gettype(...); + $valueTypes = array_map($callableGetType, $mapping); + $uniqueValueTypes = array_unique($valueTypes); + if (count($uniqueValueTypes) === 1) { + $identifierType = reset($uniqueValueTypes); + if ($identifierType === 'integer') { + $identifierType = 'int'; + } + } else { + $identifierType = 'string'; + } + + return $identifierType; } } diff --git a/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php b/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php new file mode 100644 index 00000000000..43dc0f1f1b1 --- /dev/null +++ b/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php @@ -0,0 +1,271 @@ +value; + + if ($this->valueResolver->isNull($argValue)) { + $args[$position]->value = $targetType === 'string' ? new String_('') : new Int_(0); + $funcCall->args = $args; + return $funcCall; + } + + // skip (string) ternary conditions with both values + if ($this->isStringCastedTernaryOfMixedTypes($argValue, $scope)) { + return null; + } + + if ($this->shouldSkipValue($argValue, $scope, $isTrait, $targetType)) { + return null; + } + + $parameterReflection = $parametersAcceptor->getParameters()[$position] ?? null; + if ($parameterReflection instanceof ExtendedNativeParameterReflection && $parameterReflection->getType() instanceof UnionType) { + $parameterType = $parameterReflection->getType(); + if (! $this->isValidUnionType($parameterType)) { + return null; + } + } + + if ($argValue instanceof Ternary && ! $this->shouldSkipValue($argValue->else, $scope, $isTrait, $targetType)) { + + if ($this->valueResolver->isNull($argValue->else)) { + $argValue->else = $targetType === 'string' ? new String_('') : new Int_(0); + } else { + $argValue->else = $targetType === 'string' ? new CastString_($argValue->else) : new CastInt_( + $argValue->else + ); + } + + $args[$position]->value = $argValue; + + return $funcCall; + } + + $wrapInParentheses = false; + if ($argValue instanceof Ternary && $argValue->cond instanceof CastString_) { + $wrapInParentheses = true; + } + + if ($targetType === 'string') { + $castedType = new CastString_($argValue); + } else { + $castedType = new CastInt_($argValue); + } + + if ($wrapInParentheses) { + $argValue->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + } + + $args[$position]->value = $castedType; + + return $funcCall; + } + + private function shouldSkipValue(Expr $expr, Scope $scope, bool $isTrait, string $targetType): bool + { + $type = $this->nodeTypeResolver->getType($expr); + if ($type->isString()->yes() && $targetType === 'string') { + return true; + } + + if ($type->isInteger()->yes() && $targetType === 'int') { + return true; + } + + $nativeType = $this->nodeTypeResolver->getNativeType($expr); + if ($nativeType->isString()->yes() && $targetType === 'string') { + return true; + } + + if ($nativeType->isInteger()->yes() && $targetType === 'int') { + return true; + } + + if ($this->isPossibleArrayVariableName($type, $nativeType, $expr)) { + return true; + } + + if ($this->shouldSkipType($type)) { + return true; + } + + if ($expr instanceof InterpolatedString) { + return true; + } + + if ($this->isAnErrorType($expr, $nativeType, $scope)) { + return true; + } + + return $this->shouldSkipTrait($expr, $type, $isTrait); + } + + private function isValidUnionType(Type $type): bool + { + if (! $type instanceof UnionType) { + return false; + } + + foreach ($type->getTypes() as $childType) { + if ($childType->isString()->yes()) { + continue; + } + + if ($childType->isInteger()->yes()) { + continue; + } + + if ($childType->isNull()->yes()) { + continue; + } + + return false; + } + + return true; + } + + private function shouldSkipType(Type $type): bool + { + return ! $type instanceof MixedType + && ! $type->isNull() + ->yes() + && ! $this->isValidUnionType($type); + } + + private function shouldSkipTrait(Expr $expr, Type $type, bool $isTrait): bool + { + if (! $type instanceof MixedType) { + return false; + } + + if (! $isTrait) { + return false; + } + + if ($type->isExplicitMixed()) { + return false; + } + + if (! $expr instanceof MethodCall) { + return $this->propertyFetchAnalyzer->isLocalPropertyFetch($expr); + } + + return true; + } + + private function isAnErrorType(Expr $expr, Type $type, Scope $scope): bool + { + if ($type instanceof ErrorType) { + return true; + } + + $parentScope = $scope->getParentScope(); + if ($parentScope instanceof FiberScope) { + $parentScope = $parentScope->toMutatingScope(); + } + + if ($parentScope instanceof Scope) { + return $parentScope->getType($expr) instanceof ErrorType; + } + + return $type instanceof MixedType + && ! $type->isExplicitMixed() + && $type->getSubtractedType() instanceof NullType; + } + + /** + * @see https://github.com/rectorphp/rector/issues/9447 for context + */ + private function isPossibleArrayVariableName(Type $passedType, Type $reflectionParamType, Expr $expr): bool + { + // could mixed, resp. array, no need to (string) cast array + if (! $passedType instanceof MixedType) { + return false; + } + + if (! $reflectionParamType->isArray()->maybe()) { + return false; + } + + if ($expr instanceof Variable && is_string($expr->name)) { + $variableName = $expr->name; + // most likely plural variable + return strlen($variableName) > 3 && str_ends_with($variableName, 's'); + } + + return false; + } + + private function isStringCastedTernaryOfMixedTypes(Expr $expr, Scope $scope): bool + { + if (! $expr instanceof Ternary) { + return false; + } + + if (! $expr->cond instanceof CastString_) { + return false; + } + + if (! $expr->if instanceof Expr) { + return false; + } + + $ifType = $scope->getType($expr->if); + $elseType = $scope->getType($expr->else); + return $ifType instanceof MixedType || $elseType instanceof MixedType; + } +} diff --git a/rules/Php81/Rector/Array_/ArrayToFirstClassCallableRector.php b/rules/Php81/Rector/Array_/ArrayToFirstClassCallableRector.php new file mode 100644 index 00000000000..7112ac968d8 --- /dev/null +++ b/rules/Php81/Rector/Array_/ArrayToFirstClassCallableRector.php @@ -0,0 +1,195 @@ +name(...); + } + + public function name() + { + } +} +CODE_SAMPLE + , + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Array_::class]; + } + + /** + * @param Array_ $node + */ + public function refactor(Node $node): StaticCall|MethodCall|null + { + if ($node->getAttribute(AttributeKey::IS_INSIDE_SYMFONY_PHP_CLOSURE)) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + + $arrayCallable = $this->arrayCallableMethodMatcher->match($node, $scope); + if (! $arrayCallable instanceof ArrayCallable) { + return null; + } + + $callerExpr = $arrayCallable->getCallerExpr(); + if (! $callerExpr instanceof Variable && ! $callerExpr instanceof PropertyFetch && ! $callerExpr instanceof ClassConstFetch) { + return null; + } + + if ($node->getAttribute(AttributeKey::IS_CLASS_CONST_VALUE)) { + return null; + } + + if ($node->getAttribute(AttributeKey::IS_DEFAULT_PROPERTY_VALUE)) { + return null; + } + + if ($node->getAttribute(AttributeKey::IS_PARAM_DEFAULT)) { + return null; + } + + $args = [new VariadicPlaceholder()]; + if ($callerExpr instanceof ClassConstFetch) { + $type = $this->getType($callerExpr->class); + if ($type instanceof FullyQualifiedObjectType && $this->isNonStaticOtherObject( + $type, + $arrayCallable, + $scope, + )) { + return null; + } + + return new StaticCall($callerExpr->class, $arrayCallable->getMethod(), $args); + } + + $methodName = $arrayCallable->getMethod(); + $methodCall = new MethodCall($callerExpr, $methodName, $args); + + if ($this->isReferenceToNonPublicMethodOutsideOwningScope($methodCall, $methodName)) { + return null; + } + + return $methodCall; + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_81; + } + + private function isNonStaticOtherObject( + FullyQualifiedObjectType $fullyQualifiedObjectType, + ArrayCallable $arrayCallable, + Scope $scope + ): bool { + $classReflection = $scope->getClassReflection(); + if ($classReflection instanceof ClassReflection && $classReflection->getName() === $fullyQualifiedObjectType->getClassName()) { + return false; + } + + $arrayClassReflection = $this->reflectionProvider->getClass($arrayCallable->getClass()); + + // we're unable to find it + if (! $arrayClassReflection->hasMethod($arrayCallable->getMethod())) { + return false; + } + + $extendedMethodReflection = $arrayClassReflection->getMethod($arrayCallable->getMethod(), $scope); + if (! $extendedMethodReflection->isStatic()) { + return true; + } + + return ! $extendedMethodReflection->isPublic(); + } + + private function isReferenceToNonPublicMethodOutsideOwningScope(MethodCall $methodCall, string $methodName): bool + { + if ($methodCall->var instanceof Variable && $methodCall->var->name === 'this') { + // If the callable is scoped to `$this` then it can be converted even if it is protected / private + return false; + } + + // If the callable is scoped to another object / variable then it should only be converted if it is public + // https://github.com/rectorphp/rector/issues/8659 + $classReflection = $this->reflectionResolver->resolveClassReflectionSourceObject($methodCall); + + if ($classReflection instanceof ClassReflection && $classReflection->hasNativeMethod($methodName)) { + $method = $classReflection->getNativeMethod($methodName); + if (! $method->isPublic()) { + return true; + } + } + + return false; + } +} diff --git a/rules/Php81/Rector/Array_/FirstClassCallableRector.php b/rules/Php81/Rector/Array_/FirstClassCallableRector.php new file mode 100644 index 00000000000..146d510fd32 --- /dev/null +++ b/rules/Php81/Rector/Array_/FirstClassCallableRector.php @@ -0,0 +1,28 @@ +> - */ - public function getNodeTypes(): array - { - return [ClassConst::class]; - } - - /** - * @param ClassConst $node - */ - public function refactor(Node $node): ?Node - { - if ($node->isPrivate()) { - return null; - } - if ($node->isProtected()) { - return null; - } - if ($node->isFinal()) { - return null; - } - - $this->visibilityManipulator->makeFinal($node); - return $node; - } -} diff --git a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php new file mode 100644 index 00000000000..9f6b8ce280e --- /dev/null +++ b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php @@ -0,0 +1,73 @@ +logger = $logger ?? new NullLogger; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function __construct( + private ?Logger $logger = new NullLogger, + ) { + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + throw new ShouldNotHappenException(sprintf( + '"%s" is deprecated as depends on context. Cannot be automated. Use manually where needed instead', + self::class + )); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NEW_INITIALIZERS; + } +} diff --git a/rules/Php81/Rector/Class_/MyCLabsClassToEnumRector.php b/rules/Php81/Rector/Class_/MyCLabsClassToEnumRector.php index cdaf4e675c5..ca29fe760f0 100644 --- a/rules/Php81/Rector/Class_/MyCLabsClassToEnumRector.php +++ b/rules/Php81/Rector/Class_/MyCLabsClassToEnumRector.php @@ -7,21 +7,20 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; use Rector\Php81\NodeFactory\EnumFactory; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/enumerations - * @changelog https://github.com/myclabs/php-enum - * * @see \Rector\Tests\Php81\Rector\Class_\MyCLabsClassToEnumRector\MyCLabsClassToEnumRectorTest */ -final class MyCLabsClassToEnumRector extends AbstractRector +final class MyCLabsClassToEnumRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private EnumFactory $enumFactory + private readonly EnumFactory $enumFactory ) { } @@ -41,7 +40,7 @@ final class Action extends Enum , <<<'CODE_SAMPLE' -enum Action +enum Action : string { case VIEW = 'view'; case EDIT = 'edit'; @@ -70,4 +69,9 @@ public function refactor(Node $node): ?Node return $this->enumFactory->createFromClass($node); } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ENUM; + } } diff --git a/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php b/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php index 378933c20da..2290c89f825 100644 --- a/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php +++ b/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php @@ -6,29 +6,39 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\Enum_; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\Php81\NodeFactory\EnumFactory; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/enumerations - * @changelog https://github.com/spatie/enum - * * @see \Rector\Tests\Php81\Rector\Class_\SpatieEnumClassToEnumRector\SpatieEnumClassToEnumRectorTest */ -final class SpatieEnumClassToEnumRector extends AbstractRector +final class SpatieEnumClassToEnumRector extends AbstractRector implements MinPhpVersionInterface, ConfigurableRectorInterface { + public const string TO_UPPER_SNAKE_CASE = 'toUpperSnakeCase'; + + private bool $toUpperSnakeCase = false; + public function __construct( - private EnumFactory $enumFactory + private readonly EnumFactory $enumFactory ) { } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ENUM; + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Refactor Spatie enum class to native Enum', [ - new CodeSample( + new ConfiguredCodeSample( <<<'CODE_SAMPLE' use \Spatie\Enum\Enum; @@ -44,13 +54,17 @@ class StatusEnum extends Enum , <<<'CODE_SAMPLE' -enum StatusEnum +enum StatusEnum : string { - case draft = 'draft'; - case published = 'published'; - case archived = 'archived'; + case DRAFT = 'draft'; + case PUBLISHED = 'published'; + case ARCHIVED = 'archived'; } CODE_SAMPLE + , + [ + self::TO_UPPER_SNAKE_CASE => false, + ] ), ]); } @@ -66,12 +80,20 @@ public function getNodeTypes(): array /** * @param Class_ $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?Enum_ { if (! $this->isObjectType($node, new ObjectType('Spatie\Enum\Enum'))) { return null; } - return $this->enumFactory->createFromSpatieClass($node); + return $this->enumFactory->createFromSpatieClass($node, $this->toUpperSnakeCase); + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + $this->toUpperSnakeCase = $configuration[self::TO_UPPER_SNAKE_CASE] ?? false; } } diff --git a/rules/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector.php b/rules/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector.php new file mode 100644 index 00000000000..766bbf06fbf --- /dev/null +++ b/rules/Php81/Rector/FuncCall/NullToStrictIntPregSlitFuncCallLimitArgRector.php @@ -0,0 +1,134 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($this->shouldSkip($node)) { + return null; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + $args = $node->getArgs(); + $position = $this->argsAnalyzer->resolveArgPosition($args, 'limit', 2); + + if (! isset($args[$position])) { + return null; + } + + $classReflection = $scope->getClassReflection(); + $isTrait = $classReflection instanceof ClassReflection && $classReflection->isTrait(); + + $functionReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + if (! $functionReflection instanceof FunctionReflection) { + return null; + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($functionReflection, $node, $scope); + $result = $this->nullToStrictStringIntConverter->convertIfNull( + $node, + $args, + $position, + $isTrait, + $scope, + $parametersAcceptor, + 'int' + ); + + if ($result instanceof Node) { + return $result; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_NULL_ARG_IN_STRING_FUNCTION; + } + + private function shouldSkip(FuncCall $funcCall): bool + { + if (! $this->isName($funcCall, 'preg_split')) { + return true; + } + + return $funcCall->isFirstClassCallable(); + } +} diff --git a/rules/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector.php b/rules/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector.php new file mode 100644 index 00000000000..5a41f2cb531 --- /dev/null +++ b/rules/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector.php @@ -0,0 +1,191 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($this->shouldSkip($node)) { + return null; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + $args = $node->getArgs(); + $positions = $this->resolveStringPositions($node, $args, $scope); + + if ($positions === []) { + return null; + } + + $classReflection = $scope->getClassReflection(); + $isTrait = $classReflection instanceof ClassReflection && $classReflection->isTrait(); + + $functionReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + if (! $functionReflection instanceof FunctionReflection) { + return null; + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($functionReflection, $node, $scope); + $isChanged = false; + + foreach ($positions as $position) { + $result = $this->nullToStrictStringIntConverter->convertIfNull( + $node, + $args, + (int) $position, + $isTrait, + $scope, + $parametersAcceptor + ); + if ($result instanceof Node) { + $node = $result; + $isChanged = true; + } + } + + if ($isChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_NULL_ARG_IN_STRING_FUNCTION; + } + + /** + * @param Arg[] $args + * @return int[] + */ + private function resolveStringPositions(FuncCall $funcCall, array $args, Scope $scope): array + { + $positions = []; + + $functionName = $this->getName($funcCall); + $argNames = NameNullToStrictNullFunctionMap::FUNCTION_TO_PARAM_NAMES[$functionName] ?? []; + $excludedArgNames = []; + + foreach ($args as $position => $arg) { + if (! $arg->name instanceof Identifier) { + continue; + } + + if (! $this->isNames($arg->name, $argNames)) { + continue; + } + + $excludedArgNames[] = $arg->name->toString(); + $positions[] = $position; + } + + $functionReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($funcCall); + if (! $functionReflection instanceof NativeFunctionReflection) { + return $positions; + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select( + $functionReflection, + $funcCall, + $scope + ); + $functionName = $functionReflection->getName(); + $argNames = NameNullToStrictNullFunctionMap::FUNCTION_TO_PARAM_NAMES[$functionName]; + + foreach ($parametersAcceptor->getParameters() as $position => $parameterReflection) { + if (in_array($parameterReflection->getName(), $argNames, true) + && ! in_array($parameterReflection->getName(), $excludedArgNames, true)) { + $positions[] = $position; + } + } + + return $positions; + } + + private function shouldSkip(FuncCall $funcCall): bool + { + $functionNames = array_keys(NameNullToStrictNullFunctionMap::FUNCTION_TO_PARAM_NAMES); + if (! $this->isNames($funcCall, $functionNames)) { + return true; + } + + return $funcCall->isFirstClassCallable(); + } +} diff --git a/rules/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector.php b/rules/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector.php index 4412c097dc4..c09ab986c2f 100644 --- a/rules/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector.php +++ b/rules/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector.php @@ -5,21 +5,37 @@ namespace Rector\Php81\Rector\MethodCall; use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\Variable; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/enumerations - * @changelog https://github.com/myclabs/php-enum - * * @see \Rector\Tests\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector\MyCLabsMethodCallToEnumConstRectorTest */ -final class MyCLabsMethodCallToEnumConstRector extends AbstractRector +final class MyCLabsMethodCallToEnumConstRector extends AbstractRector implements MinPhpVersionInterface { + /** + * @var string[] + */ + private const array ENUM_METHODS = ['from', 'values', 'keys', 'isValid', 'search', 'toArray', 'assertValidValue']; + + public function __construct( + private readonly ReflectionProvider $reflectionProvider, + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Refactor MyCLabs enum fetch to Enum const', [ @@ -30,7 +46,7 @@ public function getRuleDefinition(): RuleDefinition , <<<'CODE_SAMPLE' -$name = SomeEnum::VALUE; +$name = SomeEnum::VALUE->name; CODE_SAMPLE ), ]); @@ -41,27 +57,204 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [MethodCall::class]; + return [MethodCall::class, StaticCall::class]; } /** - * @param MethodCall $node + * @param MethodCall|StaticCall $node */ public function refactor(Node $node): ?Node { - if (! $this->isObjectType($node->var, new ObjectType('MyCLabs\Enum\Enum'))) { + if ($node->name instanceof Expr) { + return null; + } + + $enumCaseName = $this->getName($node->name); + if ($enumCaseName === null) { + return null; + } + + if ($this->shouldOmitEnumCase($enumCaseName)) { + return null; + } + + if ($node instanceof MethodCall) { + return $this->refactorMethodCall($node, $enumCaseName); + } + + if (! $this->isObjectType($node->class, new ObjectType('MyCLabs\Enum\Enum'))) { + return null; + } + + $className = $this->getName($node->class); + if (! is_string($className)) { + return null; + } + + if (! $this->isEnumConstant($className, $enumCaseName)) { + return null; + } + + return $this->nodeFactory->createClassConstFetch($className, $enumCaseName); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ENUM; + } + + private function isEnumConstant(string $className, string $constant): bool + { + if (! $this->reflectionProvider->hasClass($className)) { + return false; + } + + $classReflection = $this->reflectionProvider->getClass($className); + + return $classReflection->hasConstant($constant); + } + + private function refactorGetKeyMethodCall(MethodCall $methodCall): ?PropertyFetch + { + if (! $methodCall->var instanceof StaticCall) { + return $this->nodeFactory->createPropertyFetch($methodCall->var, 'name'); + } + + $staticCall = $methodCall->var; + $className = $this->getName($staticCall->class); + if ($className === null) { + return null; + } + + $enumCaseName = $this->getName($staticCall->name); + if ($enumCaseName === null) { + return null; + } + + if ($this->shouldOmitEnumCase($enumCaseName)) { + return null; + } + + $classConstFetch = $this->nodeFactory->createClassConstFetch($className, $enumCaseName); + + return new PropertyFetch($classConstFetch, 'name'); + } + + private function refactorGetValueMethodCall(MethodCall $methodCall): ?PropertyFetch + { + if (! $methodCall->var instanceof StaticCall) { + return $this->nodeFactory->createPropertyFetch($methodCall->var, 'value'); + } + + $staticCall = $methodCall->var; + $className = $this->getName($staticCall->class); + if ($className === null) { + return null; + } + + $enumCaseName = $this->getName($staticCall->name); + if ($enumCaseName === null) { + return null; + } + + if ($this->shouldOmitEnumCase($enumCaseName)) { + return null; + } + + $classConstFetch = $this->nodeFactory->createClassConstFetch($className, $enumCaseName); + + return new PropertyFetch($classConstFetch, 'value'); + } + + private function refactorEqualsMethodCall(MethodCall $methodCall): ?Identical + { + $expr = $this->getNonEnumReturnTypeExpr($methodCall->var); + if (! $expr instanceof Expr) { + $expr = $this->getValidEnumExpr($methodCall->var); + if (! $expr instanceof Expr) { + return null; + } + } + + $arg = $methodCall->getArgs()[0] ?? null; + if (! $arg instanceof Arg) { return null; } - if (! $this->isName($node->name, 'getKey')) { + $right = $this->getNonEnumReturnTypeExpr($arg->value); + if (! $right instanceof Expr) { + $right = $this->getValidEnumExpr($arg->value); + if (! $right instanceof Expr) { + return null; + } + } + + return new Identical($expr, $right); + } + + private function isCallerClassEnum(StaticCall|MethodCall $node): bool + { + if ($node instanceof StaticCall) { + return $this->isObjectType($node->class, new ObjectType('MyCLabs\Enum\Enum')); + } + + return $this->isObjectType($node->var, new ObjectType('MyCLabs\Enum\Enum')); + } + + private function getNonEnumReturnTypeExpr(Node $node): ?Expr + { + if (! $node instanceof StaticCall && ! $node instanceof MethodCall) { return null; } - if (! $node->var instanceof StaticCall) { + if ($this->isCallerClassEnum($node)) { + $methodName = $this->getName($node->name); + if ($methodName === null) { + return null; + } + + if ($node instanceof StaticCall) { + $className = $this->getName($node->class); + } + + if ($node instanceof MethodCall) { + $className = $this->getName($node->var); + } + + if ($className === null) { + return null; + } + + $classReflection = $this->reflectionProvider->getClass($className); + // method self::getValidEnumExpr process enum static methods from constants + if ($classReflection->hasConstant($methodName)) { + return null; + } + } + + return $node; + } + + private function getValidEnumExpr(Node $node): null|ClassConstFetch|Expr + { + return match ($node::class) { + Variable::class, PropertyFetch::class => $this->getPropertyFetchOrVariable($node), + StaticCall::class => $this->getEnumConstFetch($node), + default => null, + }; + } + + private function getPropertyFetchOrVariable(PropertyFetch|Variable $expr): null|PropertyFetch|Variable + { + if (! $this->isObjectType($expr, new ObjectType('MyCLabs\Enum\Enum'))) { return null; } - $staticCall = $node->var; + return $expr; + } + + private function getEnumConstFetch(StaticCall $staticCall): null|ClassConstFetch + { $className = $this->getName($staticCall->class); if ($className === null) { return null; @@ -72,6 +265,36 @@ public function refactor(Node $node): ?Node return null; } + if ($this->shouldOmitEnumCase($enumCaseName)) { + return null; + } + return $this->nodeFactory->createClassConstFetch($className, $enumCaseName); } + + private function refactorMethodCall(MethodCall $methodCall, string $methodName): null|PropertyFetch|Identical + { + if (! $this->isObjectType($methodCall->var, new ObjectType('MyCLabs\Enum\Enum'))) { + return null; + } + + if ($methodName === 'getKey') { + return $this->refactorGetKeyMethodCall($methodCall); + } + + if ($methodName === 'getValue') { + return $this->refactorGetValueMethodCall($methodCall); + } + + if ($methodName === 'equals') { + return $this->refactorEqualsMethodCall($methodCall); + } + + return null; + } + + private function shouldOmitEnumCase(string $enumCaseName): bool + { + return in_array($enumCaseName, self::ENUM_METHODS, true); + } } diff --git a/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php b/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php new file mode 100644 index 00000000000..a8d5e05abe4 --- /dev/null +++ b/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php @@ -0,0 +1,88 @@ +> + */ + public function getNodeTypes(): array + { + return [Expression::class]; + } + + /** + * @param Expression $node + */ + public function refactor(Node $node): ?int + { + if ($node->expr instanceof MethodCall === false) { + return null; + } + + $methodCall = $node->expr; + + if ($this->isName($methodCall->name, 'setAccessible') === false) { + return null; + } + + if ($this->isObjectType($methodCall->var, new ObjectType('ReflectionProperty')) + || $this->isObjectType($methodCall->var, new ObjectType('ReflectionMethod')) + ) { + return NodeVisitor::REMOVE_NODE; + } + + return null; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Remove Reflection::setAccessible() calls', [ + new CodeSample( + <<<'CODE_SAMPLE' +$reflectionProperty = new ReflectionProperty($object, 'property'); +$reflectionProperty->setAccessible(true); +$value = $reflectionProperty->getValue($object); + +$reflectionMethod = new ReflectionMethod($object, 'method'); +$reflectionMethod->setAccessible(false); +$reflectionMethod->invoke($object); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$reflectionProperty = new ReflectionProperty($object, 'property'); +$value = $reflectionProperty->getValue($object); + +$reflectionMethod = new ReflectionMethod($object, 'method'); +$reflectionMethod->invoke($object); +CODE_SAMPLE + ), + ]); + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_81; + } +} diff --git a/rules/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector.php b/rules/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector.php new file mode 100644 index 00000000000..852255272f4 --- /dev/null +++ b/rules/Php81/Rector/MethodCall/SpatieEnumMethodCallToEnumConstRector.php @@ -0,0 +1,157 @@ +getValue(); +$value2 = SomeEnum::SOME_CONSTANT()->value; +$name1 = SomeEnum::SOME_CONSTANT()->getName(); +$name2 = SomeEnum::SOME_CONSTANT()->name; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$value1 = SomeEnum::SOME_CONSTANT->value; +$value2 = SomeEnum::SOME_CONSTANT->value; +$name1 = SomeEnum::SOME_CONSTANT->name; +$name2 = SomeEnum::SOME_CONSTANT->name; +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class]; + } + + /** + * @param MethodCall|StaticCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->name instanceof Expr) { + return null; + } + + $enumCaseName = $this->getName($node->name); + if ($enumCaseName === null) { + return null; + } + + if ($this->shouldOmitEnumCase($enumCaseName)) { + return null; + } + + if ($node instanceof MethodCall) { + return $this->refactorMethodCall($node, $enumCaseName); + } + + if (! $this->isObjectType($node->class, new ObjectType(self::SPATIE_FQN))) { + return null; + } + + $className = $this->getName($node->class); + if (! is_string($className)) { + return null; + } + + $constantName = strtoupper($enumCaseName); + + return $this->nodeFactory->createClassConstFetch($className, $constantName); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ENUM; + } + + private function refactorGetterToMethodCall(MethodCall $methodCall, string $property): ?PropertyFetch + { + if (! $methodCall->var instanceof StaticCall) { + return null; + } + + $staticCall = $methodCall->var; + $className = $this->getName($staticCall->class); + if ($className === null) { + return null; + } + + $enumCaseName = $this->getName($staticCall->name); + if ($enumCaseName === null) { + return null; + } + + if ($this->shouldOmitEnumCase($enumCaseName)) { + return null; + } + + $upperCaseName = strtoupper($enumCaseName); + $classConstFetch = $this->nodeFactory->createClassConstFetch($className, $upperCaseName); + + return new PropertyFetch($classConstFetch, $property); + } + + private function refactorMethodCall(MethodCall $methodCall, string $methodName): null|PropertyFetch + { + if (! $this->isObjectType($methodCall->var, new ObjectType(self::SPATIE_FQN))) { + return null; + } + + if ($methodName === 'getName') { + return $this->refactorGetterToMethodCall($methodCall, 'name'); + } + + if ($methodName === 'label') { + return $this->refactorGetterToMethodCall($methodCall, 'name'); + } + + if ($methodName === 'getValue') { + return $this->refactorGetterToMethodCall($methodCall, 'value'); + } + + if ($methodName === 'value') { + return $this->refactorGetterToMethodCall($methodCall, 'value'); + } + + return null; + } + + private function shouldOmitEnumCase(string $enumCaseName): bool + { + return in_array($enumCaseName, self::ENUM_METHODS, true); + } +} diff --git a/rules/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector.php b/rules/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector.php new file mode 100644 index 00000000000..21954686454 --- /dev/null +++ b/rules/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector.php @@ -0,0 +1,108 @@ +> + */ + public function getNodeTypes(): array + { + return [New_::class]; + } + + /** + * @param New_ $node + */ + public function refactor(Node $node): ?Node + { + return $this->refactorConstructorCallToStaticFromCall($node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ENUM; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Refactor MyCLabs Enum using constructor for instantiation', + [ + new CodeSample( + <<<'CODE_SAMPLE' +$enum = new Enum($args); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$enum = Enum::from($args); +CODE_SAMPLE + )] + ); + } + + private function refactorConstructorCallToStaticFromCall(New_ $new): ?StaticCall + { + if (! $this->isObjectType($new->class, new ObjectType(self::MY_C_LABS_CLASS))) { + return null; + } + + $classname = $this->getName($new->class); + if (in_array($classname, [ObjectReference::SELF, ObjectReference::STATIC], true)) { + $classname = ScopeFetcher::fetch($new)->getClassReflection()?->getName(); + } + + if ($classname === null) { + return null; + } + + if (! $this->isMyCLabsConstructor($new, $classname)) { + return null; + } + + return new StaticCall(new FullyQualified($classname), self::DEFAULT_ENUM_CONSTRUCTOR, $new->args); + } + + private function isMyCLabsConstructor(New_ $new, string $classname): bool + { + $classReflection = $this->reflectionProvider->getClass($classname); + if (! $classReflection->hasMethod(MethodName::CONSTRUCT)) { + return true; + } + + return $classReflection + ->getMethod(MethodName::CONSTRUCT, ScopeFetcher::fetch($new)) + ->getDeclaringClass() + ->getName() === self::MY_C_LABS_CLASS; + } +} diff --git a/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php b/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php index 8b97a4ea17e..66c3f6c111f 100644 --- a/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php +++ b/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php @@ -5,23 +5,48 @@ namespace Rector\Php81\Rector\Property; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\Clone_; +use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use Rector\Core\NodeManipulator\PropertyManipulator; -use Rector\Core\Rector\AbstractRector; +use PhpParser\NodeVisitor; +use PHPStan\Analyser\Scope; +use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\NodeAnalyzer\ParamAnalyzer; +use Rector\NodeManipulator\PropertyFetchAssignManipulator; +use Rector\NodeManipulator\PropertyManipulator; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; +use Rector\ValueObject\Visibility; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/readonly_properties_v2 - * * @see \Rector\Tests\Php81\Rector\Property\ReadOnlyPropertyRector\ReadOnlyPropertyRectorTest */ -final class ReadOnlyPropertyRector extends AbstractRector +final class ReadOnlyPropertyRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private PropertyManipulator $propertyManipulator + private readonly PropertyManipulator $propertyManipulator, + private readonly PropertyFetchAssignManipulator $propertyFetchAssignManipulator, + private readonly ParamAnalyzer $paramAnalyzer, + private readonly VisibilityManipulator $visibilityManipulator, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly DocBlockUpdater $docBlockUpdater ) { } @@ -68,47 +93,213 @@ public function getName() */ public function getNodeTypes(): array { - return [Property::class, Param::class]; + return [Class_::class]; } /** - * @param Property|Param $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($node instanceof Param) { - return $this->refactorParam($node); + if ($this->shouldSkip($node)) { + return null; + } + + $hasChanged = false; + + $classMethod = $node->getMethod(MethodName::CONSTRUCT); + $scope = ScopeFetcher::fetch($node); + + if ($classMethod instanceof ClassMethod) { + foreach ($classMethod->params as $param) { + $justChanged = $this->refactorParam($node, $classMethod, $param, $scope); + // different variable to ensure $hasRemoved not replaced + if ($justChanged instanceof Param) { + $hasChanged = true; + } + } } + foreach ($node->getProperties() as $property) { + $changedProperty = $this->refactorProperty($node, $property, $scope); + if ($changedProperty instanceof Property) { + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::READONLY_PROPERTY; + } + + private function refactorProperty(Class_ $class, Property $property, Scope $scope): ?Property + { // 1. is property read-only? - if ($this->propertyManipulator->isPropertyChangeableExceptConstructor($node)) { + if ($property->isReadonly()) { return null; } - if ($node->isReadonly()) { + if ($property->hooks !== []) { return null; } - $this->visibilityManipulator->makeReadonly($node); - return $node; + if ($property->props[0]->default instanceof Expr) { + return null; + } + + if (! $property->type instanceof Node) { + return null; + } + + if ($property->isStatic()) { + return null; + } + + if (! $this->visibilityManipulator->hasVisibility($property, Visibility::PRIVATE)) { + return null; + } + + if ($this->propertyManipulator->isPropertyChangeableExceptConstructor($class, $property, $scope)) { + return null; + } + + if ($this->propertyFetchAssignManipulator->isAssignedMultipleTimesInConstructor($class, $property)) { + return null; + } + + $this->visibilityManipulator->makeReadonly($property); + $this->removeReadOnlyDoc($property); + + return $property; } - private function refactorParam(Param $param): Param | null + private function removeReadOnlyDoc(Property|Param $node): void { - if ($param->flags === 0) { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + $readonlyDoc = $phpDocInfo->getByName('readonly'); + if (! $readonlyDoc instanceof PhpDocTagNode) { + return; + } + + if (! $readonlyDoc->value instanceof GenericTagValueNode) { + return; + } + + if ($readonlyDoc->value->value !== '') { + return; + } + + $phpDocInfo->removeByName('readonly'); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + } + + private function refactorParam(Class_ $class, ClassMethod $classMethod, Param $param, Scope $scope): Param | null + { + if (! $this->visibilityManipulator->hasVisibility($param, Visibility::PRIVATE)) { + return null; + } + + if (! $param->type instanceof Node) { return null; } - // promoted property? - if ($this->propertyManipulator->isPropertyChangeableExceptConstructor($param)) { + // early check not property promotion and already readonly + if (! $param->isPromoted() || $this->visibilityManipulator->isReadonly($param)) { return null; } - if ($this->visibilityManipulator->hasVisibility($param, Class_::MODIFIER_READONLY)) { + if ($param->hooks !== []) { + return null; + } + + if ($this->propertyManipulator->isPropertyChangeableExceptConstructor($class, $param, $scope)) { + return null; + } + + if ($param->byRef) { + return null; + } + + if ($this->paramAnalyzer->isParamReassign($classMethod, $param)) { + return null; + } + + if ($this->isPromotedPropertyAssigned($class, $param)) { return null; } $this->visibilityManipulator->makeReadonly($param); + + $this->removeReadOnlyDoc($param); + return $param; } + + private function isPromotedPropertyAssigned(Class_ $class, Param $param): bool + { + $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { + return false; + } + + if (! $param->isPromoted()) { + return false; + } + + $propertyFetch = new PropertyFetch(new Variable('this'), $this->getName($param)); + + $isAssigned = false; + $this->traverseNodesWithCallable($class->stmts, function (Node $node) use ($propertyFetch, &$isAssigned): ?int { + if (! $node instanceof Assign) { + return null; + } + + if ($this->nodeComparator->areNodesEqual($propertyFetch, $node->var)) { + $isAssigned = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + return null; + }); + + return $isAssigned; + } + + private function shouldSkip(Class_ $class): bool + { + if ($class->isReadonly()) { + return true; + } + + // not safe + if ($class->getTraitUses() !== []) { + return true; + } + + // skip "clone $this" cases, as can create unexpected write to local constructor property + return $this->hasCloneThis($class); + } + + private function hasCloneThis(Class_ $class): bool + { + return (bool) $this->betterNodeFinder->findFirst($class, function (Node $node): bool { + if (! $node instanceof Clone_) { + return false; + } + + if (! $node->expr instanceof Variable) { + return false; + } + + return $this->isName($node->expr, 'this'); + }); + } } diff --git a/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php b/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php new file mode 100644 index 00000000000..a24ee847841 --- /dev/null +++ b/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php @@ -0,0 +1,236 @@ +shouldSkip($class, $scope)) { + return null; + } + + $this->visibilityManipulator->makeReadonly($class); + + $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); + + if ($constructClassMethod instanceof ClassMethod) { + foreach ($constructClassMethod->getParams() as $param) { + $this->visibilityManipulator->removeReadonly($param); + } + } + + foreach ($class->getProperties() as $property) { + $this->visibilityManipulator->removeReadonly($property); + } + + return $class; + } + + /** + * @return ClassReflection[] + */ + private function resolveParentClassReflections(Scope $scope): array + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return []; + } + + return $classReflection->getParents(); + } + + /** + * @param Property[] $properties + */ + private function hasNonTypedProperty(array $properties): bool + { + foreach ($properties as $property) { + // properties of readonly class must always have type + if ($property->type === null) { + return true; + } + } + + return false; + } + + private function shouldSkip(Class_ $class, Scope $scope): bool + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return true; + } + + if ($this->shouldSkipClass($class)) { + return true; + } + + $parents = $this->resolveParentClassReflections($scope); + + if (! $class->isAnonymous() && ! $class->isFinal()) { + return ! $this->isExtendsReadonlyClass($parents); + } + + foreach ($parents as $parent) { + if (! $parent->isReadOnly()) { + return true; + } + } + + $properties = $class->getProperties(); + if ($this->hasWritableProperty($properties)) { + return true; + } + + if ($this->hasNonTypedProperty($properties)) { + return true; + } + + if ($this->shouldSkipConsumeTraitProperty($class)) { + return true; + } + + $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { + // no __construct means no property promotion, skip if class has no property defined + return $properties === []; + } + + $params = $constructClassMethod->getParams(); + if ($params === []) { + // no params means no property promotion, skip if class has no property defined + return $properties === []; + } + + return $this->shouldSkipParams($params); + } + + private function shouldSkipConsumeTraitProperty(Class_ $class): bool + { + $traitUses = $class->getTraitUses(); + foreach ($traitUses as $traitUse) { + foreach ($traitUse->traits as $trait) { + $traitName = $trait->toString(); + + // trait not autoloaded + if (! $this->reflectionProvider->hasClass($traitName)) { + return true; + } + + $traitClassReflection = $this->reflectionProvider->getClass($traitName); + $nativeReflection = $traitClassReflection->getNativeReflection(); + + if ($this->hasReadonlyProperty($nativeReflection->getProperties())) { + return true; + } + } + } + + return false; + } + + /** + * @param ReflectionProperty[] $properties + */ + private function hasReadonlyProperty(array $properties): bool + { + foreach ($properties as $property) { + if (! $property->isReadOnly()) { + return true; + } + } + + return false; + } + + /** + * @param ClassReflection[] $parents + */ + private function isExtendsReadonlyClass(array $parents): bool + { + foreach ($parents as $parent) { + if ($parent->isReadOnly()) { + return true; + } + } + + return false; + } + + /** + * @param Property[] $properties + */ + private function hasWritableProperty(array $properties): bool + { + foreach ($properties as $property) { + if (! $property->isReadonly()) { + return true; + } + } + + return false; + } + + private function shouldSkipClass(Class_ $class): bool + { + // need to have test fixture once feature added to nikic/PHP-Parser + if ($this->visibilityManipulator->hasVisibility($class, Visibility::READONLY)) { + return true; + } + + if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, AttributeName::ALLOW_DYNAMIC_PROPERTIES)) { + return true; + } + + return $class->extends instanceof FullyQualified && ! $this->reflectionProvider->hasClass( + $class->extends->toString() + ); + } + + /** + * @param Param[] $params + */ + private function shouldSkipParams(array $params): bool + { + foreach ($params as $param) { + // has non-readonly property promotion + if (! $this->visibilityManipulator->hasVisibility($param, Visibility::READONLY) && $param->isPromoted()) { + return true; + } + + // type is missing, invalid syntax + if ($param->type === null) { + return true; + } + } + + return false; + } +} diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php new file mode 100644 index 00000000000..8aad77813c9 --- /dev/null +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -0,0 +1,78 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isAnonymous()) { + return null; + } + + return $this->readonlyClassManipulator->process($node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::READONLY_CLASS; + } +} diff --git a/rules/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector.php b/rules/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector.php new file mode 100644 index 00000000000..3adf2a8cf47 --- /dev/null +++ b/rules/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector.php @@ -0,0 +1,93 @@ +> + */ + public function getNodeTypes(): array + { + return [InterpolatedString::class]; + } + + /** + * @param InterpolatedString $node + */ + public function refactor(Node $node): ?Node + { + $oldTokens = $this->getFile() + ->getOldTokens(); + $hasChanged = false; + + foreach ($node->parts as $part) { + if (! $part instanceof Variable && ! ($part instanceof ArrayDimFetch && $part->var instanceof Variable)) { + continue; + } + + $startTokenPos = $part->getStartTokenPos(); + + if (! isset($oldTokens[$startTokenPos])) { + continue; + } + + if ((string) $oldTokens[$startTokenPos] !== '${') { + continue; + } + + if ($part instanceof Variable) { + $part->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } else { + $oldTokens[$startTokenPos]->text = '{$'; + } + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_VARIABLE_IN_STRING_INTERPOLATION; + } +} diff --git a/rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php b/rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php new file mode 100644 index 00000000000..358ec397f56 --- /dev/null +++ b/rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php @@ -0,0 +1,81 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + if ($this->isName($node, 'utf8_decode')) { + $node->name = new Name('mb_convert_encoding'); + $node->args[1] = new Arg(new String_('ISO-8859-1')); + + return $node; + } + + if ($this->isName($node, 'utf8_encode')) { + $node->name = new Name('mb_convert_encoding'); + $node->args[1] = new Arg(new String_('UTF-8')); + $node->args[2] = new Arg(new String_('ISO-8859-1')); + + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_UTF8_DECODE_ENCODE_FUNCTION; + } +} diff --git a/rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php b/rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php new file mode 100644 index 00000000000..c12253ce959 --- /dev/null +++ b/rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php @@ -0,0 +1,118 @@ +isFirstClassCallable()) { + return null; + } + + if (! $this->isObjectType($node->class, new ObjectType('FilesystemIterator'))) { + return null; + } + + if (! isset($node->args[1])) { + return null; + } + + $flags = $node->getArgs()[1] + ->value; + if ($this->isSkipDotsPresent($flags)) { + return null; + } + + $classConstFetch = new ClassConstFetch(new FullyQualified('FilesystemIterator'), 'SKIP_DOTS'); + $node->args[1] = new Arg(new BitwiseOr($flags, $classConstFetch)); + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::FILESYSTEM_ITERATOR_SKIP_DOTS; + } + + /** + * Is the constant {@see \FilesystemIterator::SKIP_DOTS} present within $node? + */ + private function isSkipDotsPresent(Expr $expr): bool + { + while ($expr instanceof BitwiseOr) { + if ($this->isSkipDots($expr->right)) { + return true; + } + + $expr = $expr->left; + } + + return $this->isSkipDots($expr); + } + + /** + * Tells if $expr is equal to {@see \FilesystemIterator::SKIP_DOTS}. + */ + private function isSkipDots(Expr $expr): bool + { + if (! $expr instanceof ClassConstFetch) { + // can be anything + return true; + } + + if (! defined('FilesystemIterator::SKIP_DOTS')) { + return true; + } + + $value = constant('FilesystemIterator::SKIP_DOTS'); + return $this->valueResolver->isValue($expr, $value); + } +} diff --git a/rules/Php82/Rector/Param/AddSensitiveParameterAttributeRector.php b/rules/Php82/Rector/Param/AddSensitiveParameterAttributeRector.php new file mode 100644 index 00000000000..7578c57cc3a --- /dev/null +++ b/rules/Php82/Rector/Param/AddSensitiveParameterAttributeRector.php @@ -0,0 +1,107 @@ + $configuration + */ + public function configure(array $configuration): void + { + Assert::allString($configuration[self::SENSITIVE_PARAMETERS] ?? []); + $this->sensitiveParameters = (array) ($configuration[self::SENSITIVE_PARAMETERS] ?? []); + } + + public function getNodeTypes(): array + { + return [Param::class]; + } + + /** + * @param Node\Param $node + */ + public function refactor(Node $node): ?Param + { + if (! $this->isNames($node, $this->sensitiveParameters)) { + return null; + } + + if ($this->phpAttributeAnalyzer->hasPhpAttribute($node, 'SensitiveParameter')) { + return null; + } + + $node->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified('SensitiveParameter'))]); + + return $node; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Add SensitiveParameter attribute to method and function configured parameters', + [ + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run(string $password) + { + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run(#[\SensitiveParameter] string $password) + { + } +} +CODE_SAMPLE + , + [ + self::SENSITIVE_PARAMETERS => ['password'], + ] + ), + + ] + ); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SENSITIVE_PARAMETER_ATTRIBUTE; + } +} diff --git a/rules/Php83/Rector/BooleanAnd/JsonValidateRector.php b/rules/Php83/Rector/BooleanAnd/JsonValidateRector.php new file mode 100644 index 00000000000..8dda7174789 --- /dev/null +++ b/rules/Php83/Rector/BooleanAnd/JsonValidateRector.php @@ -0,0 +1,182 @@ +> + */ + public function getNodeTypes(): array + { + return [BooleanAnd::class]; + } + + /** + * @param BooleanAnd $node + */ + public function refactor(Node $node): ?Node + { + $funcCall = $this->matchJsonValidateArg($node); + + if (! $funcCall instanceof FuncCall) { + return null; + } + + if ($funcCall->isFirstClassCallable()) { + return null; + } + + $args = $funcCall->getArgs(); + + if (count($args) < 1) { + return null; + } + + if (! $this->validateArgs($funcCall)) { + return null; + } + + // Remove associative argument (position 1 or named) - json_validate does not have this param + foreach ($args as $index => $arg) { + if ($arg instanceof Arg && ( + ($arg->name instanceof Identifier && $arg->name->toString() === 'associative') || + (! $arg->name instanceof Identifier && $index === 1) + )) { + unset($funcCall->args[$index]); + break; + } + } + + $funcCall->name = new Name('json_validate'); + + return $funcCall; + } + + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_83; + } + + public function matchJsonValidateArg(BooleanAnd $booleanAnd): ?FuncCall + { + // match: json_decode(...) !== null OR null !== json_decode(...) + if (! ($booleanAnd->left instanceof NotIdentical)) { + return null; + } + + $decodeMatch = $this->binaryOpManipulator->matchFirstAndSecondConditionNode( + $booleanAnd->left, + fn (Node $node): bool => $node instanceof FuncCall && $this->isName($node->name, 'json_decode'), + fn (Node $node): bool => $node instanceof ConstFetch && $this->isName($node->name, 'null') + ); + + if (! $decodeMatch instanceof TwoNodeMatch) { + return null; + } + + // match: json_last_error() === JSON_ERROR_NONE OR JSON_ERROR_NONE === json_last_error() + if (! ($booleanAnd->right instanceof Identical)) { + return null; + } + + $errorMatch = $this->binaryOpManipulator->matchFirstAndSecondConditionNode( + $booleanAnd->right, + fn (Node $node): bool => $node instanceof FuncCall && $this->isName($node->name, 'json_last_error'), + fn (Node $node): bool => $node instanceof ConstFetch && $this->isName($node->name, 'JSON_ERROR_NONE') + ); + + if (! $errorMatch instanceof TwoNodeMatch) { + return null; + } + + // always return the json_decode(...) call + $expr = $decodeMatch->getFirstExpr(); + if (! $expr instanceof FuncCall) { + return null; + } + + return $expr; + } + + private function validateArgs(FuncCall $funcCall): bool + { + $depth = $funcCall->getArg('depth', 2); + $flags = $funcCall->getArg('flags', 3); + + if ($flags instanceof Arg) { + $flagsValue = $this->valueResolver->getValue($flags); + if ($flagsValue !== JSON_INVALID_UTF8_IGNORE) { + return false; + } + } + + if ($depth instanceof Arg) { + $depthValue = $this->valueResolver->getValue($depth); + if ($depthValue <= 0 || $depthValue > self::JSON_MAX_DEPTH) { + return false; + } + } + + return true; + } +} diff --git a/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php b/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php new file mode 100644 index 00000000000..d6157bcdbd7 --- /dev/null +++ b/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php @@ -0,0 +1,242 @@ +getName($node); + if (! is_string($className)) { + return null; + } + + $classConsts = $node->getConstants(); + if ($classConsts === []) { + return null; + } + + $parentClassReflections = $this->getParentReflections($className); + + $hasChanged = false; + + foreach ($classConsts as $classConst) { + $valueTypes = []; + // If a type is set, skip + if ($classConst->type !== null) { + continue; + } + + foreach ($classConst->consts as $constNode) { + if ($node->isAbstract() && ! $classConst->isPrivate()) { + continue; + } + + if ($this->isConstGuardedByParents($constNode, $parentClassReflections)) { + continue; + } + + if ($this->canBeInherited($classConst, $node)) { + continue; + } + + $valueTypes[] = $this->findValueType($constNode->value); + } + + if ($valueTypes === []) { + continue; + } + + if (count($valueTypes) > 1) { + $valueTypes = array_unique($valueTypes, SORT_REGULAR); + } + + // once more verify after uniquate + if (count($valueTypes) > 1) { + continue; + } + + $valueType = current($valueTypes); + if (! $valueType instanceof Identifier) { + continue; + } + + $classConst->type = $valueType; + $hasChanged = true; + + $classConstPhpDocInfo = $this->phpDocInfoFactory->createFromNode($classConst); + + if ($classConstPhpDocInfo instanceof PhpDocInfo) { + $this->varTagRemover->removeVarTagIfUseless($classConstPhpDocInfo, $classConst); + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_CLASS_CONSTANTS; + } + + /** + * @param ClassReflection[] $parentClassReflections + */ + public function isConstGuardedByParents(Const_ $const, array $parentClassReflections): bool + { + $constantName = $this->getName($const); + + foreach ($parentClassReflections as $parentClassReflection) { + if ($parentClassReflection->hasConstant($constantName)) { + return true; + } + } + + return false; + } + + private function findValueType(Expr $expr): ?Identifier + { + if ($expr instanceof UnaryPlus || $expr instanceof UnaryMinus) { + return $this->findValueType($expr->expr); + } + + if ($expr instanceof String_) { + return new Identifier('string'); + } + + if ($expr instanceof Int_) { + return new Identifier('int'); + } + + if ($expr instanceof Float_) { + return new Identifier('float'); + } + + if ($expr instanceof ConstFetch || $expr instanceof ClassConstFetch) { + if ($expr instanceof ConstFetch && $expr->name->toLowerString() === 'null') { + return new Identifier('null'); + } + + $type = $this->nodeTypeResolver->getNativeType($expr); + $nodeType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PROPERTY); + + if (! $nodeType instanceof Identifier) { + return null; + } + + return $nodeType; + } + + if ($expr instanceof Array_) { + return new Identifier('array'); + } + + if ($expr instanceof Concat) { + return new Identifier('string'); + } + + return null; + } + + /** + * @return ClassReflection[] + */ + private function getParentReflections(string $className): array + { + if (! $this->reflectionProvider->hasClass($className)) { + return []; + } + + $currentClassReflection = $this->reflectionProvider->getClass($className); + + return array_filter($currentClassReflection->getAncestors(), static fn (ClassReflection $classReflection): bool => + // skip base class + $currentClassReflection !== $classReflection); + } + + private function canBeInherited(ClassConst $classConst, Class_ $class): bool + { + if (FeatureFlags::treatClassesAsFinal($class)) { + return false; + } + + return ! $class->isFinal() && ! $classConst->isPrivate() && ! $classConst->isFinal(); + } +} diff --git a/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php b/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php new file mode 100644 index 00000000000..81b3ee9902e --- /dev/null +++ b/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php @@ -0,0 +1,410 @@ + false, + ] + ), + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +interface ParentInterface +{ + public function foo(); +} + +final class ChildClass implements ParentInterface +{ + public function foo() + { + echo 'implements interface'; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +interface ParentInterface +{ + public function foo(); +} + +final class ChildClass implements ParentInterface +{ + #[\Override] + public function foo() + { + echo 'implements interface'; + } +} +CODE_SAMPLE + , + [ + self::ADD_TO_INTERFACE_METHODS => true, + ] + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + $this->allowOverrideEmptyMethod = $configuration[self::ALLOW_OVERRIDE_EMPTY_METHOD] ?? false; + $this->addToInterfaceMethods = $configuration[self::ADD_TO_INTERFACE_METHODS] ?? false; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $this->hasChanged = false; + + if ($this->classAnalyzer->isAnonymousClass($node)) { + return null; + } + + if ($this->shouldSkipNode($node)) { + return null; + } + + $className = (string) $this->getName($node); + if (! $this->reflectionProvider->hasClass($className)) { + return null; + } + + $classReflection = $this->reflectionProvider->getClass($className); + $parentClassReflections = $classReflection->getParents(); + + // interfaces are added for Stringable + if ($this->addToInterfaceMethods || $this->allowOverrideEmptyMethod) { + $parentClassReflections = array_merge($parentClassReflections, $classReflection->getInterfaces()); + } + + if ($this->allowOverrideEmptyMethod) { + // place on last to ensure verify method exists on parent early + // for non abstract method from trait + $parentClassReflections = array_merge($parentClassReflections, $classReflection->getTraits()); + } + + if ($parentClassReflections === []) { + return null; + } + + foreach ($node->getMethods() as $classMethod) { + $this->processAddOverrideAttribute($classMethod, $parentClassReflections); + } + + if (! $this->hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::OVERRIDE_ATTRIBUTE; + } + + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_83; + } + + /** + * @param ClassReflection[] $parentClassReflections + */ + private function processAddOverrideAttribute(ClassMethod $classMethod, array $parentClassReflections): void + { + if ($this->shouldSkipClassMethod($classMethod)) { + return; + } + + /** @var string $classMethodName */ + $classMethodName = $this->getName($classMethod->name); + + // Private methods should be ignored + $shouldAddOverride = false; + foreach ($parentClassReflections as $parentClassReflection) { + if (! $parentClassReflection->hasNativeMethod($classMethod->name->toString())) { + continue; + } + + // ignore if it is a private method on the parent + if (! $parentClassReflection->hasNativeMethod($classMethodName)) { + continue; + } + + $parentMethod = $parentClassReflection->getNativeMethod($classMethodName); + if ($parentMethod->isPrivate()) { + break; + } + + if ($this->shouldSkipParentClassMethod($parentClassReflection, $classMethod)) { + continue; + } + + if ($parentClassReflection->isTrait() && ! $parentMethod->isAbstract()) { + break; + } + + $shouldAddOverride = true; + break; + } + + if ($shouldAddOverride) { + $classMethod->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified(self::OVERRIDE_CLASS))]); + $this->hasChanged = true; + } + } + + private function shouldSkipClassMethod(ClassMethod $classMethod): bool + { + if ($this->isName($classMethod->name, MethodName::CONSTRUCT)) { + return true; + } + + // nothing to override + if ($classMethod->isPrivate()) { + return true; + } + + // ignore if it already uses the attribute + if ($this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, self::OVERRIDE_CLASS)) { + return true; + } + + // skip test setup method override, as rather clutters the code than helps + return $this->isNames($classMethod, ['setUp', 'tearDown']) && $this->parentClassAnalyzer->hasParentCall( + $classMethod + ); + } + + private function shouldSkipParentClassMethod(ClassReflection $parentClassReflection, ClassMethod $classMethod): bool + { + // special case for Stringable interface + if ($this->allowOverrideEmptyMethod && $parentClassReflection->getName() === 'Stringable') { + return false; + } + + // if the method is on interface, skip based on the config flag + if ($parentClassReflection->isInterface()) { + return ! $this->addToInterfaceMethods; + } + + // parse parent method, if it has some contents or not + $parentClass = $this->astResolver->resolveClassFromClassReflection($parentClassReflection); + if (! $parentClass instanceof ClassLike) { + return true; + } + + $parentClassMethod = $parentClass->getMethod($classMethod->name->toString()); + if (! $parentClassMethod instanceof ClassMethod) { + $parentClassMethod = $this->resolveClassMethodFromTraitUse($parentClass, $classMethod->name->toString()); + } + + if (! $parentClassMethod instanceof ClassMethod) { + return true; + } + + if ($this->allowOverrideEmptyMethod) { + return false; + } + + // just override abstract method also skipped on purpose + // only grand child of abstract method that parent has content will have + if ($parentClassMethod->isAbstract()) { + return true; + } + + // has any stmts? + if ($parentClassMethod->stmts === null || $parentClassMethod->stmts === []) { + return true; + } + + if (count($parentClassMethod->stmts) === 1) { + /** @var Stmt $soleStmt */ + $soleStmt = $parentClassMethod->stmts[0]; + // most likely, return null; is interface to be designed to override + if ($soleStmt instanceof Return_ && $soleStmt->expr instanceof Expr && $this->valueResolver->isNull( + $soleStmt->expr + )) { + return true; + } + + if ($soleStmt instanceof Expression && $soleStmt->expr instanceof Throw_) { + return true; + } + } + + return false; + } + + private function resolveClassMethodFromTraitUse(ClassLike $classLike, string $methodName): ?ClassMethod + { + foreach ($classLike->getTraitUses() as $traitUse) { + foreach ($traitUse->traits as $traitName) { + $traitClass = $this->astResolver->resolveClassFromName($traitName->__toString()); + if (! $traitClass instanceof Trait_) { + continue; + } + + $traitClassMethod = $traitClass->getMethod($methodName); + if ($traitClassMethod instanceof ClassMethod) { + return $traitClassMethod; + } + } + } + + return null; + } + + // early return for the class if it does not extend anything + private function shouldSkipNode(Class_ $class): bool + { + if ($class->extends instanceof Name) { + return false; + } + + if ($class->getTraitUses() !== []) { + return false; + } + + if ($this->addToInterfaceMethods && $class->implements !== []) { + return false; + } + + // add override to Stringable if flag is set + if ($this->allowOverrideEmptyMethod) { + foreach ($class->implements as $implement) { + if ($this->isName($implement, 'Stringable')) { + return false; + } + } + } + + return true; + } +} diff --git a/rules/Php83/Rector/Class_/ReadOnlyAnonymousClassRector.php b/rules/Php83/Rector/Class_/ReadOnlyAnonymousClassRector.php new file mode 100644 index 00000000000..f3ac0a0cdd9 --- /dev/null +++ b/rules/Php83/Rector/Class_/ReadOnlyAnonymousClassRector.php @@ -0,0 +1,78 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->isAnonymous()) { + return null; + } + + return $this->readonlyClassManipulator->process($node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::READONLY_ANONYMOUS_CLASS; + } +} diff --git a/rules/Php83/Rector/FuncCall/CombineHostPortLdapUriRector.php b/rules/Php83/Rector/FuncCall/CombineHostPortLdapUriRector.php new file mode 100644 index 00000000000..c81338aee9a --- /dev/null +++ b/rules/Php83/Rector/FuncCall/CombineHostPortLdapUriRector.php @@ -0,0 +1,102 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node, 'ldap_connect')) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + if (count($args) !== 2) { + return null; + } + + $firstArg = $args[0]->value; + $secondArg = $args[1]->value; + + if ($firstArg instanceof String_ && $secondArg instanceof Int_) { + $args[0]->value = new String_($firstArg->value . ':' . $secondArg->value); + } elseif ($this->exprAnalyzer->isDynamicExpr($firstArg) && $this->exprAnalyzer->isDynamicExpr($secondArg)) { + if ($firstArg instanceof Concat && ! $secondArg instanceof Concat) { + $args[0]->value = new Concat($firstArg, new String_(':')); + $args[0]->value = new Concat($args[0]->value, $secondArg); + } else { + $args[0]->value = new InterpolatedString([$firstArg, new InterpolatedStringPart(':'), $secondArg]); + } + } else { + return null; + } + + unset($args[1]); + $node->args = $args; + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_HOST_PORT_SEPARATE_ARGS; + } +} diff --git a/rules/Php83/Rector/FuncCall/DynamicClassConstFetchRector.php b/rules/Php83/Rector/FuncCall/DynamicClassConstFetchRector.php new file mode 100644 index 00000000000..d21960dde7b --- /dev/null +++ b/rules/Php83/Rector/FuncCall/DynamicClassConstFetchRector.php @@ -0,0 +1,95 @@ +isName($node, 'constant')) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + if (count($args) !== 1) { + return null; + } + + $value = $args[0]->value; + if (! $value instanceof Concat) { + return null; + } + + if (! $value->left instanceof Concat) { + return null; + } + + if (! $value->left->left instanceof ClassConstFetch) { + return null; + } + + if (! $value->left->left->name instanceof Identifier || $value->left->left->name->toString() !== 'class') { + return null; + } + + if (! $value->left->right instanceof String_ || $value->left->right->value !== '::') { + return null; + } + + return new ClassConstFetch($value->left->left->class, $value->right); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DYNAMIC_CLASS_CONST_FETCH; + } +} diff --git a/rules/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector.php b/rules/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector.php new file mode 100644 index 00000000000..1750c26fc68 --- /dev/null +++ b/rules/Php83/Rector/FuncCall/RemoveGetClassGetParentClassNoArgsRector.php @@ -0,0 +1,90 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + if (count($node->getArgs()) !== 0) { + return null; + } + + $target = null; + if ($this->isName($node, 'get_class')) { + $target = 'self'; + } + + if ($this->isName($node, 'get_parent_class')) { + $target = 'parent'; + } + + if ($target !== null) { + return new ClassConstFetch(new Name([$target]), new VarLikeIdentifier('class')); + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_GET_CLASS_WITHOUT_ARGS; + } +} diff --git a/rules/Php84/NodeAnalyzer/ForeachKeyUsedInConditionalAnalyzer.php b/rules/Php84/NodeAnalyzer/ForeachKeyUsedInConditionalAnalyzer.php new file mode 100644 index 00000000000..296b0f9833e --- /dev/null +++ b/rules/Php84/NodeAnalyzer/ForeachKeyUsedInConditionalAnalyzer.php @@ -0,0 +1,25 @@ +nodeNameResolver->getName($variable); + return (bool) $this->betterNodeFinder->findVariableOfName($expr, $keyVarName); + } +} diff --git a/rules/Php84/NodeFactory/PropertyHookFactory.php b/rules/Php84/NodeFactory/PropertyHookFactory.php new file mode 100644 index 00000000000..634574d6aaf --- /dev/null +++ b/rules/Php84/NodeFactory/PropertyHookFactory.php @@ -0,0 +1,43 @@ +name->toString(); + + if ($methodName === 'get' . ucfirst($propertyName)) { + $methodName = 'get'; + } elseif ($methodName === 'set' . ucfirst($propertyName)) { + $methodName = 'set'; + } else { + return null; + } + + Assert::notNull($classMethod->stmts); + + $soleStmt = $classMethod->stmts[0]; + + // use sole Expr + if (($soleStmt instanceof Expression || $soleStmt instanceof Return_) && $methodName !== 'set') { + $body = $soleStmt->expr; + } else { + $body = [$soleStmt]; + } + + $setterPropertyHook = new PropertyHook($methodName, $body); + $setterPropertyHook->params = $classMethod->params; + + return $setterPropertyHook; + } +} diff --git a/rules/Php84/NodeFinder/SetterAndGetterFinder.php b/rules/Php84/NodeFinder/SetterAndGetterFinder.php new file mode 100644 index 00000000000..2adcc7f15be --- /dev/null +++ b/rules/Php84/NodeFinder/SetterAndGetterFinder.php @@ -0,0 +1,68 @@ +findGetterClassMethod($class, $propertyName); + if ($getterClassMethod instanceof ClassMethod) { + $classMethods[] = $getterClassMethod; + } + + $setterClassMethod = $this->findSetterClassMethod($class, $propertyName); + if ($setterClassMethod instanceof ClassMethod) { + $classMethods[] = $setterClassMethod; + } + + return $classMethods; + } + + public function findGetterClassMethod(Class_ $class, string $propertyName): ?ClassMethod + { + foreach ($class->getMethods() as $classMethod) { + if (! $this->classMethodAndPropertyAnalyzer->hasPropertyFetchReturn($classMethod, $propertyName)) { + continue; + } + + return $classMethod; + } + + return null; + } + + public function findSetterClassMethod(Class_ $class, string $propertyName): ?ClassMethod + { + foreach ($class->getMethods() as $classMethod) { + + if ($classMethod->isMagic()) { + continue; + } + + if (! $this->classMethodAndPropertyAnalyzer->hasOnlyPropertyAssign($classMethod, $propertyName)) { + continue; + } + + return $classMethod; + } + + return null; + } +} diff --git a/rules/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector.php b/rules/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector.php new file mode 100644 index 00000000000..74e1c56ffba --- /dev/null +++ b/rules/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector.php @@ -0,0 +1,75 @@ +deprecatedAnnotationToDeprecatedAttributeConverter->convert($node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATED_ATTRIBUTE; + } + + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_84; + } +} diff --git a/rules/Php84/Rector/Class_/PropertyHookRector.php b/rules/Php84/Rector/Class_/PropertyHookRector.php new file mode 100644 index 00000000000..5cdd9f103eb --- /dev/null +++ b/rules/Php84/Rector/Class_/PropertyHookRector.php @@ -0,0 +1,176 @@ +name; + } + + public function setName(string $name): void + { + $this->name = ucfirst($name); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class Product +{ + public string $name + { + get => $this->name; + set($value) => $this->name = ucfirst($value); + } +} + +CODE_SAMPLE + ), + ]); + } + + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isReadonly()) { + return null; + } + + // avoid breaking of child class getter/setter method use + if (! $node->isFinal() && FeatureFlags::treatClassesAsFinal($node) === false) { + return null; + } + + if ($this->hasMagicGetSetMethod($node)) { + return null; + } + + // nothing to hook to + if ($node->getProperties() === []) { + return null; + } + + $classMethodsToRemove = []; + + foreach ($node->getProperties() as $property) { + $propertyName = $this->getName($property); + + if ($property->isReadonly()) { + continue; + } + + $candidateClassMethods = $this->setterAndGetterFinder->findGetterAndSetterClassMethods( + $node, + $propertyName + ); + + foreach ($candidateClassMethods as $candidateClassMethod) { + if (count((array) $candidateClassMethod->stmts) !== 1) { + continue; + } + + // skip attributed methods + if ($candidateClassMethod->attrGroups !== []) { + continue; + } + + // avoid parent contract/method override + if ($this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($candidateClassMethod)) { + continue; + } + + $propertyHook = $this->propertyHookFactory->create($candidateClassMethod, $propertyName); + if (! $propertyHook instanceof PropertyHook) { + continue; + } + + if (! $property->isPublic()) { + $property->flags = Modifiers::PUBLIC; + } + + $property->hooks[] = $propertyHook; + $classMethodsToRemove[] = $candidateClassMethod; + } + } + + if ($classMethodsToRemove === []) { + return null; + } + + foreach ($node->stmts as $key => $classStmt) { + if (! $classStmt instanceof ClassMethod) { + continue; + } + + if (! in_array($classStmt, $classMethodsToRemove)) { + continue; + } + + unset($node->stmts[$key]); + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::PROPERTY_HOOKS; + } + + private function hasMagicGetSetMethod(Class_ $class): bool + { + $magicGetMethod = $class->getMethod(MethodName::__GET); + if ($magicGetMethod instanceof ClassMethod) { + return true; + } + + $magicSetMethod = $class->getMethod(MethodName::__SET); + return $magicSetMethod instanceof ClassMethod; + } +} diff --git a/rules/Php84/Rector/Foreach_/ForeachToArrayAllRector.php b/rules/Php84/Rector/Foreach_/ForeachToArrayAllRector.php new file mode 100644 index 00000000000..eeac9d2aa78 --- /dev/null +++ b/rules/Php84/Rector/Foreach_/ForeachToArrayAllRector.php @@ -0,0 +1,346 @@ + str_starts_with($animal, 'c')); +CODE_SAMPLE + ), + new CodeSample( + <<<'CODE_SAMPLE' +foreach ($animals as $animal) { + if (!str_starts_with($animal, 'c')) { + return false; + } +} +return true; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +return array_all($animals, fn($animal) => str_starts_with($animal, 'c')); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + return $this->refactorBooleanAssignmentPattern($node) ?? + $this->refactorEarlyReturnPattern($node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_ALL; + } + + /** + * @param StmtsAware $stmtsAware + */ + private function refactorBooleanAssignmentPattern(Node $stmtsAware): ?Node + { + if ($stmtsAware->stmts === null) { + return null; + } + + foreach ($stmtsAware->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } + + $prevStmt = $stmtsAware->stmts[$key - 1] ?? null; + if (! $prevStmt instanceof Expression) { + continue; + } + + if (! $prevStmt->expr instanceof Assign) { + continue; + } + + $foreach = $stmt; + $prevAssign = $prevStmt->expr; + + if (! $this->valueResolver->isTrue($prevAssign->expr)) { + continue; + } + + if (! $prevAssign->var instanceof Variable) { + continue; + } + + $assignedVariable = $prevAssign->var; + + if (! $this->isValidBooleanAssignmentForeachStructure($foreach, $assignedVariable)) { + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt( + $stmtsAware, + $key + 1, + (string) $this->getName($foreach->valueVar) + )) { + continue; + } + + /** @var If_ $firstNodeInsideForeach */ + $firstNodeInsideForeach = $foreach->stmts[0]; + + $condition = $firstNodeInsideForeach->cond; + $valueParam = $foreach->valueVar; + + if (! $valueParam instanceof Variable) { + continue; + } + + $params = [new Param($valueParam)]; + + if ($foreach->keyVar instanceof Variable && $this->foreachKeyUsedInConditionalAnalyzer->isUsed( + $foreach->keyVar, + $condition + )) { + $params[] = new Param(new Variable((string) $this->getName($foreach->keyVar))); + } + + $negatedCondition = $condition instanceof BooleanNot ? $condition->expr : new BooleanNot($condition); + + $arrowFunction = new ArrowFunction([ + 'params' => $params, + 'expr' => $negatedCondition, + ]); + + $funcCall = $this->nodeFactory->createFuncCall('array_all', [$foreach->expr, $arrowFunction]); + + $newAssign = new Assign($assignedVariable, $funcCall); + $newExpression = new Expression($newAssign); + + unset($stmtsAware->stmts[$key - 1]); + $stmtsAware->stmts[$key] = $newExpression; + + $stmtsAware->stmts = array_values($stmtsAware->stmts); + + return $stmtsAware; + } + + return null; + } + + /** + * @param StmtsAware $stmtsAware + */ + private function refactorEarlyReturnPattern(Node $stmtsAware): ?Node + { + if ($stmtsAware->stmts === null) { + return null; + } + + foreach ($stmtsAware->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } + + $foreach = $stmt; + $nextStmt = $stmtsAware->stmts[$key + 1] ?? null; + + if (! $nextStmt instanceof Return_) { + continue; + } + + if (! $nextStmt->expr instanceof Expr) { + continue; + } + + if (! $this->valueResolver->isTrue($nextStmt->expr)) { + continue; + } + + if (! $this->isValidEarlyReturnForeachStructure($foreach)) { + continue; + } + + /** @var If_ $firstNodeInsideForeach */ + $firstNodeInsideForeach = $foreach->stmts[0]; + $condition = $firstNodeInsideForeach->cond; + + $params = []; + if ($foreach->valueVar instanceof Variable) { + $params[] = new Param($foreach->valueVar); + } + + if ( + $foreach->keyVar instanceof Variable && + $this->foreachKeyUsedInConditionalAnalyzer->isUsed($foreach->keyVar, $condition) + ) { + $params[] = new Param(new Variable((string) $this->getName($foreach->keyVar))); + } + + $negatedCondition = $condition instanceof BooleanNot ? $condition->expr : new BooleanNot($condition); + + $arrowFunction = new ArrowFunction([ + 'params' => $params, + 'expr' => $negatedCondition, + ]); + + $funcCall = $this->nodeFactory->createFuncCall('array_all', [$foreach->expr, $arrowFunction]); + + $stmtsAware->stmts[$key] = new Return_($funcCall); + unset($stmtsAware->stmts[$key + 1]); + $stmtsAware->stmts = array_values($stmtsAware->stmts); + + return $stmtsAware; + } + + return null; + } + + private function isValidEarlyReturnForeachStructure(Foreach_ $foreach): bool + { + if (count($foreach->stmts) !== 1) { + return false; + } + + if (! $foreach->stmts[0] instanceof If_) { + return false; + } + + $ifStmt = $foreach->stmts[0]; + + if (count($ifStmt->stmts) !== 1) { + return false; + } + + if (! $ifStmt->stmts[0] instanceof Return_) { + return false; + } + + $returnStmt = $ifStmt->stmts[0]; + + if (! $returnStmt->expr instanceof Expr) { + return false; + } + + if (! $this->valueResolver->isFalse($returnStmt->expr)) { + return false; + } + + if (! $foreach->valueVar instanceof Variable) { + return false; + } + + $type = $this->nodeTypeResolver->getNativeType($foreach->expr); + + return $type->isArray() + ->yes(); + } + + private function isValidBooleanAssignmentForeachStructure(Foreach_ $foreach, Variable $assignedVariable): bool + { + if (count($foreach->stmts) !== 1) { + return false; + } + + $firstStmt = $foreach->stmts[0]; + if ( + ! $firstStmt instanceof If_ || + count($firstStmt->stmts) !== 2 + ) { + return false; + } + + $assignmentStmt = $firstStmt->stmts[0]; + $breakStmt = $firstStmt->stmts[1]; + + if ( + ! $assignmentStmt instanceof Expression || + ! $assignmentStmt->expr instanceof Assign || + ! $breakStmt instanceof Break_ + ) { + return false; + } + + $assignment = $assignmentStmt->expr; + + if (! $this->nodeComparator->areNodesEqual($assignment->var, $assignedVariable)) { + return false; + } + + if (! $this->valueResolver->isFalse($assignment->expr)) { + return false; + } + + if (! $foreach->valueVar instanceof Variable) { + return false; + } + + $type = $this->nodeTypeResolver->getNativeType($foreach->expr); + return $type->isArray() + ->yes(); + } +} diff --git a/rules/Php84/Rector/Foreach_/ForeachToArrayAnyRector.php b/rules/Php84/Rector/Foreach_/ForeachToArrayAnyRector.php new file mode 100644 index 00000000000..e12d4140398 --- /dev/null +++ b/rules/Php84/Rector/Foreach_/ForeachToArrayAnyRector.php @@ -0,0 +1,338 @@ + str_starts_with($animal, 'c')); +CODE_SAMPLE + ), + new CodeSample( + <<<'CODE_SAMPLE' +foreach ($animals as $animal) { + if (str_starts_with($animal, 'c')) { + return true; + } +} +return false; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +return array_any($animals, fn($animal) => str_starts_with($animal, 'c')); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + return $this->refactorBooleanAssignmentPattern($node) + ?? $this->refactorEarlyReturnPattern($node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_ANY; + } + + /** + * @param StmtsAware $stmtsAware + */ + private function refactorBooleanAssignmentPattern(Node $stmtsAware): ?Node + { + if ($stmtsAware->stmts === null) { + return null; + } + + foreach ($stmtsAware->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } + + $prevStmt = $stmtsAware->stmts[$key - 1] ?? null; + if (! $prevStmt instanceof Expression) { + continue; + } + + if (! $prevStmt->expr instanceof Assign) { + continue; + } + + $foreach = $stmt; + $prevAssign = $prevStmt->expr; + + if (! $this->valueResolver->isFalse($prevAssign->expr)) { + continue; + } + + if (! $prevAssign->var instanceof Variable) { + continue; + } + + $assignedVariable = $prevAssign->var; + + if (! $this->isValidBooleanAssignmentForeachStructure($foreach, $assignedVariable)) { + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt( + $stmtsAware, + $key + 1, + (string) $this->getName($foreach->valueVar) + )) { + continue; + } + + /** @var If_ $firstNodeInsideForeach */ + $firstNodeInsideForeach = $foreach->stmts[0]; + + $condition = $firstNodeInsideForeach->cond; + $valueParam = $foreach->valueVar; + + if (! $valueParam instanceof Variable) { + continue; + } + + $params = [new Param($valueParam)]; + + if ($foreach->keyVar instanceof Variable && $this->foreachKeyUsedInConditionalAnalyzer->isUsed( + $foreach->keyVar, + $condition + )) { + $params[] = new Param(new Variable((string) $this->getName($foreach->keyVar))); + } + + $arrowFunction = new ArrowFunction([ + 'params' => $params, + 'expr' => $condition, + ]); + + $funcCall = $this->nodeFactory->createFuncCall('array_any', [$foreach->expr, $arrowFunction]); + + $newAssign = new Assign($assignedVariable, $funcCall); + $newExpression = new Expression($newAssign); + + unset($stmtsAware->stmts[$key - 1]); + $stmtsAware->stmts[$key] = $newExpression; + + $stmtsAware->stmts = array_values($stmtsAware->stmts); + + return $stmtsAware; + } + + return null; + } + + /** + * @param StmtsAware $stmtsAware + */ + private function refactorEarlyReturnPattern(Node $stmtsAware): ?Node + { + if ($stmtsAware->stmts === null) { + return null; + } + + foreach ($stmtsAware->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } + + $foreach = $stmt; + $nextStmt = $stmtsAware->stmts[$key + 1] ?? null; + + if (! $nextStmt instanceof Return_) { + continue; + } + + if (! $nextStmt->expr instanceof Expr) { + continue; + } + + if (! $this->valueResolver->isFalse($nextStmt->expr)) { + continue; + } + + if (! $this->isValidEarlyReturnForeachStructure($foreach)) { + continue; + } + + /** @var If_ $firstNodeInsideForeach */ + $firstNodeInsideForeach = $foreach->stmts[0]; + $condition = $firstNodeInsideForeach->cond; + + $params = []; + + if ($foreach->valueVar instanceof Variable) { + $params[] = new Param($foreach->valueVar); + } + + if ( + $foreach->keyVar instanceof Variable && + $this->foreachKeyUsedInConditionalAnalyzer->isUsed($foreach->keyVar, $condition) + ) { + $params[] = new Param(new Variable((string) $this->getName($foreach->keyVar))); + } + + $arrowFunction = new ArrowFunction([ + 'params' => $params, + 'expr' => $condition, + ]); + + $funcCall = $this->nodeFactory->createFuncCall('array_any', [$foreach->expr, $arrowFunction]); + + $stmtsAware->stmts[$key] = new Return_($funcCall); + unset($stmtsAware->stmts[$key + 1]); + $stmtsAware->stmts = array_values($stmtsAware->stmts); + + return $stmtsAware; + } + + return null; + } + + private function isValidBooleanAssignmentForeachStructure(Foreach_ $foreach, Variable $assignedVariable): bool + { + if (count($foreach->stmts) !== 1) { + return false; + } + + $firstStmt = $foreach->stmts[0]; + if ( + ! $firstStmt instanceof If_ || + count($firstStmt->stmts) !== 2 + ) { + return false; + } + + $assignmentStmt = $firstStmt->stmts[0]; + $breakStmt = $firstStmt->stmts[1]; + + if ( + ! $assignmentStmt instanceof Expression || + ! $assignmentStmt->expr instanceof Assign || + ! $breakStmt instanceof Break_ + ) { + return false; + } + + $assignment = $assignmentStmt->expr; + + if (! $this->nodeComparator->areNodesEqual($assignment->var, $assignedVariable)) { + return false; + } + + if (! $this->valueResolver->isTrue($assignment->expr)) { + return false; + } + + $type = $this->nodeTypeResolver->getNativeType($foreach->expr); + return $type->isArray() + ->yes(); + } + + private function isValidEarlyReturnForeachStructure(Foreach_ $foreach): bool + { + if (count($foreach->stmts) !== 1) { + return false; + } + + if (! $foreach->stmts[0] instanceof If_) { + return false; + } + + $ifStmt = $foreach->stmts[0]; + + if (count($ifStmt->stmts) !== 1) { + return false; + } + + if (! $ifStmt->stmts[0] instanceof Return_) { + return false; + } + + $returnStmt = $ifStmt->stmts[0]; + + if (! $returnStmt->expr instanceof Expr) { + return false; + } + + if (! $this->valueResolver->isTrue($returnStmt->expr)) { + return false; + } + + if (! $foreach->valueVar instanceof Variable) { + return false; + } + + $type = $this->nodeTypeResolver->getNativeType($foreach->expr); + + return $type->isArray() + ->yes(); + } +} diff --git a/rules/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector.php b/rules/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector.php new file mode 100644 index 00000000000..10223631be0 --- /dev/null +++ b/rules/Php84/Rector/Foreach_/ForeachToArrayFindKeyRector.php @@ -0,0 +1,214 @@ + $animal) { + if (str_starts_with($animal, 'c')) { + $found = $idx; + break; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$animals = ['dog', 'cat', 'cow', 'duck', 'goose']; + +$found = array_find_key($animals, fn($animal) => str_starts_with($animal, 'c')); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } + + $prevStmt = $node->stmts[$key - 1] ?? null; + if (! $prevStmt instanceof Expression) { + continue; + } + + if (! $prevStmt->expr instanceof Assign) { + continue; + } + + $foreach = $stmt; + $prevAssign = $prevStmt->expr; + + if (! $this->valueResolver->isNull($prevAssign->expr)) { + continue; + } + + if (! $prevAssign->var instanceof Variable) { + continue; + } + + $assignedVariable = $prevAssign->var; + + if (! $this->isValidForeachStructure($foreach, $assignedVariable)) { + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt( + $node, + $key + 1, + (string) $this->getName($foreach->valueVar) + )) { + continue; + } + + /** @var If_ $firstNodeInsideForeach */ + $firstNodeInsideForeach = $foreach->stmts[0]; + + $condition = $firstNodeInsideForeach->cond; + $valueParam = $foreach->valueVar; + + if (! $valueParam instanceof Variable) { + continue; + } + + $params = [new Param($valueParam)]; + + if ($foreach->keyVar instanceof Variable && $this->foreachKeyUsedInConditionalAnalyzer->isUsed( + $foreach->keyVar, + $condition + )) { + $params[] = new Param(new Variable((string) $this->getName($foreach->keyVar))); + } + + $arrowFunction = new ArrowFunction([ + 'params' => $params, + 'expr' => $condition, + ]); + + $funcCall = $this->nodeFactory->createFuncCall('array_find_key', [$foreach->expr, $arrowFunction]); + + $newAssign = new Assign($assignedVariable, $funcCall); + $newExpression = new Expression($newAssign); + + unset($node->stmts[$key - 1]); + $node->stmts[$key] = $newExpression; + + $node->stmts = array_values($node->stmts); + + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_FIND_KEY; + } + + private function isValidForeachStructure(Foreach_ $foreach, Variable $assignedVariable): bool + { + if ( + ! $foreach->keyVar instanceof Expr || + count($foreach->stmts) !== 1 + ) { + return false; + } + + $firstStmt = $foreach->stmts[0]; + if ( + ! $firstStmt instanceof If_ || + count($firstStmt->stmts) !== 2 + ) { + return false; + } + + $assignmentStmt = $firstStmt->stmts[0]; + $breakStmt = $firstStmt->stmts[1]; + + if ( + ! $assignmentStmt instanceof Expression || + ! $assignmentStmt->expr instanceof Assign || + ! $breakStmt instanceof Break_ + ) { + return false; + } + + $assignment = $assignmentStmt->expr; + + if (! $this->nodeComparator->areNodesEqual($assignment->var, $assignedVariable)) { + return false; + } + + if (! $this->nodeComparator->areNodesEqual($assignment->expr, $foreach->keyVar)) { + return false; + } + + if (! $foreach->valueVar instanceof Variable) { + return false; + } + + $type = $this->nodeTypeResolver->getNativeType($foreach->expr); + return $type->isArray() + ->yes(); + } +} diff --git a/rules/Php84/Rector/Foreach_/ForeachToArrayFindRector.php b/rules/Php84/Rector/Foreach_/ForeachToArrayFindRector.php new file mode 100644 index 00000000000..82d73649e79 --- /dev/null +++ b/rules/Php84/Rector/Foreach_/ForeachToArrayFindRector.php @@ -0,0 +1,206 @@ + str_starts_with($animal, 'c')); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof Foreach_) { + continue; + } + + $prevStmt = $node->stmts[$key - 1] ?? null; + if (! $prevStmt instanceof Expression) { + continue; + } + + if (! $prevStmt->expr instanceof Assign) { + continue; + } + + $foreach = $stmt; + $prevAssign = $prevStmt->expr; + + if (! $this->valueResolver->isNull($prevAssign->expr)) { + continue; + } + + if (! $prevAssign->var instanceof Variable) { + continue; + } + + $assignedVariable = $prevAssign->var; + + if (! $this->isValidForeachStructure($foreach, $assignedVariable)) { + continue; + } + + if ($this->stmtsManipulator->isVariableUsedInNextStmt( + $node, + $key + 1, + (string) $this->getName($foreach->valueVar) + )) { + continue; + } + + /** @var If_ $firstNodeInsideForeach */ + $firstNodeInsideForeach = $foreach->stmts[0]; + + $condition = $firstNodeInsideForeach->cond; + $valueParam = $foreach->valueVar; + + if (! $valueParam instanceof Variable) { + continue; + } + + $params = [new Param($valueParam)]; + + if ($foreach->keyVar instanceof Variable && $this->foreachKeyUsedInConditionalAnalyzer->isUsed( + $foreach->keyVar, + $condition + )) { + $params[] = new Param(new Variable((string) $this->getName($foreach->keyVar))); + } + + $arrowFunction = new ArrowFunction([ + 'params' => $params, + 'expr' => $condition, + ]); + + $funcCall = $this->nodeFactory->createFuncCall('array_find', [$foreach->expr, $arrowFunction]); + + $newAssign = new Assign($assignedVariable, $funcCall); + $newExpression = new Expression($newAssign); + + unset($node->stmts[$key - 1]); + $node->stmts[$key] = $newExpression; + + $node->stmts = array_values($node->stmts); + + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_FIND; + } + + private function isValidForeachStructure(Foreach_ $foreach, Variable $assignedVariable): bool + { + if (count($foreach->stmts) !== 1) { + return false; + } + + $firstStmt = $foreach->stmts[0]; + if ( + ! $firstStmt instanceof If_ || + count($firstStmt->stmts) !== 2 + ) { + return false; + } + + $assignmentStmt = $firstStmt->stmts[0]; + $breakStmt = $firstStmt->stmts[1]; + + if ( + ! $assignmentStmt instanceof Expression || + ! $assignmentStmt->expr instanceof Assign || + ! $breakStmt instanceof Break_ + ) { + return false; + } + + $assignment = $assignmentStmt->expr; + + if (! $this->nodeComparator->areNodesEqual($assignment->var, $assignedVariable)) { + return false; + } + + if (! $this->nodeComparator->areNodesEqual($assignment->expr, $foreach->valueVar)) { + return false; + } + + if (! $foreach->valueVar instanceof Variable) { + return false; + } + + $type = $this->nodeTypeResolver->getNativeType($foreach->expr); + return $type->isArray() + ->yes(); + } +} diff --git a/rules/Php84/Rector/FuncCall/AddEscapeArgumentRector.php b/rules/Php84/Rector/FuncCall/AddEscapeArgumentRector.php new file mode 100644 index 00000000000..64b8985a157 --- /dev/null +++ b/rules/Php84/Rector/FuncCall/AddEscapeArgumentRector.php @@ -0,0 +1,120 @@ +isFirstClassCallable()) { + return null; + } + + if ($node instanceof FuncCall) { + if (! $this->isNames($node, ['fputcsv', 'fgetcsv', 'str_getcsv'])) { + return null; + } + + if ($this->shouldSkipNamedArg($node)) { + return null; + } + + $name = $this->getName($node); + + if (in_array($name, ['fputcsv', 'fgetcsv'], true) && isset($node->getArgs()[4])) { + return null; + } + + if ($name === 'str_getcsv' && isset($node->getArgs()[3])) { + return null; + } + + $node->args[count($node->getArgs())] = new Arg(new String_('\\'), name: new Identifier('escape')); + return $node; + } + + if (! $this->isObjectType($node->var, new ObjectType('SplFileObject'))) { + return null; + } + + $name = $this->getName($node->name); + + if (! in_array($name, ['setCsvControl', 'fputcsv', 'fgetcsv'], true)) { + return null; + } + + if ($this->shouldSkipNamedArg($node)) { + return null; + } + + if (in_array($name, ['setCsvControl', 'fgetcsv'], true) && isset($node->getArgs()[2])) { + return null; + } + + if ($name === 'fputcsv' && isset($node->getArgs()[3])) { + return null; + } + + $node->args[count($node->getArgs())] = new Arg(new String_('\\'), name: new Identifier('escape')); + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::REQUIRED_ESCAPE_PARAMETER; + } + + private function shouldSkipNamedArg(FuncCall|MethodCall $node): bool + { + foreach ($node->getArgs() as $arg) { + // already defined in named arg + if ($arg->name instanceof Identifier && $arg->name->toString() === 'escape') { + return true; + } + } + + return false; + } +} diff --git a/rules/Php84/Rector/FuncCall/RoundingModeEnumRector.php b/rules/Php84/Rector/FuncCall/RoundingModeEnumRector.php new file mode 100644 index 00000000000..40df90da2e6 --- /dev/null +++ b/rules/Php84/Rector/FuncCall/RoundingModeEnumRector.php @@ -0,0 +1,99 @@ +isName($node, 'round')) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + + if (count($args) !== 3) { + return null; + } + + if (! isset($args[2])) { + return null; + } + + $modeArg = $args[2]->value; + + $hasChanged = false; + if ($modeArg instanceof ConstFetch) { + $enumCase = match ($modeArg->name->toString()) { + 'PHP_ROUND_HALF_UP' => 'HalfAwayFromZero', + 'PHP_ROUND_HALF_DOWN' => 'HalfTowardsZero', + 'PHP_ROUND_HALF_EVEN' => 'HalfEven', + 'PHP_ROUND_HALF_ODD' => 'HalfOdd', + default => null, + }; + + if ($enumCase === null) { + return null; + } + + $args[2]->value = new ClassConstFetch(new FullyQualified('RoundingMode'), $enumCase); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ROUNDING_MODES; + } +} diff --git a/rules/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector.php b/rules/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector.php new file mode 100644 index 00000000000..b559a7b1539 --- /dev/null +++ b/rules/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector.php @@ -0,0 +1,97 @@ +> + */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Remove parentheses on new method call with parentheses', + [ + new CodeSample( + <<<'CODE_SAMPLE' +(new Request())->withMethod('GET')->withUri('/hello-world'); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +new Request()->withMethod('GET')->withUri('/hello-world'); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @param MethodCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->var instanceof New_) { + return null; + } + + $oldTokens = $this->getFile() + ->getOldTokens(); + + $loop = 1; + while (isset($oldTokens[$node->var->getStartTokenPos() + $loop])) { + if (trim((string) $oldTokens[$node->var->getStartTokenPos() + $loop]) === '') { + ++$loop; + continue; + } + + if ((string) $oldTokens[$node->var->getStartTokenPos() + $loop] !== '(') { + break; + } + + return null; + } + + // start node + if (! isset($oldTokens[$node->getStartTokenPos()])) { + return null; + } + + // end of "var" node + if (! isset($oldTokens[$node->var->getEndTokenPos()])) { + return null; + } + + if ((string) $oldTokens[$node->getStartTokenPos()] === '(' && (string) $oldTokens[$node->var->getEndTokenPos()] === ')') { + $oldTokens[$node->getStartTokenPos()]->text = ''; + $oldTokens[$node->var->getEndTokenPos()]->text = ''; + + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NEW_METHOD_CALL_WITHOUT_PARENTHESES; + } +} diff --git a/rules/Php84/Rector/Param/ExplicitNullableParamTypeRector.php b/rules/Php84/Rector/Param/ExplicitNullableParamTypeRector.php new file mode 100644 index 00000000000..f85b909e68b --- /dev/null +++ b/rules/Php84/Rector/Param/ExplicitNullableParamTypeRector.php @@ -0,0 +1,107 @@ +type instanceof Node) { + return null; + } + + if (! $node->default instanceof ConstFetch || ! $this->valueResolver->isNull($node->default)) { + return null; + } + + $nodeType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($node->type); + if (TypeCombinator::containsNull($nodeType)) { + return null; + } + + // mixed can't be nullable, ref https://3v4l.org/YUkhH/rfc#vgit.master + if ($nodeType instanceof MixedType) { + return null; + } + + $newNodeType = TypeCombinator::addNull($nodeType); + $paramType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($newNodeType, TypeKind::PARAM); + + // ensure it process valid Node, otherwise, just return null + if (! $paramType instanceof Node) { + return null; + } + + // re-use existing node instead of reprint Node that may cause unnecessary FQCN + if ($node->type instanceof UnionType) { + $node->type->types[] = new Name('null'); + } elseif ($node->type instanceof ComplexType) { + /** @var IntersectionType $nodeType */ + $nodeType = $node->type; + $node->type = new UnionType([$nodeType, new Name('null')]); + } else { + $node->type = new NullableType($node->type); + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_IMPLICIT_NULLABLE_PARAM_TYPE; + } +} diff --git a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php new file mode 100644 index 00000000000..067d5463399 --- /dev/null +++ b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php @@ -0,0 +1,193 @@ +> + */ + public function getNodeTypes(): array + { + return [ArrayDimFetch::class]; + } + + /** + * @param ArrayDimFetch $node + */ + public function refactor(Node $node): ?FuncCall + { + if ($node->dim instanceof FuncCall) { + return $this->refactorArrayKeyPattern($node); + } + + if ($node->var instanceof FuncCall && ($node->dim instanceof Int_ || $node->dim instanceof Minus)) { + return $this->refactorArrayValuesPattern($node); + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_FIRST_LAST; + } + + private function refactorArrayKeyPattern(ArrayDimFetch $arrayDimFetch): ?FuncCall + { + if (! $arrayDimFetch->dim instanceof FuncCall) { + return null; + } + + if (! $this->isNames($arrayDimFetch->dim, [self::ARRAY_KEY_FIRST, self::ARRAY_KEY_LAST])) { + return null; + } + + if ($arrayDimFetch->dim->isFirstClassCallable()) { + return null; + } + + if (count($arrayDimFetch->dim->getArgs()) !== 1) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($arrayDimFetch->var, $arrayDimFetch->dim->getArgs()[0]->value)) { + return null; + } + + if ($this->shouldSkip($arrayDimFetch, $arrayDimFetch->var)) { + return null; + } + + $functionName = $this->isName($arrayDimFetch->dim, self::ARRAY_KEY_FIRST) + ? 'array_first' + : 'array_last'; + + return $this->nodeFactory->createFuncCall($functionName, [$arrayDimFetch->var]); + } + + private function refactorArrayValuesPattern(ArrayDimFetch $arrayDimFetch): ?FuncCall + { + if (! $arrayDimFetch->var instanceof FuncCall) { + return null; + } + + if (! $this->isName($arrayDimFetch->var, 'array_values')) { + return null; + } + + if ($arrayDimFetch->var->isFirstClassCallable()) { + return null; + } + + if (count($arrayDimFetch->var->getArgs()) !== 1) { + return null; + } + + if ($this->shouldSkip($arrayDimFetch, $arrayDimFetch)) { + return null; + } + + $arrayArg = $arrayDimFetch->var->getArgs()[0] + ->value; + + if ($arrayDimFetch->dim instanceof Int_ && $arrayDimFetch->dim->value === 0) { + return $this->nodeFactory->createFuncCall('array_first', [$arrayArg]); + } + + if ($arrayDimFetch->dim instanceof Minus) { + if (! $arrayDimFetch->dim->left instanceof FuncCall) { + return null; + } + + if (! $this->isName($arrayDimFetch->dim->left, 'count')) { + return null; + } + + if ($arrayDimFetch->dim->left->isFirstClassCallable()) { + return null; + } + + if (count($arrayDimFetch->dim->left->getArgs()) !== 1) { + return null; + } + + if (! $arrayDimFetch->dim->right instanceof Int_ || $arrayDimFetch->dim->right->value !== 1) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($arrayArg, $arrayDimFetch->dim->left->getArgs()[0]->value)) { + return null; + } + + return $this->nodeFactory->createFuncCall('array_last', [$arrayArg]); + } + + return null; + } + + private function shouldSkip(ArrayDimFetch $arrayDimFetch, Node $scopeNode): bool + { + $scope = ScopeFetcher::fetch($scopeNode); + if ($scope->isInExpressionAssign($arrayDimFetch)) { + return true; + } + + if ($arrayDimFetch->getAttribute(AttributeKey::IS_BEING_ASSIGNED) === true) { + return true; + } + + if ($arrayDimFetch->getAttribute(AttributeKey::IS_ASSIGN_OP_VAR) === true) { + return true; + } + + return (bool) $arrayDimFetch->getAttribute(AttributeKey::IS_UNSET_VAR); + } +} diff --git a/rules/Php85/Rector/ClassMethod/NullDebugInfoReturnRector.php b/rules/Php85/Rector/ClassMethod/NullDebugInfoReturnRector.php new file mode 100644 index 00000000000..7f42e4d59d8 --- /dev/null +++ b/rules/Php85/Rector/ClassMethod/NullDebugInfoReturnRector.php @@ -0,0 +1,108 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node, '__debugInfo')) { + return null; + } + + $hasChanged = \false; + + $this->traverseNodesWithCallable((array) $node->stmts, function (Node $node) use ( + &$hasChanged + ): int|Return_|null { + if ($node instanceof Class_ || $node instanceof Function_ || $node instanceof Closure) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($node instanceof Return_ && (! $node->expr instanceof Expr || $this->valueResolver->isNull( + $node->expr + ))) { + $hasChanged = \true; + $node->expr = new Array_(); + return $node; + } + + return null; + }); + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATED_NULL_DEBUG_INFO_RETURN; + } +} diff --git a/rules/Php85/Rector/Class_/SleepToSerializeRector.php b/rules/Php85/Rector/Class_/SleepToSerializeRector.php new file mode 100644 index 00000000000..a738cc852d4 --- /dev/null +++ b/rules/Php85/Rector/Class_/SleepToSerializeRector.php @@ -0,0 +1,142 @@ + $this->id, + 'name' => $this->name, + ]; + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->getMethod('__serialize') instanceof ClassMethod) { + return null; + } + + $classMethod = $node->getMethod('__sleep'); + if (! $classMethod instanceof ClassMethod) { + return null; + } + + if ($classMethod->returnType instanceof Identifier && $this->isName($classMethod->returnType, 'array')) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($classMethod); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($classMethod, $returns)) { + return null; + } + + $hasChanged = false; + foreach ($returns as $return) { + if (! $return->expr instanceof Array_) { + return null; + } + + if ($return->expr->items !== []) { + $newItems = []; + foreach ($return->expr->items as $item) { + if ($item !== null && $item->value instanceof String_) { + $propName = $item->value->value; + $newItems[] = new ArrayItem( + new PropertyFetch(new Variable('this'), $propName), + $item->value + ); + } + } + + if ($newItems !== []) { + $hasChanged = true; + $return->expr->items = $newItems; + } + } + } + + if ($hasChanged) { + $classMethod->name = new Identifier('__serialize'); + $classMethod->returnType = new Identifier('array'); + return $node; + } + + return null; + } +} diff --git a/rules/Php85/Rector/Class_/WakeupToUnserializeRector.php b/rules/Php85/Rector/Class_/WakeupToUnserializeRector.php new file mode 100644 index 00000000000..6f3e0c2b6a0 --- /dev/null +++ b/rules/Php85/Rector/Class_/WakeupToUnserializeRector.php @@ -0,0 +1,129 @@ + $value) { + if (property_exists($this, $property)) { + $this->{$property} = $value; + } + } + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->getMethod('__unserialize') instanceof ClassMethod) { + return null; + } + + $classMethod = $node->getMethod('__wakeup'); + if (! $classMethod instanceof ClassMethod) { + return null; + } + + $classMethod->name = new Identifier('__unserialize'); + $classMethod->returnType = new Identifier('void'); + + $param = new Param(var: new Variable('data'), type: new Identifier('array')); + + $classMethod->params[] = $param; + + $classMethod->stmts = [$this->assignProperties()]; + + return $node; + } + + private function assignProperties(): Foreach_ + { + $assign = new Assign( + new PropertyFetch(new Variable('this'), new Variable('property')), + new Variable('value') + ); + + $if = new If_( + new FuncCall(new Name('property_exists'), [ + new Arg(new Variable('this')), + new Arg(new Variable('property')), + ]), + [ + 'stmts' => [new Expression($assign)], + ] + ); + + return new Foreach_( + new Variable('data'), + new Variable('value'), + [ + 'keyVar' => new Variable('property'), + 'stmts' => [$if], + ] + ); + } +} diff --git a/rules/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector.php b/rules/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector.php new file mode 100644 index 00000000000..079de99d966 --- /dev/null +++ b/rules/Php85/Rector/Const_/ConstAndTraitDeprecatedAttributeRector.php @@ -0,0 +1,66 @@ +deprecatedAnnotationToDeprecatedAttributeConverter->convert($node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_85; + } +} diff --git a/rules/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector.php b/rules/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector.php new file mode 100644 index 00000000000..58cd90de250 --- /dev/null +++ b/rules/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector.php @@ -0,0 +1,160 @@ + pipe operator', + [ + new CodeSample( + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run($input) + { + $result = trim(strtolower(htmlspecialchars($input))); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run($input) + { + $result = $input + |> htmlspecialchars(...) + |> strtolower(...) + |> trim(...); + } +} +CODE_SAMPLE + )] + ); + } + + public function getNodeTypes(): array + { + return [Expression::class, Return_::class]; + } + + /** + * @param Expression|Return_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + if ($node->expr instanceof Assign) { + $assign = $node->expr; + + $assignedValue = $assign->expr; + $processedValue = $this->processNestedCalls($assignedValue); + + if ($processedValue instanceof Expr && $processedValue !== $assignedValue) { + $assign->expr = $processedValue; + $hasChanged = true; + } + } elseif ($node->expr instanceof FuncCall) { + $funcCall = $node->expr; + + $processedValue = $this->processNestedCalls($funcCall); + + if ($processedValue instanceof Expr && $processedValue !== $funcCall) { + $node->expr = $processedValue; + $hasChanged = true; + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::PIPE_OPERATOER; + } + + private function buildPipeExpression(FuncCall $funcCall, Expr $expr): Pipe + { + // Recursively process inner call to unwrap all nested levels + if ($expr instanceof FuncCall) { + $processed = $this->processNestedCalls($expr, true); + $nestedInner = $processed instanceof Expr ? $processed : $expr; + } else { + $nestedInner = $expr; + } + + return new Pipe($nestedInner, $this->createPlaceholderCall($funcCall)); + } + + private function createPlaceholderCall(FuncCall $funcCall): FuncCall + { + return new FuncCall($funcCall->name, [new VariadicPlaceholder()]); + } + + private function processNestedCalls(Expr $expr, bool $deep = false): ?Expr + { + if (! $expr instanceof FuncCall) { + return null; + } + + if ($expr->isFirstClassCallable()) { + return null; + } + + if (count($expr->args) !== 1) { + return null; + } + + // Check if any argument is a function call + foreach ($expr->args as $arg) { + if (! $arg instanceof Arg) { + return null; + } + + if ($arg->value instanceof FuncCall) { + return $this->buildPipeExpression($expr, $arg->value); + } + + // If we're deep in recursion and hit a non-FuncCall, this is the base + if ($deep) { + // Spread argument can't be converted to pipe — keep the call as-is + if ($arg->unpack) { + return null; + } + + // Return a pipe with the base expression on the left + return new Pipe($arg->value, $this->createPlaceholderCall($expr)); + } + } + + return null; + } +} diff --git a/rules/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector.php b/rules/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector.php new file mode 100644 index 00000000000..5f48a6b067e --- /dev/null +++ b/rules/Php85/Rector/FuncCall/ArrayKeyExistsNullToEmptyStringRector.php @@ -0,0 +1,123 @@ +isFirstClassCallable()) { + return null; + } + + if (! $this->isName($node, 'array_key_exists')) { + return null; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + $args = $node->getArgs(); + + if (count($args) !== 2) { + return null; + } + + $classReflection = $scope->getClassReflection(); + $isTrait = $classReflection instanceof ClassReflection && $classReflection->isTrait(); + + $functionReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + if (! $functionReflection instanceof FunctionReflection) { + return null; + } + + $argPosition = $this->argsAnalyzer->resolveArgPosition($args, 'key', 0); + $originalType = $this->getType($args[$argPosition]->value); + + if ($originalType instanceof UnionType) { + $withoutNullParameterType = TypeCombinator::removeNull($originalType); + if ($withoutNullParameterType->equals($originalType)) { + return null; + } + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($functionReflection, $node, $scope); + $result = $this->nullToStrictStringIntConverter->convertIfNull( + $node, + $args, + $argPosition, + $isTrait, + $scope, + $parametersAcceptor + ); + if ($result instanceof Node) { + return $result; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_NULL_ARG_IN_ARRAY_KEY_EXISTS_FUNCTION; + } +} diff --git a/rules/Php85/Rector/FuncCall/ChrArgModuloRector.php b/rules/Php85/Rector/FuncCall/ChrArgModuloRector.php new file mode 100644 index 00000000000..73af63c27ab --- /dev/null +++ b/rules/Php85/Rector/FuncCall/ChrArgModuloRector.php @@ -0,0 +1,97 @@ +isFirstClassCallable()) { + return null; + } + + if (! $this->isName($node, 'chr')) { + return null; + } + + $args = $node->getArgs(); + + if (! isset($node->args[0])) { + return null; + } + + $argExpr = $args[0]->value; + + if ($argExpr instanceof Mod) { + return null; + } + + $value = $this->valueResolver->getValue($argExpr); + if (! is_int($value)) { + return null; + } + + if ($value >= 0 && $value <= 255) { + return null; + } + + $args[0]->value = new Mod($argExpr, new LNumber(256)); + $node->args = $args; + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_OUTSIDE_INTERVEL_VAL_IN_CHR_FUNCTION; + } +} diff --git a/rules/Php85/Rector/FuncCall/OrdSingleByteRector.php b/rules/Php85/Rector/FuncCall/OrdSingleByteRector.php new file mode 100644 index 00000000000..6b413368af6 --- /dev/null +++ b/rules/Php85/Rector/FuncCall/OrdSingleByteRector.php @@ -0,0 +1,140 @@ +isFirstClassCallable()) { + return null; + } + + if (! $this->isName($node, 'ord')) { + return null; + } + + $args = $node->getArgs(); + if (! isset($args[0])) { + return null; + } + + $firstArg = $args[0]; + + $argExpr = $firstArg->value; + $type = $this->nodeTypeResolver->getNativeType($argExpr); + + if (! $type->isString()->yes() && ! $type->isInteger()->yes()) { + return null; + } + + $value = $this->valueResolver->getValue($argExpr); + $isInt = is_int($value); + + if ($argExpr instanceof String_ && strlen($argExpr->value) === 1) { + return null; + } + + if ($argExpr instanceof Int_ && strlen((string) $argExpr->value) === 1) { + return null; + } + + if (! $argExpr instanceof Int_) { + return $this->refactorStringType($argExpr, $isInt, $args, $node); + } + + return $this->refactorInt($value, $isInt, $args, $node); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_ORD_WITH_MULTIBYTE_STRING; + } + + /** + * @param Arg[] $args + */ + private function refactorStringType(Expr $argExpr, bool $isInt, array $args, FuncCall $funcCall): null|FuncCall + { + if ($argExpr instanceof ArrayDimFetch) { + return null; + } + + if ($isInt) { + return null; + } + + $args[0]->value = new ArrayDimFetch($argExpr, new Int_(0)); + $funcCall->args = $args; + + return $funcCall; + } + + /** + * @param Arg[] $args + */ + private function refactorInt(mixed $value, bool $isInt, array $args, FuncCall $funcCall): FuncCall + { + $value = (string) $value; + $byte = $value[0] ?? ''; + + $byteValue = $isInt ? new Int_((int) $byte) : new String_($byte); + + $args[0]->value = $byteValue; + $funcCall->args = $args; + + return $funcCall; + } +} diff --git a/rules/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector.php b/rules/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector.php new file mode 100644 index 00000000000..7db9eb0514c --- /dev/null +++ b/rules/Php85/Rector/FuncCall/RemoveFinfoBufferContextArgRector.php @@ -0,0 +1,130 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class, MethodCall::class]; + } + + /** + * @param MethodCall|FuncCall $node + */ + public function refactor(Node $node): ?Node + { + // Cannot handle variadic args + if ($node->isFirstClassCallable()) { + return null; + } + + if ($node instanceof FuncCall && ! $this->isName($node->name, 'finfo_buffer')) { + return null; + } + + $objectType = new ObjectType('finfo'); + if ($node instanceof MethodCall && (! $this->nodeTypeResolver->isObjectType( + $node->var, + $objectType + ) || ! $this->isName($node->name, 'buffer'))) { + return null; + } + + if ($this->removeContextArg($node)) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_FINFO_BUFFER_CONTEXT; + } + + private function removeContextArg(FuncCall|MethodCall $callLike): bool + { + // In `finfo::buffer` method calls, the first parameter, compared to `finfo_buffer`, does not exist. + $methodArgCorrection = 0; + if ($callLike instanceof MethodCall) { + $methodArgCorrection = -1; + } + + if (count($callLike->args) <= 2 + $methodArgCorrection) { + return false; + } + + $args = $callLike->getArgs(); + + // Argument 3 ($flags) and argument 4 ($context) are optional, thus named parameters must be considered + if (! $this->argsAnalyzer->hasNamedArg($args)) { + if (count($args) < 4 + $methodArgCorrection) { + return false; + } + + unset($callLike->args[3 + $methodArgCorrection]); + + // update indexed to make printer work as expected + $callLike->args = array_values($callLike->args); + + return true; + } + + // process named arguments + foreach ($args as $position => $arg) { + if ($arg->name instanceof Identifier && $this->isName($arg->name, 'context')) { + unset($callLike->args[$position]); + + // update indexed to make printer work as expected + $callLike->args = array_values($callLike->args); + + return true; + } + } + + return false; + } +} diff --git a/rules/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector.php b/rules/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector.php new file mode 100644 index 00000000000..81957e33bb6 --- /dev/null +++ b/rules/Php85/Rector/Property/AddOverrideAttributeToOverriddenPropertiesRector.php @@ -0,0 +1,171 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::OVERRIDE_ATTRIBUTE_ON_PROPERTIES; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($this->classAnalyzer->isAnonymousClass($node)) { + return null; + } + + $className = (string) $this->getName($node); + if (! $this->reflectionProvider->hasClass($className)) { + return null; + } + + $classReflection = $this->reflectionProvider->getClass($className); + $parentClassReflections = $classReflection->getParents(); + + if ($parentClassReflections === []) { + return null; + } + + $this->hasChanged = false; + + foreach ($node->getProperties() as $property) { + $this->processProperty($property, $parentClassReflections); + } + + if ($this->hasChanged) { + return $node; + } + + return null; + } + + /** + * @param ClassReflection[] $parentClassReflections + */ + private function processProperty(Property $property, array $parentClassReflections): void + { + if ($this->shouldSkipProperty($property)) { + return; + } + + foreach ($property->props as $propertyProperty) { + $propertyName = $this->getName($propertyProperty); + if ($propertyName === null) { + continue; + } + + if ($this->isPropertyOverridden($propertyName, $parentClassReflections)) { + $property->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified(self::OVERRIDE_CLASS))]); + $this->hasChanged = true; + return; + } + } + } + + private function shouldSkipProperty(Property $property): bool + { + if ($property->isPrivate()) { + return true; + } + + return $this->phpAttributeAnalyzer->hasPhpAttribute($property, self::OVERRIDE_CLASS); + } + + /** + * @param ClassReflection[] $parentClassReflections + */ + private function isPropertyOverridden(string $propertyName, array $parentClassReflections): bool + { + foreach ($parentClassReflections as $parentClassReflection) { + if (! $parentClassReflection->hasNativeProperty($propertyName)) { + continue; + } + + $parentProperty = $parentClassReflection->getNativeProperty($propertyName); + return ! $parentProperty->isPrivate(); + } + + return false; + } +} diff --git a/rules/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector.php b/rules/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector.php new file mode 100644 index 00000000000..ab63d8dc69a --- /dev/null +++ b/rules/Php85/Rector/ShellExec/ShellExecFunctionCallOverBackticksRector.php @@ -0,0 +1,85 @@ +$output"; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$output = shell_exec('ls -al'); +echo "
$output
"; +CODE_SAMPLE + ), + ] + ); + } + + public function getNodeTypes(): array + { + return [ShellExec::class]; + } + + /** + * @param ShellExec $node + */ + public function refactor(Node $node): ?Node + { + if ($node->parts === []) { + return null; + } + + $exprs = []; + foreach ($node->parts as $part) { + if ($part instanceof InterpolatedStringPart) { + $exprs[] = new String_($part->value); + continue; + } + + // other parts are Expr (variables, function calls, etc.) + // keep them as-is so they are concatenated + $exprs[] = $part; + } + + // reduce to single concatenated expression + $argExpr = array_shift($exprs); + foreach ($exprs as $expr) { + $argExpr = new Concat($argExpr, $expr); + } + + // create single Arg and call shell_exec + return $this->nodeFactory->createFuncCall('shell_exec', [new Arg($argExpr)]); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::DEPRECATE_BACKTICKS; + } +} diff --git a/rules/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector.php b/rules/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector.php new file mode 100644 index 00000000000..9340ada430d --- /dev/null +++ b/rules/Php85/Rector/StmtsAwareInterface/SequentialAssignmentsToPipeOperatorRector.php @@ -0,0 +1,239 @@ + function1(...) + |> function2(...) + |> function3(...); +CODE_SAMPLE + ), + ] + ); + } + + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::PIPE_OPERATOER; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + $hasChanged = false; + + $statements = $node->stmts; + $totalStatements = count($statements) - 1; + + for ($i = 0; $i < $totalStatements; ++$i) { + $chain = $this->findAssignmentChain($statements, $i); + + if ($chain && count($chain) >= 2) { + $this->processAssignmentChain($node, $chain, $i); + $hasChanged = true; + // Skip processed statements + $i += count($chain) - 1; + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + /** + * @param array $statements + * @return array|null + */ + private function findAssignmentChain(array $statements, int $startIndex): ?array + { + $chain = []; + $currentIndex = $startIndex; + $totalStatements = count($statements); + + while ($currentIndex < $totalStatements) { + $stmt = $statements[$currentIndex]; + + if (! $stmt instanceof Expression) { + break; + } + + $expr = $stmt->expr; + if (! $expr instanceof Assign) { + return null; + } + + // Check if this is a simple function call with one argument + if (! $expr->expr instanceof FuncCall) { + return null; + } + + $funcCall = $expr->expr; + if (count($funcCall->args) !== 1) { + return null; + } + + $arg = $funcCall->args[0]; + if (! $arg instanceof Arg) { + return null; + } + + if ($currentIndex === $startIndex) { + + // First in chain - must be a variable or simple value + if (! $arg->value instanceof Variable && ! $this->exprAnalyzer->isDynamicExpr($arg->value)) { + return null; + } + + $chain[] = [ + 'stmt' => $stmt, + 'assign' => $expr, + 'funcCall' => $funcCall, + ]; + } else { + // Subsequent in chain - must use previous assignment's variable + $previousAssign = $chain[count($chain) - 1]['assign']; + $previousVarName = $this->getName($previousAssign->var); + + if (! $arg->value instanceof Variable || $this->getName($arg->value) !== $previousVarName) { + break; + } + + $chain[] = [ + 'stmt' => $stmt, + 'assign' => $expr, + 'funcCall' => $funcCall, + ]; + } + + ++$currentIndex; + } + + return $chain; + } + + /** + * @param StmtsAware $stmtsAware + * @param array $chain + */ + private function processAssignmentChain(Node $stmtsAware, array $chain, int $startIndex): void + { + if ($stmtsAware->stmts === null) { + return; + } + + $lastAssignment = $chain[count($chain) - 1]['assign']; + + // Get the initial value from the first function call's argument + $firstFuncCall = $chain[0]['funcCall']; + + if (! $firstFuncCall instanceof FuncCall) { + return; + } + + $firstArg = $firstFuncCall->args[0]; + if (! $firstArg instanceof Arg) { + return; + } + + $initialValue = $firstArg->value; + + // Build the pipe chain + $pipeExpression = $initialValue; + + foreach ($chain as $chainItem) { + $funcCall = $chainItem['funcCall']; + $placeholderCall = $this->createPlaceholderCall($funcCall); + $pipeExpression = new Pipe($pipeExpression, $placeholderCall); + } + + if (! $lastAssignment instanceof Assign) { + return; + } + + // Create the final assignment + $assign = new Assign($lastAssignment->var, $pipeExpression); + $finalExpression = new Expression($assign); + + // Replace the statements + $endIndex = $startIndex + count($chain) - 1; + + // Remove all intermediate statements and replace with the final pipe expression + for ($i = $startIndex; $i <= $endIndex; ++$i) { + if ($i === $startIndex) { + $stmtsAware->stmts[$i] = $finalExpression; + } else { + unset($stmtsAware->stmts[$i]); + } + } + + // Reindex the array + $stmtsAware->stmts = array_values($stmtsAware->stmts); + } + + private function createPlaceholderCall(FuncCall $funcCall): FuncCall + { + return new FuncCall($funcCall->name, [new VariadicPlaceholder()]); + } +} diff --git a/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php b/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php new file mode 100644 index 00000000000..da74a4273ef --- /dev/null +++ b/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php @@ -0,0 +1,115 @@ +getFile() + ->getOldTokens(); + + foreach ($node->cases as $key => $case) { + $cond = $case->cond; + + if (! $cond instanceof Expr) { + continue; + } + + $endTokenPos = $cond->getEndTokenPos(); + + if ($endTokenPos < 0) { + continue; + } + + if (count($case->stmts) === 0) { + $startCaseStmtsPos = isset($node->cases[$key + 1]) + ? $node->cases[$key + 1]->getStartTokenPos() + : $node->getEndTokenPos(); + } else { + $startCaseStmtsPos = $case->stmts[0]->getStartTokenPos(); + } + + if ($startCaseStmtsPos < 0) { + continue; + } + + $nextTokenPos = $endTokenPos; + while (++$nextTokenPos < $startCaseStmtsPos) { + if (! isset($oldTokens[$nextTokenPos])) { + continue 2; + } + + $nextToken = $oldTokens[$nextTokenPos]; + if (trim($nextToken->text) === '') { + continue; + } + + if ($nextToken->text === ':') { + continue 2; + } + + if ($nextToken->text === ';') { + $hasChanged = true; + $nextToken->text = ':'; + continue 2; + } + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::COLON_AFTER_SWITCH_CASE; + } +} diff --git a/rules/Php86/Rector/FuncCall/MinMaxToClampRector.php b/rules/Php86/Rector/FuncCall/MinMaxToClampRector.php new file mode 100644 index 00000000000..82cfae518db --- /dev/null +++ b/rules/Php86/Rector/FuncCall/MinMaxToClampRector.php @@ -0,0 +1,161 @@ +isFirstClassCallable()) { + return null; + } + + if ($this->isName($node, 'max')) { + return $this->matchClampFuncCall($node, 'min'); + } + + if ($this->isName($node, 'min')) { + return $this->matchClampFuncCall($node, 'max'); + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::CLAMP; + } + + private function matchClampFuncCall(FuncCall $outerFuncCall, string $expectedInnerFuncName): ?FuncCall + { + $args = $outerFuncCall->getArgs(); + + if (count($args) !== 2) { + return null; + } + + if (! $this->isSupportedArg($args[0]) || ! $this->isSupportedArg($args[1])) { + return null; + } + + $leftValue = $args[0]->value; + $rightValue = $args[1]->value; + + if ($leftValue instanceof FuncCall) { + return $this->createClampFuncCall($outerFuncCall, $leftValue, $rightValue, $expectedInnerFuncName); + } + + if ($rightValue instanceof FuncCall) { + return $this->createClampFuncCall($outerFuncCall, $rightValue, $leftValue, $expectedInnerFuncName); + } + + return null; + } + + private function createClampFuncCall( + FuncCall $outerFuncCall, + FuncCall $innerFuncCall, + Expr $outerBoundExpr, + string $expectedInnerFuncName + ): ?FuncCall { + if ($innerFuncCall->isFirstClassCallable()) { + return null; + } + + if (! $this->isName($innerFuncCall, $expectedInnerFuncName)) { + return null; + } + + $args = $innerFuncCall->getArgs(); + if (count($args) !== 2) { + return null; + } + + if (! $this->isSupportedArg($args[0]) || ! $this->isSupportedArg($args[1])) { + return null; + } + + $valueAndBound = $this->matchValueAndKnownBound($args[0]->value, $args[1]->value); + if ($valueAndBound === null) { + return null; + } + + [$valueExpr, $innerBoundExpr] = $valueAndBound; + + if ($this->isName($outerFuncCall, 'max')) { + return $this->nodeFactory->createFuncCall('clamp', [$valueExpr, $outerBoundExpr, $innerBoundExpr]); + } + + return $this->nodeFactory->createFuncCall('clamp', [$valueExpr, $innerBoundExpr, $outerBoundExpr]); + } + + private function isSupportedArg(Arg $arg): bool + { + return ! $arg->unpack && ! $arg->name instanceof Identifier; + } + + /** + * @return array{Expr, Expr}|null + */ + private function matchValueAndKnownBound(Expr $firstExpr, Expr $secondExpr): ?array + { + $isFirstKnownBound = $this->isKnownBound($firstExpr); + $isSecondKnownBound = $this->isKnownBound($secondExpr); + + if ($isFirstKnownBound === $isSecondKnownBound) { + return null; + } + + if ($isFirstKnownBound) { + return [$secondExpr, $firstExpr]; + } + + return [$firstExpr, $secondExpr]; + } + + private function isKnownBound(Expr $expr): bool + { + return $this->getNativeType($expr) + ->isConstantScalarValue() + ->yes(); + } +} diff --git a/rules/PhpSpecToPHPUnit/LetManipulator.php b/rules/PhpSpecToPHPUnit/LetManipulator.php deleted file mode 100644 index b2b38470fb1..00000000000 --- a/rules/PhpSpecToPHPUnit/LetManipulator.php +++ /dev/null @@ -1,49 +0,0 @@ -getMethods() as $classMethod) { - // new test - if ($this->nodeNameResolver->isName($classMethod, 'test*')) { - continue; - } - - $hasBeConstructedThrough = (bool) $this->betterNodeFinder->find( - (array) $classMethod->stmts, - function (Node $node): ?bool { - if (! $node instanceof MethodCall) { - return null; - } - - return $this->nodeNameResolver->isName($node->name, 'beConstructedThrough'); - } - ); - - if ($hasBeConstructedThrough) { - continue; - } - - return true; - } - - return false; - } -} diff --git a/rules/PhpSpecToPHPUnit/MatchersManipulator.php b/rules/PhpSpecToPHPUnit/MatchersManipulator.php deleted file mode 100644 index 8c38fa6feaf..00000000000 --- a/rules/PhpSpecToPHPUnit/MatchersManipulator.php +++ /dev/null @@ -1,52 +0,0 @@ -getMethod('getMatchers'); - if (! $classMethod instanceof ClassMethod) { - return []; - } - - if (! isset($classMethod->stmts[0])) { - return []; - } - - if (! $classMethod->stmts[0] instanceof Return_) { - return []; - } - - /** @var Return_ $return */ - $return = $classMethod->stmts[0]; - if (! $return->expr instanceof Array_) { - return []; - } - - $keys = []; - foreach ($return->expr->items as $arrayItem) { - if ($arrayItem === null) { - continue; - } - - if ($arrayItem->key instanceof String_) { - $keys[] = $arrayItem->key->value; - } - } - - return $keys; - } -} diff --git a/rules/PhpSpecToPHPUnit/Naming/PhpSpecRenaming.php b/rules/PhpSpecToPHPUnit/Naming/PhpSpecRenaming.php deleted file mode 100644 index b6bb03ad644..00000000000 --- a/rules/PhpSpecToPHPUnit/Naming/PhpSpecRenaming.php +++ /dev/null @@ -1,127 +0,0 @@ -isPrivate()) { - return; - } - - $classMethodName = $this->nodeNameResolver->getName($classMethod); - $classMethodName = $this->removeNamePrefixes($classMethodName); - - // from PhpSpec to PHPUnit method naming convention - $classMethodName = $this->stringFormatConverter->underscoreAndHyphenToCamelCase($classMethodName); - - // add "test", so PHPUnit runs the method - if (! \str_starts_with($classMethodName, 'test')) { - $classMethodName = 'test' . ucfirst($classMethodName); - } - - $classMethod->name = new Identifier($classMethodName); - } - - public function renameExtends(Class_ $class): void - { - $class->extends = new FullyQualified('PHPUnit\Framework\TestCase'); - } - - public function renameNamespace(Class_ $class): void - { - $namespace = $this->betterNodeFinder->findParentType($class, Namespace_::class); - if (! $namespace instanceof Namespace_) { - return; - } - - $namespaceName = $this->nodeNameResolver->getName($namespace); - if ($namespaceName === null) { - return; - } - - $newNamespaceName = StaticRectorStrings::removePrefixes($namespaceName, ['spec\\']); - - $namespace->name = new Name('Tests\\' . $newNamespaceName); - } - - public function renameClass(Class_ $class): void - { - $classShortName = $this->nodeNameResolver->getShortName($class); - - // anonymous class? - if ($classShortName === '') { - throw new ShouldNotHappenException(); - } - - // 2. change class name - $newClassName = StaticRectorStrings::removeSuffixes($classShortName, [self::SPEC]); - $newTestClassName = $newClassName . 'Test'; - - $class->name = new Identifier($newTestClassName); - } - - public function resolveObjectPropertyName(Class_ $class): string - { - // anonymous class? - if ($class->name === null) { - throw new ShouldNotHappenException(); - } - - $shortClassName = $this->nodeNameResolver->getShortName($class); - $bareClassName = StaticRectorStrings::removeSuffixes($shortClassName, [self::SPEC, 'Test']); - - return lcfirst($bareClassName); - } - - public function resolveTestedClass(Node $node): string - { - /** @var string $className */ - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - - $newClassName = StaticRectorStrings::removePrefixes($className, ['spec\\']); - - return StaticRectorStrings::removeSuffixes($newClassName, [self::SPEC]); - } - - private function removeNamePrefixes(string $name): string - { - $originalName = $name; - - $name = StaticRectorStrings::removePrefixes( - $name, - ['it_should_have_', 'it_should_be', 'it_should_', 'it_is_', 'it_', 'is_'] - ); - - return $name ?: $originalName; - } -} diff --git a/rules/PhpSpecToPHPUnit/NodeFactory/AssertMethodCallFactory.php b/rules/PhpSpecToPHPUnit/NodeFactory/AssertMethodCallFactory.php deleted file mode 100644 index f680c6cd865..00000000000 --- a/rules/PhpSpecToPHPUnit/NodeFactory/AssertMethodCallFactory.php +++ /dev/null @@ -1,86 +0,0 @@ -isBoolAssert = false; - - // special case with bool! - if ($expected !== null) { - $name = $this->resolveBoolMethodName($name, $expected); - } - - $assetMethodCall = $this->nodeFactory->createMethodCall('this', $name); - - if (! $this->isBoolAssert && $expected) { - $assetMethodCall->args[] = new Arg($this->thisToTestedObjectPropertyFetch( - $expected, - $testedObjectPropertyFetch - )); - } - - $assetMethodCall->args[] = new Arg($this->thisToTestedObjectPropertyFetch($value, $testedObjectPropertyFetch)); - - return $assetMethodCall; - } - - private function resolveBoolMethodName(string $name, Expr $expr): string - { - if (! $this->valueResolver->isTrueOrFalse($expr)) { - return $name; - } - - $isFalse = $this->valueResolver->isFalse($expr); - if ($name === 'assertSame') { - $this->isBoolAssert = true; - return $isFalse ? 'assertFalse' : 'assertTrue'; - } - - if ($name === 'assertNotSame') { - $this->isBoolAssert = true; - return $isFalse ? 'assertNotFalse' : 'assertNotTrue'; - } - - return $name; - } - - private function thisToTestedObjectPropertyFetch(Expr $expr, PropertyFetch $propertyFetch): Expr - { - if (! $expr instanceof Variable) { - return $expr; - } - - if (! $this->nodeNameResolver->isName($expr, 'this')) { - return $expr; - } - - return $propertyFetch; - } -} diff --git a/rules/PhpSpecToPHPUnit/NodeFactory/BeConstructedWithAssignFactory.php b/rules/PhpSpecToPHPUnit/NodeFactory/BeConstructedWithAssignFactory.php deleted file mode 100644 index 4b46ac9454d..00000000000 --- a/rules/PhpSpecToPHPUnit/NodeFactory/BeConstructedWithAssignFactory.php +++ /dev/null @@ -1,70 +0,0 @@ -nodeNameResolver->isName($methodCall->name, 'beConstructedWith')) { - $new = new New_(new FullyQualified($testedClass)); - $new->args = $methodCall->args; - - return new Assign($propertyFetch, $new); - } - - if ($this->nodeNameResolver->isName($methodCall->name, 'beConstructedThrough')) { - $methodName = $this->valueResolver->getValue($methodCall->args[0]->value); - $staticCall = $this->nodeFactory->createStaticCall($testedClass, $methodName); - - $this->moveConstructorArguments($methodCall, $staticCall); - - return new Assign($propertyFetch, $staticCall); - } - - return null; - } - - private function moveConstructorArguments(MethodCall $methodCall, StaticCall $staticCall): void - { - if (! isset($methodCall->args[1])) { - return; - } - - if (! $methodCall->args[1]->value instanceof Array_) { - return; - } - - /** @var Array_ $array */ - $array = $methodCall->args[1]->value; - foreach ($array->items as $arrayItem) { - if (! $arrayItem instanceof ArrayItem) { - continue; - } - - $staticCall->args[] = new Arg($arrayItem->value); - } - } -} diff --git a/rules/PhpSpecToPHPUnit/NodeFactory/DuringMethodCallFactory.php b/rules/PhpSpecToPHPUnit/NodeFactory/DuringMethodCallFactory.php deleted file mode 100644 index e3320739ab2..00000000000 --- a/rules/PhpSpecToPHPUnit/NodeFactory/DuringMethodCallFactory.php +++ /dev/null @@ -1,51 +0,0 @@ -args[0])) { - throw new ShouldNotHappenException(); - } - - $name = $this->valueResolver->getValue($methodCall->args[0]->value); - $thisObjectPropertyMethodCall = new MethodCall($propertyFetch, $name); - - if (isset($methodCall->args[1]) && $methodCall->args[1]->value instanceof Array_) { - /** @var Array_ $array */ - $array = $methodCall->args[1]->value; - - if (isset($array->items[0])) { - $thisObjectPropertyMethodCall->args[] = new Arg($array->items[0]->value); - } - } - - /** @var MethodCall $parentMethodCall */ - $parentMethodCall = $methodCall->var; - $parentMethodCall->name = new Identifier('expectException'); - - // add $this->object->someCall($withArgs) - $this->nodesToAddCollector->addNodeAfterNode($thisObjectPropertyMethodCall, $methodCall); - - return $parentMethodCall; - } -} diff --git a/rules/PhpSpecToPHPUnit/PHPUnitTypeDeclarationDecorator.php b/rules/PhpSpecToPHPUnit/PHPUnitTypeDeclarationDecorator.php deleted file mode 100644 index 5c4a0ff4965..00000000000 --- a/rules/PhpSpecToPHPUnit/PHPUnitTypeDeclarationDecorator.php +++ /dev/null @@ -1,36 +0,0 @@ -astResolver->resolveClassMethod('PHPUnit\Framework\TestCase', MethodName::SET_UP); - if (! $setUpClassMethod instanceof ClassMethod) { - return; - } - - $classMethod->returnType = $setUpClassMethod->returnType; - } -} diff --git a/rules/PhpSpecToPHPUnit/PhpSpecMockCollector.php b/rules/PhpSpecToPHPUnit/PhpSpecMockCollector.php deleted file mode 100644 index c7aef93101f..00000000000 --- a/rules/PhpSpecToPHPUnit/PhpSpecMockCollector.php +++ /dev/null @@ -1,119 +0,0 @@ -nodeNameResolver->getName($class); - - if (isset($this->mocks[$className]) && $this->mocks[$className] !== []) { - return $this->mocks[$className]; - } - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($class, function (Node $node): void { - if (! $node instanceof ClassMethod) { - return; - } - - if (! $node->isPublic()) { - return; - } - - foreach ($node->params as $param) { - $this->addMockFromParam($param); - } - }); - - // set default value if none was found - if (! isset($this->mocks[$className])) { - $this->mocks[$className] = []; - } - - return $this->mocks[$className]; - } - - public function isVariableMockInProperty(Variable $variable): bool - { - $variableName = $this->nodeNameResolver->getName($variable); - $className = $variable->getAttribute(AttributeKey::CLASS_NAME); - - return in_array($variableName, $this->propertyMocksByClass[$className] ?? [], true); - } - - public function getTypeForClassAndVariable(Class_ $class, string $variable): string - { - $className = $this->nodeNameResolver->getName($class); - - if (! isset($this->mocksWithsTypes[$className][$variable])) { - throw new ShouldNotHappenException(); - } - - return $this->mocksWithsTypes[$className][$variable]; - } - - public function addPropertyMock(string $class, string $property): void - { - $this->propertyMocksByClass[$class][] = $property; - } - - private function addMockFromParam(Param $param): void - { - $variable = $this->nodeNameResolver->getName($param->var); - - /** @var string $class */ - $class = $param->getAttribute(AttributeKey::CLASS_NAME); - - $classMethod = $param->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - throw new ShouldNotHappenException(); - } - - $methodName = $this->nodeNameResolver->getName($classMethod); - $this->mocks[$class][$variable][] = $methodName; - - if ($param->type === null) { - throw new ShouldNotHappenException(); - } - - $paramType = (string) ($param->type->getAttribute(AttributeKey::ORIGINAL_NAME) ?: $param->type); - $this->mocksWithsTypes[$class][$variable] = $paramType; - } -} diff --git a/rules/PhpSpecToPHPUnit/Rector/AbstractPhpSpecToPHPUnitRector.php b/rules/PhpSpecToPHPUnit/Rector/AbstractPhpSpecToPHPUnitRector.php deleted file mode 100644 index 3df3b0c2bfe..00000000000 --- a/rules/PhpSpecToPHPUnit/Rector/AbstractPhpSpecToPHPUnitRector.php +++ /dev/null @@ -1,73 +0,0 @@ -createShippingMethodFor(Argument::any())->shouldBeCalled()->willReturn($shippingMethod); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -namespace spec\SomeNamespaceForThisTest; - -class OrderSpec extends ObjectBehavior -{ - /** - * @var \SomeNamespaceForThisTest\Order - */ - private $order; - protected function setUp() - { - /** @var OrderFactory|\PHPUnit\Framework\MockObject\MockObject $factory */ - $factory = $this->createMock(OrderFactory::class); - - /** @var ShippingMethod|\PHPUnit\Framework\MockObject\MockObject $shippingMethod */ - $shippingMethod = $this->createMock(ShippingMethod::class); - - $factory->expects($this->once())->method('createShippingMethodFor')->willReturn($shippingMethod); - } -} -CODE_SAMPLE - ), - ]); - } - - public function isInPhpSpecBehavior(Node $node): bool - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof ClassLike) { - return false; - } - - return $this->isObjectType($classLike, new ObjectType('PhpSpec\ObjectBehavior')); - } -} diff --git a/rules/PhpSpecToPHPUnit/Rector/ClassMethod/PhpSpecMethodToPHPUnitMethodRector.php b/rules/PhpSpecToPHPUnit/Rector/ClassMethod/PhpSpecMethodToPHPUnitMethodRector.php deleted file mode 100644 index 1870bd12c10..00000000000 --- a/rules/PhpSpecToPHPUnit/Rector/ClassMethod/PhpSpecMethodToPHPUnitMethodRector.php +++ /dev/null @@ -1,86 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isInPhpSpecBehavior($node)) { - return null; - } - - if ($this->isName($node, 'letGo')) { - $node->name = new Identifier(MethodName::TEAR_DOWN); - $this->visibilityManipulator->makeProtected($node); - $this->phpUnitTypeDeclarationDecorator->decorate($node); - } elseif ($this->isName($node, 'let')) { - $node->name = new Identifier(MethodName::SET_UP); - $this->visibilityManipulator->makeProtected($node); - $this->phpUnitTypeDeclarationDecorator->decorate($node); - } elseif ($node->isPublic()) { - $this->processTestMethod($node); - } else { - return null; - } - - return $node; - } - - private function processTestMethod(ClassMethod $classMethod): void - { - // special case, @see https://johannespichler.com/writing-custom-phpspec-matchers/ - if ($this->isName($classMethod, 'getMatchers')) { - return; - } - - // change name to phpunit test case format - $this->phpSpecRenaming->renameMethod($classMethod); - - // reorder instantiation + expected exception - $previousStmt = null; - foreach ((array) $classMethod->stmts as $key => $stmt) { - $printedStmt = $this->print($stmt); - - if ($previousStmt && \str_contains($printedStmt, 'duringInstantiation')) { - $printedPreviousStmt = $this->print($previousStmt); - if (\str_contains($printedPreviousStmt, 'beConstructedThrough')) { - $classMethod->stmts[$key - 1] = $stmt; - $classMethod->stmts[$key] = $previousStmt; - } - } - - $previousStmt = $stmt; - } - } -} diff --git a/rules/PhpSpecToPHPUnit/Rector/Class_/AddMockPropertiesRector.php b/rules/PhpSpecToPHPUnit/Rector/Class_/AddMockPropertiesRector.php deleted file mode 100644 index 44e801126d2..00000000000 --- a/rules/PhpSpecToPHPUnit/Rector/Class_/AddMockPropertiesRector.php +++ /dev/null @@ -1,73 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isInPhpSpecBehavior($node)) { - return null; - } - - $classMocks = $this->phpSpecMockCollector->resolveClassMocksFromParam($node); - - /** @var string $class */ - $class = $node->getAttribute(AttributeKey::CLASS_NAME); - - foreach ($classMocks as $name => $methods) { - if ((is_countable($methods) ? count($methods) : 0) <= 1) { - continue; - } - - // non-ctor used mocks are probably local only - if (! in_array('let', $methods, true)) { - continue; - } - - $this->phpSpecMockCollector->addPropertyMock($class, $name); - - $variableType = $this->phpSpecMockCollector->getTypeForClassAndVariable($node, $name); - - $unionType = new UnionType([ - new ObjectType($variableType), - new ObjectType('PHPUnit\Framework\MockObject\MockObject'), - ]); - - $this->classInsertManipulator->addPropertyToClass($node, $name, $unionType); - } - - return null; - } -} diff --git a/rules/PhpSpecToPHPUnit/Rector/Class_/PhpSpecClassToPHPUnitClassRector.php b/rules/PhpSpecToPHPUnit/Rector/Class_/PhpSpecClassToPHPUnitClassRector.php deleted file mode 100644 index 61486723bdd..00000000000 --- a/rules/PhpSpecToPHPUnit/Rector/Class_/PhpSpecClassToPHPUnitClassRector.php +++ /dev/null @@ -1,164 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isInPhpSpecBehavior($node)) { - return null; - } - - $isAlreadyRenamedToTest = $node->getAttribute(self::ALREADY_RENAMED_TO_TEST); - if ($isAlreadyRenamedToTest) { - return null; - } - - // 1. change namespace name to PHPUnit-like - $this->phpSpecRenaming->renameNamespace($node); - - $propertyName = $this->phpSpecRenaming->resolveObjectPropertyName($node); - - $node->setAttribute(self::ALREADY_RENAMED_TO_TEST, true); - $this->phpSpecRenaming->renameClass($node); - $this->phpSpecRenaming->renameExtends($node); - - $testedClass = $this->phpSpecRenaming->resolveTestedClass($node); - - $testedObjectType = new ObjectType($testedClass); - $this->classInsertManipulator->addPropertyToClass($node, $propertyName, $testedObjectType); - $classMethod = $node->getMethod('let'); - - // add let if missing - if (! $classMethod instanceof ClassMethod) { - if (! $this->letManipulator->isLetNeededInClass($node)) { - return null; - } - - $letClassMethod = $this->createLetClassMethod($propertyName, $testedObjectType); - $this->classInsertManipulator->addAsFirstMethod($node, $letClassMethod); - } - - return $this->removeSelfTypeMethod($node, $testedObjectType); - } - - private function createLetClassMethod(string $propertyName, ObjectType $testedObjectType): ClassMethod - { - $propertyFetch = new PropertyFetch(new Variable('this'), $propertyName); - - $testedObjectType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $testedObjectType, - TypeKind::RETURN() - ); - if (! $testedObjectType instanceof Name) { - throw new ShouldNotHappenException(); - } - - $new = new New_($testedObjectType); - $assign = new Assign($propertyFetch, $new); - - return $this->setUpClassMethodFactory->createSetUpMethod([$assign]); - } - - /** - * This is already checked on construction of object - */ - private function removeSelfTypeMethod(Class_ $class, ObjectType $testedObjectType): Class_ - { - foreach ($class->getMethods() as $classMethod) { - $classMethodStmts = (array) $classMethod->stmts; - if (count($classMethodStmts) !== 1) { - continue; - } - - $innerClassMethodStmt = $this->resolveFirstNonExpressionStmt($classMethodStmts); - if (! $innerClassMethodStmt instanceof MethodCall) { - continue; - } - - if (! $this->isName($innerClassMethodStmt->name, 'shouldHaveType')) { - continue; - } - - // not the tested type - if (! $this->valueResolver->isValue( - $innerClassMethodStmt->args[0]->value, - $testedObjectType->getClassName() - )) { - continue; - } - - // remove it - $this->removeNodeFromStatements($class, $classMethod); - } - - return $class; - } - - /** - * @param Stmt[] $stmts - */ - private function resolveFirstNonExpressionStmt(array $stmts): ?Node - { - if (! isset($stmts[0])) { - return null; - } - - $firstStmt = $stmts[0]; - if ($firstStmt instanceof Expression) { - return $firstStmt->expr; - } - - return $firstStmt; - } -} diff --git a/rules/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector.php b/rules/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector.php deleted file mode 100644 index 5a0563f30b4..00000000000 --- a/rules/PhpSpecToPHPUnit/Rector/Class_/RenameSpecFileToTestFileRector.php +++ /dev/null @@ -1,89 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - $smartFileInfo = $this->file->getSmartFileInfo(); - $oldPathname = $smartFileInfo->getPathname(); - - // ends with Spec.php - if (! Strings::match($oldPathname, self::SPEC_SUFFIX_REGEX)) { - return null; - } - - $newPathName = $this->createPathName($oldPathname); - - $file = $node->getAttribute(AttributeKey::FILE); - $this->removedAndAddedFilesCollector->addMovedFile($file, $newPathName); - - return null; - } - - private function createPathName(string $oldRealPath): string - { - // suffix - $newRealPath = Strings::replace($oldRealPath, self::SPEC_SUFFIX_REGEX, 'Test.php'); - - // directory - return Strings::replace($newRealPath, self::SPEC_REGEX, '/tests/'); - } -} diff --git a/rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecMocksToPHPUnitMocksRector.php b/rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecMocksToPHPUnitMocksRector.php deleted file mode 100644 index 53324cefd3e..00000000000 --- a/rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecMocksToPHPUnitMocksRector.php +++ /dev/null @@ -1,247 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, MethodCall::class]; - } - - /** - * @param ClassMethod|MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isInPhpSpecBehavior($node)) { - return null; - } - - if ($node instanceof ClassMethod) { - // public = tests, protected = internal, private = own (no framework magic) - if ($node->isPrivate()) { - return null; - } - - $this->processMethodParamsToMocks($node); - - return $node; - } - - return $this->processMethodCall($node); - } - - private function processMethodParamsToMocks(ClassMethod $classMethod): void - { - // remove params and turn them to instances - $assigns = []; - foreach ($classMethod->params as $param) { - if (! $param->type instanceof Name) { - throw new ShouldNotHappenException(); - } - - $createMockCall = $this->createCreateMockCall($param, $param->type); - if ($createMockCall !== null) { - $assigns[] = $createMockCall; - } - } - - // remove all params - $classMethod->params = []; - - $classMethod->stmts = array_merge($assigns, (array) $classMethod->stmts); - } - - private function processMethodCall(MethodCall $methodCall): ?MethodCall - { - if ($this->isName($methodCall->name, 'shouldBeCalled')) { - if (! $methodCall->var instanceof MethodCall) { - throw new ShouldNotHappenException(); - } - - $mockMethodName = $this->getName($methodCall->var->name); - if ($mockMethodName === null) { - throw new ShouldNotHappenException(); - } - - $expectedArg = $methodCall->var->args[0]->value ?? null; - - $methodCall->var->name = new Identifier('expects'); - $thisOnceMethodCall = $this->nodeFactory->createLocalMethodCall('atLeastOnce'); - $methodCall->var->args = [new Arg($thisOnceMethodCall)]; - - $methodCall->name = new Identifier('method'); - $methodCall->args = [new Arg(new String_($mockMethodName))]; - - if ($expectedArg !== null) { - return $this->appendWithMethodCall($methodCall, $expectedArg); - } - - return $methodCall; - } - - return null; - } - - /** - * Variable or property fetch, based on number of present params in whole class - */ - private function createCreateMockCall(Param $param, Name $name): ?Expression - { - /** @var Class_ $classLike */ - $classLike = $param->getAttribute(AttributeKey::CLASS_NODE); - - $classMocks = $this->phpSpecMockCollector->resolveClassMocksFromParam($classLike); - - $variable = $this->getName($param->var); - - $classMethod = $param->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - throw new ShouldNotHappenException(); - } - - $methodName = $this->nodeNameResolver->getName($classMethod); - $methodsWithWThisMock = $classMocks[$variable]; - - if ($param->var instanceof Error) { - return null; - } - - // single use: "$mock = $this->createMock()" - if (! $this->phpSpecMockCollector->isVariableMockInProperty($param->var)) { - return $this->createNewMockVariableAssign($param, $name); - } - - $reversedMethodsWithThisMock = array_flip($methodsWithWThisMock); - - // first use of many: "$this->mock = $this->createMock()" - if ($reversedMethodsWithThisMock[$methodName] === 0) { - return $this->createPropertyFetchMockVariableAssign($param, $name); - } - - return null; - } - - private function appendWithMethodCall(MethodCall $methodCall, Expr $expr): MethodCall - { - $withMethodCall = new MethodCall($methodCall, 'with'); - - if ($expr instanceof StaticCall) { - if ($this->isName($expr->class, '*Argument')) { - if ($this->isName($expr->name, 'any')) { - // no added value having this method - return $methodCall; - } - - if ($this->isName($expr->name, 'type')) { - $expr = $this->createIsTypeOrIsInstanceOf($expr); - } - } - } else { - $newExpr = $this->nodeFactory->createLocalMethodCall('equalTo'); - $newExpr->args = [new Arg($expr)]; - $expr = $newExpr; - } - - $withMethodCall->args = [new Arg($expr)]; - - return $withMethodCall; - } - - private function createNewMockVariableAssign(Param $param, Name $name): Expression - { - $methodCall = $this->nodeFactory->createLocalMethodCall('createMock'); - $methodCall->args[] = new Arg(new ClassConstFetch($name, 'class')); - - $assign = new Assign($param->var, $methodCall); - $assignExpression = new Expression($assign); - - // add @var doc comment - $varDoc = $this->createMockVarDoc($param, $name); - $assignExpression->setDocComment(new Doc($varDoc)); - - return $assignExpression; - } - - private function createPropertyFetchMockVariableAssign(Param $param, Name $name): Expression - { - $variable = $this->getName($param->var); - if ($variable === null) { - throw new ShouldNotHappenException(); - } - - $propertyFetch = new PropertyFetch(new Variable('this'), $variable); - - $methodCall = $this->nodeFactory->createLocalMethodCall('createMock'); - $methodCall->args[] = new Arg(new ClassConstFetch($name, 'class')); - - $assign = new Assign($propertyFetch, $methodCall); - - return new Expression($assign); - } - - private function createIsTypeOrIsInstanceOf(StaticCall $staticCall): MethodCall - { - $type = $this->valueResolver->getValue($staticCall->args[0]->value); - - $name = $this->typeAnalyzer->isPhpReservedType($type) ? 'isType' : 'isInstanceOf'; - - return $this->nodeFactory->createLocalMethodCall($name, $staticCall->args); - } - - private function createMockVarDoc(Param $param, Name $name): string - { - $paramType = (string) ($name->getAttribute(AttributeKey::ORIGINAL_NAME) ?: $name); - $variableName = $this->getName($param->var); - - if ($variableName === null) { - throw new ShouldNotHappenException(); - } - - return sprintf( - '/** @var %s|\%s $%s */', - $paramType, - 'PHPUnit\Framework\MockObject\MockObject', - $variableName - ); - } -} diff --git a/rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecPromisesToPHPUnitAssertRector.php b/rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecPromisesToPHPUnitAssertRector.php deleted file mode 100644 index cdef036c5a7..00000000000 --- a/rules/PhpSpecToPHPUnit/Rector/MethodCall/PhpSpecPromisesToPHPUnitAssertRector.php +++ /dev/null @@ -1,289 +0,0 @@ - - */ - private const NEW_METHOD_TO_OLD_METHODS = [ - 'assertInstanceOf' => ['shouldBeAnInstanceOf', 'shouldHaveType', 'shouldReturnAnInstanceOf'], - 'assertSame' => ['shouldBe', 'shouldReturn'], - 'assertNotSame' => ['shouldNotBe', 'shouldNotReturn'], - 'assertCount' => ['shouldHaveCount'], - 'assertEquals' => ['shouldBeEqualTo', 'shouldEqual'], - 'assertNotEquals' => ['shouldNotBeEqualTo'], - 'assertContains' => ['shouldContain'], - 'assertNotContains' => ['shouldNotContain'], - // types - 'assertIsIterable' => ['shouldBeArray'], - 'assertIsNotIterable' => ['shouldNotBeArray'], - 'assertIsString' => ['shouldBeString'], - 'assertIsNotString' => ['shouldNotBeString'], - 'assertIsBool' => ['shouldBeBool', 'shouldBeBoolean'], - 'assertIsNotBool' => ['shouldNotBeBool', 'shouldNotBeBoolean'], - 'assertIsCallable' => ['shouldBeCallable'], - 'assertIsNotCallable' => ['shouldNotBeCallable'], - 'assertIsFloat' => ['shouldBeDouble', 'shouldBeFloat'], - 'assertIsNotFloat' => ['shouldNotBeDouble', 'shouldNotBeFloat'], - 'assertIsInt' => ['shouldBeInt', 'shouldBeInteger'], - 'assertIsNotInt' => ['shouldNotBeInt', 'shouldNotBeInteger'], - 'assertIsNull' => ['shouldBeNull'], - 'assertIsNotNull' => ['shouldNotBeNull'], - 'assertIsNumeric' => ['shouldBeNumeric'], - 'assertIsNotNumeric' => ['shouldNotBeNumeric'], - 'assertIsObject' => ['shouldBeObject'], - 'assertIsNotObject' => ['shouldNotBeObject'], - 'assertIsResource' => ['shouldBeResource'], - 'assertIsNotResource' => ['shouldNotBeResource'], - 'assertIsScalar' => ['shouldBeScalar'], - 'assertIsNotScalar' => ['shouldNotBeScalar'], - 'assertNan' => ['shouldBeNan'], - 'assertFinite' => ['shouldBeFinite', 'shouldNotBeFinite'], - 'assertInfinite' => ['shouldBeInfinite', 'shouldNotBeInfinite'], - ]; - - /** - * @var string - */ - private const THIS = 'this'; - - private ?string $testedClass = null; - - private bool $isPrepared = false; - - /** - * @var string[] - */ - private array $matchersKeys = []; - - private ?PropertyFetch $testedObjectPropertyFetch = null; - - public function __construct( - private MatchersManipulator $matchersManipulator, - private PhpSpecRenaming $phpSpecRenaming, - private AssertMethodCallFactory $assertMethodCallFactory, - private BeConstructedWithAssignFactory $beConstructedWithAssignFactory, - private DuringMethodCallFactory $duringMethodCallFactory - ) { - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - $this->isPrepared = false; - $this->matchersKeys = []; - - if (! $this->isInPhpSpecBehavior($node)) { - return null; - } - - if ($this->isName($node->name, 'getWrappedObject')) { - return $node->var; - } - - if ($this->isName($node->name, 'during')) { - return $this->duringMethodCallFactory->create($node, $this->getTestedObjectPropertyFetch()); - } - - if ($this->isName($node->name, 'duringInstantiation')) { - return $this->processDuringInstantiation($node); - } - - // skip reserved names - if ($this->isNames($node->name, ['getMatchers', 'expectException', 'assert*'])) { - return null; - } - - $this->prepareMethodCall($node); - - if ($this->isName($node->name, 'beConstructed*')) { - return $this->beConstructedWithAssignFactory->create( - $node, - $this->getTestedClass(), - $this->getTestedObjectPropertyFetch() - ); - } - - $this->processMatchersKeys($node); - - foreach (self::NEW_METHOD_TO_OLD_METHODS as $newMethod => $oldMethods) { - if (! $this->isNames($node->name, $oldMethods)) { - continue; - } - - return $this->assertMethodCallFactory->createAssertMethod( - $newMethod, - $node->var, - $node->args[0]->value ?? null, - $this->getTestedObjectPropertyFetch() - ); - } - - if ($this->shouldSkip($node)) { - return null; - } - - if ($this->isName($node->name, 'clone')) { - return new Clone_($this->getTestedObjectPropertyFetch()); - } - - $methodName = $this->getName($node->name); - if ($methodName === null) { - return null; - } - - /** @var Class_ $classLike */ - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - $classMethod = $classLike->getMethod($methodName); - // it's a method call, skip - if ($classMethod !== null) { - return null; - } - - $node->var = $this->getTestedObjectPropertyFetch(); - - return $node; - } - - private function processDuringInstantiation(MethodCall $methodCall): MethodCall - { - /** @var MethodCall $parentMethodCall */ - $parentMethodCall = $methodCall->var; - $parentMethodCall->name = new Identifier('expectException'); - - return $parentMethodCall; - } - - private function prepareMethodCall(MethodCall $methodCall): void - { - if ($this->isPrepared) { - return; - } - - /** @var Class_ $classLike */ - $classLike = $methodCall->getAttribute(AttributeKey::CLASS_NODE); - - $this->matchersKeys = $this->matchersManipulator->resolveMatcherNamesFromClass($classLike); - $this->testedClass = $this->phpSpecRenaming->resolveTestedClass($methodCall); - $this->testedObjectPropertyFetch = $this->createTestedObjectPropertyFetch($classLike); - - $this->isPrepared = true; - } - - private function getTestedObjectPropertyFetch(): PropertyFetch - { - if ($this->testedObjectPropertyFetch === null) { - throw new ShouldNotHappenException(); - } - - return $this->testedObjectPropertyFetch; - } - - private function getTestedClass(): string - { - if ($this->testedClass === null) { - throw new ShouldNotHappenException(); - } - - return $this->testedClass; - } - - /** - * @changelog https://johannespichler.com/writing-custom-phpspec-matchers/ - */ - private function processMatchersKeys(MethodCall $methodCall): void - { - foreach ($this->matchersKeys as $matcherKey) { - if (! $this->isName($methodCall->name, 'should' . ucfirst($matcherKey))) { - continue; - } - - if (! $methodCall->var instanceof MethodCall) { - continue; - } - - // 1. assign callable to variable - $thisGetMatchers = $this->nodeFactory->createMethodCall(self::THIS, 'getMatchers'); - $arrayDimFetch = new ArrayDimFetch($thisGetMatchers, new String_($matcherKey)); - $matcherCallableVariable = new Variable('matcherCallable'); - $assign = new Assign($matcherCallableVariable, $arrayDimFetch); - - // 2. call it on result - $funcCall = new FuncCall($matcherCallableVariable); - $funcCall->args = $methodCall->args; - - $methodCall->name = $methodCall->var->name; - $methodCall->var = $this->getTestedObjectPropertyFetch(); - $methodCall->args = []; - $funcCall->args[] = new Arg($methodCall); - - $this->addNodesAfterNode([$assign, $funcCall], $methodCall); - - $this->removeNode($methodCall); - - return; - } - } - - private function shouldSkip(MethodCall $methodCall): bool - { - if (! $methodCall->var instanceof Variable) { - return true; - } - - if (! $this->nodeNameResolver->isName($methodCall->var, self::THIS)) { - return true; - } - - // skip "createMock" method - return $this->isName($methodCall->name, 'createMock'); - } - - private function createTestedObjectPropertyFetch(Class_ $class): PropertyFetch - { - $propertyName = $this->phpSpecRenaming->resolveObjectPropertyName($class); - - return new PropertyFetch(new Variable(self::THIS), $propertyName); - } -} diff --git a/rules/PhpSpecToPHPUnit/Rector/Variable/MockVariableToPropertyFetchRector.php b/rules/PhpSpecToPHPUnit/Rector/Variable/MockVariableToPropertyFetchRector.php deleted file mode 100644 index 2445d4ec59a..00000000000 --- a/rules/PhpSpecToPHPUnit/Rector/Variable/MockVariableToPropertyFetchRector.php +++ /dev/null @@ -1,53 +0,0 @@ -call() - * ↓ - * $this->mock->call() - * - * @see \Rector\Tests\PhpSpecToPHPUnit\Rector\Variable\PhpSpecToPHPUnitRector\PhpSpecToPHPUnitRectorTest - */ -final class MockVariableToPropertyFetchRector extends AbstractPhpSpecToPHPUnitRector -{ - public function __construct( - private PhpSpecMockCollector $phpSpecMockCollector - ) { - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Variable::class]; - } - - /** - * @param Variable $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isInPhpSpecBehavior($node)) { - return null; - } - - if (! $this->phpSpecMockCollector->isVariableMockInProperty($node)) { - return null; - } - - /** @var string $variableName */ - $variableName = $this->getName($node); - - return new PropertyFetch(new Variable('this'), $variableName); - } -} diff --git a/rules/Privatization/Guard/LaravelModelGuard.php b/rules/Privatization/Guard/LaravelModelGuard.php new file mode 100644 index 00000000000..330c0722b79 --- /dev/null +++ b/rules/Privatization/Guard/LaravelModelGuard.php @@ -0,0 +1,77 @@ +is(LaravelClassName::MODEL)) { + return false; + } + + $name = (string) $this->nodeNameResolver->getName($classMethod->name); + if ($this->isAttributeMethod($name, $classMethod)) { + return true; + } + + return $this->isScopeMethod($name, $classMethod); + } + + private function isAttributeMethod(string $name, ClassMethod $classMethod): bool + { + if (StringUtils::isMatch($name, self::LARAVEL_MODEL_ATTRIBUTE_REGEX)) { + return true; + } + + if (! $classMethod->returnType instanceof Node) { + return false; + } + + return $this->nodeTypeResolver->isObjectType( + $classMethod->returnType, + new ObjectType(LaravelClassName::CAST_ATTRIBUTE) + ); + } + + private function isScopeMethod(string $name, ClassMethod $classMethod): bool + { + if (StringUtils::isMatch($name, self::LARAVEL_MODEL_SCOPE_REGEX)) { + return true; + } + + return $this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, LaravelClassName::ATTRIBUTES_SCOPE); + } +} diff --git a/rules/Privatization/Guard/OverrideByParentClassGuard.php b/rules/Privatization/Guard/OverrideByParentClassGuard.php new file mode 100644 index 00000000000..7ac82b14120 --- /dev/null +++ b/rules/Privatization/Guard/OverrideByParentClassGuard.php @@ -0,0 +1,37 @@ +extends instanceof FullyQualified && ! $this->reflectionProvider->hasClass( + $class->extends->toString() + )) { + return false; + } + + foreach ($class->implements as $implement) { + if (! $this->reflectionProvider->hasClass($implement->toString())) { + return false; + } + } + + return true; + } +} diff --git a/rules/Privatization/Guard/ParentClassMagicCallGuard.php b/rules/Privatization/Guard/ParentClassMagicCallGuard.php new file mode 100644 index 00000000000..0245c0e6557 --- /dev/null +++ b/rules/Privatization/Guard/ParentClassMagicCallGuard.php @@ -0,0 +1,88 @@ + + */ + private array $cachedContainsByClassName = []; + + public function __construct( + private readonly NodeNameResolver $nodeNameResolver, + private readonly AstResolver $astResolver, + private readonly BetterNodeFinder $betterNodeFinder + ) { + foreach (self::KNOWN_DYNAMIC_CALL_CLASSES as $knownDynamicCallClass) { + $this->cachedContainsByClassName[$knownDynamicCallClass] = true; + } + } + + /** + * E.g. parent class has $this->{$magicName} call that might call the protected method + * If we make it private, it will break the code + */ + public function containsParentClassMagicCall(Class_ $class): bool + { + if (! $class->extends instanceof Name) { + return false; + } + + // cache as heavy AST parsing here + $className = $this->nodeNameResolver->getName($class); + + if (isset($this->cachedContainsByClassName[$className])) { + return $this->cachedContainsByClassName[$className]; + } + + $parentClassName = $this->nodeNameResolver->getName($class->extends); + if (isset($this->cachedContainsByClassName[$parentClassName])) { + return $this->cachedContainsByClassName[$parentClassName]; + } + + $parentClass = $this->astResolver->resolveClassFromName($parentClassName); + if (! $parentClass instanceof Class_) { + $this->cachedContainsByClassName[$parentClassName] = false; + return false; + } + + foreach ($parentClass->getMethods() as $classMethod) { + if ($classMethod->isAbstract()) { + continue; + } + + /** @var MethodCall[] $methodCalls */ + $methodCalls = $this->betterNodeFinder->findInstancesOfScoped( + (array) $classMethod->stmts, + MethodCall::class + ); + foreach ($methodCalls as $methodCall) { + if ($methodCall->name instanceof Expr) { + $this->cachedContainsByClassName[$parentClassName] = true; + return true; + } + } + } + + return $this->containsParentClassMagicCall($parentClass); + } +} diff --git a/rules/Privatization/Guard/ParentPropertyLookupGuard.php b/rules/Privatization/Guard/ParentPropertyLookupGuard.php new file mode 100644 index 00000000000..d45a508d8ca --- /dev/null +++ b/rules/Privatization/Guard/ParentPropertyLookupGuard.php @@ -0,0 +1,146 @@ +isAnonymous()) { + return false; + } + + $propertyName = $property instanceof Property + ? $this->nodeNameResolver->getName($property) + : $property; + + if ($this->propertyManipulator->isUsedByTrait($classReflection, $propertyName)) { + return false; + } + + $parentClassName = $this->classReflectionAnalyzer->resolveParentClassName($classReflection); + if ($parentClassName === null) { + return true; + } + + $className = $classReflection->getName(); + $parentClassReflections = $classReflection->getParents(); + + // parent class not autoloaded + if ($parentClassReflections === []) { + return false; + } + + return $this->isGuardedByParents($parentClassReflections, $propertyName, $className); + } + + private function isFoundInParentClassMethods( + ClassReflection $parentClassReflection, + string $propertyName, + string $className + ): bool { + $classLike = $this->astResolver->resolveClassFromClassReflection($parentClassReflection); + if (! $classLike instanceof Class_) { + return false; + } + + $methods = $classLike->getMethods(); + foreach ($methods as $method) { + $isFound = $this->isFoundInMethodStmts((array) $method->stmts, $propertyName, $className); + if ($isFound) { + return true; + } + } + + return false; + } + + /** + * @param Stmt[] $stmts + */ + private function isFoundInMethodStmts(array $stmts, string $propertyName, string $className): bool + { + return (bool) $this->betterNodeFinder->findFirst($stmts, function (Node $subNode) use ( + $propertyName, + $className + ): bool { + if (! $this->propertyFetchAnalyzer->isPropertyFetch($subNode)) { + return false; + } + + if ($subNode instanceof PropertyFetch) { + if (! $subNode->var instanceof Variable) { + return false; + } + + if (! $this->nodeNameResolver->isName($subNode->var, 'this')) { + return false; + } + + return $this->nodeNameResolver->isName($subNode, $propertyName); + } + + if (! $this->nodeNameResolver->isNames( + $subNode->class, + [ObjectReference::SELF, ObjectReference::STATIC, $className] + )) { + return false; + } + + return $this->nodeNameResolver->isName($subNode->name, $propertyName); + }); + } + + /** + * @param ClassReflection[] $parentClassReflections + */ + private function isGuardedByParents(array $parentClassReflections, string $propertyName, string $className): bool + { + foreach ($parentClassReflections as $parentClassReflection) { + if ($parentClassReflection->hasInstanceProperty($propertyName)) { + return false; + } + + if ($parentClassReflection->hasStaticProperty($propertyName)) { + return false; + } + + if ($this->isFoundInParentClassMethods($parentClassReflection, $propertyName, $className)) { + return false; + } + } + + return true; + } +} diff --git a/rules/Privatization/Naming/ConstantNaming.php b/rules/Privatization/Naming/ConstantNaming.php deleted file mode 100644 index bafc5889a8b..00000000000 --- a/rules/Privatization/Naming/ConstantNaming.php +++ /dev/null @@ -1,26 +0,0 @@ -nodeNameResolver->getName($propertyProperty); - - $stringy = new Stringy($propertyName); - return (string) $stringy->underscored() - ->toUpperCase(); - } -} diff --git a/rules/Privatization/NodeAnalyzer/EventSubscriberMethodNamesResolver.php b/rules/Privatization/NodeAnalyzer/EventSubscriberMethodNamesResolver.php deleted file mode 100644 index eaa56c4cec6..00000000000 --- a/rules/Privatization/NodeAnalyzer/EventSubscriberMethodNamesResolver.php +++ /dev/null @@ -1,43 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $classMethod->stmts, - function (Node $node) use (&$methodNames) { - if (! $node instanceof ArrayItem) { - return null; - } - - if (! $node->value instanceof String_) { - return null; - } - - $methodNames[] = $node->value->value; - } - ); - - return $methodNames; - } -} diff --git a/rules/Privatization/NodeAnalyzer/PropertyFetchByMethodAnalyzer.php b/rules/Privatization/NodeAnalyzer/PropertyFetchByMethodAnalyzer.php deleted file mode 100644 index 5a807c5097d..00000000000 --- a/rules/Privatization/NodeAnalyzer/PropertyFetchByMethodAnalyzer.php +++ /dev/null @@ -1,214 +0,0 @@ -> - */ - private const SCOPE_CHANGING_NODE_TYPES = [Do_::class, While_::class, If_::class, Else_::class]; - - public function __construct( - private NodeNameResolver $nodeNameResolver, - private PropertyFetchAnalyzer $propertyFetchAnalyzer, - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser - ) { - } - - /** - * @param string[] $propertyNames - * @return array - */ - public function collectPropertyFetchByMethods(Class_ $class, array $propertyNames): array - { - $propertyUsageByMethods = []; - - foreach ($propertyNames as $propertyName) { - if ($this->isPropertyHasDefaultValue($class, $propertyName)) { - continue; - } - - foreach ($class->getMethods() as $classMethod) { - // assigned in constructor injection → skip - if ($this->isInConstructWithPropertyChanging($classMethod, $propertyName)) { - return []; - } - - if ($this->isContainsLocalPropertyFetchNameOrPropertyChangingInMultipleMethodCalls( - $classMethod, - $propertyName - )) { - continue; - } - - if (! $this->isPropertyChanging($classMethod, $propertyName)) { - continue; - } - - $classMethodName = $this->nodeNameResolver->getName($classMethod); - $propertyUsageByMethods[$propertyName][] = $classMethodName; - } - } - - return $propertyUsageByMethods; - } - - private function isContainsLocalPropertyFetchNameOrPropertyChangingInMultipleMethodCalls( - ClassMethod $classMethod, - string $propertyName - ): bool { - if (! $this->propertyFetchAnalyzer->containsLocalPropertyFetchName($classMethod, $propertyName)) { - return true; - } - - return $this->isPropertyChangingInMultipleMethodCalls($classMethod, $propertyName); - } - - private function isPropertyHasDefaultValue(Class_ $class, string $propertyName): bool - { - $property = $class->getProperty($propertyName); - return $property instanceof Property && $property->props[0]->default; - } - - private function isInConstructWithPropertyChanging(ClassMethod $classMethod, string $propertyName): bool - { - if (! $this->nodeNameResolver->isName($classMethod, MethodName::CONSTRUCT)) { - return false; - } - return $this->isPropertyChanging($classMethod, $propertyName); - } - - /** - * Covers https://github.com/rectorphp/rector/pull/2558#discussion_r363036110 - */ - private function isPropertyChangingInMultipleMethodCalls(ClassMethod $classMethod, string $propertyName): bool - { - $isPropertyChanging = false; - $isPropertyReadInIf = false; - $isIfFollowedByAssign = false; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $classMethod->getStmts(), - function (Node $node) use ( - &$isPropertyChanging, - $propertyName, - &$isPropertyReadInIf, - &$isIfFollowedByAssign - ): ?int { - if ($isPropertyReadInIf) { - if (! $this->propertyFetchAnalyzer->isLocalPropertyOfNames($node, [$propertyName])) { - return null; - } - - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Assign && $parentNode->var === $node) { - $isIfFollowedByAssign = true; - } - } - - if (! $this->isScopeChangingNode($node)) { - return null; - } - - $isPropertyReadInIf = $this->verifyPropertyReadInIf($isPropertyReadInIf, $node, $propertyName); - $isPropertyChanging = $this->isPropertyChanging($node, $propertyName); - - if (! $isPropertyChanging) { - return null; - } - - return NodeTraverser::STOP_TRAVERSAL; - } - ); - - return $isPropertyChanging || $isIfFollowedByAssign || $isPropertyReadInIf; - } - - private function verifyPropertyReadInIf(?bool $isPropertyReadInIf, Node $node, string $propertyName): ?bool - { - if ($node instanceof If_) { - $isPropertyReadInIf = $this->refactorIf($node, $propertyName); - } - - return $isPropertyReadInIf; - } - - private function isScopeChangingNode(Node $node): bool - { - foreach (self::SCOPE_CHANGING_NODE_TYPES as $type) { - if (is_a($node, $type, true)) { - return true; - } - } - - return false; - } - - private function refactorIf(If_ $if, string $privatePropertyName): ?bool - { - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($if->cond, function (Node $node) use ( - $privatePropertyName, - &$isPropertyReadInIf - ): ?int { - if (! $this->propertyFetchAnalyzer->isLocalPropertyOfNames($node, [$privatePropertyName])) { - return null; - } - - $isPropertyReadInIf = true; - - return NodeTraverser::STOP_TRAVERSAL; - }); - - return $isPropertyReadInIf; - } - - private function isPropertyChanging(Node $node, string $privatePropertyName): bool - { - $isPropertyChanging = false; - // here cannot be any property assign - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($node, function (Node $node) use ( - &$isPropertyChanging, - $privatePropertyName - ): ?int { - if (! $node instanceof Assign) { - return null; - } - - if (! $node->var instanceof PropertyFetch) { - return null; - } - - if (! $this->nodeNameResolver->isName($node->var->name, $privatePropertyName)) { - return null; - } - - $isPropertyChanging = true; - - return NodeTraverser::STOP_TRAVERSAL; - }); - - return $isPropertyChanging; - } -} diff --git a/rules/Privatization/NodeFactory/ClassConstantFactory.php b/rules/Privatization/NodeFactory/ClassConstantFactory.php deleted file mode 100644 index 39127d3ec4a..00000000000 --- a/rules/Privatization/NodeFactory/ClassConstantFactory.php +++ /dev/null @@ -1,44 +0,0 @@ -props[0]; - - $constantName = $this->constantNaming->createFromProperty($propertyProperty); - - /** @var Expr $defaultValue */ - $defaultValue = $propertyProperty->default; - $const = new Const_($constantName, $defaultValue); - - $classConst = new ClassConst([$const]); - $classConst->flags = $property->flags & ~ Class_::MODIFIER_STATIC; - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - $phpDocInfo->markAsChanged(); - - $classConst->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); - - return $classConst; - } -} diff --git a/rules/Privatization/NodeFactory/ClassConstantFetchValueFactory.php b/rules/Privatization/NodeFactory/ClassConstantFetchValueFactory.php deleted file mode 100644 index 4bd8e480371..00000000000 --- a/rules/Privatization/NodeFactory/ClassConstantFetchValueFactory.php +++ /dev/null @@ -1,43 +0,0 @@ -valueResolver->getValue($expr); - if ($value === null) { - return null; - } - - $constantNamesToValues = $this->classConstantsResolver->getClassConstantNamesToValues($classWithConstants); - foreach ($constantNamesToValues as $constantName => $constantValue) { - if ($constantValue !== $value) { - continue; - } - - return $this->nodeFactory->createClassConstFetch($classWithConstants, $constantName); - } - - return null; - } -} diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index f1681b6fc9e..078a9fe16ad 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -4,75 +4,75 @@ namespace Rector\Privatization\NodeManipulator; +use PhpParser\Modifiers; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use Rector\Core\ValueObject\Visibility; +use Rector\ValueObject\Visibility; use Webmozart\Assert\Assert; +/** + * @see \Rector\Tests\Privatization\NodeManipulator\VisibilityManipulatorTest + */ final class VisibilityManipulator { - public function hasVisibility(ClassMethod | Property | ClassConst | Param $node, int $visibility): bool + public function hasVisibility(Class_ | ClassMethod | Property | ClassConst | Param $node, int $visibility): bool { return (bool) ($node->flags & $visibility); } - public function makeStatic(ClassMethod | Property | ClassConst $node): void + /** + * @api + */ + public function makeStatic(ClassMethod | Property | ClassConst | Param $node): void { $this->addVisibilityFlag($node, Visibility::STATIC); } - public function makeAbstract(ClassMethod | Class_ $node): void - { - $this->addVisibilityFlag($node, Visibility::ABSTRACT); - } - + /** + * @api + */ public function makeNonStatic(ClassMethod | Property $node): void { if (! $node->isStatic()) { return; } - $node->flags -= Class_::MODIFIER_STATIC; + $node->flags -= Modifiers::STATIC; } - public function makeFinal(Class_ | ClassMethod | ClassConst $node): void - { - $this->addVisibilityFlag($node, Visibility::FINAL); - } - - public function makeNonFinal(Class_ | ClassMethod $node): void + /** + * @api + */ + public function makeNonAbstract(ClassMethod | Class_ $node): void { - if (! $node->isFinal()) { + if (! $node->isAbstract()) { return; } - $node->flags -= Class_::MODIFIER_FINAL; + $node->flags -= Modifiers::ABSTRACT; } /** - * This way "abstract", "static", "final" are kept + * @api */ - public function removeVisibility(ClassMethod | Property | ClassConst $node): void + public function makeFinal(Class_ | ClassMethod | Param | ClassConst $node): void { - // no modifier - if ($node->flags === 0) { - return; - } - - if ($node->isPublic()) { - $node->flags -= Class_::MODIFIER_PUBLIC; - } + $this->addVisibilityFlag($node, Visibility::FINAL); + } - if ($node->isProtected()) { - $node->flags -= Class_::MODIFIER_PROTECTED; + /** + * @api + */ + public function makeNonFinal(Class_ | ClassMethod | Param $node): void + { + if (! $this->hasVisibility($node, Visibility::FINAL)) { + return; } - if ($node->isPrivate()) { - $node->flags -= Class_::MODIFIER_PRIVATE; - } + $node->flags -= Modifiers::FINAL; } public function changeNodeVisibility(ClassMethod | Property | ClassConst $node, int $visibility): void @@ -89,36 +89,105 @@ public function changeNodeVisibility(ClassMethod | Property | ClassConst $node, $this->replaceVisibilityFlag($node, $visibility); } - public function makePublic(ClassMethod | Property | ClassConst $node): void + public function makePublic(ClassMethod | Property | ClassConst | Param $node): void { $this->replaceVisibilityFlag($node, Visibility::PUBLIC); } + /** + * @api + */ public function makeProtected(ClassMethod | Property | ClassConst $node): void { $this->replaceVisibilityFlag($node, Visibility::PROTECTED); } - public function makePrivate(ClassMethod | Property | ClassConst $node): void + public function makePrivate(ClassMethod | Property | ClassConst | Param $node): void { $this->replaceVisibilityFlag($node, Visibility::PRIVATE); } + /** + * @api + */ public function removeFinal(Class_ | ClassConst $node): void { - $node->flags -= Class_::MODIFIER_FINAL; + $node->flags -= Modifiers::FINAL; + } + + public function makeReadonly(Class_ | Property | Param $node): void + { + $this->addVisibilityFlag($node, Visibility::READONLY); } - public function removeAbstract(ClassMethod $classMethod): void + /** + * @api + */ + public function isReadonly(Class_ | Property | Param $node): bool { - $classMethod->flags -= Class_::MODIFIER_ABSTRACT; + return $this->hasVisibility($node, Visibility::READONLY); } - public function makeReadonly(Property | Param $node): void + public function removeReadonly(Class_ | Property | Param $node): void { - $this->addVisibilityFlag($node, Class_::MODIFIER_READONLY); + $isConstructorPromotionBefore = $node instanceof Param && $node->isPromoted(); + + $node->flags &= ~Modifiers::READONLY; + + $isConstructorPromotionAfter = $node instanceof Param && $node->isPromoted(); + + if ($node instanceof Param && $isConstructorPromotionBefore && ! $isConstructorPromotionAfter) { + $this->makePublic($node); + } + + if ($node instanceof Property) { + $this->publicize($node); + } } + public function publicize(ClassConst|ClassMethod|Property $node): ClassConst|ClassMethod|Property|null + { + // already non-public + if (! $node->isPublic()) { + return null; + } + + // explicitly public + if ($this->hasVisibility($node, Visibility::PUBLIC)) { + return null; + } + + $this->makePublic($node); + return $node; + } + + /** + * This way "abstract", "static", "final" are kept + */ + private function removeVisibility(ClassMethod | Property | ClassConst | Param $node): void + { + // no modifier + if ($node->flags === 0) { + return; + } + + if ($node->isPublic()) { + $node->flags |= Modifiers::PUBLIC; + $node->flags -= Modifiers::PUBLIC; + } + + if ($node->isProtected()) { + $node->flags -= Modifiers::PROTECTED; + } + + if ($node->isPrivate()) { + $node->flags -= Modifiers::PRIVATE; + } + } + + /** + * @api + */ private function addVisibilityFlag( Class_ | ClassMethod | Property | ClassConst | Param $node, int $visibility @@ -126,14 +195,14 @@ private function addVisibilityFlag( $node->flags |= $visibility; } - private function replaceVisibilityFlag(ClassMethod | Property | ClassConst $node, int $visibility): void + private function replaceVisibilityFlag(ClassMethod | Property | ClassConst | Param $node, int $visibility): void { $isStatic = $node instanceof ClassMethod && $node->isStatic(); if ($isStatic) { - $this->removeVisibility($node); + $this->makeNonStatic($node); } - if ($visibility !== Visibility::STATIC && $visibility !== Visibility::ABSTRACT && $visibility !== Visibility::FINAL) { + if (! in_array($visibility, [Visibility::STATIC, Visibility::ABSTRACT, Visibility::FINAL], true)) { $this->removeVisibility($node); } diff --git a/rules/Privatization/NodeReplacer/PropertyFetchWithConstFetchReplacer.php b/rules/Privatization/NodeReplacer/PropertyFetchWithConstFetchReplacer.php deleted file mode 100644 index 4a426ce07fa..00000000000 --- a/rules/Privatization/NodeReplacer/PropertyFetchWithConstFetchReplacer.php +++ /dev/null @@ -1,54 +0,0 @@ -props[0]; - - $propertyName = $this->nodeNameResolver->getName($property); - $constantName = $this->constantNaming->createFromProperty($propertyProperty); - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($class, function (Node $node) use ( - $propertyName, - $constantName - ): ?ClassConstFetch { - if (! $this->propertyFetchAnalyzer->isLocalPropertyFetch($node)) { - return null; - } - - /** @var PropertyFetch|StaticPropertyFetch $node */ - if (! $this->nodeNameResolver->isName($node->name, $propertyName)) { - return null; - } - - // replace with constant fetch - return $this->nodeFactory->createSelfFetchConstant($constantName, $node); - }); - } -} diff --git a/rules/Privatization/NodeReplacer/PropertyFetchWithVariableReplacer.php b/rules/Privatization/NodeReplacer/PropertyFetchWithVariableReplacer.php deleted file mode 100644 index 08eb3e8b54c..00000000000 --- a/rules/Privatization/NodeReplacer/PropertyFetchWithVariableReplacer.php +++ /dev/null @@ -1,51 +0,0 @@ - $methodsByPropertyName - */ - public function replacePropertyFetchesByVariable(Class_ $class, array $methodsByPropertyName): void - { - foreach ($methodsByPropertyName as $propertyName => $methodNames) { - $methodName = $methodNames[0]; - $classMethod = $class->getMethod($methodName); - if (! $classMethod instanceof ClassMethod) { - continue; - } - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $classMethod->getStmts(), - function (Node $node) use ($propertyName): ?Variable { - if (! $node instanceof PropertyFetch) { - return null; - } - - if (! $this->nodeNameResolver->isName($node, $propertyName)) { - return null; - } - - return new Variable($propertyName); - } - ); - } - } -} diff --git a/rules/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector.php b/rules/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector.php new file mode 100644 index 00000000000..08f8ac42bf4 --- /dev/null +++ b/rules/Privatization/Rector/ClassConst/PrivatizeFinalClassConstantRector.php @@ -0,0 +1,129 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->isFinal()) { + return null; + } + + if (! $this->overrideByParentClassGuard->isLegal($node)) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $hasChanged = false; + + foreach ($node->getConstants() as $classConst) { + if ($this->shouldSkipClassConst($classConst)) { + continue; + } + + if ($this->isDefinedInParentOrInterface($classConst, $classReflection)) { + continue; + } + + $this->visibilityManipulator->makePrivate($classConst); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function shouldSkipClassConst(ClassConst $classConst): bool + { + // Skip if multiple constants are defined in one line to avoid partial visibility complexity + if (count($classConst->consts) !== 1) { + return true; + } + + return ! $classConst->isProtected(); + } + + private function isDefinedInParentOrInterface(ClassConst $classConst, ClassReflection $classReflection): bool + { + $constantName = $this->getName($classConst); + if ($constantName === null) { + return true; + } + + foreach ($classReflection->getAncestors() as $ancestorClassReflection) { + // Skip the class itself + if ($ancestorClassReflection->getName() === $classReflection->getName()) { + continue; + } + + if ($ancestorClassReflection->hasConstant($constantName)) { + return true; + } + } + + return false; + } +} diff --git a/rules/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector.php b/rules/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector.php deleted file mode 100644 index 1d4b5119b3f..00000000000 --- a/rules/Privatization/Rector/ClassMethod/ChangeGlobalVariablesToPropertiesRector.php +++ /dev/null @@ -1,190 +0,0 @@ -variable = 5; - } - - public function run() - { - var_dump($this->variable); - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } - - $this->collectGlobalVariableNamesAndRefactorToPropertyFetch($classLike, $node); - - if ($this->globalVariableNames === []) { - return null; - } - - foreach ($this->globalVariableNames as $globalVariableName) { - $this->propertyToAddCollector->addPropertyWithoutConstructorToClass($globalVariableName, null, $classLike); - } - - return $node; - } - - private function collectGlobalVariableNamesAndRefactorToPropertyFetch(Class_ $class, ClassMethod $classMethod): void - { - $this->globalVariableNames = []; - - $this->traverseNodesWithCallable($classMethod, function (Node $node) use ($class): ?PropertyFetch { - if ($node instanceof Global_) { - $this->refactorGlobal($class, $node); - return null; - } - - if ($node instanceof Variable) { - return $this->refactorGlobalVariable($node); - } - - return null; - }); - } - - private function refactorGlobal(Class_ $class, Global_ $global): void - { - foreach ($global->vars as $var) { - $varName = $this->getName($var); - if ($varName === null) { - return; - } - - if ($this->isReadOnly($class, $varName)) { - return; - } - - $this->globalVariableNames[] = $varName; - } - - $this->removeNode($global); - } - - private function refactorGlobalVariable(Variable $variable): ?PropertyFetch - { - if (! $this->isNames($variable, $this->globalVariableNames)) { - return null; - } - - // replace with property fetch - $variableName = $this->getName($variable); - if ($variableName === null) { - return null; - } - - return $this->nodeFactory->createPropertyFetch('this', $variableName); - } - - private function isReadOnly(Class_ $class, string $globalVariableName): bool - { - /** @var ClassMethod[] $classMethods */ - $classMethods = $this->betterNodeFinder->findInstanceOf($class, ClassMethod::class); - foreach ($classMethods as $classMethod) { - $isReAssign = (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node) use ( - $globalVariableName - ): bool { - if (! $node instanceof Assign) { - return false; - } - if ($node->var instanceof Variable) { - return $this->nodeNameResolver->isName($node->var, $globalVariableName); - } - if ($node->var instanceof PropertyFetch) { - return $this->nodeNameResolver->isName($node->var, $globalVariableName); - } - return false; - }); - - if ($isReAssign) { - return false; - } - } - - return true; - } -} diff --git a/rules/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector.php b/rules/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector.php index f6a532383c9..4b8061775e6 100644 --- a/rules/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector.php +++ b/rules/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector.php @@ -5,12 +5,18 @@ namespace Rector\Privatization\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\Privatization\Guard\LaravelModelGuard; +use Rector\Privatization\Guard\OverrideByParentClassGuard; +use Rector\Privatization\Guard\ParentClassMagicCallGuard; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; use Rector\Privatization\VisibilityGuard\ClassMethodVisibilityGuard; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,7 +26,12 @@ final class PrivatizeFinalClassMethodRector extends AbstractRector { public function __construct( - private ClassMethodVisibilityGuard $classMethodVisibilityGuard + private readonly ClassMethodVisibilityGuard $classMethodVisibilityGuard, + private readonly VisibilityManipulator $visibilityManipulator, + private readonly OverrideByParentClassGuard $overrideByParentClassGuard, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly LaravelModelGuard $laravelModelGuard, + private readonly ParentClassMagicCallGuard $parentClassMagicCallGuard, ) { } @@ -38,7 +49,7 @@ protected function someMethod() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' final class SomeClass { @@ -57,51 +68,98 @@ private function someMethod() */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { + if (! $node->isFinal()) { return null; } - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { + if (! $this->overrideByParentClassGuard->isLegal($node)) { return null; } - if (! $classReflection->isFinal()) { - return null; - } + $scope = ScopeFetcher::fetch($node); - if ($this->shouldSkipClassMethod($node)) { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { return null; } - if ($this->classMethodVisibilityGuard->isClassMethodVisibilityGuardedByParent($node, $classReflection)) { - return null; + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($this->shouldSkipClassMethod($classMethod)) { + continue; + } + + if ($this->laravelModelGuard->isProtectedMethod($classReflection, $classMethod)) { + continue; + } + + if ($this->classMethodVisibilityGuard->isClassMethodVisibilityGuardedByParent( + $classMethod, + $classReflection + )) { + continue; + } + + if ($this->classMethodVisibilityGuard->isClassMethodVisibilityGuardedByTrait( + $classMethod, + $classReflection + )) { + continue; + } + + if ($this->parentClassMagicCallGuard->containsParentClassMagicCall($node)) { + continue; + } + + $this->visibilityManipulator->makePrivate($classMethod); + $hasChanged = true; } - if ($this->classMethodVisibilityGuard->isClassMethodVisibilityGuardedByTrait($node, $classReflection)) { - return null; + if ($hasChanged) { + return $node; } - $this->visibilityManipulator->makePrivate($node); - - return $node; + return null; } private function shouldSkipClassMethod(ClassMethod $classMethod): bool { - if ($this->isName($classMethod, 'createComponent*')) { + // edge case in nette framework + /** @var string $methodName */ + $methodName = $this->getName($classMethod->name); + if (str_starts_with($methodName, 'createComponent')) { return true; } - return ! $classMethod->isProtected(); + if (! $classMethod->isProtected()) { + return true; + } + + if ($classMethod->isMagic()) { + return true; + } + + // if has parent call, its probably overriding parent one → skip it + $hasParentCall = (bool) $this->betterNodeFinder->findFirst( + (array) $classMethod->stmts, + function (Node $node): bool { + if (! $node instanceof StaticCall) { + return false; + } + + return $this->isName($node->class, 'parent'); + } + ); + + return $hasParentCall; } } diff --git a/rules/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector.php b/rules/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector.php deleted file mode 100644 index 43edf17b199..00000000000 --- a/rules/Privatization/Rector/Class_/ChangeLocalPropertyToVariableRector.php +++ /dev/null @@ -1,112 +0,0 @@ -count = 5; - return $this->count; - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $count = 5; - return $count; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->classAnalyzer->isAnonymousClass($node)) { - return null; - } - - $privatePropertyNames = $this->classManipulator->getPrivatePropertyNames($node); - - $propertyUsageByMethods = $this->propertyFetchByMethodAnalyzer->collectPropertyFetchByMethods( - $node, - $privatePropertyNames - ); - if ($propertyUsageByMethods === []) { - return null; - } - - foreach ($propertyUsageByMethods as $propertyName => $methodNames) { - if (count($methodNames) === 1) { - continue; - } - - unset($propertyUsageByMethods[$propertyName]); - } - - $this->propertyFetchWithVariableReplacer->replacePropertyFetchesByVariable($node, $propertyUsageByMethods); - - // remove properties - foreach ($node->getProperties() as $property) { - $classMethodNames = array_keys($propertyUsageByMethods); - if (! $this->isNames($property, $classMethodNames)) { - continue; - } - - $this->removeNode($property); - } - - return $node; - } -} diff --git a/rules/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector.php b/rules/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector.php deleted file mode 100644 index 160c16519ad..00000000000 --- a/rules/Privatization/Rector/Class_/ChangeReadOnlyVariableWithDefaultValueToConstantRector.php +++ /dev/null @@ -1,282 +0,0 @@ - 'expectNotice', - 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', - ]; - - foreach ($replacements as $class => $method) { - } - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var string[] - */ - private const REPLACEMENTS = [ - 'PHPUnit\Framework\TestCase\Notice' => 'expectNotice', - 'PHPUnit\Framework\TestCase\Deprecated' => 'expectDeprecation', - ]; - - public function run() - { - foreach (self::REPLACEMENTS as $class => $method) { - } - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - $readOnlyVariableAssigns = $this->collectReadOnlyVariableAssigns($node); - $readOnlyVariableAssigns = $this->filterOutUniqueNames($readOnlyVariableAssigns); - - if ($readOnlyVariableAssigns === []) { - return null; - } - - foreach ($readOnlyVariableAssigns as $readOnlyVariableAssign) { - $classMethod = $readOnlyVariableAssign->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - throw new ShouldNotHappenException(); - } - - $methodName = $this->getName($classMethod); - $classMethod = $node->getMethod($methodName); - if (! $classMethod instanceof ClassMethod) { - throw new ShouldNotHappenException(); - } - - $this->refactorClassMethod($classMethod, $node, $readOnlyVariableAssigns); - } - - return $node; - } - - /** - * @return Assign[] - */ - private function collectReadOnlyVariableAssigns(Class_ $class): array - { - $readOnlyVariables = []; - - foreach ($class->getMethods() as $classMethod) { - if ($this->isFoundByRefParam($classMethod)) { - return []; - } - - $readOnlyVariableAssignScalarVariables = $this->classMethodAssignManipulator->collectReadyOnlyAssignScalarVariables( - $classMethod - ); - $readOnlyVariables = array_merge($readOnlyVariables, $readOnlyVariableAssignScalarVariables); - } - - return $readOnlyVariables; - } - - /** - * @param Assign[] $assigns - * @return Assign[] - */ - private function filterOutUniqueNames(array $assigns): array - { - $assignsByName = $this->collectAssignsByName($assigns); - $assignsWithUniqueName = []; - foreach ($assignsByName as $assigns) { - $count = count($assigns); - if ($count > 1) { - continue; - } - - $assignsWithUniqueName = array_merge($assignsWithUniqueName, $assigns); - } - - return $assignsWithUniqueName; - } - - /** - * @param Assign[] $assigns - * @return array - */ - private function collectAssignsByName(array $assigns): array - { - $assignsByName = []; - foreach ($assigns as $assign) { - /** @var string $variableName */ - $variableName = $this->getName($assign->var); - - $assignsByName[$variableName][] = $assign; - } - - return $assignsByName; - } - - /** - * @param Assign[] $readOnlyVariableAssigns - */ - private function refactorClassMethod(ClassMethod $classMethod, Class_ $class, array $readOnlyVariableAssigns): void - { - foreach ($readOnlyVariableAssigns as $readOnlyVariableAssign) { - $this->removeNode($readOnlyVariableAssign); - - /** @var Variable|ClassConstFetch $variable */ - $variable = $readOnlyVariableAssign->var; - // already overridden - if (! $variable instanceof Variable) { - continue; - } - - $classConst = $this->createPrivateClassConst($variable, $readOnlyVariableAssign->expr); - - // replace $variable usage in the code with constant - $this->propertyToAddCollector->addConstantToClass($class, $classConst); - - $variableName = $this->getName($variable); - if ($variableName === null) { - throw new ShouldNotHappenException(); - } - - $this->replaceVariableWithClassConstFetch($classMethod, $variableName, $classConst); - } - } - - private function isFoundByRefParam(ClassMethod $classMethod): bool - { - $params = $classMethod->getParams(); - foreach ($params as $param) { - if ($param->byRef) { - return true; - } - } - - return false; - } - - private function createPrivateClassConst(Variable $variable, Expr $expr): ClassConst - { - $constantName = $this->createConstantNameFromVariable($variable); - - $const = new Const_($constantName, $expr); - - $classConst = new ClassConst([$const]); - $classConst->flags = Class_::MODIFIER_PRIVATE; - - $this->mirrorComments($classConst, $variable); - - $constantType = $this->getStaticType($classConst->consts[0]->value); - $this->varAnnotationManipulator->decorateNodeWithType($classConst, $constantType); - - return $classConst; - } - - private function replaceVariableWithClassConstFetch( - ClassMethod $classMethod, - string $variableName, - ClassConst $classConst - ): void { - $constantName = $this->getName($classConst); - if ($constantName === null) { - throw new ShouldNotHappenException(); - } - - $this->traverseNodesWithCallable($classMethod, function (Node $node) use ( - $variableName, - $constantName - ): ?ClassConstFetch { - if (! $node instanceof Variable) { - return null; - } - - if (! $this->nodeNameResolver->isName($node, $variableName)) { - return null; - } - - // replace with constant fetch - $classConstFetch = new ClassConstFetch(new Name('self'), new Identifier($constantName)); - - // needed later - $classConstFetch->setAttribute(AttributeKey::CLASS_NAME, $node->getAttribute(AttributeKey::CLASS_NAME)); - - return $classConstFetch; - }); - } - - private function createConstantNameFromVariable(Variable $variable): string - { - $variableName = $this->getName($variable); - if ($variableName === null) { - throw new ShouldNotHappenException(); - } - - $stringy = new Stringy($variableName); - return (string) $stringy->underscored() - ->toUpperCase(); - } -} diff --git a/rules/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector.php b/rules/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector.php deleted file mode 100644 index 611ab347b3c..00000000000 --- a/rules/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector.php +++ /dev/null @@ -1,149 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkipClass($node)) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - if ($phpDocInfo->hasByAnnotationClasses(self::DOCTRINE_ORM_MAPPING_ANNOTATION)) { - return null; - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); - if ($childrenClassReflections !== []) { - return null; - } - - if ($this->hasEntityOrEmbeddableAttr($node)) { - return null; - } - - $this->visibilityManipulator->makeFinal($node); - - return $node; - } - - private function hasEntityOrEmbeddableAttr(Class_ $class): bool - { - foreach ($class->attrGroups as $attrGroup) { - foreach ($attrGroup->attrs as $attribute) { - if (! $attribute->name instanceof FullyQualified) { - continue; - } - - $className = $this->nodeNameResolver->getName($attribute->name); - if (in_array($className, self::DOCTRINE_ORM_MAPPING_ANNOTATION, true)) { - return true; - } - } - } - - return false; - } - - private function shouldSkipClass(Class_ $class): bool - { - if ($class->isFinal()) { - return true; - } - - if ($class->isAbstract()) { - return true; - } - - return $this->classAnalyzer->isAnonymousClass($class); - } -} diff --git a/rules/Privatization/Rector/Class_/FinalizeTestCaseClassRector.php b/rules/Privatization/Rector/Class_/FinalizeTestCaseClassRector.php new file mode 100644 index 00000000000..0a8db64329d --- /dev/null +++ b/rules/Privatization/Rector/Class_/FinalizeTestCaseClassRector.php @@ -0,0 +1,90 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + // skip obvious cases + if ($node->isAbstract() || $node->isAnonymous() || $node->isFinal()) { + return null; + } + + $className = $this->getName($node); + if (! is_string($className)) { + return null; + } + + if (str_ends_with($className, 'TestCase')) { + return null; + } + + if (! $this->reflectionProvider->hasClass($className)) { + return null; + } + + $classReflection = $this->reflectionProvider->getClass($className); + if (! $classReflection->is('PHPUnit\Framework\TestCase')) { + return null; + } + + $this->visibilityManipulator->makeFinal($node); + + return $node; + } +} diff --git a/rules/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector.php b/rules/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector.php deleted file mode 100644 index 50b0d5414f6..00000000000 --- a/rules/Privatization/Rector/Class_/RepeatedLiteralToClassConstantRector.php +++ /dev/null @@ -1,265 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - // skip tests, where string values are often used as fixtures - if ($this->isName($node, '*Test')) { - return null; - } - - /** @var String_[] $strings */ - $strings = $this->betterNodeFinder->findInstanceOf($node, String_::class); - - $stringsToReplace = $this->resolveStringsToReplace($strings); - if ($stringsToReplace === []) { - return null; - } - - $this->replaceStringsWithClassConstReferences($node, $stringsToReplace); - $this->addClassConsts($stringsToReplace, $node); - - return $node; - } - - /** - * @param String_[] $strings - * @return string[] - */ - private function resolveStringsToReplace(array $strings): array - { - $stringsByValue = []; - foreach ($strings as $string) { - if ($this->shouldSkipString($string)) { - continue; - } - - $stringsByValue[$string->value][] = $string; - } - - $stringsToReplace = []; - - foreach ($stringsByValue as $value => $strings) { - if (count($strings) < self::MINIMAL_VALUE_OCCURRENCE) { - continue; - } - - $stringsToReplace[] = $value; - } - - return $stringsToReplace; - } - - /** - * @param string[] $stringsToReplace - */ - private function replaceStringsWithClassConstReferences(Class_ $class, array $stringsToReplace): void - { - $this->traverseNodesWithCallable($class, function (Node $node) use ($stringsToReplace): ?ClassConstFetch { - if (! $node instanceof String_) { - return null; - } - - if (! $this->valueResolver->isValues($node, $stringsToReplace)) { - return null; - } - - $constantName = $this->createConstName($node->value); - return $this->nodeFactory->createSelfFetchConstant($constantName, $node); - }); - } - - /** - * @param string[] $stringsToReplace - */ - private function addClassConsts(array $stringsToReplace, Class_ $class): void - { - foreach ($stringsToReplace as $stringToReplace) { - $constantName = $this->createConstName($stringToReplace); - - $classConst = $this->nodeFactory->createClassConstant( - $constantName, - new String_($stringToReplace), - Class_::MODIFIER_PRIVATE - ); - - $this->classInsertManipulator->addConstantToClass($class, $stringToReplace, $classConst); - } - } - - private function shouldSkipString(String_ $string): bool - { - $value = $string->value; - - // value is too short - if (strlen($value) < 2) { - return true; - } - - if ($this->reservedKeywordAnalyzer->isReserved($value)) { - return true; - } - - if ($this->isNativeConstantResemblingValue($value)) { - return true; - } - - // is replaceable value? - $matches = Strings::match($value, '#(?<' . self::VALUE . '>[\w\-\/\\_]+)#'); - if (! isset($matches[self::VALUE])) { - return true; - } - - // skip values in another constants - $parentConst = $this->scopeAwareNodeFinder->findParentType($string, [ClassConst::class]); - if ($parentConst !== null) { - return true; - } - - return $matches[self::VALUE] !== $string->value; - } - - private function createConstName(string $value): string - { - // replace slashes and dashes - $value = Strings::replace($value, self::SLASH_AND_DASH_REGEX, self::UNDERSCORE); - - // find beginning numbers - $beginningNumbers = ''; - - $matches = Strings::match($value, '#(?<' . self::NUMBERS . '>[0-9]*)(?<' . self::VALUE . '>.*)#'); - - if (isset($matches[self::NUMBERS])) { - $beginningNumbers = $matches[self::NUMBERS]; - } - - if (isset($matches[self::VALUE])) { - $value = $matches[self::VALUE]; - } - - // convert camelcase parts to underscore - $parts = explode(self::UNDERSCORE, $value); - - $parts = array_map( - fn (string $value): string => StaticRectorStrings::camelCaseToUnderscore($value), - $parts - ); - - // apply "CONST" prefix if constant beginning with number - if ($beginningNumbers !== '') { - $parts = array_merge(['CONST', $beginningNumbers], $parts); - } - - $value = implode(self::UNDERSCORE, $parts); - - return strtoupper(Strings::replace($value, '#_+#', self::UNDERSCORE)); - } - - private function isNativeConstantResemblingValue(string $value): bool - { - $loweredValue = strtolower($value); - - return in_array($loweredValue, ['true', 'false', 'bool', 'null'], true); - } -} diff --git a/rules/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector.php b/rules/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector.php index c0a1d0c82fd..65ac151438a 100644 --- a/rules/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector.php +++ b/rules/Privatization/Rector/MethodCall/PrivatizeLocalGetterToPropertyRector.php @@ -11,8 +11,7 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Return_; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -43,7 +42,7 @@ private function getSome() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -70,42 +69,75 @@ private function getSome() */ public function getNodeTypes(): array { - return [MethodCall::class]; + return [Class_::class]; } /** - * @param MethodCall $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if (! $node->var instanceof Variable) { - return null; - } - - if (! $this->nodeNameResolver->isName($node->var, 'this')) { - return null; - } - - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } - - $methodName = $this->getName($node->name); - if ($methodName === null) { - return null; - } - - $classMethod = $classLike->getMethod($methodName); - if (! $classMethod instanceof ClassMethod) { - return null; + $class = $node; + $hasChanged = false; + $isFinal = $class->isFinal(); + + $this->traverseNodesWithCallable($node, function (Node $node) use ( + $class, + &$hasChanged, + $isFinal + ): ?PropertyFetch { + if (! $node instanceof MethodCall) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + if (! $node->var instanceof Variable) { + return null; + } + + if (! $this->isName($node->var, 'this')) { + return null; + } + + $methodName = $this->getName($node->name); + if ($methodName === null) { + return null; + } + + $classMethod = $class->getMethod($methodName); + if (! $classMethod instanceof ClassMethod) { + return null; + } + + if (! $classMethod->isPrivate() && ! $isFinal) { + return null; + } + + $propertyFetch = $this->matchLocalPropertyFetchInGetterMethod($classMethod); + if (! $propertyFetch instanceof PropertyFetch) { + return null; + } + + $hasChanged = true; + return $propertyFetch; + }); + + if ($hasChanged) { + return $node; } - return $this->matchLocalPropertyFetchInGetterMethod($classMethod); + return null; } private function matchLocalPropertyFetchInGetterMethod(ClassMethod $classMethod): ?PropertyFetch { + if ($classMethod->params !== []) { + return null; + } + $stmts = (array) $classMethod->stmts; if (count($stmts) !== 1) { return null; diff --git a/rules/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector.php b/rules/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector.php deleted file mode 100644 index 5b718006faa..00000000000 --- a/rules/Privatization/Rector/MethodCall/ReplaceStringWithClassConstantRector.php +++ /dev/null @@ -1,147 +0,0 @@ -call('name'); - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $this->call(Placeholder::NAME); - } -} -CODE_SAMPLE -, - [ - self::REPLACE_STRING_WITH_CLASS_CONSTANT => [ - new ReplaceStringWithClassConstant('SomeClass', 'call', 0, 'Placeholder'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node->args === []) { - return null; - } - - $hasChanged = false; - - foreach ($this->replaceStringWithClassConstants as $replaceStringWithClassConstant) { - $desiredArg = $this->matchArg($node, $replaceStringWithClassConstant); - if (! $desiredArg instanceof Arg) { - continue; - } - - $classConstFetch = $this->classConstantFetchValueFactory->create( - $desiredArg->value, - $replaceStringWithClassConstant->getClassWithConstants() - ); - - if (! $classConstFetch instanceof ClassConstFetch) { - continue; - } - - $desiredArg->value = $classConstFetch; - $hasChanged = true; - } - - if ($hasChanged) { - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $this->replaceStringWithClassConstants = $configuration[self::REPLACE_STRING_WITH_CLASS_CONSTANT] ?? []; - } - - private function matchArg( - MethodCall $methodCall, - ReplaceStringWithClassConstant $replaceStringWithClassConstant - ): ?Arg { - if (! $this->isObjectType($methodCall->var, $replaceStringWithClassConstant->getObjectType())) { - return null; - } - - if (! $this->isName($methodCall->name, $replaceStringWithClassConstant->getMethod())) { - return null; - } - - $desiredArg = $methodCall->args[$replaceStringWithClassConstant->getArgPosition()] ?? null; - if (! $desiredArg instanceof Arg) { - return null; - } - - if ($desiredArg->value instanceof ClassConstFetch) { - return null; - } - - return $desiredArg; - } -} diff --git a/rules/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector.php b/rules/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector.php deleted file mode 100644 index 6e565535ae6..00000000000 --- a/rules/Privatization/Rector/Property/ChangeReadOnlyPropertyWithDefaultValueToConstantRector.php +++ /dev/null @@ -1,136 +0,0 @@ -magicMethods as $magicMethod) { - echo $magicMethod; - } - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var string[] - */ - private const MAGIC_METHODS = [ - '__toString', - '__wakeup', - ]; - - public function run() - { - foreach (self::MAGIC_METHODS as $magicMethod) { - echo $magicMethod; - } - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Property::class]; - } - - /** - * @param Property $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkip($node)) { - return null; - } - - /** @var PropertyProperty $onlyProperty */ - $onlyProperty = $node->props[0]; - - // we need default value - if ($onlyProperty->default === null) { - return null; - } - - if (! $node->isPrivate()) { - return null; - } - - // is property read only? - if ($this->propertyManipulator->isPropertyChangeable($node)) { - return null; - } - - /** @var Class_ $classLike */ - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - $this->propertyFetchWithConstFetchReplacer->replace($classLike, $node); - - return $this->classConstantFactory->createFromProperty($node); - } - - private function shouldSkip(Property $property): bool - { - if (count($property->props) !== 1) { - return true; - } - - $classLike = $property->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return true; - } - - return $this->isObjectType($classLike, new ObjectType('PHP_CodeSniffer\Sniffs\Sniff')); - } -} diff --git a/rules/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector.php b/rules/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector.php index 1591a554f97..eacd9346fda 100644 --- a/rules/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector.php +++ b/rules/Privatization/Rector/Property/PrivatizeFinalClassPropertyRector.php @@ -6,11 +6,15 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Privatization\Guard\ParentPropertyLookupGuard; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\Visibility; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,6 +23,13 @@ */ final class PrivatizeFinalClassPropertyRector extends AbstractRector { + public function __construct( + private readonly VisibilityManipulator $visibilityManipulator, + private readonly ParentPropertyLookupGuard $parentPropertyLookupGuard, + private readonly ReflectionResolver $reflectionResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Change property to private if possible', [ @@ -29,7 +40,7 @@ final class SomeClass protected $value; } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' final class SomeClass { @@ -45,39 +56,63 @@ final class SomeClass */ public function getNodeTypes(): array { - return [Property::class]; + return [Class_::class]; } /** - * @param Property $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { + if (! $node->isFinal()) { return null; } - if (! $classLike->isFinal()) { - return null; - } + $hasChanged = false; + $classReflection = $this->reflectionResolver->resolveClassReflection($node); - if ($this->shouldSkipProperty($node)) { + if (! $classReflection instanceof ClassReflection) { return null; } - if ($classLike->extends === null) { - $this->visibilityManipulator->makePrivate($node); - return $node; + foreach ($node->getProperties() as $property) { + if ($this->shouldSkipProperty($property)) { + continue; + } + + if (! $this->parentPropertyLookupGuard->isLegal($property, $classReflection)) { + continue; + } + + $this->visibilityManipulator->makePrivate($property); + $hasChanged = true; } - if ($this->isPropertyVisibilityGuardedByParent($node, $classLike)) { - return null; + $construct = $node->getMethod(MethodName::CONSTRUCT); + if ($construct instanceof ClassMethod) { + foreach ($construct->params as $param) { + if (! $param->isPromoted()) { + continue; + } + + if (! $this->visibilityManipulator->hasVisibility($param, Visibility::PROTECTED)) { + continue; + } + + if (! $this->parentPropertyLookupGuard->isLegal((string) $this->getName($param), $classReflection)) { + continue; + } + + $this->visibilityManipulator->makePrivate($param); + $hasChanged = true; + } } - $this->visibilityManipulator->makePrivate($node); + if ($hasChanged) { + return $node; + } - return $node; + return null; } private function shouldSkipProperty(Property $property): bool @@ -88,27 +123,4 @@ private function shouldSkipProperty(Property $property): bool return ! $property->isProtected(); } - - private function isPropertyVisibilityGuardedByParent(Property $property, Class_ $class): bool - { - if ($class->extends === null) { - return false; - } - - /** @var Scope $scope */ - $scope = $property->getAttribute(AttributeKey::SCOPE); - - /** @var ClassReflection $classReflection */ - $classReflection = $scope->getClassReflection(); - - $propertyName = $this->getName($property); - - foreach ($classReflection->getParents() as $parentClassReflection) { - if ($parentClassReflection->hasProperty($propertyName)) { - return true; - } - } - - return false; - } } diff --git a/rules/Privatization/Reflection/ClassConstantsResolver.php b/rules/Privatization/Reflection/ClassConstantsResolver.php deleted file mode 100644 index 2aae8c2036d..00000000000 --- a/rules/Privatization/Reflection/ClassConstantsResolver.php +++ /dev/null @@ -1,42 +0,0 @@ -> - */ - private array $cachedConstantNamesToValues = []; - - public function __construct( - private ReflectionProvider $reflectionProvider - ) { - } - - /** - * @return array - */ - public function getClassConstantNamesToValues(string $classWithConstants): array - { - if (isset($this->cachedConstantNamesToValues[$classWithConstants])) { - return $this->cachedConstantNamesToValues[$classWithConstants]; - } - - if (! $this->reflectionProvider->hasClass($classWithConstants)) { - return []; - } - - $classReflection = $this->reflectionProvider->getClass($classWithConstants); - $reflectionClass = $classReflection->getNativeReflection(); - - $constantNamesToValues = $reflectionClass->getConstants(); - $this->cachedConstantNamesToValues = $constantNamesToValues; - - return $constantNamesToValues; - } -} diff --git a/rules/Privatization/TypeManipulator/ArrayTypeLeastCommonDenominatorResolver.php b/rules/Privatization/TypeManipulator/ArrayTypeLeastCommonDenominatorResolver.php new file mode 100644 index 00000000000..184554d2467 --- /dev/null +++ b/rules/Privatization/TypeManipulator/ArrayTypeLeastCommonDenominatorResolver.php @@ -0,0 +1,105 @@ + preserve shape. + $allConstantArrayTypes = array_reduce($types, fn ($c, $t): bool => $c && $t instanceof ConstantArrayType, true); + if ($allConstantArrayTypes) { + /** @var ConstantArrayType[] $consts */ + $consts = $types; + + // Compare key sets (by stringified key types) + $firstKeys = array_map( + fn (Type $type): string => $type->describe(VerbosityLevel::typeOnly()), + $consts[0]->getKeyTypes() + ); + foreach ($consts as $c) { + $keys = array_map( + fn (Type $type): string => $type->describe(VerbosityLevel::typeOnly()), + $c->getKeyTypes() + ); + if ($keys !== $firstKeys) { + $allConstantArrayTypes = false; + break; + } + } + + if ($allConstantArrayTypes) { + $resultKeyTypes = $consts[0]->getKeyTypes(); + $valueColumns = []; + foreach ($consts as $const) { + $valueColumns[] = $const->getValueTypes(); + } + + $resultValueTypes = []; + foreach (array_keys($resultKeyTypes) as $i) { + $col = array_column($valueColumns, $i); + $resultValueTypes[] = $this->sharedArrayStructure(...$col); + } + + return new ConstantArrayType($resultKeyTypes, $resultValueTypes); + } + } + + // Generic ArrayType path: reconcile key type + recurse into item types + /** @var ArrayType[] $types */ + /** @var ArrayType[] $arrayTypes */ + $arrayTypes = $types; + + // Try to keep a compatible key type (intersection; fall back to mixed if impossible) + $firstArrayType = array_shift($arrayTypes); + if (! $firstArrayType instanceof ArrayType) { + return new MixedType(); + } + + $keyType = $firstArrayType->getKeyType(); + foreach ($arrayTypes as $arr) { + $keyType = TypeCombinator::intersect($keyType, $arr->getKeyType()); + } + + if ($keyType instanceof NeverType) { + $keyType = new MixedType(); // incompatible key types + } + + // Recurse on item types; if mixed is returned, that’s our stop depth. + $itemTypes = array_map(fn (ArrayType $arrayType): Type => $arrayType->getItemType(), $types); + $itemType = $this->sharedArrayStructure(...$itemTypes); + + return new ArrayType($keyType, $itemType); + } +} diff --git a/rules/Privatization/TypeManipulator/NormalizeTypeToRespectArrayScalarType.php b/rules/Privatization/TypeManipulator/NormalizeTypeToRespectArrayScalarType.php deleted file mode 100644 index 8b30a541b5d..00000000000 --- a/rules/Privatization/TypeManipulator/NormalizeTypeToRespectArrayScalarType.php +++ /dev/null @@ -1,83 +0,0 @@ -nodeNameResolver->isName($returnNode, 'array')) { - return $type; - } - - if ($type instanceof UnionType) { - return $this->normalizeUnionType($type); - } - - if ($type instanceof MixedType) { - return new ArrayType($type, $type); - } - - if ($type instanceof ArrayType) { - return $this->resolveArrayType($type); - } - - return $type; - } - - private function resolveArrayType(ArrayType $arrayType): ArrayType - { - $itemType = $arrayType->getItemType(); - if (! $itemType instanceof IntersectionType) { - return $arrayType; - } - - $types = $itemType->getTypes(); - foreach ($types as $key => $itemTypeType) { - if ($itemTypeType instanceof NonEmptyArrayType) { - unset($types[$key]); - } - } - - return new ArrayType($arrayType->getKeyType(), new IntersectionType($types)); - } - - private function normalizeUnionType(UnionType $unionType): UnionType - { - $normalizedTypes = []; - foreach ($unionType->getTypes() as $unionedType) { - if ($unionedType instanceof MixedType) { - $normalizedTypes[] = new ArrayType($unionedType, $unionedType); - continue; - } - - $normalizedTypes[] = $unionedType; - } - - if ($unionType->getTypes() === $normalizedTypes) { - return $unionType; - } - - return new UnionType($normalizedTypes); - } -} diff --git a/rules/Privatization/TypeManipulator/TypeNormalizer.php b/rules/Privatization/TypeManipulator/TypeNormalizer.php new file mode 100644 index 00000000000..11ce83a5cc5 --- /dev/null +++ b/rules/Privatization/TypeManipulator/TypeNormalizer.php @@ -0,0 +1,240 @@ +isImplicitNumberedListKeyType($type)) { + $keyType = $deep === 1 ? new MixedType() : new IntegerType(); + } else { + $keyType = $this->generalizeConstantTypes($type->getKeyType()); + } + + // should be string[] + $itemType = $traverseCallback($type->getItemType(), $traverseCallback); + + if ($itemType instanceof ConstantStringType) { + $itemType = new StringType(); + } + + if ($itemType instanceof ConstantArrayType) { + $itemType = $this->generalizeConstantTypes($itemType); + } + + return new ArrayType($keyType, $itemType); + } + + if ($type instanceof UnionType) { + $generalizedUnionedTypes = []; + foreach ($type->getTypes() as $unionedType) { + $generalizedUnionedType = $this->generalizeConstantTypes($unionedType); + + if ($generalizedUnionedType instanceof ArrayType) { + $keyType = $this->typeHasher->createTypeHash($generalizedUnionedType->getKeyType()); + + foreach ($generalizedUnionedTypes as $key => $existingUnionedType) { + if (! $existingUnionedType instanceof ArrayType) { + continue; + } + + $existingKeyType = $this->typeHasher->createTypeHash( + $existingUnionedType->getKeyType() + ); + + if ($keyType !== $existingKeyType) { + continue; + } + + $uniqueTypes = $this->typeFactory->uniquateTypes( + [$existingUnionedType->getItemType(), $generalizedUnionedType->getItemType()] + ); + + if (count($uniqueTypes) !== 1) { + continue; + } + + $generalizedUnionedTypes[$key] = new ArrayType( + $existingUnionedType->getKeyType(), + $uniqueTypes[0] + ); + continue 2; + } + } + + $generalizedUnionedTypes[] = $generalizedUnionedType; + } + + $uniqueGeneralizedUnionTypes = $this->typeFactory->uniquateTypes($generalizedUnionedTypes); + + if (count($uniqueGeneralizedUnionTypes) > 1) { + $generalizedUnionType = new UnionType($uniqueGeneralizedUnionTypes); + + $shortUnionedDocType = $this->resolveNameShortDocTypeNode($generalizedUnionType); + + if (strlen( + (string) $shortUnionedDocType + ) > self::MAX_PRINTED_UNION_DOC_LENGTH && $this->avoidPrintedDocblockTrimming( + $generalizedUnionType + ) === false) { + $alwaysKnownArrayType = $this->narrowToAlwaysKnownArrayType($generalizedUnionType); + if ($alwaysKnownArrayType instanceof ArrayType) { + return $alwaysKnownArrayType; + } + + return new MixedType(); + } + + return $generalizedUnionType; + } + + return $uniqueGeneralizedUnionTypes[0]; + } + + $convertedType = $traverseCallback($type, $traverseCallback); + if ($convertedType instanceof NeverType) { + return new MixedType(); + } + + return $convertedType; + }); + } + + private function isImplicitNumberedListKeyType(ConstantArrayType $constantArrayType): bool + { + if (! $constantArrayType->getKeyType() instanceof UnionType) { + return false; + } + + foreach ($constantArrayType->getKeyType()->getTypes() as $key => $keyType) { + if ($keyType instanceof ConstantIntegerType) { + if ($keyType->getValue() === $key) { + continue; + } + + return false; + } + + return false; + } + + return true; + } + + private function narrowToAlwaysKnownArrayType(UnionType $unionType): ?ArrayType + { + // always an array? + if (count($unionType->getArrays()) !== count($unionType->getTypes())) { + return null; + } + + $arrayUniqueKeyType = $this->arrayTypeLeastCommonDenominatorResolver->sharedArrayStructure( + ...$unionType->getTypes() + ); + return new ArrayType($arrayUniqueKeyType, new MixedType()); + } + + /** + * Is object only? avoid trimming, as auto import handles it better + */ + private function avoidPrintedDocblockTrimming(UnionType $unionType): bool + { + if ($unionType->getConstantScalarTypes() !== []) { + return false; + } + + if ($unionType->getConstantArrays() !== []) { + return false; + } + + return $unionType->getObjectClassNames() !== []; + } + + private function resolveNameShortDocTypeNode(UnionType $unionType): TypeNode + { + // we have to convert name to short here, to make sure the not FQN, but short name is counted to the full length + $objectShortGeneralizedUnionType = TypeTraverser::map($unionType, function ( + Type $type, + callable $traverseCallback + ): Type { + if ($type instanceof ObjectType && str_contains($type->getClassName(), '\\')) { + // after last "\\" + $shortClassName = substr($type->getClassName(), strrpos($type->getClassName(), '\\') + 1); + + return new ShortenedObjectType($shortClassName, $type->getClassName()); + } + + return $traverseCallback($type, $traverseCallback); + }); + + return $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($objectShortGeneralizedUnionType); + } +} diff --git a/rules/Privatization/ValueObject/ReplaceStringWithClassConstant.php b/rules/Privatization/ValueObject/ReplaceStringWithClassConstant.php deleted file mode 100644 index 1d5bd557864..00000000000 --- a/rules/Privatization/ValueObject/ReplaceStringWithClassConstant.php +++ /dev/null @@ -1,44 +0,0 @@ -class); - } - - public function getMethod(): string - { - return $this->method; - } - - /** - * @return class-string - */ - public function getClassWithConstants(): string - { - return $this->classWithConstants; - } - - public function getArgPosition(): int - { - return $this->argPosition; - } -} diff --git a/rules/Privatization/VisibilityGuard/ClassMethodVisibilityGuard.php b/rules/Privatization/VisibilityGuard/ClassMethodVisibilityGuard.php index 35e2de8f9f4..ee85d5d1b2f 100644 --- a/rules/Privatization/VisibilityGuard/ClassMethodVisibilityGuard.php +++ b/rules/Privatization/VisibilityGuard/ClassMethodVisibilityGuard.php @@ -8,7 +8,7 @@ use PHPStan\Reflection\ClassReflection; use Rector\NodeNameResolver\NodeNameResolver; -final class ClassMethodVisibilityGuard +final readonly class ClassMethodVisibilityGuard { public function __construct( private NodeNameResolver $nodeNameResolver @@ -22,7 +22,7 @@ public function isClassMethodVisibilityGuardedByParent( $methodName = $this->nodeNameResolver->getName($classMethod); /** @var ClassReflection[] $parentClassReflections */ - $parentClassReflections = array_merge($classReflection->getParents(), $classReflection->getInterfaces()); + $parentClassReflections = [...$classReflection->getParents(), ...$classReflection->getInterfaces()]; foreach ($parentClassReflections as $parentClassReflection) { if ($parentClassReflection->hasMethod($methodName)) { diff --git a/rules/Removing/NodeManipulator/ComplexNodeRemover.php b/rules/Removing/NodeManipulator/ComplexNodeRemover.php index dab4187add6..0373dd6cc23 100644 --- a/rules/Removing/NodeManipulator/ComplexNodeRemover.php +++ b/rules/Removing/NodeManipulator/ComplexNodeRemover.php @@ -4,164 +4,62 @@ namespace Rector\Removing\NodeManipulator; -use PhpParser\Node; -use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\StaticPropertyFetch; -use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Property; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder; -use Rector\Core\ValueObject\MethodName; +use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeRemoval\AssignRemover; -use Rector\NodeRemoval\NodeRemover; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\PostRector\Collector\NodesToRemoveCollector; -final class ComplexNodeRemover +final readonly class ComplexNodeRemover { public function __construct( - private AssignRemover $assignRemover, - private PropertyFetchFinder $propertyFetchFinder, + private PhpDocInfoFactory $phpDocInfoFactory, + private PhpDocTagRemover $phpDocTagRemover, private NodeNameResolver $nodeNameResolver, - private BetterNodeFinder $betterNodeFinder, - private NodeRemover $nodeRemover, - private NodesToRemoveCollector $nodesToRemoveCollector, - private NodeComparator $nodeComparator + private DocBlockUpdater $docBlockUpdater ) { } /** - * @param string[] $classMethodNamesToSkip + * @param int[] $paramKeysToBeRemoved + * @return int[] */ - public function removePropertyAndUsages(Property $property, array $classMethodNamesToSkip = []): void + public function processRemoveParamWithKeys(ClassMethod $classMethod, array $paramKeysToBeRemoved): array { - $shouldKeepProperty = false; - - $propertyFetches = $this->propertyFetchFinder->findPrivatePropertyFetches($property); - - foreach ($propertyFetches as $propertyFetch) { - if ($this->shouldSkipPropertyForClassMethod($propertyFetch, $classMethodNamesToSkip)) { - $shouldKeepProperty = true; - continue; - } - - $assign = $this->resolveAssign($propertyFetch); - if (! $assign instanceof Assign) { - return; + $totalKeys = count($classMethod->params) - 1; + $removedParamKeys = []; + + $phpdocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + foreach ($paramKeysToBeRemoved as $paramKeyToBeRemoved) { + $startNextKey = $paramKeyToBeRemoved + 1; + for ($nextKey = $startNextKey; $nextKey <= $totalKeys; ++$nextKey) { + if (! isset($classMethod->params[$nextKey])) { + // no next param, break the inner loop, remove the param + break; + } + + if (in_array($nextKey, $paramKeysToBeRemoved, true)) { + // keep searching next key not in $paramKeysToBeRemoved + continue; + } + + return []; } - // remove assigns - $this->assignRemover->removeAssignNode($assign); - $this->removeConstructorDependency($assign); - } - - if ($shouldKeepProperty) { - return; - } - - // remove __construct param + $paramName = (string) $this->nodeNameResolver->getName($classMethod->params[$paramKeyToBeRemoved]); - /** @var Property $property */ - $this->nodeRemover->removeNode($property); + unset($classMethod->params[$paramKeyToBeRemoved]); - foreach ($property->props as $prop) { - if (! $this->nodesToRemoveCollector->isNodeRemoved($prop)) { - // if the property has at least one node left -> return - return; + $paramTagValueByName = $phpdocInfo->getParamTagValueByName($paramName); + if ($paramTagValueByName instanceof ParamTagValueNode) { + $this->phpDocTagRemover->removeTagValueFromNode($phpdocInfo, $paramTagValueByName); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); } - } - - $this->nodeRemover->removeNode($property); - } - - /** - * @param string[] $classMethodNamesToSkip - */ - private function shouldSkipPropertyForClassMethod( - StaticPropertyFetch | PropertyFetch $expr, - array $classMethodNamesToSkip - ): bool { - $classMethodNode = $expr->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethodNode instanceof ClassMethod) { - return false; - } - - $classMethodName = $this->nodeNameResolver->getName($classMethodNode); - return in_array($classMethodName, $classMethodNamesToSkip, true); - } - - private function resolveAssign(PropertyFetch | StaticPropertyFetch $expr): ?Assign - { - $assign = $expr->getAttribute(AttributeKey::PARENT_NODE); - - while ($assign !== null && ! $assign instanceof Assign) { - $assign = $assign->getAttribute(AttributeKey::PARENT_NODE); - } - if (! $assign instanceof Assign) { - return null; - } - - return $assign; - } - - private function removeConstructorDependency(Assign $assign): void - { - $classMethod = $assign->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return; - } - - if (! $this->nodeNameResolver->isName($classMethod, MethodName::CONSTRUCT)) { - return; - } - - $class = $assign->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { - return; - } - - $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); - if (! $constructClassMethod instanceof ClassMethod) { - return; - } - - foreach ($constructClassMethod->getParams() as $param) { - $variable = $this->betterNodeFinder->findFirst( - (array) $constructClassMethod->stmts, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($param->var, $node) - ); - - if (! $variable instanceof Node) { - continue; - } - - if ($this->isExpressionVariableNotAssign($variable)) { - continue; - } - - if (! $this->nodeComparator->areNodesEqual($param->var, $assign->expr)) { - continue; - } - - $this->nodeRemover->removeNode($param); - } - } - - private function isExpressionVariableNotAssign(Node $node): bool - { - if ($node !== null) { - $expressionVariable = $node->getAttribute(AttributeKey::PARENT_NODE); - - if (! $expressionVariable instanceof Assign) { - return true; - } + $removedParamKeys[] = $paramKeyToBeRemoved; } - return false; + return $removedParamKeys; } } diff --git a/rules/Removing/Rector/ClassMethod/ArgumentRemoverRector.php b/rules/Removing/Rector/ClassMethod/ArgumentRemoverRector.php index 7cb79e46521..e22927f40e7 100644 --- a/rules/Removing/Rector/ClassMethod/ArgumentRemoverRector.php +++ b/rules/Removing/Rector/ClassMethod/ArgumentRemoverRector.php @@ -9,8 +9,10 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\VariadicPlaceholder; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Rector\Removing\ValueObject\ArgumentRemover; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,20 +23,22 @@ */ final class ArgumentRemoverRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const REMOVED_ARGUMENTS = 'removed_arguments'; - /** * @var ArgumentRemover[] */ private array $removedArguments = []; + private bool $hasChanged = false; + + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Removes defined arguments in defined methods and their calls.', + 'Remove defined arguments in defined methods and their calls', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' @@ -47,9 +51,7 @@ public function getRuleDefinition(): RuleDefinition $someObject->someMethod(); CODE_SAMPLE , - [ - self::REMOVED_ARGUMENTS => [new ArgumentRemover('ExampleClass', 'someMethod', 0, [true])], - ] + [new ArgumentRemover('ExampleClass', 'someMethod', 0, [true])] ), ] ); @@ -66,9 +68,15 @@ public function getNodeTypes(): array /** * @param MethodCall|StaticCall|ClassMethod $node */ - public function refactor(Node $node): MethodCall | StaticCall | ClassMethod + public function refactor(Node $node): MethodCall | StaticCall | ClassMethod | null { + $this->hasChanged = false; + foreach ($this->removedArguments as $removedArgument) { + if (! $this->isName($node->name, $removedArgument->getMethod())) { + continue; + } + if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType( $node, $removedArgument->getObjectType() @@ -76,24 +84,23 @@ public function refactor(Node $node): MethodCall | StaticCall | ClassMethod continue; } - if (! $this->isName($node->name, $removedArgument->getMethod())) { - continue; - } - $this->processPosition($node, $removedArgument); } - return $node; + if ($this->hasChanged) { + return $node; + } + + return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $removedArguments = $configuration[self::REMOVED_ARGUMENTS] ?? []; - Assert::allIsInstanceOf($removedArguments, ArgumentRemover::class); - $this->removedArguments = $removedArguments; + Assert::allIsAOf($configuration, ArgumentRemover::class); + $this->removedArguments = $configuration; } private function processPosition( @@ -102,9 +109,9 @@ private function processPosition( ): void { if ($argumentRemover->getValue() === null) { if ($node instanceof MethodCall || $node instanceof StaticCall) { - $this->nodeRemover->removeArg($node, $argumentRemover->getPosition()); + unset($node->args[$argumentRemover->getPosition()]); } else { - $this->nodeRemover->removeParam($node, $argumentRemover->getPosition()); + unset($node->params[$argumentRemover->getPosition()]); } return; @@ -126,7 +133,8 @@ private function processPosition( } if ($this->isArgumentValueMatch($node->args[$argumentRemover->getPosition()], $match)) { - $this->nodeRemover->removeArg($node, $argumentRemover->getPosition()); + $this->hasChanged = true; + unset($node->args[$argumentRemover->getPosition()]); } } @@ -134,7 +142,7 @@ private function removeByName(ClassMethod | StaticCall | MethodCall $node, int $ { if ($node instanceof MethodCall || $node instanceof StaticCall) { if (isset($node->args[$position]) && $this->isName($node->args[$position], $name)) { - $this->nodeRemover->removeArg($node, $position); + unset($node->args[$position]); } return; @@ -144,14 +152,18 @@ private function removeByName(ClassMethod | StaticCall | MethodCall $node, int $ return; } - $this->nodeRemover->removeParam($node, $position); + unset($node->params[$position]); } /** * @param mixed[] $values */ - private function isArgumentValueMatch(Arg $arg, array $values): bool + private function isArgumentValueMatch(Arg|VariadicPlaceholder $arg, array $values): bool { + if (! $arg instanceof Arg) { + return false; + } + $nodeValue = $this->valueResolver->getValue($arg->value); return in_array($nodeValue, $values, true); } diff --git a/rules/Removing/Rector/Class_/RemoveInterfacesRector.php b/rules/Removing/Rector/Class_/RemoveInterfacesRector.php index 4bd138a3eda..69f4d1599ef 100644 --- a/rules/Removing/Rector/Class_/RemoveInterfacesRector.php +++ b/rules/Removing/Rector/Class_/RemoveInterfacesRector.php @@ -6,10 +6,12 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Stmt\Interface_; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\Removing\Rector\Class_\RemoveInterfacesRector\RemoveInterfacesRectorTest @@ -17,18 +19,13 @@ final class RemoveInterfacesRector extends AbstractRector implements ConfigurableRectorInterface { /** - * @var string - */ - public const INTERFACES_TO_REMOVE = 'interfaces_to_remove'; - - /** - * @var class-string[] + * @var string[] */ private array $interfacesToRemove = []; public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Removes interfaces usage from class.', [ + return new RuleDefinition('Remove interfaces from class', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' class SomeClass implements SomeInterface @@ -42,9 +39,7 @@ class SomeClass } CODE_SAMPLE , - [ - self::INTERFACES_TO_REMOVE => ['SomeInterface'], - ] + ['SomeInterface'] ), ]); } @@ -54,32 +49,70 @@ class SomeClass */ public function getNodeTypes(): array { - return [Class_::class]; + return [Class_::class, Interface_::class]; } /** - * @param Class_ $node + * @param Class_|Interface_ $node */ public function refactor(Node $node): ?Node { - if ($node->implements === []) { + if ($node instanceof Class_) { + return $this->refactorClass($node); + } + + return $this->refactorInterface($node); + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allString($configuration); + + /** @var string[] $configuration */ + $this->interfacesToRemove = $configuration; + } + + private function refactorClass(Class_ $class): ?Class_ + { + if ($class->implements === []) { return null; } - foreach ($node->implements as $key => $implement) { + $isInterfacesRemoved = false; + foreach ($class->implements as $key => $implement) { if ($this->isNames($implement, $this->interfacesToRemove)) { - unset($node->implements[$key]); + unset($class->implements[$key]); + $isInterfacesRemoved = true; } } - return $node; + if (! $isInterfacesRemoved) { + return null; + } + + return $class; } - /** - * @param array $configuration - */ - public function configure(array $configuration): void + private function refactorInterface(Interface_ $interface): Interface_|null { - $this->interfacesToRemove = $configuration[self::INTERFACES_TO_REMOVE] ?? []; + $isInterfacesRemoved = false; + + foreach ($interface->extends as $key => $extend) { + if (! $this->isNames($extend, $this->interfacesToRemove)) { + continue; + } + + unset($interface->extends[$key]); + $isInterfacesRemoved = true; + } + + if (! $isInterfacesRemoved) { + return null; + } + + return $interface; } } diff --git a/rules/Removing/Rector/Class_/RemoveParentRector.php b/rules/Removing/Rector/Class_/RemoveParentRector.php deleted file mode 100644 index 5d20eb763ed..00000000000 --- a/rules/Removing/Rector/Class_/RemoveParentRector.php +++ /dev/null @@ -1,98 +0,0 @@ - ['SomeParentClass'], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - $parentClassName = $this->parentClassScopeResolver->resolveParentClassName($node); - if ($parentClassName === null) { - return null; - } - - foreach ($this->parentClassesToRemove as $parentClassToRemove) { - if ($parentClassName !== $parentClassToRemove) { - continue; - } - - // remove parent class - $node->extends = null; - - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $this->parentClassesToRemove = $configuration[self::PARENT_TYPES_TO_REMOVE] ?? []; - } -} diff --git a/rules/Removing/Rector/Class_/RemoveTraitUseRector.php b/rules/Removing/Rector/Class_/RemoveTraitUseRector.php index 60bf7ebc53a..3d4927052b3 100644 --- a/rules/Removing/Rector/Class_/RemoveTraitUseRector.php +++ b/rules/Removing/Rector/Class_/RemoveTraitUseRector.php @@ -7,24 +7,18 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Trait_; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use PhpParser\Node\Stmt\TraitUse; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\Removing\Rector\Class_\RemoveTraitUseRector\RemoveTraitUseRectorTest */ final class RemoveTraitUseRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const TRAITS_TO_REMOVE = 'traits_to_remove'; - - private bool $classHasChanged = false; - /** * @var string[] */ @@ -46,10 +40,8 @@ class SomeClass { } CODE_SAMPLE -, - [ - self::TRAITS_TO_REMOVE => ['TraitNameToRemove'], - ] + , + ['TraitNameToRemove'] ), ]); } @@ -67,22 +59,29 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $this->classHasChanged = false; + $hasChanged = false; - foreach ($node->getTraitUses() as $traitUse) { - foreach ($traitUse->traits as $trait) { + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof TraitUse) { + continue; + } + + foreach ($stmt->traits as $traitKey => $trait) { if (! $this->isNames($trait, $this->traitsToRemove)) { continue; } - $this->removeNode($traitUse); - $this->classHasChanged = true; + unset($stmt->traits[$traitKey]); + $hasChanged = true; + } + + // remove empty trait uses + if ($stmt->traits === []) { + unset($node->stmts[$key]); } } - // invoke re-print - if ($this->classHasChanged) { - $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); + if ($hasChanged) { return $node; } @@ -90,10 +89,12 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $this->traitsToRemove = $configuration[self::TRAITS_TO_REMOVE] ?? []; + Assert::allString($configuration); + + $this->traitsToRemove = $configuration; } } diff --git a/rules/Removing/Rector/FuncCall/RemoveFuncCallArgRector.php b/rules/Removing/Rector/FuncCall/RemoveFuncCallArgRector.php index 3f12f102a23..41e62771f86 100644 --- a/rules/Removing/Rector/FuncCall/RemoveFuncCallArgRector.php +++ b/rules/Removing/Rector/FuncCall/RemoveFuncCallArgRector.php @@ -7,8 +7,8 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Removing\ValueObject\RemoveFuncCallArg; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,11 +19,6 @@ */ final class RemoveFuncCallArgRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const REMOVED_FUNCTION_ARGUMENTS = 'removed_function_arguments'; - /** * @var RemoveFuncCallArg[] */ @@ -41,9 +36,7 @@ public function getRuleDefinition(): RuleDefinition remove_last_arg(1); CODE_SAMPLE , - [ - self::REMOVED_FUNCTION_ARGUMENTS => [new RemoveFuncCallArg('remove_last_arg', 1)], - ] + [new RemoveFuncCallArg('remove_last_arg', 1)] ), ]); } @@ -65,6 +58,7 @@ public function refactor(Node $node): ?Node return null; } + $hasChanged = false; foreach ($this->removedFunctionArguments as $removedFunctionArgument) { if (! $this->isName($node->name, $removedFunctionArgument->getFunction())) { continue; @@ -75,20 +69,24 @@ public function refactor(Node $node): ?Node continue; } - $this->nodeRemover->removeArg($node, $position); + unset($node->args[$position]); + $hasChanged = true; } } + if (! $hasChanged) { + return null; + } + return $node; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $removedFunctionArguments = $configuration[self::REMOVED_FUNCTION_ARGUMENTS] ?? []; - Assert::allIsInstanceOf($removedFunctionArguments, RemoveFuncCallArg::class); - $this->removedFunctionArguments = $removedFunctionArguments; + Assert::allIsAOf($configuration, RemoveFuncCallArg::class); + $this->removedFunctionArguments = $configuration; } } diff --git a/rules/Removing/Rector/FuncCall/RemoveFuncCallRector.php b/rules/Removing/Rector/FuncCall/RemoveFuncCallRector.php index 5a3f36868f6..3386fee6d13 100644 --- a/rules/Removing/Rector/FuncCall/RemoveFuncCallRector.php +++ b/rules/Removing/Rector/FuncCall/RemoveFuncCallRector.php @@ -6,10 +6,10 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeRemoval\BreakingRemovalGuard; -use Rector\Removing\ValueObject\RemoveFuncCall; +use PhpParser\Node\Stmt\Expression; +use PhpParser\NodeVisitor; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; @@ -20,46 +20,26 @@ final class RemoveFuncCallRector extends AbstractRector implements ConfigurableRectorInterface { /** - * @api - * @var string + * @var string[] */ - public const REMOVE_FUNC_CALLS = 'remove_func_calls'; - - /** - * @var RemoveFuncCall[] - */ - private array $removeFuncCalls = []; - - public function __construct( - private BreakingRemovalGuard $breakingRemovalGuard - ) { - } + private array $removedFunctions = []; public function getRuleDefinition(): RuleDefinition { - $configuration = [ - self::REMOVE_FUNC_CALLS => [ - new RemoveFuncCall('ini_get', [ - 1 => ['y2k_compliance'], - ]), - ], - ]; - return new RuleDefinition( - 'Remove ini_get by configuration', - [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -ini_get('y2k_compliance'); -ini_get('keep_me'); + return new RuleDefinition('Remove defined function calls', [ + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +$x = 'something'; +var_dump($x); CODE_SAMPLE - , - <<<'CODE_SAMPLE' -ini_get('keep_me'); + , + <<<'CODE_SAMPLE' +$x = 'something'; CODE_SAMPLE - , - $configuration - ), ] - ); + , + ['var_dump'] + ), + ]); } /** @@ -67,62 +47,36 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [FuncCall::class]; + return [Expression::class]; } /** - * @param FuncCall $node + * @param Expression $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?int { - foreach ($this->removeFuncCalls as $removeFuncCall) { - if (! $this->isName($node, $removeFuncCall->getFuncCall())) { - continue; - } + $expr = $node->expr; + if (! $expr instanceof FuncCall) { + return null; + } - if ($removeFuncCall->getArgumentPositionAndValues() === []) { - $this->removeNode($node); - return null; + foreach ($this->removedFunctions as $removedFunction) { + if (! $this->isName($expr->name, $removedFunction)) { + continue; } - $this->refactorFuncCallsWithPositions($node, $removeFuncCall); + return NodeVisitor::REMOVE_NODE; } return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $removeFuncCalls = $configuration[self::REMOVE_FUNC_CALLS] ?? []; - Assert::allIsInstanceOf($removeFuncCalls, RemoveFuncCall::class); - $this->removeFuncCalls = $removeFuncCalls; - } - - private function refactorFuncCallsWithPositions(FuncCall $funcCall, RemoveFuncCall $removeFuncCall): void - { - foreach ($removeFuncCall->getArgumentPositionAndValues() as $argumentPosition => $values) { - if (! $this->isArgumentPositionValueMatch($funcCall, $argumentPosition, $values)) { - continue; - } - - if ($this->breakingRemovalGuard->isLegalNodeRemoval($funcCall)) { - $this->removeNode($funcCall); - } - } - } - - /** - * @param mixed[] $values - */ - private function isArgumentPositionValueMatch(FuncCall $funcCall, int $argumentPosition, array $values): bool - { - if (! isset($funcCall->args[$argumentPosition])) { - return false; - } - - return $this->valueResolver->isValues($funcCall->args[$argumentPosition]->value, $values); + Assert::allString($configuration); + $this->removedFunctions = $configuration; } } diff --git a/rules/Removing/ValueObject/ArgumentRemover.php b/rules/Removing/ValueObject/ArgumentRemover.php index 1dd14f67292..68d46325293 100644 --- a/rules/Removing/ValueObject/ArgumentRemover.php +++ b/rules/Removing/ValueObject/ArgumentRemover.php @@ -5,18 +5,17 @@ namespace Rector\Removing\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class ArgumentRemover +final readonly class ArgumentRemover { - /** - * @param mixed $value - */ public function __construct( private string $class, private string $method, private int $position, - private $value + private mixed $value ) { + RectorAssert::className($class); } public function getObjectType(): ObjectType @@ -34,10 +33,7 @@ public function getPosition(): int return $this->position; } - /** - * @return mixed - */ - public function getValue() + public function getValue(): mixed { return $this->value; } diff --git a/rules/Removing/ValueObject/RemoveFuncCall.php b/rules/Removing/ValueObject/RemoveFuncCall.php deleted file mode 100644 index 42c0c136177..00000000000 --- a/rules/Removing/ValueObject/RemoveFuncCall.php +++ /dev/null @@ -1,30 +0,0 @@ - $argumentPositionAndValues - */ - public function __construct( - private string $funcCall, - private array $argumentPositionAndValues = [] - ) { - } - - public function getFuncCall(): string - { - return $this->funcCall; - } - - /** - * @return array - */ - public function getArgumentPositionAndValues(): array - { - return $this->argumentPositionAndValues; - } -} diff --git a/rules/Removing/ValueObject/RemoveFuncCallArg.php b/rules/Removing/ValueObject/RemoveFuncCallArg.php index ef9123f411d..4696d2d72be 100644 --- a/rules/Removing/ValueObject/RemoveFuncCallArg.php +++ b/rules/Removing/ValueObject/RemoveFuncCallArg.php @@ -4,12 +4,15 @@ namespace Rector\Removing\ValueObject; -final class RemoveFuncCallArg +use Rector\Validation\RectorAssert; + +final readonly class RemoveFuncCallArg { public function __construct( private string $function, private int $argumentPosition ) { + RectorAssert::functionName($function); } public function getFunction(): string diff --git a/rules/RemovingStatic/NodeAnalyzer/StaticCallPresenceAnalyzer.php b/rules/RemovingStatic/NodeAnalyzer/StaticCallPresenceAnalyzer.php deleted file mode 100644 index 8b225e7cedc..00000000000 --- a/rules/RemovingStatic/NodeAnalyzer/StaticCallPresenceAnalyzer.php +++ /dev/null @@ -1,53 +0,0 @@ -betterNodeFinder->findFirst( - (array) $classMethod->stmts, - function (Node $node) use ($objectType): bool { - if (! $node instanceof StaticCall) { - return false; - } - - return $this->nodeTypeResolver->isObjectType($node->class, $objectType); - } - ); - } - - public function hasClassAnyMethodWithStaticCallOnType(Class_ $class, ObjectType $objectType): bool - { - foreach ($class->getMethods() as $classMethod) { - // handled else where - if ((string) $classMethod->name === MethodName::CONSTRUCT) { - continue; - } - - if ($this->hasMethodStaticCallOnType($classMethod, $objectType)) { - return true; - } - } - - return false; - } -} diff --git a/rules/RemovingStatic/NodeFactory/SetUpFactory.php b/rules/RemovingStatic/NodeFactory/SetUpFactory.php deleted file mode 100644 index 35b85f4ed58..00000000000 --- a/rules/RemovingStatic/NodeFactory/SetUpFactory.php +++ /dev/null @@ -1,23 +0,0 @@ -nodeFactory->createStaticCall('parent', MethodName::SET_UP); - return new Expression($parentSetupStaticCall); - } -} diff --git a/rules/RemovingStatic/Printer/FactoryClassPrinter.php b/rules/RemovingStatic/Printer/FactoryClassPrinter.php deleted file mode 100644 index 7161859f7c9..00000000000 --- a/rules/RemovingStatic/Printer/FactoryClassPrinter.php +++ /dev/null @@ -1,61 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Namespace_) { - $newNamespace = clone $parentNode; - $newNamespace->stmts = []; - $newNamespace->stmts[] = $factoryClass; - $nodeToPrint = $newNamespace; - } else { - $nodeToPrint = $factoryClass; - } - - $factoryClassFilePath = $this->createFactoryClassFilePath($oldClass); - $factoryClassContent = $this->betterStandardPrinter->prettyPrintFile([$nodeToPrint]); - - $this->smartFileSystem->dumpFile($factoryClassFilePath, $factoryClassContent); - } - - private function createFactoryClassFilePath(Class_ $oldClass): string - { - $file = $this->currentFileProvider->getFile(); - - $smartFileInfo = $file->getSmartFileInfo(); - - $directoryPath = Strings::before($smartFileInfo->getRealPath(), DIRECTORY_SEPARATOR, -1); - $resolvedOldClass = $this->nodeNameResolver->getName($oldClass); - if ($resolvedOldClass === null) { - throw new ShouldNotHappenException(); - } - - $bareClassName = Strings::after($resolvedOldClass, '\\', -1) . 'Factory.php'; - - return $directoryPath . DIRECTORY_SEPARATOR . $bareClassName; - } -} diff --git a/rules/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector.php b/rules/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector.php deleted file mode 100644 index 8938f7ad898..00000000000 --- a/rules/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector.php +++ /dev/null @@ -1,161 +0,0 @@ -someStatic(); - } - - private function someStatic() - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class, StaticCall::class]; - } - - /** - * @param ClassMethod|StaticCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof ClassMethod) { - if (! $node->isPrivate()) { - return null; - } - - return $this->refactorClassMethod($node); - } - - return $this->refactorStaticCall($node); - } - - private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod - { - if (! $classMethod->isStatic()) { - return null; - } - - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - if ($this->classMethodVisibilityGuard->isClassMethodVisibilityGuardedByParent($classMethod, $classReflection)) { - return null; - } - - // change static calls to non-static ones, but only if in non-static method!!! - $this->visibilityManipulator->makeNonStatic($classMethod); - - return $classMethod; - } - - private function refactorStaticCall(StaticCall $staticCall): ?MethodCall - { - $class = $staticCall->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof ClassLike) { - return null; - } - - /** @var ClassMethod[] $classMethods */ - $classMethods = $this->betterNodeFinder->findInstanceOf($class, ClassMethod::class); - - foreach ($classMethods as $classMethod) { - if (! $this->isClassMethodMatchingStaticCall($classMethod, $staticCall)) { - continue; - } - - if ($this->isInStaticClassMethod($staticCall)) { - continue; - } - - $thisVariable = new Variable('this'); - return new MethodCall($thisVariable, $staticCall->name, $staticCall->args); - } - - return null; - } - - private function isInStaticClassMethod(StaticCall $staticCall): bool - { - $locationClassMethod = $staticCall->getAttribute(AttributeKey::METHOD_NODE); - if (! $locationClassMethod instanceof ClassMethod) { - return false; - } - - return $locationClassMethod->isStatic(); - } - - private function isClassMethodMatchingStaticCall(ClassMethod $classMethod, StaticCall $staticCall): bool - { - $className = $classMethod->getAttribute(AttributeKey::CLASS_NAME); - $objectType = new ObjectType($className); - - $callerType = $this->nodeTypeResolver->resolve($staticCall->class); - return $objectType->equals($callerType); - } -} diff --git a/rules/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector.php b/rules/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector.php deleted file mode 100644 index 40d439767bf..00000000000 --- a/rules/RemovingStatic/Rector/Class_/DesiredClassTypeToDynamicRector.php +++ /dev/null @@ -1,190 +0,0 @@ -provideArrayParameter(Option::TYPES_TO_REMOVE_STATIC_FROM); - foreach ($typesToRemoveStaticFrom as $typeToRemoveStaticFrom) { - $this->staticObjectTypes[] = new ObjectType($typeToRemoveStaticFrom); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Change full static service, to dynamic one', [ - new CodeSample( - <<<'CODE_SAMPLE' -class AnotherClass -{ - public function run() - { - SomeClass::someStatic(); - } -} - -class SomeClass -{ - public static function run() - { - self::someStatic(); - } - - private static function someStatic() - { - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class AnotherClass -{ - /** - * @var SomeClass - */ - private $someClass; - - public fuction __construct(SomeClass $someClass) - { - $this->someClass = $someClass; - } - - public function run() - { - SomeClass::someStatic(); - } -} - -class SomeClass -{ - public function run() - { - $this->someStatic(); - } - - private function someStatic() - { - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->staticObjectTypes as $staticObjectType) { - // do not any dependencies to class itself - if ($this->isObjectType($node, $staticObjectType)) { - continue; - } - - $this->completeDependencyToConstructorOnly($node, $staticObjectType); - - if ($this->staticCallPresenceAnalyzer->hasClassAnyMethodWithStaticCallOnType($node, $staticObjectType)) { - $propertyExpectedName = $this->propertyNaming->fqnToVariableName($staticObjectType); - - $propertyMetadata = new PropertyMetadata( - $propertyExpectedName, - $staticObjectType, - Class_::MODIFIER_PRIVATE - ); - $this->propertyToAddCollector->addPropertyToClass($node, $propertyMetadata); - - return $node; - } - } - - return null; - } - - private function completeDependencyToConstructorOnly(Class_ $class, ObjectType $objectType): void - { - $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); - if (! $constructClassMethod instanceof ClassMethod) { - return; - } - - $hasStaticCall = $this->staticCallPresenceAnalyzer->hasMethodStaticCallOnType( - $constructClassMethod, - $objectType - ); - - if (! $hasStaticCall) { - return; - } - - $propertyExpectedName = $this->propertyNaming->fqnToVariableName($objectType); - - if ($this->isTypeAlreadyInParamMethod($constructClassMethod, $objectType)) { - return; - } - - $constructClassMethod->params[] = $this->createParam($propertyExpectedName, $objectType); - } - - private function isTypeAlreadyInParamMethod(ClassMethod $classMethod, ObjectType $objectType): bool - { - foreach ($classMethod->getParams() as $param) { - if ($param->type === null) { - continue; - } - - if ($this->isName($param->type, $objectType->getClassName())) { - return true; - } - } - - return false; - } - - private function createParam(string $propertyName, ObjectType $objectType): Param - { - return new Param(new Variable($propertyName), null, new FullyQualified($objectType->getClassName())); - } -} diff --git a/rules/RemovingStatic/Rector/Class_/NewUniqueObjectToEntityFactoryRector.php b/rules/RemovingStatic/Rector/Class_/NewUniqueObjectToEntityFactoryRector.php deleted file mode 100644 index d765f670d62..00000000000 --- a/rules/RemovingStatic/Rector/Class_/NewUniqueObjectToEntityFactoryRector.php +++ /dev/null @@ -1,179 +0,0 @@ -anotherClassFactory = $anotherClassFactory; - } - - public function run() - { - return $this->anotherClassFactory->create(); - } -} - -class AnotherClass -{ - public function someFun() - { - return StaticClass::staticMethod(); - } -} -CODE_SAMPLE - , - [ - self::TYPES_TO_SERVICES => ['ClassName'], - ] - ), ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): Class_ - { - $this->matchedObjectTypes = []; - - // collect classes with new to factory in all classes - - $this->traverseNodesWithCallable($node->stmts, function (Node $node): ?MethodCall { - if (! $node instanceof New_) { - return null; - } - - $class = $this->getName($node->class); - if ($class === null) { - return null; - } - - if (! $this->isClassMatching($class)) { - return null; - } - - $objectType = new FullyQualifiedObjectType($class); - $this->matchedObjectTypes[] = $objectType; - - $propertyName = $this->propertyNaming->fqnToVariableName($objectType) . self::FACTORY; - $propertyFetch = new PropertyFetch(new Variable('this'), $propertyName); - - return new MethodCall($propertyFetch, 'create', $node->args); - }); - - foreach ($this->matchedObjectTypes as $matchedObjectType) { - $propertyName = $this->propertyNaming->fqnToVariableName($matchedObjectType) . self::FACTORY; - $propertyType = new FullyQualifiedObjectType($matchedObjectType->getClassName() . self::FACTORY); - - $propertyMetadata = new PropertyMetadata($propertyName, $propertyType, Class_::MODIFIER_PRIVATE); - $this->propertyToAddCollector->addPropertyToClass($node, $propertyMetadata); - } - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $typesToServices = $configuration[self::TYPES_TO_SERVICES] ?? []; - foreach ($typesToServices as $typeToService) { - $this->serviceObjectTypes[] = new ObjectType($typeToService); - } - } - - private function isClassMatching(string $class): bool - { - foreach ($this->serviceObjectTypes as $serviceObjectType) { - if ($serviceObjectType->isInstanceOf($class)->yes()) { - return true; - } - } - - return false; - } -} diff --git a/rules/RemovingStatic/Rector/Class_/PassFactoryToUniqueObjectRector.php b/rules/RemovingStatic/Rector/Class_/PassFactoryToUniqueObjectRector.php deleted file mode 100644 index f1b0e304cd7..00000000000 --- a/rules/RemovingStatic/Rector/Class_/PassFactoryToUniqueObjectRector.php +++ /dev/null @@ -1,195 +0,0 @@ -anotherClassFactory = $anotherClassFactory; - } - - public function run() - { - return $this->anotherClassFactory->create(); - } -} - -class AnotherClass -{ - public function __construct(StaticClass $staticClass) - { - $this->staticClass = $staticClass; - } - - public function someFun() - { - return $this->staticClass->staticMethod(); - } -} - -final class AnotherClassFactory -{ - /** - * @var StaticClass - */ - private $staticClass; - - public function __construct(StaticClass $staticClass) - { - $this->staticClass = $staticClass; - } - - public function create(): AnotherClass - { - return new AnotherClass($this->staticClass); - } -} -CODE_SAMPLE - , - [ - self::TYPES_TO_SERVICES => ['StaticClass'], - ] - ), ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class, StaticCall::class]; - } - - /** - * @param StaticCall|Class_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof Class_) { - return $this->refactorClass($node); - } - - foreach ($this->serviceObjectTypes as $serviceObjectType) { - if (! $this->isObjectType($node->class, $serviceObjectType)) { - continue; - } - - // is this object created via new somewhere else? use factory! - $variableName = $this->propertyNaming->fqnToVariableName($serviceObjectType); - $thisPropertyFetch = new PropertyFetch(new Variable('this'), $variableName); - - return new MethodCall($thisPropertyFetch, $node->name, $node->args); - } - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $typesToServices = $configuration[self::TYPES_TO_SERVICES] ?? []; - foreach ($typesToServices as $typeToService) { - $this->serviceObjectTypes[] = new ObjectType($typeToService); - } - } - - private function refactorClass(Class_ $class): Class_ - { - $staticTypesInClass = $this->staticTypesInClassResolver->collectStaticCallTypeInClass( - $class, - $this->serviceObjectTypes - ); - - foreach ($staticTypesInClass as $staticTypeInClass) { - $variableName = $this->propertyNaming->fqnToVariableName($staticTypeInClass); - - $propertyMetadata = new PropertyMetadata($variableName, $staticTypeInClass, Class_::MODIFIER_PRIVATE); - $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); - - // is this an object? create factory for it next to this :) - if ($this->uniqueObjectOrServiceDetector->isUniqueObject()) { - $factoryClass = $this->uniqueObjectFactoryFactory->createFactoryClass($class, $staticTypeInClass); - - $this->factoryClassPrinter->printFactoryForClass($factoryClass, $class); - } - } - - return $class; - } -} diff --git a/rules/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector.php b/rules/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector.php deleted file mode 100644 index 8c4d8498bc3..00000000000 --- a/rules/RemovingStatic/Rector/Class_/StaticTypeToSetterInjectionRector.php +++ /dev/null @@ -1,168 +0,0 @@ - - */ - private array $staticTypes = []; - - public function __construct( - private PropertyNaming $propertyNaming, - private PhpDocTypeChanger $phpDocTypeChanger - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - // custom made only for Elasticr - return new RuleDefinition('Changes types to setter injection', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -final class CheckoutEntityFactory -{ - public function run() - { - return SomeStaticClass::go(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class CheckoutEntityFactory -{ - /** - * @var SomeStaticClass - */ - private $someStaticClass; - - public function setSomeStaticClass(SomeStaticClass $someStaticClass) - { - $this->someStaticClass = $someStaticClass; - } - - public function run() - { - return $this->someStaticClass->go(); - } -} -CODE_SAMPLE - , - [ - self::STATIC_TYPES => ['SomeStaticClass'], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class, StaticCall::class]; - } - - /** - * @param StaticCall|Class_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof Class_) { - return $this->processClass($node); - } - - foreach ($this->staticTypes as $staticType) { - $objectType = new ObjectType($staticType); - if (! $this->isObjectType($node->class, $objectType)) { - continue; - } - - $variableName = $this->propertyNaming->fqnToVariableName($objectType); - $propertyFetch = new PropertyFetch(new Variable('this'), $variableName); - - return new MethodCall($propertyFetch, $node->name, $node->args); - } - - return null; - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->staticTypes = $configuration[self::STATIC_TYPES] ?? []; - } - - private function processClass(Class_ $class): Class_ - { - foreach ($this->staticTypes as $implements => $staticType) { - $objectType = new ObjectType($staticType); - - $containsEntityFactoryStaticCall = (bool) $this->betterNodeFinder->findFirst( - $class->stmts, - fn (Node $node): bool => $this->isEntityFactoryStaticCall($node, $objectType) - ); - - if (! $containsEntityFactoryStaticCall) { - continue; - } - - if (is_string($implements)) { - $class->implements[] = new FullyQualified($implements); - } - - $variableName = $this->propertyNaming->fqnToVariableName($objectType); - $setEntityFactoryMethod = $this->nodeFactory->createSetterClassMethod($variableName, $objectType); - - $entityFactoryProperty = $this->nodeFactory->createPrivateProperty($variableName); - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($entityFactoryProperty); - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $objectType); - - $class->stmts = array_merge([$entityFactoryProperty, $setEntityFactoryMethod], $class->stmts); - break; - } - - return $class; - } - - private function isEntityFactoryStaticCall(Node $node, ObjectType $objectType): bool - { - if (! $node instanceof StaticCall) { - return false; - } - - return $this->isObjectType($node->class, $objectType); - } -} diff --git a/rules/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector.php b/rules/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector.php deleted file mode 100644 index 7a31aeac3c8..00000000000 --- a/rules/RemovingStatic/Rector/Property/DesiredPropertyClassMethodTypeToDynamicRector.php +++ /dev/null @@ -1,106 +0,0 @@ -provideArrayParameter(Option::TYPES_TO_REMOVE_STATIC_FROM); - foreach ($typesToRemoveStaticFrom as $typeToRemoveStaticFrom) { - $this->staticObjectTypes[] = new ObjectType($typeToRemoveStaticFrom); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Change defined static properties and methods to dynamic', [ - new CodeSample( - <<<'CODE_SAMPLE' -final class SomeClass -{ - public static $name; - - public static function go() - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - public $name; - - public function go() - { - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return class-string[] - */ - public function getNodeTypes(): array - { - return [Property::class, ClassMethod::class]; - } - - /** - * @param Property|ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - /** @var Scope $scope */ - $scope = $node->getAttribute(AttributeKey::SCOPE); - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $classObjectType = new ObjectType($classReflection->getName()); - - foreach ($this->staticObjectTypes as $staticObjectType) { - if (! $staticObjectType->isSuperTypeOf($classObjectType)->yes()) { - continue; - } - - if (! $node->isStatic()) { - return null; - } - - $this->visibilityManipulator->makeNonStatic($node); - - return $node; - } - - return null; - } -} diff --git a/rules/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector.php b/rules/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector.php deleted file mode 100644 index dd925431941..00000000000 --- a/rules/RemovingStatic/Rector/StaticCall/DesiredStaticCallTypeToDynamicRector.php +++ /dev/null @@ -1,112 +0,0 @@ -provideArrayParameter(Option::TYPES_TO_REMOVE_STATIC_FROM); - foreach ($typesToRemoveStaticFrom as $typeToRemoveStaticFrom) { - $this->staticObjectTypes[] = new ObjectType($typeToRemoveStaticFrom); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Change defined static service to dynamic one', [ - new CodeSample( - <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run() - { - SomeStaticMethod::someStatic(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run() - { - $this->someStaticMethod->someStatic(); - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [StaticCall::class]; - } - - /** - * @param StaticCall $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->staticObjectTypes as $staticObjectType) { - if (! $this->isObjectType($node->class, $staticObjectType)) { - continue; - } - - // is the same class or external call? - $className = $this->getName($node->class); - if ($className === 'self') { - return $this->createFromSelf($node); - } - - $propertyName = $this->propertyNaming->fqnToVariableName($staticObjectType); - - $classMethod = $node->getAttribute(AttributeKey::METHOD_NODE); - if ($this->nodeNameResolver->isName($classMethod, MethodName::CONSTRUCT)) { - $propertyFetch = new Variable($propertyName); - } else { - $propertyFetch = new PropertyFetch(new Variable('this'), $propertyName); - } - - return new MethodCall($propertyFetch, $node->name, $node->args); - } - - return null; - } - - private function createFromSelf(StaticCall $staticCall): MethodCall - { - return new MethodCall(new Variable('this'), $staticCall->name, $staticCall->args); - } -} diff --git a/rules/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector.php b/rules/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector.php deleted file mode 100644 index ae66fd97c81..00000000000 --- a/rules/RemovingStatic/Rector/StaticPropertyFetch/DesiredStaticPropertyFetchTypeToDynamicRector.php +++ /dev/null @@ -1,125 +0,0 @@ -provideArrayParameter(Option::TYPES_TO_REMOVE_STATIC_FROM); - foreach ($typesToRemoveStaticFrom as $typeToRemoveStaticFrom) { - $this->staticObjectTypes[] = new ObjectType($typeToRemoveStaticFrom); - } - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Change defined static service to dynamic one', [ - new CodeSample( - <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run() - { - SomeStaticMethod::$someStatic; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run() - { - $this->someStaticMethod->someStatic; - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [StaticPropertyFetch::class]; - } - - /** - * @param StaticPropertyFetch $node - */ - public function refactor(Node $node): ?Node - { - /** @var Scope $scope */ - $scope = $node->getAttribute(AttributeKey::SCOPE); - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $classObjectType = new ObjectType($classReflection->getName()); - - // A. remove local fetch - foreach ($this->staticObjectTypes as $staticObjectType) { - if (! $staticObjectType->isSuperTypeOf($classObjectType)->yes()) { - continue; - } - - return new PropertyFetch(new Variable('this'), $node->name); - } - - // B. external property fetch - foreach ($this->staticObjectTypes as $staticObjectType) { - if (! $this->isObjectType($node->class, $staticObjectType)) { - continue; - } - - $propertyName = $this->propertyNaming->fqnToVariableName($staticObjectType); - - /** @var Class_ $class */ - $class = $node->getAttribute(AttributeKey::CLASS_NODE); - - $propertyMetadata = new PropertyMetadata($propertyName, $staticObjectType, Class_::MODIFIER_PRIVATE); - $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); - - $objectPropertyFetch = new PropertyFetch(new Variable('this'), $propertyName); - return new PropertyFetch($objectPropertyFetch, $node->name); - } - - return null; - } -} diff --git a/rules/RemovingStatic/StaticTypesInClassResolver.php b/rules/RemovingStatic/StaticTypesInClassResolver.php deleted file mode 100644 index fb975ed56d2..00000000000 --- a/rules/RemovingStatic/StaticTypesInClassResolver.php +++ /dev/null @@ -1,49 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable($class->stmts, function (Node $class) use ( - $objectTypes, - &$staticTypesInClass - ) { - if (! $class instanceof StaticCall) { - return null; - } - - foreach ($objectTypes as $objectType) { - if ($this->nodeTypeResolver->isObjectType($class->class, $objectType)) { - $staticTypesInClass[] = $objectType; - } - } - - return null; - }); - - return $staticTypesInClass; - } -} diff --git a/rules/RemovingStatic/UniqueObjectFactoryFactory.php b/rules/RemovingStatic/UniqueObjectFactoryFactory.php deleted file mode 100644 index 50fddff5433..00000000000 --- a/rules/RemovingStatic/UniqueObjectFactoryFactory.php +++ /dev/null @@ -1,176 +0,0 @@ -nodeNameResolver->getName($class); - if ($className === null) { - throw new ShouldNotHappenException(); - } - - $name = $className . 'Factory'; - - $shortName = $this->resolveClassShortName($name); - - $factoryClassBuilder = new ClassBuilder($shortName); - $factoryClassBuilder->makeFinal(); - - $properties = $this->createPropertiesFromTypes($objectType); - $factoryClassBuilder->addStmts($properties); - - // constructor - $constructorClassMethod = $this->createConstructMethod($objectType); - $factoryClassBuilder->addStmt($constructorClassMethod); - - // create - $classMethod = $this->createCreateMethod($class, $className, $properties); - $factoryClassBuilder->addStmt($classMethod); - - return $factoryClassBuilder->getNode(); - } - - private function resolveClassShortName(string $name): string - { - if (\str_contains($name, '\\')) { - return (string) Strings::after($name, '\\', -1); - } - - return $name; - } - - /** - * @return Property[] - */ - private function createPropertiesFromTypes(ObjectType $objectType): array - { - $properties = []; - $properties[] = $this->createPropertyFromObjectType($objectType); - - return $properties; - } - - private function createConstructMethod(ObjectType $objectType): ClassMethod - { - $propertyName = $this->propertyNaming->fqnToVariableName($objectType); - $paramBuilder = new ParamBuilder($propertyName); - - $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($objectType, TypeKind::PARAM()); - if ($typeNode !== null) { - $paramBuilder->setType($typeNode); - } - - $params = [$paramBuilder->getNode()]; - - $assigns = $this->createAssignsFromParams($params); - - $methodBuilder = new MethodBuilder(MethodName::CONSTRUCT); - $methodBuilder->makePublic(); - $methodBuilder->addParams($params); - $methodBuilder->addStmts($assigns); - - return $methodBuilder->getNode(); - } - - /** - * @param Property[] $properties - */ - private function createCreateMethod(Class_ $class, string $className, array $properties): ClassMethod - { - $new = new New_(new FullyQualified($className)); - - $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); - $params = []; - if ($constructClassMethod !== null) { - foreach ($constructClassMethod->params as $param) { - $params[] = $param; - $new->args[] = new Arg($param->var); - } - } - - foreach ($properties as $property) { - $propertyName = $this->nodeNameResolver->getName($property); - $propertyFetch = new PropertyFetch(new Variable('this'), $propertyName); - $new->args[] = new Arg($propertyFetch); - } - - $return = new Return_($new); - - $methodBuilder = new MethodBuilder('create'); - $methodBuilder->setReturnType(new FullyQualified($className)); - $methodBuilder->makePublic(); - $methodBuilder->addStmt($return); - $methodBuilder->addParams($params); - - return $methodBuilder->getNode(); - } - - private function createPropertyFromObjectType(ObjectType $objectType): Property - { - $propertyName = $this->propertyNaming->fqnToVariableName($objectType); - $property = $this->nodeFactory->createPrivateProperty($propertyName); - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $objectType); - - return $property; - } - - /** - * @param Param[] $params - * - * @return Assign[] - */ - private function createAssignsFromParams(array $params): array - { - $assigns = []; - - /** @var Param $param */ - foreach ($params as $param) { - $propertyFetch = new PropertyFetch(new Variable('this'), $param->var->name); - $assigns[] = new Assign($propertyFetch, new Variable($param->var->name)); - } - - return $assigns; - } -} diff --git a/rules/RemovingStatic/UniqueObjectOrServiceDetector.php b/rules/RemovingStatic/UniqueObjectOrServiceDetector.php deleted file mode 100644 index 9d1ddc1f9ff..00000000000 --- a/rules/RemovingStatic/UniqueObjectOrServiceDetector.php +++ /dev/null @@ -1,19 +0,0 @@ -methodCallRenames = array_merge($this->methodCallRenames, $methodCallRenames); - } - - /** - * @return MethodCallRenameInterface[] - */ - public function getMethodCallRenames(): array - { - return $this->methodCallRenames; - } -} diff --git a/rules/Renaming/Collector/RenamedNameCollector.php b/rules/Renaming/Collector/RenamedNameCollector.php new file mode 100644 index 00000000000..bba44eef707 --- /dev/null +++ b/rules/Renaming/Collector/RenamedNameCollector.php @@ -0,0 +1,28 @@ +names[] = $name; + } + + public function has(string $name): bool + { + return in_array($name, $this->names, true); + } + + public function reset(): void + { + $this->names = []; + } +} diff --git a/rules/Renaming/Contract/MethodCallRenameInterface.php b/rules/Renaming/Contract/MethodCallRenameInterface.php index d62dc6bc794..498403a7435 100644 --- a/rules/Renaming/Contract/MethodCallRenameInterface.php +++ b/rules/Renaming/Contract/MethodCallRenameInterface.php @@ -8,7 +8,9 @@ interface MethodCallRenameInterface { - public function getOldObjectType(): ObjectType; + public function getClass(): string; + + public function getObjectType(): ObjectType; public function getOldMethod(): string; diff --git a/rules/Renaming/Contract/RenameAnnotationInterface.php b/rules/Renaming/Contract/RenameAnnotationInterface.php new file mode 100644 index 00000000000..326a70e1afa --- /dev/null +++ b/rules/Renaming/Contract/RenameAnnotationInterface.php @@ -0,0 +1,12 @@ + */ - private array $alreadyProcessedClasses = []; + private array $oldToNewTypesByCacheKey = []; public function __construct( - private BetterNodeFinder $betterNodeFinder, - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private ClassNaming $classNaming, - private NodeNameResolver $nodeNameResolver, - private PhpDocClassRenamer $phpDocClassRenamer, - private PhpDocInfoFactory $phpDocInfoFactory, - private DocBlockClassRenamer $docBlockClassRenamer, - private ReflectionProvider $reflectionProvider, - private NodeRemover $nodeRemover, - private ParameterProvider $parameterProvider + private readonly PhpDocClassRenamer $phpDocClassRenamer, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly DocBlockClassRenamer $docBlockClassRenamer, + private readonly ReflectionProvider $reflectionProvider, + private readonly FileHasher $fileHasher, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly RenamedNameCollector $renamedNameCollector ) { } /** * @param array $oldToNewClasses + * @return ($node is FullyQualified ? FullyQualified : Node) */ - public function renameNode(Node $node, array $oldToNewClasses): ?Node + public function renameNode(Node $node, array $oldToNewClasses, ?Scope $scope): ?Node { - $oldToNewTypes = []; - foreach ($oldToNewClasses as $oldClass => $newClass) { - $oldToNewTypes[] = new OldToNewType(new ObjectType($oldClass), new FullyQualifiedObjectType($newClass)); - } + $oldToNewTypes = $this->createOldToNewTypes($oldToNewClasses); - $this->refactorPhpDoc($node, $oldToNewTypes, $oldToNewClasses); + // execute FullyQualified before Name on purpose so next Name check is pure Name node + if ($node instanceof FullyQualified) { + return $this->refactorName($node, $oldToNewClasses, $scope); + } + // Name as parent of FullyQualified executed for fallback annotation to attribute rename to Name if ($node instanceof Name) { - return $this->refactorName($node, $oldToNewClasses); + $phpAttributeName = $node->getAttribute(AttributeKey::PHP_ATTRIBUTE_NAME); + if (is_string($phpAttributeName)) { + return $this->refactorName(new FullyQualified($phpAttributeName), $oldToNewClasses, $scope); + } + + return null; } - if ($node instanceof Namespace_) { - return $this->refactorNamespace($node, $oldToNewClasses); + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if ($phpDocInfo instanceof PhpDocInfo) { + $hasPhpDocChanged = $this->refactorPhpDoc($node, $oldToNewTypes, $oldToNewClasses, $phpDocInfo); + + if ($hasPhpDocChanged) { + return $node; + } } if ($node instanceof ClassLike) { - return $this->refactorClassLike($node, $oldToNewClasses); + return $this->refactorClassLike($node, $oldToNewClasses, $scope); } return null; @@ -85,161 +87,110 @@ public function renameNode(Node $node, array $oldToNewClasses): ?Node * @param OldToNewType[] $oldToNewTypes * @param array $oldToNewClasses */ - private function refactorPhpDoc(Node $node, array $oldToNewTypes, array $oldToNewClasses): void - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + private function refactorPhpDoc( + Node $node, + array $oldToNewTypes, + array $oldToNewClasses, + PhpDocInfo $phpDocInfo + ): bool { if (! $phpDocInfo->hasByTypes(NodeTypes::TYPE_AWARE_NODES) && ! $phpDocInfo->hasByAnnotationClasses( NodeTypes::TYPE_AWARE_DOCTRINE_ANNOTATION_CLASSES )) { - return; - } - - $this->docBlockClassRenamer->renamePhpDocType($phpDocInfo, $oldToNewTypes); - - $this->phpDocClassRenamer->changeTypeInAnnotationTypes($node, $phpDocInfo, $oldToNewClasses); - } - - /** - * @param array $oldToNewClasses - */ - private function refactorName(Name $name, array $oldToNewClasses): ?Name - { - $stringName = $this->nodeNameResolver->getName($name); - - $newName = $oldToNewClasses[$stringName] ?? null; - if (! $newName) { - return null; - } - - if (! $this->isClassToInterfaceValidChange($name, $newName)) { - return null; + return false; } - $parentNode = $name->getAttribute(AttributeKey::PARENT_NODE); - // no need to preslash "use \SomeNamespace" of imported namespace - if ($parentNode instanceof UseUse && ($parentNode->type === Use_::TYPE_NORMAL || $parentNode->type === Use_::TYPE_UNKNOWN)) { - - // no need to rename imports, they will be handled by autoimport and coding standard - // also they might cause some rename - return null; + if ($node instanceof AttributeGroup) { + return false; } - $last = $name->getLast(); - $newFullyQualified = new FullyQualified($newName); - $newNameLastName = $newFullyQualified->getLast(); - - $importNames = $this->parameterProvider->provideBoolParameter(Option::AUTO_IMPORT_NAMES); + $hasChanged = $this->docBlockClassRenamer->renamePhpDocType($phpDocInfo, $oldToNewTypes, $node); + $hasChanged = $this->phpDocClassRenamer->changeTypeInAnnotationTypes( + $node, + $phpDocInfo, + $oldToNewClasses, + $hasChanged + ); - if ($this->shouldRemoveUseName($last, $newNameLastName, $importNames)) { - $this->removeUseName($name); + if ($hasChanged) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + return true; } - return new FullyQualified($newName); + return false; } - private function removeUseName(Name $oldName): void + private function shouldSkip(string $newName, FullyQualified $fullyQualified): bool { - $uses = $this->betterNodeFinder->findFirstPrevious( - $oldName, - fn (Node $node): bool => $node instanceof UseUse && $this->nodeNameResolver->areNamesEqual($node, $oldName) - ); - - if (! $uses instanceof UseUse) { - return; - } - - if ($uses->alias !== null) { - return; + if ($fullyQualified->getAttribute(AttributeKey::IS_STATICCALL_CLASS_NAME) === true + && $this->reflectionProvider->hasClass($newName)) { + $classReflection = $this->reflectionProvider->getClass($newName); + return $classReflection->isInterface(); } - // ios the only one? Remove whole use instead to avoid "use ;" constructions - $parentUse = $uses->getAttribute(AttributeKey::PARENT_NODE); - if ($parentUse instanceof Use_ && count($parentUse->uses) === 1) { - $this->nodeRemover->removeNode($parentUse); - } else { - $this->nodeRemover->removeNode($uses); - } + return false; } /** * @param array $oldToNewClasses */ - private function refactorNamespace(Namespace_ $namespace, array $oldToNewClasses): ?Node - { - $name = $this->nodeNameResolver->getName($namespace); - if ($name === null) { + private function refactorName( + FullyQualified $fullyQualified, + array $oldToNewClasses, + ?Scope $scope + ): ?FullyQualified { + if ($fullyQualified->getAttribute(AttributeKey::IS_FUNCCALL_NAME) === true) { return null; } - $classLike = $this->getClassOfNamespaceToRefactor($namespace, $oldToNewClasses); - if (! $classLike instanceof ClassLike) { + $stringName = $fullyQualified->toString(); + $newName = $oldToNewClasses[$stringName] ?? null; + + if ($newName === null) { return null; } - $currentName = $this->nodeNameResolver->getName($classLike); - $newClassFullyQualified = $oldToNewClasses[$currentName]; - - if ($this->reflectionProvider->hasClass($newClassFullyQualified)) { + if (! $this->isClassToInterfaceValidChange($fullyQualified, $newName, $scope)) { return null; } - $newNamespace = $this->classNaming->getNamespace($newClassFullyQualified); - // Renaming to class without namespace (example MyNamespace\DateTime -> DateTimeImmutable) - if (! $newNamespace) { - $classLike->name = new Identifier($newClassFullyQualified); - - return $classLike; + if ($this->shouldSkip($newName, $fullyQualified)) { + return null; } - $namespace->name = new Name($newNamespace); - - return $namespace; + $this->renamedNameCollector->add($stringName); + return new FullyQualified($newName); } /** * @param array $oldToNewClasses */ - private function refactorClassLike(ClassLike $classLike, array $oldToNewClasses): ?Node + private function refactorClassLike(ClassLike $classLike, array $oldToNewClasses, ?Scope $scope): ?Node { // rename interfaces - $this->renameClassImplements($classLike, $oldToNewClasses); - - $name = $this->nodeNameResolver->getName($classLike); - if ($name === null) { - return null; - } - - $newName = $oldToNewClasses[$name] ?? null; - if (! $newName) { + if (! $classLike instanceof Class_) { return null; } - // prevents re-iterating same class in endless loop - if (in_array($name, $this->alreadyProcessedClasses, true)) { - return null; - } + $hasChanged = false; + $classLike->implements = array_unique($classLike->implements); + foreach ($classLike->implements as $key => $implementName) { + $namespaceName = $scope instanceof Scope ? $scope->getNamespace() : null; - $this->alreadyProcessedClasses[] = $name; + $fullyQualifiedName = $namespaceName . '\\' . $implementName->toString(); + $newName = $oldToNewClasses[$fullyQualifiedName] ?? null; + if ($newName === null) { + continue; + } - $newName = $oldToNewClasses[$name]; - $newClassNamePart = $this->nodeNameResolver->getShortName($newName); - $newNamespacePart = $this->classNaming->getNamespace($newName); - if ($this->isClassAboutToBeDuplicated($newName)) { - return null; + $classLike->implements[$key] = new FullyQualified($newName); + $hasChanged = true; } - $classLike->name = new Identifier($newClassNamePart); - $classNamingGetNamespace = $this->classNaming->getNamespace($name); - - // Old class did not have any namespace, we need to wrap class with Namespace_ node - if ($newNamespacePart && ! $classNamingGetNamespace) { - $this->changeNameToFullyQualifiedName($classLike); - - $nameNode = new Name($newNamespacePart); - return new Namespace_($nameNode, [$classLike]); + if ($hasChanged) { + return $classLike; } - return $classLike; + return null; } /** @@ -254,103 +205,39 @@ private function refactorClassLike(ClassLike $classLike, array $oldToNewClasses) * - implements SomeInterface * - implements SomeClass */ - private function isClassToInterfaceValidChange(Name $name, string $newClassName): bool - { + private function isClassToInterfaceValidChange( + FullyQualified $fullyQualified, + string $newClassName, + ?Scope $scope + ): bool { if (! $this->reflectionProvider->hasClass($newClassName)) { return true; } $classReflection = $this->reflectionProvider->getClass($newClassName); - // ensure new is not with interface - $parentNode = $name->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof New_ && $classReflection->isInterface()) { - return false; - } - - if ($parentNode instanceof Class_) { - return $this->isValidClassNameChange($name, $parentNode, $classReflection); - } + if ($fullyQualified->getAttribute(AttributeKey::IS_NEW_INSTANCE_NAME) !== true) { + if ($fullyQualified->getAttribute(AttributeKey::IS_CLASS_EXTENDS) === true && $scope instanceof Scope) { + $currentClassReflection = $scope->getClassReflection(); - // prevent to change to import, that already exists - if ($parentNode instanceof UseUse) { - return $this->isValidUseImportChange($newClassName, $parentNode); - } - - return true; - } - - /** - * @param array $oldToNewClasses - */ - private function getClassOfNamespaceToRefactor(Namespace_ $namespace, array $oldToNewClasses): ?ClassLike - { - $foundClass = $this->betterNodeFinder->findFirst($namespace, function (Node $node) use ( - $oldToNewClasses - ): bool { - if (! $node instanceof ClassLike) { - return false; + if ($currentClassReflection instanceof ClassReflection && $currentClassReflection->getName() === $newClassName) { + return false; + } } - $classLikeName = $this->nodeNameResolver->getName($node); - - return isset($oldToNewClasses[$classLikeName]); - }); - - return $foundClass instanceof ClassLike ? $foundClass : null; - } - - /** - * @param string[] $oldToNewClasses - */ - private function renameClassImplements(ClassLike $classLike, array $oldToNewClasses): void - { - if (! $classLike instanceof Class_) { - return; + return $this->isValidClassNameChange($fullyQualified, $classReflection); } - /** @var Scope|null $scope */ - $scope = $classLike->getAttribute(AttributeKey::SCOPE); - - $classLike->implements = array_unique($classLike->implements); - foreach ($classLike->implements as $key => $implementName) { - $virtualNode = $implementName->getAttribute(AttributeKey::VIRTUAL_NODE); - if (! $virtualNode) { - continue; - } - - $namespaceName = $scope instanceof Scope ? $scope->getNamespace() : null; - - $fullyQualifiedName = $namespaceName . '\\' . $implementName->toString(); - $newName = $oldToNewClasses[$fullyQualifiedName] ?? null; - if ($newName === null) { - continue; - } - - $classLike->implements[$key] = new FullyQualified($newName); + if (! $classReflection->isInterface()) { + return $this->isValidClassNameChange($fullyQualified, $classReflection); } - } - - private function isClassAboutToBeDuplicated(string $newName): bool - { - return $this->reflectionProvider->hasClass($newName); - } - - private function changeNameToFullyQualifiedName(ClassLike $classLike): void - { - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classLike, function (Node $node) { - if (! $node instanceof FullyQualified) { - return null; - } - // invoke override - $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); - }); + return false; } - private function isValidClassNameChange(Name $name, Class_ $class, ClassReflection $classReflection): bool + private function isValidClassNameChange(FullyQualified $fullyQualified, ClassReflection $classReflection): bool { - if ($class->extends === $name) { + if ($fullyQualified->getAttribute(AttributeKey::IS_CLASS_EXTENDS) === true) { // is class to interface? if ($classReflection->isInterface()) { return false; @@ -361,30 +248,37 @@ private function isValidClassNameChange(Name $name, Class_ $class, ClassReflecti } } - // is interface to class? - return ! (in_array($name, $class->implements, true) && $classReflection->isClass()); + if ($fullyQualified->getAttribute(AttributeKey::IS_CLASS_IMPLEMENT) === true) { + // is interface to class? + return ! $classReflection->isClass(); + } + + return true; } - private function isValidUseImportChange(string $newName, UseUse $useUse): bool + /** + * @param array $oldToNewClasses + * @return OldToNewType[] + */ + private function createOldToNewTypes(array $oldToNewClasses): array { - /** @var Use_[]|null $useNodes */ - $useNodes = $useUse->getAttribute(AttributeKey::USE_NODES); - if ($useNodes === null) { - return true; + $serialized = \serialize($oldToNewClasses); + $cacheKey = $this->fileHasher->hash($serialized); + + if (isset($this->oldToNewTypesByCacheKey[$cacheKey])) { + return $this->oldToNewTypesByCacheKey[$cacheKey]; } - foreach ($useNodes as $useNode) { - if ($this->nodeNameResolver->isName($useNode, $newName)) { - // name already exists - return false; - } + $oldToNewTypes = []; + + foreach ($oldToNewClasses as $oldClass => $newClass) { + $oldObjectType = new ObjectType($oldClass); + $newObjectType = new FullyQualifiedObjectType($newClass); + $oldToNewTypes[] = new OldToNewType($oldObjectType, $newObjectType); } - return true; - } + $this->oldToNewTypesByCacheKey[$cacheKey] = $oldToNewTypes; - private function shouldRemoveUseName(string $last, string $newNameLastName, bool $importNames): bool - { - return $last === $newNameLastName && $importNames; + return $oldToNewTypes; } } diff --git a/rules/Renaming/NodeManipulator/IdentifierManipulator.php b/rules/Renaming/NodeManipulator/IdentifierManipulator.php deleted file mode 100644 index 24ea523db4b..00000000000 --- a/rules/Renaming/NodeManipulator/IdentifierManipulator.php +++ /dev/null @@ -1,67 +0,0 @@ -resolveOldMethodName($node); - if ($oldNodeMethodName === null) { - return; - } - - $node->name = new Identifier($renameMethodMap[$oldNodeMethodName]); - } - - public function removeSuffix( - ClassConstFetch | MethodCall | PropertyFetch | StaticCall | ClassMethod $node, - string $suffixToRemove - ): void { - $name = $this->nodeNameResolver->getName($node); - if ($name === null) { - return; - } - - $newName = Strings::replace($name, sprintf('#%s$#', $suffixToRemove), ''); - $node->name = new Identifier($newName); - } - - private function resolveOldMethodName( - ClassConstFetch | MethodCall | PropertyFetch | StaticCall | ClassMethod $node - ): ?string { - if ($node instanceof StaticCall || $node instanceof MethodCall) { - return $this->nodeNameResolver->getName($node->name); - } - - return $this->nodeNameResolver->getName($node); - } -} diff --git a/rules/Renaming/NodeManipulator/SwitchManipulator.php b/rules/Renaming/NodeManipulator/SwitchManipulator.php index e70e03dfad1..ace241dcb73 100644 --- a/rules/Renaming/NodeManipulator/SwitchManipulator.php +++ b/rules/Renaming/NodeManipulator/SwitchManipulator.php @@ -4,7 +4,7 @@ namespace Rector\Renaming\NodeManipulator; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Break_; @@ -20,11 +20,13 @@ public function removeBreakNodes(array $stmts): array if (! $node instanceof Break_) { continue; } - if (! $node->num instanceof LNumber || $node->num->value === 1) { + + if (! $node->num instanceof Int_ || $node->num->value === 1) { unset($stmts[$key]); continue; } - $node->num = $node->num->value === 2 ? null : new LNumber($node->num->value - 1); + + $node->num = $node->num->value === 2 ? null : new Int_($node->num->value - 1); } return $stmts; diff --git a/rules/Renaming/Rector/Cast/RenameCastRector.php b/rules/Renaming/Rector/Cast/RenameCastRector.php new file mode 100644 index 00000000000..b0f0f0bcb0e --- /dev/null +++ b/rules/Renaming/Rector/Cast/RenameCastRector.php @@ -0,0 +1,82 @@ + + */ + private array $renameCasts = []; + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Renames casts', [ + new ConfiguredCodeSample( + '$real = (real) $real;', + '$real = (float) $real;', + [new RenameCast(Double::class, Double::KIND_REAL, Double::KIND_FLOAT)] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Cast::class]; + } + + /** + * @param Cast $node + */ + public function refactor(Node $node): ?Node + { + foreach ($this->renameCasts as $renameCast) { + $expectedClassName = $renameCast->getFromCastExprClass(); + if (! $node instanceof $expectedClassName) { + continue; + } + + if ($node->getAttribute(AttributeKey::KIND) !== $renameCast->getFromCastKind()) { + continue; + } + + $node->setAttribute(AttributeKey::KIND, $renameCast->getToCastKind()); + $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); + $node->setAttribute('startTokenPos', -1); + $node->setAttribute('endTokenPos', -1); + + return $node; + } + + return null; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsInstanceOf($configuration, RenameCast::class); + + $this->renameCasts = $configuration; + } +} diff --git a/rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php b/rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php index e2b170e5093..9aa17b147da 100644 --- a/rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php +++ b/rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php @@ -8,8 +8,8 @@ use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Identifier; use PhpParser\Node\Name\FullyQualified; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Renaming\Contract\RenameClassConstFetchInterface; use Rector\Renaming\ValueObject\RenameClassAndConstFetch; use Rector\Renaming\ValueObject\RenameClassConstFetch; @@ -22,11 +22,6 @@ */ final class RenameClassConstFetchRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const CLASS_CONSTANT_RENAME = 'constant_rename'; - /** * @var RenameClassConstFetchInterface[] */ @@ -34,15 +29,8 @@ final class RenameClassConstFetchRector extends AbstractRector implements Config public function getRuleDefinition(): RuleDefinition { - $configuration = [ - self::CLASS_CONSTANT_RENAME => [ - new RenameClassConstFetch('SomeClass', 'OLD_CONSTANT', 'NEW_CONSTANT'), - new RenameClassAndConstFetch('SomeClass', 'OTHER_OLD_CONSTANT', 'DifferentClass', 'NEW_CONSTANT'), - ], - ]; - return new RuleDefinition( - 'Replaces defined class constants in their calls.', + 'Replace defined class constants in their calls', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' @@ -55,7 +43,15 @@ public function getRuleDefinition(): RuleDefinition $value = DifferentClass::NEW_CONSTANT; CODE_SAMPLE , - $configuration + [ + new RenameClassConstFetch('SomeClass', 'OLD_CONSTANT', 'NEW_CONSTANT'), + new RenameClassAndConstFetch( + 'SomeClass', + 'OTHER_OLD_CONSTANT', + 'DifferentClass', + 'NEW_CONSTANT' + ), + ] ), ] ); @@ -75,11 +71,11 @@ public function getNodeTypes(): array public function refactor(Node $node): ?ClassConstFetch { foreach ($this->renameClassConstFetches as $renameClassConstFetch) { - if (! $this->isObjectType($node->class, $renameClassConstFetch->getOldObjectType())) { + if (! $this->isName($node->name, $renameClassConstFetch->getOldConstant())) { continue; } - if (! $this->isName($node->name, $renameClassConstFetch->getOldConstant())) { + if (! $this->isObjectType($node->class, $renameClassConstFetch->getOldObjectType())) { continue; } @@ -96,14 +92,13 @@ public function refactor(Node $node): ?ClassConstFetch } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $renameClassConstFetches = $configuration[self::CLASS_CONSTANT_RENAME] ?? []; - Assert::allIsInstanceOf($renameClassConstFetches, RenameClassConstFetchInterface::class); + Assert::allIsAOf($configuration, RenameClassConstFetchInterface::class); - $this->renameClassConstFetches = $renameClassConstFetches; + $this->renameClassConstFetches = $configuration; } private function createClassAndConstFetch(RenameClassAndConstFetch $renameClassAndConstFetch): ClassConstFetch diff --git a/rules/Renaming/Rector/ClassMethod/RenameAnnotationRector.php b/rules/Renaming/Rector/ClassMethod/RenameAnnotationRector.php index a79a236cd01..ccb80b81f41 100644 --- a/rules/Renaming/Rector/ClassMethod/RenameAnnotationRector.php +++ b/rules/Renaming/Rector/ClassMethod/RenameAnnotationRector.php @@ -7,12 +7,16 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockTagReplacer; -use Rector\Renaming\ValueObject\RenameAnnotation; +use Rector\Rector\AbstractRector; +use Rector\Renaming\Contract\RenameAnnotationInterface; +use Rector\Renaming\ValueObject\RenameAnnotationByType; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; @@ -23,28 +27,27 @@ final class RenameAnnotationRector extends AbstractRector implements ConfigurableRectorInterface { /** - * @var string + * @var RenameAnnotationInterface[] */ - public const RENAMED_ANNOTATIONS_IN_TYPES = 'renamed_annotations_in_types'; - - /** - * @var RenameAnnotation[] - */ - private array $renamedAnnotations = []; + private array $renameAnnotations = []; public function __construct( - private DocBlockTagReplacer $docBlockTagReplacer + private readonly DocBlockTagReplacer $docBlockTagReplacer, + private readonly DocBlockUpdater $docBlockUpdater, + private readonly PhpDocInfoFactory $phpDocInfoFactory, ) { } public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Turns defined annotations above properties and methods to their new values.', + 'Turn defined annotations above properties and methods to their new values', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' -class SomeTest extends PHPUnit\Framework\TestCase +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase { /** * @test @@ -56,7 +59,9 @@ public function someMethod() CODE_SAMPLE , <<<'CODE_SAMPLE' -class SomeTest extends PHPUnit\Framework\TestCase +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase { /** * @scenario @@ -67,11 +72,7 @@ public function someMethod() } CODE_SAMPLE , - [ - self::RENAMED_ANNOTATIONS_IN_TYPES => [ - new RenameAnnotation('PHPUnit\Framework\TestCase', 'test', 'scenario'), - ], - ] + [new RenameAnnotationByType('PHPUnit\Framework\TestCase', 'test', 'scenario')] ), ] ); @@ -82,43 +83,89 @@ public function someMethod() */ public function getNodeTypes(): array { - return [ClassMethod::class, Property::class]; + return [Class_::class, Expression::class]; } /** - * @param ClassMethod|Property $node + * @param Class_|Expression $node */ public function refactor(Node $node): ?Node { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; + if ($node instanceof Expression) { + return $this->refactorExpression($node); } - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $hasChanged = false; + foreach ($node->stmts as $stmt) { + if (! $stmt instanceof ClassMethod && ! $stmt instanceof Property) { + continue; + } - foreach ($this->renamedAnnotations as $renamedAnnotation) { - if (! $this->isObjectType($classLike, $renamedAnnotation->getObjectType())) { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($stmt); + if (! $phpDocInfo instanceof PhpDocInfo) { continue; } - $this->docBlockTagReplacer->replaceTagByAnother( - $phpDocInfo, - $renamedAnnotation->getOldAnnotation(), - $renamedAnnotation->getNewAnnotation() - ); + foreach ($this->renameAnnotations as $renameAnnotation) { + if ($renameAnnotation instanceof RenameAnnotationByType && ! $this->isObjectType( + $node, + $renameAnnotation->getObjectType() + )) { + continue; + } + + $hasDocBlockChanged = $this->docBlockTagReplacer->replaceTagByAnother( + $phpDocInfo, + $renameAnnotation->getOldAnnotation(), + $renameAnnotation->getNewAnnotation() + ); + + if ($hasDocBlockChanged) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); + $hasChanged = true; + } + } + } + + if (! $hasChanged) { + return null; } return $node; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $renamedAnnotationsInTypes = $configuration[self::RENAMED_ANNOTATIONS_IN_TYPES] ?? []; - Assert::allIsInstanceOf($renamedAnnotationsInTypes, RenameAnnotation::class); - $this->renamedAnnotations = $renamedAnnotationsInTypes; + Assert::allIsAOf($configuration, RenameAnnotationInterface::class); + $this->renameAnnotations = $configuration; + } + + private function refactorExpression(Expression $expression): ?Expression + { + $hasChanged = false; + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($expression); + + foreach ($this->renameAnnotations as $renameAnnotation) { + $hasDocBlockChanged = $this->docBlockTagReplacer->replaceTagByAnother( + $phpDocInfo, + $renameAnnotation->getOldAnnotation(), + $renameAnnotation->getNewAnnotation() + ); + + if ($hasDocBlockChanged) { + $hasChanged = true; + } + } + + if ($hasChanged) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($expression); + return $expression; + } + + return null; } } diff --git a/rules/Renaming/Rector/Class_/RenameAttributeRector.php b/rules/Renaming/Rector/Class_/RenameAttributeRector.php new file mode 100644 index 00000000000..71740e2b2fb --- /dev/null +++ b/rules/Renaming/Rector/Class_/RenameAttributeRector.php @@ -0,0 +1,118 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class, ClassMethod::class, Property::class, Param::class]; + } + + /** + * @param Class_|ClassMethod|Property|Param $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + foreach ($node->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + $newAttributeName = $this->matchNewAttributeName($attr); + if (! is_string($newAttributeName)) { + continue; + } + + $attr->name = new FullyQualified($newAttributeName); + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsAOf($configuration, RenameAttribute::class); + + $this->renameAttributes = $configuration; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ATTRIBUTES; + } + + private function matchNewAttributeName(Attribute $attribute): ?string + { + foreach ($this->renameAttributes as $renameAttribute) { + if ($this->isName($attribute->name, $renameAttribute->getOldAttribute())) { + return $renameAttribute->getNewAttribute(); + } + } + + return null; + } +} diff --git a/rules/Renaming/Rector/ConstFetch/RenameConstantRector.php b/rules/Renaming/Rector/ConstFetch/RenameConstantRector.php index 35921b1e1cc..5bbe33113e8 100644 --- a/rules/Renaming/Rector/ConstFetch/RenameConstantRector.php +++ b/rules/Renaming/Rector/ConstFetch/RenameConstantRector.php @@ -7,21 +7,18 @@ use PhpParser\Node; use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Name; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; +use Rector\Validation\RectorAssert; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\Renaming\Rector\ConstFetch\RenameConstantRector\RenameConstantRectorTest */ final class RenameConstantRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const OLD_TO_NEW_CONSTANTS = 'old_to_new_constants'; - /** * @var array */ @@ -52,10 +49,8 @@ public function run() CODE_SAMPLE , [ - self::OLD_TO_NEW_CONSTANTS => [ - 'MYSQL_ASSOC' => 'MYSQLI_ASSOC', - 'OLD_CONSTANT' => 'NEW_CONSTANT', - ], + 'MYSQL_ASSOC' => 'MYSQLI_ASSOC', + 'OLD_CONSTANT' => 'NEW_CONSTANT', ] ), ]); @@ -87,10 +82,19 @@ public function refactor(Node $node): ?Node } /** - * @param array> $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $this->oldToNewConstants = $configuration[self::OLD_TO_NEW_CONSTANTS] ?? []; + Assert::allString(array_keys($configuration)); + Assert::allString($configuration); + + foreach ($configuration as $oldConstant => $newConstant) { + RectorAssert::constantName($oldConstant); + RectorAssert::constantName($newConstant); + } + + /** @var array $configuration */ + $this->oldToNewConstants = $configuration; } } diff --git a/rules/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector.php b/rules/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector.php deleted file mode 100644 index 444fe3b9d9f..00000000000 --- a/rules/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector.php +++ /dev/null @@ -1,227 +0,0 @@ - [ - new PseudoNamespaceToNamespace('Some_', ['Some_Class_To_Keep']), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - // property, method - return [FileWithoutNamespace::class, Namespace_::class]; - } - - /** - * @param Namespace_|FileWithoutNamespace $node - */ - public function refactor(Node $node): ?Node - { - $this->newNamespace = null; - - if ($node instanceof FileWithoutNamespace) { - $stmts = $this->refactorStmts($node->stmts); - $node->stmts = $stmts; - - // add a new namespace? - if ($this->newNamespace) { - $namespace = new Namespace_(new Name($this->newNamespace)); - $namespace->stmts = $stmts; - - return $namespace; - } - } - - if ($node instanceof Namespace_) { - $this->refactorStmts([$node]); - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $namespacePrefixesWithExcludedClasses = $configuration[self::NAMESPACE_PREFIXES_WITH_EXCLUDED_CLASSES] ?? []; - Assert::allIsInstanceOf($namespacePrefixesWithExcludedClasses, PseudoNamespaceToNamespace::class); - - $this->pseudoNamespacesToNamespaces = $namespacePrefixesWithExcludedClasses; - } - - /** - * @param Stmt[] $stmts - * @return Stmt[] - */ - private function refactorStmts(array $stmts): array - { - $this->traverseNodesWithCallable($stmts, function (Node $node): ?Node { - if (! $node instanceof Name && ! $node instanceof Identifier && ! $node instanceof Property && ! $node instanceof FunctionLike) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - // replace on @var/@param/@return/@throws - foreach ($this->pseudoNamespacesToNamespaces as $pseudoNamespaceToNamespace) { - $this->phpDocTypeRenamer->changeUnderscoreType($phpDocInfo, $node, $pseudoNamespaceToNamespace); - } - - // @todo - update rule to allow for bool instanceof check - if ($node instanceof Name || $node instanceof Identifier) { - return $this->processNameOrIdentifier($node); - } - - return null; - }); - - return $stmts; - } - - /** - * @return Name|Identifier - */ - private function processNameOrIdentifier(Name | Identifier $node): ?Node - { - // no name → skip - if ($node->toString() === '') { - return null; - } - - foreach ($this->pseudoNamespacesToNamespaces as $pseudoNamespaceToNamespace) { - if (! $this->isName($node, $pseudoNamespaceToNamespace->getNamespacePrefix() . '*')) { - continue; - } - - $excludedClasses = $pseudoNamespaceToNamespace->getExcludedClasses(); - if (is_array($excludedClasses) && $this->isNames($node, $excludedClasses)) { - return null; - } - - if ($node instanceof Name) { - return $this->processName($node); - } - - return $this->processIdentifier($node); - } - - return null; - } - - private function processName(Name $name): Name - { - $nodeName = $this->getName($name); - - if ($nodeName !== null) { - $name->parts = explode('_', $nodeName); - } - - return $name; - } - - private function processIdentifier(Identifier $identifier): ?Identifier - { - $parentNode = $identifier->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Class_) { - return null; - } - - $name = $this->getName($identifier); - if ($name === null) { - return null; - } - - /** @var string $namespaceName */ - $namespaceName = Strings::before($name, '_', -1); - - /** @var string $lastNewNamePart */ - $lastNewNamePart = Strings::after($name, '_', -1); - - $newNamespace = Strings::replace($namespaceName, self::SPLIT_BY_UNDERSCORE_REGEX, '$1$2\\\\$4'); - - if ($this->newNamespace !== null && $this->newNamespace !== $newNamespace) { - throw new ShouldNotHappenException('There cannot be 2 different namespaces in one file'); - } - - $this->newNamespace = $newNamespace; - $identifier->name = $lastNewNamePart; - - return $identifier; - } -} diff --git a/rules/Renaming/Rector/FuncCall/RenameFunctionRector.php b/rules/Renaming/Rector/FuncCall/RenameFunctionRector.php index c96fa576040..b1dbbb5e905 100644 --- a/rules/Renaming/Rector/FuncCall/RenameFunctionRector.php +++ b/rules/Renaming/Rector/FuncCall/RenameFunctionRector.php @@ -8,22 +8,17 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\Renaming\Rector\FuncCall\RenameFunctionRector\RenameFunctionRectorTest */ final class RenameFunctionRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const OLD_FUNCTION_TO_NEW_FUNCTION = 'old_function_to_new_function'; - /** * @var array */ @@ -31,14 +26,12 @@ final class RenameFunctionRector extends AbstractRector implements ConfigurableR public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Turns defined function call new one.', [ + return new RuleDefinition('Turn defined function call new one', [ new ConfiguredCodeSample( 'view("...", []);', 'Laravel\Templating\render("...", []);', [ - self::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'view' => 'Laravel\Templating\render', - ], + 'view' => 'Laravel\Templating\render', ] ), ]); @@ -57,14 +50,13 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - foreach ($this->oldFunctionToNewFunction as $oldFunction => $newFunction) { - if (! $this->isName($node, $oldFunction)) { - continue; - } + $nodeName = $this->getName($node); + if ($nodeName === null) { + return null; + } - // not to refactor here - $isVirtual = $node->name->getAttribute(AttributeKey::VIRTUAL_NODE); - if ($isVirtual) { + foreach ($this->oldFunctionToNewFunction as $oldFunction => $newFunction) { + if (! $this->nodeNameResolver->isStringName($nodeName, $oldFunction)) { continue; } @@ -80,7 +72,10 @@ public function refactor(Node $node): ?Node */ public function configure(array $configuration): void { - $this->oldFunctionToNewFunction = $configuration[self::OLD_FUNCTION_TO_NEW_FUNCTION] ?? []; + Assert::allString(array_values($configuration)); + Assert::allString($configuration); + + $this->oldFunctionToNewFunction = $configuration; } private function createName(string $newFunction): Name diff --git a/rules/Renaming/Rector/MethodCall/RenameMethodRector.php b/rules/Renaming/Rector/MethodCall/RenameMethodRector.php index 6e0d6b29b68..f4d883852a0 100644 --- a/rules/Renaming/Rector/MethodCall/RenameMethodRector.php +++ b/rules/Renaming/Rector/MethodCall/RenameMethodRector.php @@ -8,15 +8,21 @@ use PhpParser\Node; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; -use PhpParser\Node\Stmt\ClassLike; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\NodeManipulator\ClassManipulator; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\Renaming\Collector\MethodCallRenameCollector; +use PhpParser\Node\Stmt\Interface_; +use PhpParser\Node\Stmt\Trait_; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\ReflectionProvider; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\NodeManipulator\ClassManipulator; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; use Rector\Renaming\Contract\MethodCallRenameInterface; use Rector\Renaming\ValueObject\MethodCallRename; use Rector\Renaming\ValueObject\MethodCallRenameWithArrayKey; @@ -29,25 +35,21 @@ */ final class RenameMethodRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const METHOD_CALL_RENAMES = 'method_call_renames'; - /** * @var MethodCallRenameInterface[] */ private array $methodCallRenames = []; public function __construct( - private ClassManipulator $classManipulator, - private MethodCallRenameCollector $methodCallRenameCollector + private readonly ClassManipulator $classManipulator, + private readonly ReflectionResolver $reflectionResolver, + private readonly ReflectionProvider $reflectionProvider ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Turns method names to new ones.', [ + return new RuleDefinition('Turn method names to new ones', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' $someObject = new SomeExampleClass; @@ -59,11 +61,7 @@ public function getRuleDefinition(): RuleDefinition $someObject->newMethod(); CODE_SAMPLE , - [ - self::METHOD_CALL_RENAMES => [ - new MethodCallRename('SomeExampleClass', 'oldMethod', 'newMethod'), - ], - ] + [new MethodCallRename('SomeExampleClass', 'oldMethod', 'newMethod')] ), ]); } @@ -73,82 +71,193 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [MethodCall::class, StaticCall::class, ClassMethod::class]; + return [ + MethodCall::class, + NullsafeMethodCall::class, + StaticCall::class, + Class_::class, + Trait_::class, + Interface_::class, + ]; } /** - * @param MethodCall|StaticCall|ClassMethod $node + * @param MethodCall|NullsafeMethodCall|StaticCall|Class_|Interface_|Trait_ $node */ public function refactor(Node $node): ?Node { - foreach ($this->methodCallRenames as $methodCallRename) { - $implementsInterface = $this->classManipulator->hasParentMethodOrInterface( - $methodCallRename->getOldObjectType(), - $methodCallRename->getOldMethod() - ); - if ($implementsInterface) { - continue; - } - - if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType( - $node, - $methodCallRename->getOldObjectType() - )) { - continue; - } - - if (! $this->isName($node->name, $methodCallRename->getOldMethod())) { - continue; - } - - if ($this->shouldSkipClassMethod($node, $methodCallRename)) { - continue; - } - - $node->name = new Identifier($methodCallRename->getNewMethod()); - - if ($methodCallRename instanceof MethodCallRenameWithArrayKey && ! $node instanceof ClassMethod) { - return new ArrayDimFetch($node, BuilderHelpers::normalizeValue($methodCallRename->getArrayKey())); - } - - return $node; + if ($node instanceof Class_ || $node instanceof Trait_ || $node instanceof Interface_) { + $scope = ScopeFetcher::fetch($node); + return $this->refactorClass($node, $scope); } - return null; + return $this->refactorMethodCallAndStaticCall($node); } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $methodCallRenames = $configuration[self::METHOD_CALL_RENAMES] ?? []; - Assert::allIsInstanceOf($methodCallRenames, MethodCallRenameInterface::class); + Assert::allIsAOf($configuration, MethodCallRenameInterface::class); - $this->methodCallRenames = $methodCallRenames; - $this->methodCallRenameCollector->addMethodCallRenames($methodCallRenames); + $this->methodCallRenames = $configuration; } private function shouldSkipClassMethod( - MethodCall | StaticCall | ClassMethod $node, + MethodCall|NullsafeMethodCall|StaticCall $call, MethodCallRenameInterface $methodCallRename ): bool { - if (! $node instanceof ClassMethod) { + $classReflection = $this->reflectionResolver->resolveClassReflectionSourceObject($call); + if (! $classReflection instanceof ClassReflection) { return false; } - return $this->shouldSkipForAlreadyExistingClassMethod($node, $methodCallRename); + $targetClass = $methodCallRename->getClass(); + if (! $this->reflectionProvider->hasClass($targetClass)) { + return false; + } + + $targetClassReflection = $this->reflectionProvider->getClass($targetClass); + if ($classReflection->getName() === $targetClassReflection->getName()) { + return false; + } + + // different with configured ClassLike source? it is a child, which may has old and new exists + if (! $classReflection->hasMethod($methodCallRename->getOldMethod())) { + return false; + } + + return $classReflection->hasMethod($methodCallRename->getNewMethod()); } - private function shouldSkipForAlreadyExistingClassMethod( - ClassMethod $classMethod, + private function hasClassNewClassMethod( + Class_|Trait_|Interface_ $classOrInterface, MethodCallRenameInterface $methodCallRename ): bool { - $classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof ClassLike) { + return (bool) $classOrInterface->getMethod($methodCallRename->getNewMethod()); + } + + private function shouldKeepForParentInterface( + MethodCallRenameInterface $methodCallRename, + ?ClassReflection $classReflection + ): bool { + if (! $classReflection instanceof ClassReflection) { + return false; + } + + // interface can change current method, as parent contract is still valid + if (! $classReflection->isInterface()) { return false; } - return (bool) $classLike->getMethod($methodCallRename->getNewMethod()); + return $this->classManipulator->hasParentMethodOrInterface( + $methodCallRename->getObjectType(), + $methodCallRename->getOldMethod() + ); + } + + private function refactorClass( + Class_|Trait_|Interface_ $classOrInterface, + Scope $scope + ): Class_|Trait_|Interface_|null { + $classReflection = $scope->getClassReflection(); + + $hasChanged = false; + + foreach ($classOrInterface->getMethods() as $classMethod) { + $methodName = $this->getName($classMethod->name); + if ($methodName === null) { + continue; + } + + foreach ($this->methodCallRenames as $methodCallRename) { + if ($this->shouldSkipRename( + $methodName, + $classMethod, + $methodCallRename, + $classOrInterface, + $classReflection + )) { + continue; + } + + $classMethod->name = new Identifier($methodCallRename->getNewMethod()); + $hasChanged = true; + + continue 2; + } + } + + if ($hasChanged) { + return $classOrInterface; + } + + return null; + } + + private function shouldSkipRename( + string $methodName, + ClassMethod $classMethod, + MethodCallRenameInterface $methodCallRename, + Class_|Trait_|Interface_ $classOrInterface, + ?ClassReflection $classReflection + ): bool { + if (! $this->nodeNameResolver->isStringName($methodName, $methodCallRename->getOldMethod())) { + return true; + } + + if (! $classReflection instanceof ClassReflection && $classOrInterface instanceof Trait_) { + return $this->hasClassNewClassMethod($classOrInterface, $methodCallRename); + } + + if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType( + $classMethod, + $methodCallRename->getObjectType() + )) { + return true; + } + + if ($this->shouldKeepForParentInterface($methodCallRename, $classReflection)) { + return true; + } + + return $this->hasClassNewClassMethod($classOrInterface, $methodCallRename); + } + + private function refactorMethodCallAndStaticCall( + StaticCall|MethodCall|NullsafeMethodCall $call + ): ArrayDimFetch|null|MethodCall|StaticCall|NullsafeMethodCall { + $callName = $this->getName($call->name); + if ($callName === null) { + return null; + } + + foreach ($this->methodCallRenames as $methodCallRename) { + if (! $this->nodeNameResolver->isStringName($callName, $methodCallRename->getOldMethod())) { + continue; + } + + if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType( + $call, + $methodCallRename->getObjectType() + )) { + continue; + } + + if ($this->shouldSkipClassMethod($call, $methodCallRename)) { + continue; + } + + $call->name = new Identifier($methodCallRename->getNewMethod()); + + if ($methodCallRename instanceof MethodCallRenameWithArrayKey) { + return new ArrayDimFetch($call, BuilderHelpers::normalizeValue($methodCallRename->getArrayKey())); + } + + return $call; + } + + return null; } } diff --git a/rules/Renaming/Rector/Name/RenameClassRector.php b/rules/Renaming/Rector/Name/RenameClassRector.php index 50661ffa9ab..e98cbd736ca 100644 --- a/rules/Renaming/Rector/Name/RenameClassRector.php +++ b/rules/Renaming/Rector/Name/RenameClassRector.php @@ -7,14 +7,16 @@ use PhpParser\Node; use PhpParser\Node\FunctionLike; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\Namespace_; +use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Property; -use Rector\Core\Configuration\RenamedClassesDataCollector; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace; -use Rector\Core\Rector\AbstractRector; +use PHPStan\Reflection\ReflectionProvider; +use Rector\Configuration\RenamedClassesDataCollector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Rector\AbstractRector; use Rector\Renaming\NodeManipulator\ClassRenamer; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -25,26 +27,16 @@ */ final class RenameClassRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const OLD_TO_NEW_CLASSES = 'old_to_new_classes'; - - /** - * @api - * @var string - */ - public const CLASS_MAP_FILES = 'class_map_files'; - public function __construct( - private RenamedClassesDataCollector $renamedClassesDataCollector, - private ClassRenamer $classRenamer + private readonly RenamedClassesDataCollector $renamedClassesDataCollector, + private readonly ClassRenamer $classRenamer, + private readonly ReflectionProvider $reflectionProvider ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Replaces defined classes by new ones.', [ + return new RuleDefinition('Replace defined classes by new ones', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' namespace App; @@ -73,9 +65,7 @@ function someFunction(SomeNewClass $someOldClass): SomeNewClass CODE_SAMPLE , [ - self::OLD_TO_NEW_CLASSES => [ - 'App\SomeOldClass' => 'App\SomeNewClass', - ], + 'App\SomeOldClass' => 'App\SomeNewClass', ] ), ]); @@ -87,51 +77,86 @@ function someFunction(SomeNewClass $someOldClass): SomeNewClass public function getNodeTypes(): array { return [ + // place FullyQualified before Name on purpose executed early before the Name as parent + FullyQualified::class, + // Name as parent of FullyQualified executed later for fallback annotation to attribute rename to Name Name::class, Property::class, FunctionLike::class, Expression::class, ClassLike::class, - Namespace_::class, - FileWithoutNamespace::class, + If_::class, ]; } /** - * @param FunctionLike|Name|ClassLike|Expression|Namespace_|Property|FileWithoutNamespace $node + * @param FunctionLike|FullyQualified|Name|ClassLike|Expression|Property|If_ $node */ public function refactor(Node $node): ?Node { $oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses(); - return $this->classRenamer->renameNode($node, $oldToNewClasses); + + if ($oldToNewClasses === []) { + return null; + } + + if ($node instanceof FullyQualified && $this->shouldSkipClassConstFetchForMissingConstantName( + $node, + $oldToNewClasses + )) { + return null; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + return $this->classRenamer->renameNode($node, $oldToNewClasses, $scope); } /** - * @param array> $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $this->addOldToNewClasses($configuration[self::OLD_TO_NEW_CLASSES] ?? []); - - $classMapFiles = $configuration[self::CLASS_MAP_FILES] ?? []; - Assert::allString($classMapFiles); + Assert::allString($configuration); + Assert::allString(array_keys($configuration)); - foreach ($classMapFiles as $classMapFile) { - Assert::fileExists($classMapFile); - - $oldToNewClasses = require_once $classMapFile; - $this->addOldToNewClasses($oldToNewClasses); - } + $this->renamedClassesDataCollector->addOldToNewClasses($configuration); } /** * @param array $oldToNewClasses */ - private function addOldToNewClasses(array $oldToNewClasses): void - { - Assert::allString(array_keys($oldToNewClasses)); - Assert::allString($oldToNewClasses); + private function shouldSkipClassConstFetchForMissingConstantName( + FullyQualified $fullyQualified, + array $oldToNewClasses + ): bool { + if (! $this->reflectionProvider->hasClass($fullyQualified->toString())) { + return false; + } + + // not part of class const fetch (e.g. SomeClass::SOME_VALUE) + $constFetchName = $fullyQualified->getAttribute(AttributeKey::CLASS_CONST_FETCH_NAME); + if (! is_string($constFetchName)) { + return false; + } + + foreach ($oldToNewClasses as $oldClass => $newClass) { + if (! $this->isName($fullyQualified, $oldClass)) { + continue; + } + + if (! $this->reflectionProvider->hasClass($newClass)) { + continue; + } + + $classReflection = $this->reflectionProvider->getClass($newClass); + $oldClassReflection = $this->reflectionProvider->getClass($oldClass); + + if ($oldClassReflection->hasConstant($constFetchName) && ! $classReflection->hasConstant($constFetchName)) { + // should be skipped as new class does not have access to the constant + return true; + } + } - $this->renamedClassesDataCollector->addOldToNewClasses($oldToNewClasses); + return false; } } diff --git a/rules/Renaming/Rector/Namespace_/RenameNamespaceRector.php b/rules/Renaming/Rector/Namespace_/RenameNamespaceRector.php deleted file mode 100644 index a5c00bc4967..00000000000 --- a/rules/Renaming/Rector/Namespace_/RenameNamespaceRector.php +++ /dev/null @@ -1,171 +0,0 @@ - - */ - private array $oldToNewNamespaces = []; - - public function __construct( - private NamespaceMatcher $namespaceMatcher - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Replaces old namespace by new one.', [ - new ConfiguredCodeSample( - '$someObject = new SomeOldNamespace\SomeClass;', - '$someObject = new SomeNewNamespace\SomeClass;', - [ - self::OLD_TO_NEW_NAMESPACES => [ - 'SomeOldNamespace' => 'SomeNewNamespace', - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Namespace_::class, Use_::class, Name::class]; - } - - /** - * @param Namespace_|Use_|Name $node - */ - public function refactor(Node $node): ?Node - { - $name = $this->getName($node); - if ($name === null) { - return null; - } - - $renamedNamespaceValueObject = $this->namespaceMatcher->matchRenamedNamespace($name, $this->oldToNewNamespaces); - if (! $renamedNamespaceValueObject instanceof RenamedNamespace) { - return null; - } - - if ($this->isClassFullyQualifiedName($node)) { - return null; - } - - if ($node instanceof Namespace_) { - $newName = $renamedNamespaceValueObject->getNameInNewNamespace(); - $node->name = new Name($newName); - - return $node; - } - - if ($node instanceof Use_) { - $newName = $renamedNamespaceValueObject->getNameInNewNamespace(); - $node->uses[0]->name = new Name($newName); - - return $node; - } - - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - // already resolved above - if ($parent instanceof Namespace_) { - return null; - } - - if ($parent instanceof UseUse && $parent->type === Use_::TYPE_UNKNOWN) { - return null; - } - - $newName = $this->isPartialNamespace($node) ? $this->resolvePartialNewName( - $node, - $renamedNamespaceValueObject - ) : $renamedNamespaceValueObject->getNameInNewNamespace(); - - return new FullyQualified($newName); - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->oldToNewNamespaces = $configuration[self::OLD_TO_NEW_NAMESPACES] ?? []; - } - - /** - * Checks for "new \ClassNoNamespace;" - * This should be skipped, not a namespace. - */ - private function isClassFullyQualifiedName(Node $node): bool - { - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - return false; - } - - if (! $parentNode instanceof New_) { - return false; - } - - /** @var FullyQualified $fullyQualifiedNode */ - $fullyQualifiedNode = $parentNode->class; - - $newClassName = $fullyQualifiedNode->toString(); - - return array_key_exists($newClassName, $this->oldToNewNamespaces); - } - - private function isPartialNamespace(Name $name): bool - { - $resolvedName = $name->getAttribute(AttributeKey::RESOLVED_NAME); - if (! $resolvedName instanceof Name) { - return false; - } - - if ($resolvedName instanceof FullyQualified) { - return ! $this->isName($name, $resolvedName->toString()); - } - - return false; - } - - private function resolvePartialNewName(Name $name, RenamedNamespace $renamedNamespace): string - { - $nameInNewNamespace = $renamedNamespace->getNameInNewNamespace(); - - // first dummy implementation - improve - $cutOffFromTheLeft = Strings::length($nameInNewNamespace) - Strings::length($name->toString()); - - return Strings::substring($nameInNewNamespace, $cutOffFromTheLeft); - } -} diff --git a/rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php b/rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php index f572be316fa..c995057363a 100644 --- a/rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php +++ b/rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php @@ -6,9 +6,14 @@ use PhpParser\Node; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Identifier; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Stmt\ClassLike; +use PhpParser\Node\Stmt\Property; +use PhpParser\Node\VarLikeIdentifier; +use PHPStan\Type\ObjectType; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Renaming\ValueObject\RenameProperty; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,27 +24,20 @@ */ final class RenamePropertyRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const RENAMED_PROPERTIES = 'old_to_new_property_by_types'; - /** * @var RenameProperty[] */ private array $renamedProperties = []; + private bool $hasChanged = false; + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Replaces defined old properties by new ones.', [ + return new RuleDefinition('Replace defined old properties by new ones', [ new ConfiguredCodeSample( '$someObject->someOldProperty;', '$someObject->someNewProperty;', - [ - self::RENAMED_PROPERTIES => [ - new RenameProperty('SomeClass', 'someOldProperty', 'someNewProperty'), - ], - ] + [new RenameProperty('SomeClass', 'someOldProperty', 'someNewProperty')] ), ]); } @@ -49,37 +47,90 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [PropertyFetch::class]; + return [PropertyFetch::class, StaticPropertyFetch::class, ClassLike::class]; } /** - * @param PropertyFetch $node + * @param PropertyFetch|StaticPropertyFetch|ClassLike $node */ public function refactor(Node $node): ?Node { - foreach ($this->renamedProperties as $renamedProperty) { - if (! $this->isObjectType($node->var, $renamedProperty->getObjectType())) { - continue; + if ($node instanceof ClassLike) { + $this->hasChanged = false; + + foreach ($this->renamedProperties as $renamedProperty) { + $this->renameProperty($node, $renamedProperty); } - if (! $this->isName($node, $renamedProperty->getOldProperty())) { - continue; + if ($this->hasChanged) { + return $node; } - $node->name = new Identifier($renamedProperty->getNewProperty()); - return $node; + return null; } - return null; + return $this->refactorPropertyFetch($node); } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $renamedProperties = $configuration[self::RENAMED_PROPERTIES] ?? []; - Assert::allIsInstanceOf($renamedProperties, RenameProperty::class); - $this->renamedProperties = $renamedProperties; + Assert::allIsAOf($configuration, RenameProperty::class); + $this->renamedProperties = $configuration; + } + + private function renameProperty(ClassLike $classLike, RenameProperty $renameProperty): void + { + $classLikeName = (string) $this->getName($classLike); + $renamePropertyObjectType = $renameProperty->getObjectType(); + $className = $renamePropertyObjectType->getClassName(); + + $classLikeNameObjectType = new ObjectType($classLikeName); + $classNameObjectType = new ObjectType($className); + + $isSuperType = $classNameObjectType->isSuperTypeOf($classLikeNameObjectType) + ->yes(); + if ($classLikeName !== $className && ! $isSuperType) { + return; + } + + $property = $classLike->getProperty($renameProperty->getOldProperty()); + if (! $property instanceof Property) { + return; + } + + $newProperty = $renameProperty->getNewProperty(); + $targetNewProperty = $classLike->getProperty($newProperty); + if ($targetNewProperty instanceof Property) { + return; + } + + $this->hasChanged = true; + $property->props[0]->name = new VarLikeIdentifier($newProperty); + } + + private function refactorPropertyFetch( + PropertyFetch|StaticPropertyFetch $propertyFetch + ): null|PropertyFetch|StaticPropertyFetch { + foreach ($this->renamedProperties as $renamedProperty) { + $oldProperty = $renamedProperty->getOldProperty(); + if (! $this->isName($propertyFetch, $oldProperty)) { + continue; + } + + $varPropertyFetch = $propertyFetch instanceof PropertyFetch ? $propertyFetch->var : $propertyFetch->class; + if (! $this->isObjectType($varPropertyFetch, $renamedProperty->getObjectType())) { + continue; + } + + $propertyFetch->name = $propertyFetch instanceof PropertyFetch + ? new Identifier($renamedProperty->getNewProperty()) + : new VarLikeIdentifier($renamedProperty->getNewProperty()); + return $propertyFetch; + } + + return null; } } diff --git a/rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php b/rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php index 93dad00756c..65e22014ef6 100644 --- a/rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php +++ b/rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php @@ -8,8 +8,8 @@ use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; use PhpParser\Node\Name\FullyQualified; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Renaming\ValueObject\RenameStaticMethod; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,16 +20,6 @@ */ final class RenameStaticMethodRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const OLD_TO_NEW_METHODS_BY_CLASSES = 'old_to_new_method_by_classes'; - - /** - * @var string - */ - private const SOME_CLASS = 'SomeClass'; - /** * @var RenameStaticMethod[] */ @@ -37,28 +27,11 @@ final class RenameStaticMethodRector extends AbstractRector implements Configura public function getRuleDefinition(): RuleDefinition { - $renameClassConfiguration = [ - self::OLD_TO_NEW_METHODS_BY_CLASSES => [ - new RenameStaticMethod(self::SOME_CLASS, 'oldMethod', 'AnotherExampleClass', 'newStaticMethod'), - ], - ]; - - $renameMethodConfiguration = [ - self::OLD_TO_NEW_METHODS_BY_CLASSES => [ - new RenameStaticMethod(self::SOME_CLASS, 'oldMethod', self::SOME_CLASS, 'newStaticMethod'), - ], - ]; - - return new RuleDefinition('Turns method names to new ones.', [ + return new RuleDefinition('Turn method names to new ones', [ new ConfiguredCodeSample( 'SomeClass::oldStaticMethod();', 'AnotherExampleClass::newStaticMethod();', - $renameClassConfiguration - ), - new ConfiguredCodeSample( - 'SomeClass::oldStaticMethod();', - 'SomeClass::newStaticMethod();', - $renameMethodConfiguration + [new RenameStaticMethod('SomeClass', 'oldMethod', 'AnotherExampleClass', 'newStaticMethod')] ), ]); } @@ -77,11 +50,11 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { foreach ($this->staticMethodRenames as $staticMethodRename) { - if (! $this->isObjectType($node->class, $staticMethodRename->getOldObjectType())) { + if (! $this->isName($node->name, $staticMethodRename->getOldMethod())) { continue; } - if (! $this->isName($node->name, $staticMethodRename->getOldMethod())) { + if (! $this->isObjectType($node->class, $staticMethodRename->getOldObjectType())) { continue; } @@ -92,13 +65,12 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $oldToNewMethodsByClasses = $configuration[self::OLD_TO_NEW_METHODS_BY_CLASSES]; - Assert::allIsInstanceOf($oldToNewMethodsByClasses, RenameStaticMethod::class); - $this->staticMethodRenames = $oldToNewMethodsByClasses; + Assert::allIsAOf($configuration, RenameStaticMethod::class); + $this->staticMethodRenames = $configuration; } private function rename(StaticCall $staticCall, RenameStaticMethod $renameStaticMethod): StaticCall diff --git a/rules/Renaming/Rector/String_/RenameStringRector.php b/rules/Renaming/Rector/String_/RenameStringRector.php index 4a5791b9e70..365af381803 100644 --- a/rules/Renaming/Rector/String_/RenameStringRector.php +++ b/rules/Renaming/Rector/String_/RenameStringRector.php @@ -6,28 +6,28 @@ use PhpParser\Node; use PhpParser\Node\Scalar\String_; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** - * @changelog https://github.com/symfony/symfony/pull/35858 - * * @see \Rector\Tests\Renaming\Rector\String_\RenameStringRector\RenameStringRectorTest */ final class RenameStringRector extends AbstractRector implements ConfigurableRectorInterface { /** - * @var string - */ - public const STRING_CHANGES = 'string_changes'; - - /** - * @var mixed[] + * @var array */ private array $stringChanges = []; + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Change string value', [ @@ -41,7 +41,7 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -51,11 +51,9 @@ public function run() } } CODE_SAMPLE -, + , [ - self::STRING_CHANGES => [ - 'ROLE_PREVIOUS_ADMIN' => 'IS_IMPERSONATOR', - ], + 'ROLE_PREVIOUS_ADMIN' => 'IS_IMPERSONATOR', ] ), ]); @@ -90,6 +88,9 @@ public function refactor(Node $node): ?Node */ public function configure(array $configuration): void { - $this->stringChanges = $configuration[self::STRING_CHANGES] ?? []; + Assert::allString(array_keys($configuration)); + Assert::allString($configuration); + + $this->stringChanges = $configuration; } } diff --git a/rules/Renaming/ValueObject/MethodCallRename.php b/rules/Renaming/ValueObject/MethodCallRename.php index 85782f59f53..ed5ea10eb62 100644 --- a/rules/Renaming/ValueObject/MethodCallRename.php +++ b/rules/Renaming/ValueObject/MethodCallRename.php @@ -6,19 +6,28 @@ use PHPStan\Type\ObjectType; use Rector\Renaming\Contract\MethodCallRenameInterface; +use Rector\Validation\RectorAssert; -final class MethodCallRename implements MethodCallRenameInterface +final readonly class MethodCallRename implements MethodCallRenameInterface { public function __construct( - private string $oldClass, + private string $class, private string $oldMethod, private string $newMethod ) { + RectorAssert::className($class); + RectorAssert::methodName($oldMethod); + RectorAssert::methodName($newMethod); } - public function getOldObjectType(): ObjectType + public function getClass(): string { - return new ObjectType($this->oldClass); + return $this->class; + } + + public function getObjectType(): ObjectType + { + return new ObjectType($this->class); } public function getOldMethod(): string diff --git a/rules/Renaming/ValueObject/MethodCallRenameWithArrayKey.php b/rules/Renaming/ValueObject/MethodCallRenameWithArrayKey.php index 75110e03994..36b6138ea45 100644 --- a/rules/Renaming/ValueObject/MethodCallRenameWithArrayKey.php +++ b/rules/Renaming/ValueObject/MethodCallRenameWithArrayKey.php @@ -6,23 +6,29 @@ use PHPStan\Type\ObjectType; use Rector\Renaming\Contract\MethodCallRenameInterface; +use Rector\Validation\RectorAssert; -final class MethodCallRenameWithArrayKey implements MethodCallRenameInterface +final readonly class MethodCallRenameWithArrayKey implements MethodCallRenameInterface { - /** - * @param mixed $arrayKey - */ public function __construct( - private string $oldClass, + private string $class, private string $oldMethod, private string $newMethod, - private $arrayKey + private mixed $arrayKey ) { + RectorAssert::className($class); + RectorAssert::methodName($oldMethod); + RectorAssert::methodName($newMethod); } - public function getOldObjectType(): ObjectType + public function getClass(): string { - return new ObjectType($this->oldClass); + return $this->class; + } + + public function getObjectType(): ObjectType + { + return new ObjectType($this->class); } public function getOldMethod(): string @@ -35,10 +41,7 @@ public function getNewMethod(): string return $this->newMethod; } - /** - * @return mixed - */ - public function getArrayKey() + public function getArrayKey(): mixed { return $this->arrayKey; } diff --git a/rules/Renaming/ValueObject/PseudoNamespaceToNamespace.php b/rules/Renaming/ValueObject/PseudoNamespaceToNamespace.php deleted file mode 100644 index b6308b37370..00000000000 --- a/rules/Renaming/ValueObject/PseudoNamespaceToNamespace.php +++ /dev/null @@ -1,30 +0,0 @@ -namespacePrefix; - } - - /** - * @return string[] - */ - public function getExcludedClasses(): array - { - return $this->excludedClasses; - } -} diff --git a/rules/Renaming/ValueObject/RenameAnnotation.php b/rules/Renaming/ValueObject/RenameAnnotation.php index ced35d18001..5fbee4ada93 100644 --- a/rules/Renaming/ValueObject/RenameAnnotation.php +++ b/rules/Renaming/ValueObject/RenameAnnotation.php @@ -4,22 +4,19 @@ namespace Rector\Renaming\ValueObject; -use PHPStan\Type\ObjectType; +use Rector\Renaming\Contract\RenameAnnotationInterface; -final class RenameAnnotation +/** + * @api + */ +final readonly class RenameAnnotation implements RenameAnnotationInterface { public function __construct( - private string $type, private string $oldAnnotation, private string $newAnnotation ) { } - public function getObjectType(): ObjectType - { - return new ObjectType($this->type); - } - public function getOldAnnotation(): string { return $this->oldAnnotation; diff --git a/rules/Renaming/ValueObject/RenameAnnotationByType.php b/rules/Renaming/ValueObject/RenameAnnotationByType.php new file mode 100644 index 00000000000..5cbcff35380 --- /dev/null +++ b/rules/Renaming/ValueObject/RenameAnnotationByType.php @@ -0,0 +1,35 @@ +type); + } + + public function getOldAnnotation(): string + { + return $this->oldAnnotation; + } + + public function getNewAnnotation(): string + { + return $this->newAnnotation; + } +} diff --git a/rules/Renaming/ValueObject/RenameAttribute.php b/rules/Renaming/ValueObject/RenameAttribute.php new file mode 100644 index 00000000000..285a84bc712 --- /dev/null +++ b/rules/Renaming/ValueObject/RenameAttribute.php @@ -0,0 +1,27 @@ +oldAttribute; + } + + public function getNewAttribute(): string + { + return $this->newAttribute; + } +} diff --git a/rules/Renaming/ValueObject/RenameCast.php b/rules/Renaming/ValueObject/RenameCast.php new file mode 100644 index 00000000000..a852c0da4ce --- /dev/null +++ b/rules/Renaming/ValueObject/RenameCast.php @@ -0,0 +1,42 @@ + $fromCastExprClass + */ + public function __construct( + private string $fromCastExprClass, + private int $fromCastKind, + private int $toCastKind, + ) { + RectorAssert::className($fromCastExprClass); + Assert::subclassOf($fromCastExprClass, Cast::class); + } + + /** + * @return class-string + */ + public function getFromCastExprClass(): string + { + return $this->fromCastExprClass; + } + + public function getFromCastKind(): int + { + return $this->fromCastKind; + } + + public function getToCastKind(): int + { + return $this->toCastKind; + } +} diff --git a/rules/Renaming/ValueObject/RenameClassAndConstFetch.php b/rules/Renaming/ValueObject/RenameClassAndConstFetch.php index f2f181a0497..d2c70021af8 100644 --- a/rules/Renaming/ValueObject/RenameClassAndConstFetch.php +++ b/rules/Renaming/ValueObject/RenameClassAndConstFetch.php @@ -6,8 +6,9 @@ use PHPStan\Type\ObjectType; use Rector\Renaming\Contract\RenameClassConstFetchInterface; +use Rector\Validation\RectorAssert; -final class RenameClassAndConstFetch implements RenameClassConstFetchInterface +final readonly class RenameClassAndConstFetch implements RenameClassConstFetchInterface { public function __construct( private string $oldClass, @@ -15,6 +16,11 @@ public function __construct( private string $newClass, private string $newConstant ) { + RectorAssert::className($oldClass); + RectorAssert::constantName($oldConstant); + + RectorAssert::className($newClass); + RectorAssert::constantName($newConstant); } public function getOldObjectType(): ObjectType diff --git a/rules/Renaming/ValueObject/RenameClassConstFetch.php b/rules/Renaming/ValueObject/RenameClassConstFetch.php index 3fb3f074d0c..424bf7bdd25 100644 --- a/rules/Renaming/ValueObject/RenameClassConstFetch.php +++ b/rules/Renaming/ValueObject/RenameClassConstFetch.php @@ -6,14 +6,18 @@ use PHPStan\Type\ObjectType; use Rector\Renaming\Contract\RenameClassConstFetchInterface; +use Rector\Validation\RectorAssert; -final class RenameClassConstFetch implements RenameClassConstFetchInterface +final readonly class RenameClassConstFetch implements RenameClassConstFetchInterface { public function __construct( private string $oldClass, private string $oldConstant, private string $newConstant ) { + RectorAssert::className($oldClass); + RectorAssert::constantName($oldConstant); + RectorAssert::constantName($newConstant); } public function getOldObjectType(): ObjectType diff --git a/rules/Renaming/ValueObject/RenameProperty.php b/rules/Renaming/ValueObject/RenameProperty.php index 1deb7751aee..919b4ecaf09 100644 --- a/rules/Renaming/ValueObject/RenameProperty.php +++ b/rules/Renaming/ValueObject/RenameProperty.php @@ -5,14 +5,18 @@ namespace Rector\Renaming\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class RenameProperty +final readonly class RenameProperty { public function __construct( private string $type, private string $oldProperty, private string $newProperty ) { + RectorAssert::className($type); + RectorAssert::propertyName($oldProperty); + RectorAssert::propertyName($newProperty); } public function getObjectType(): ObjectType diff --git a/rules/Renaming/ValueObject/RenameStaticMethod.php b/rules/Renaming/ValueObject/RenameStaticMethod.php index 2c3aba82ab1..3df7fb0b12d 100644 --- a/rules/Renaming/ValueObject/RenameStaticMethod.php +++ b/rules/Renaming/ValueObject/RenameStaticMethod.php @@ -5,8 +5,9 @@ namespace Rector\Renaming\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class RenameStaticMethod +final readonly class RenameStaticMethod { public function __construct( private string $oldClass, @@ -14,6 +15,11 @@ public function __construct( private string $newClass, private string $newMethod ) { + RectorAssert::className($oldClass); + RectorAssert::methodName($oldMethod); + + RectorAssert::className($newClass); + RectorAssert::methodName($newMethod); } public function getOldObjectType(): ObjectType diff --git a/rules/Renaming/ValueObject/RenamedNamespace.php b/rules/Renaming/ValueObject/RenamedNamespace.php deleted file mode 100644 index 05d26a8c11b..00000000000 --- a/rules/Renaming/ValueObject/RenamedNamespace.php +++ /dev/null @@ -1,20 +0,0 @@ -oldNamespace, $this->newNamespace, $this->currentName); - } -} diff --git a/rules/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector.php b/rules/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector.php deleted file mode 100644 index 9df0ec5bf3e..00000000000 --- a/rules/Restoration/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector.php +++ /dev/null @@ -1,80 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassConstFetch::class]; - } - - /** - * @param ClassConstFetch $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node->name, 'class')) { - return null; - } - - $referencedClass = $this->getName($node->class); - if ($referencedClass === null) { - return null; - } - - if ($this->reflectionProvider->hasClass($referencedClass)) { - return null; - } - - return new String_($referencedClass); - } -} diff --git a/rules/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector.php b/rules/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector.php deleted file mode 100644 index 0160530f177..00000000000 --- a/rules/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector.php +++ /dev/null @@ -1,79 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassLike::class]; - } - - /** - * @param ClassLike $node - */ - public function refactor(Node $node): ?Node - { - $className = $this->getName($node); - if ($className === null) { - return null; - } - - $classShortName = $this->nodeNameResolver->getShortName($className); - - $smartFileInfo = $this->file->getSmartFileInfo(); - - // matches - if ($classShortName === $smartFileInfo->getBasenameWithoutSuffix()) { - return null; - } - - $file = $node->getAttribute(AttributeKey::FILE); - if (! $file instanceof File) { - return null; - } - - // no match → rename file - $newFileLocation = $smartFileInfo->getPath() . DIRECTORY_SEPARATOR . $classShortName . '.php'; - $this->removedAndAddedFilesCollector->addMovedFile($file, $newFileLocation); - - return null; - } -} diff --git a/rules/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector.php b/rules/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector.php deleted file mode 100644 index fcbe1b93ce1..00000000000 --- a/rules/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector.php +++ /dev/null @@ -1,187 +0,0 @@ - [ - new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - // must be exactly 1 param - if (count($node->params) !== 1) { - return null; - } - - $firstParam = $node->params[0]; - $paramName = $this->getName($firstParam); - - foreach ($this->inferParamFromClassMethodReturn as $singleInferParamFromClassMethodReturn) { - $returnClassMethod = $this->matchReturnClassMethod($node, $singleInferParamFromClassMethodReturn); - if (! $returnClassMethod instanceof ClassMethod) { - continue; - } - - $returnType = $this->returnTypeInferer->inferFunctionLike($returnClassMethod); - - $currentPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - $paramType = $this->constantReturnToParamTypeConverter->convert($returnType); - if ($paramType instanceof MixedType) { - continue; - } - - if ($this->isParamDocTypeEqualToPhpType($firstParam, $paramType)) { - return null; - } - - $this->phpDocTypeChanger->changeParamType($currentPhpDocInfo, $paramType, $firstParam, $paramName); - - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $inferParamsFromClassMethodReturns = $configuration[self::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS] ?? []; - Assert::allIsInstanceOf($inferParamsFromClassMethodReturns, InferParamFromClassMethodReturn::class); - - $this->inferParamFromClassMethodReturn = $inferParamsFromClassMethodReturns; - } - - private function matchReturnClassMethod( - ClassMethod $classMethod, - InferParamFromClassMethodReturn $inferParamFromClassMethodReturn - ): ?ClassMethod { - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - if (! $classReflection->isSubclassOf($inferParamFromClassMethodReturn->getClass())) { - return null; - } - - if (! $this->isName($classMethod->name, $inferParamFromClassMethodReturn->getParamMethod())) { - return null; - } - - $classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } - - return $classLike->getMethod($inferParamFromClassMethodReturn->getReturnMethod()); - } - - private function isParamDocTypeEqualToPhpType(Param $param, Type $paramType): bool - { - $currentParamType = $this->nodeTypeResolver->getStaticType($param); - return $currentParamType->equals($paramType); - } -} diff --git a/rules/Restoration/Rector/Class_/RemoveFinalFromEntityRector.php b/rules/Restoration/Rector/Class_/RemoveFinalFromEntityRector.php deleted file mode 100644 index b334a6395ec..00000000000 --- a/rules/Restoration/Rector/Class_/RemoveFinalFromEntityRector.php +++ /dev/null @@ -1,73 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - if (! $phpDocInfo->hasByAnnotationClasses(['Doctrine\ORM\Mapping\Entity', 'Doctrine\ORM\Mapping\Embeddable'])) { - return null; - } - - if (! $node->isFinal()) { - return null; - } - - $this->visibilityManipulator->removeFinal($node); - - return $node; - } -} diff --git a/rules/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector.php b/rules/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector.php deleted file mode 100644 index 22c6f6d0253..00000000000 --- a/rules/Restoration/Rector/Namespace_/CompleteImportForPartialAnnotationRector.php +++ /dev/null @@ -1,160 +0,0 @@ - [ - new CompleteImportForPartialAnnotation('Doctrine\ORM\Mapping', 'ORM'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Namespace_::class]; - } - - /** - * @param Namespace_ $node - */ - public function refactor(Node $node): ?Node - { - $class = $this->betterNodeFinder->findFirstInstanceOf($node->stmts, Class_::class); - if (! $class instanceof Class_) { - return null; - } - - $printedClass = $this->print($class); - - foreach ($this->useImportsToRestore as $useImportToRestore) { - $annotationToSeek = '#\*\s+\@' . $useImportToRestore->getAlias() . '#'; - if (! Strings::match($printedClass, $annotationToSeek)) { - continue; - } - - $node = $this->addImportToNamespaceIfMissing($node, $useImportToRestore); - } - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $useImportsToRestore = $configuration[self::USE_IMPORTS_TO_RESTORE] ?? []; - Assert::allIsInstanceOf($useImportsToRestore, CompleteImportForPartialAnnotation::class); - - $default = [ - new CompleteImportForPartialAnnotation('Doctrine\ORM\Mapping', 'ORM'), - new CompleteImportForPartialAnnotation('Symfony\Component\Validator\Constraints', 'Assert'), - new CompleteImportForPartialAnnotation('JMS\Serializer\Annotation', 'Serializer'), - ]; - - $this->useImportsToRestore = array_merge($useImportsToRestore, $default); - } - - private function addImportToNamespaceIfMissing( - Namespace_ $namespace, - CompleteImportForPartialAnnotation $completeImportForPartialAnnotation - ): Namespace_ { - foreach ($namespace->stmts as $stmt) { - if (! $stmt instanceof Use_) { - continue; - } - - $useUse = $stmt->uses[0]; - // already there - if (! $this->isName($useUse->name, $completeImportForPartialAnnotation->getUse())) { - continue; - } - if ((string) $useUse->alias !== $completeImportForPartialAnnotation->getAlias()) { - continue; - } - return $namespace; - } - - return $this->addImportToNamespace($namespace, $completeImportForPartialAnnotation); - } - - private function addImportToNamespace( - Namespace_ $namespace, - CompleteImportForPartialAnnotation $completeImportForPartialAnnotation - ): Namespace_ { - $useBuilder = new UseBuilder($completeImportForPartialAnnotation->getUse()); - if ($completeImportForPartialAnnotation->getAlias() !== '') { - $useBuilder->as($completeImportForPartialAnnotation->getAlias()); - } - - /** @var Stmt $use */ - $use = $useBuilder->getNode(); - - $namespace->stmts = array_merge([$use], $namespace->stmts); - - return $namespace; - } -} diff --git a/rules/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector.php b/rules/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector.php deleted file mode 100644 index 890fd4e639d..00000000000 --- a/rules/Restoration/Rector/Property/MakeTypedPropertyNullableIfCheckedRector.php +++ /dev/null @@ -1,202 +0,0 @@ -anotherClass === null) { - $this->anotherClass = new AnotherClass; - } - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -final class SomeClass -{ - private ?AnotherClass $anotherClass = null; - - public function run() - { - if ($this->anotherClass === null) { - $this->anotherClass = new AnotherClass; - } - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Property::class]; - } - - /** - * @param Property $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkipProperty($node)) { - return null; - } - - /** @var PropertyProperty $onlyProperty */ - $onlyProperty = $node->props[0]; - - $isPropretyNullChecked = $this->isPropertyNullChecked($onlyProperty); - if (! $isPropretyNullChecked) { - return null; - } - - $currentPropertyType = $node->type; - if ($currentPropertyType === null) { - return null; - } - - $node->type = new NullableType($currentPropertyType); - $onlyProperty->default = $this->nodeFactory->createNull(); - - return $node; - } - - private function shouldSkipProperty(Property $property): bool - { - if (count($property->props) !== 1) { - return true; - } - - if ($property->type === null) { - return true; - } - - return $property->type instanceof NullableType; - } - - private function isPropertyNullChecked(PropertyProperty $onlyPropertyProperty): bool - { - $classLike = $onlyPropertyProperty->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return false; - } - - if ($this->isIdenticalOrNotIdenticalToNull($classLike, $onlyPropertyProperty)) { - return true; - } - - return $this->isBooleanNot($classLike, $onlyPropertyProperty); - } - - private function isIdenticalOrNotIdenticalToNull(Class_ $class, PropertyProperty $onlyPropertyProperty): bool - { - $isIdenticalOrNotIdenticalToNull = false; - - $this->traverseNodesWithCallable($class->stmts, function (Node $node) use ( - $onlyPropertyProperty, - &$isIdenticalOrNotIdenticalToNull - ) { - $matchedPropertyFetchName = $this->matchPropertyFetchNameComparedToNull($node); - if ($matchedPropertyFetchName === null) { - return null; - } - - if (! $this->isName($onlyPropertyProperty, $matchedPropertyFetchName)) { - return null; - } - - $isIdenticalOrNotIdenticalToNull = true; - }); - - return $isIdenticalOrNotIdenticalToNull; - } - - private function isBooleanNot(Class_ $class, PropertyProperty $onlyPropertyProperty): bool - { - $isBooleanNot = false; - - $this->traverseNodesWithCallable($class->stmts, function (Node $node) use ( - $onlyPropertyProperty, - &$isBooleanNot - ) { - if (! $node instanceof BooleanNot) { - return null; - } - - if (! $node->expr instanceof PropertyFetch) { - return null; - } - - if (! $this->isName($node->expr->var, 'this')) { - return null; - } - - if (! $this->nodeNameResolver->areNamesEqual($onlyPropertyProperty, $node->expr->name)) { - return null; - } - - $isBooleanNot = true; - }); - - return $isBooleanNot; - } - - /** - * Matches: - * $this-> === null - * null === $this-> - */ - private function matchPropertyFetchNameComparedToNull(Node $node): ?string - { - if (! $node instanceof Identical && ! $node instanceof NotIdentical) { - return null; - } - - if ($node->left instanceof PropertyFetch && $this->valueResolver->isNull($node->right)) { - $propertyFetch = $node->left; - } elseif ($node->right instanceof PropertyFetch && $this->valueResolver->isNull($node->left)) { - $propertyFetch = $node->right; - } else { - return null; - } - - if (! $this->isName($propertyFetch->var, 'this')) { - return null; - } - - return $this->getName($propertyFetch->name); - } -} diff --git a/rules/Restoration/Type/ConstantReturnToParamTypeConverter.php b/rules/Restoration/Type/ConstantReturnToParamTypeConverter.php deleted file mode 100644 index c36f136aa95..00000000000 --- a/rules/Restoration/Type/ConstantReturnToParamTypeConverter.php +++ /dev/null @@ -1,87 +0,0 @@ -getItemType(); - } - } - - $resolvedTypes = []; - foreach ($unionedTypes as $unionedType) { - $resolvedTypes[] = $this->convert($unionedType); - } - - return new UnionType($resolvedTypes); - } - - if ($type instanceof ConstantStringType) { - return $this->unwrapConstantTypeToObjectType($type); - } - - if ($type instanceof ArrayType) { - return $this->unwrapConstantTypeToObjectType($type); - } - - return new MixedType(); - } - - private function unwrapConstantTypeToObjectType(Type $type): Type - { - if ($type instanceof ArrayType) { - return $this->unwrapConstantTypeToObjectType($type->getItemType()); - } - - if ($type instanceof ConstantStringType) { - return new ObjectType($type->getValue()); - } - - if ($type instanceof GenericClassStringType && $type->getGenericType() instanceof ObjectType) { - return $type->getGenericType(); - } - - if ($type instanceof UnionType) { - return $this->unwrapUnionType($type); - } - - return new MixedType(); - } - - private function unwrapUnionType(UnionType $unionType): Type - { - $types = []; - foreach ($unionType->getTypes() as $unionedType) { - $unionType = $this->unwrapConstantTypeToObjectType($unionedType); - if ($unionType !== null) { - $types[] = $unionType; - } - } - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } -} diff --git a/rules/Restoration/ValueObject/CompleteImportForPartialAnnotation.php b/rules/Restoration/ValueObject/CompleteImportForPartialAnnotation.php deleted file mode 100644 index bb962f8e9e5..00000000000 --- a/rules/Restoration/ValueObject/CompleteImportForPartialAnnotation.php +++ /dev/null @@ -1,24 +0,0 @@ -use; - } - - public function getAlias(): string - { - return $this->alias; - } -} diff --git a/rules/Restoration/ValueObject/InferParamFromClassMethodReturn.php b/rules/Restoration/ValueObject/InferParamFromClassMethodReturn.php deleted file mode 100644 index e29b38ba98e..00000000000 --- a/rules/Restoration/ValueObject/InferParamFromClassMethodReturn.php +++ /dev/null @@ -1,30 +0,0 @@ -class; - } - - public function getParamMethod(): string - { - return $this->paramMethod; - } - - public function getReturnMethod(): string - { - return $this->returnMethod; - } -} diff --git a/rules/Strict/NodeAnalyzer/UninitializedPropertyAnalyzer.php b/rules/Strict/NodeAnalyzer/UninitializedPropertyAnalyzer.php new file mode 100644 index 00000000000..fdc4391bcf5 --- /dev/null +++ b/rules/Strict/NodeAnalyzer/UninitializedPropertyAnalyzer.php @@ -0,0 +1,71 @@ +nodeTypeResolver->getType($expr->var) + : $this->nodeTypeResolver->getType($expr->class); + + if ($varType instanceof ThisType) { + $varType = $varType->getStaticObjectType(); + } + + $className = ClassNameFromObjectTypeResolver::resolve($varType); + if ($className === null) { + return false; + } + + $classLike = $this->astResolver->resolveClassFromName($className); + + if (! $classLike instanceof ClassLike) { + return false; + } + + $propertyName = (string) $this->nodeNameResolver->getName($expr); + $property = $classLike->getProperty($propertyName); + + if (! $property instanceof Property) { + return false; + } + + if (count($property->props) !== 1) { + return false; + } + + if ($property->props[0]->default instanceof Expr) { + return false; + } + + return ! $this->constructorAssignDetector->isPropertyAssigned($classLike, $propertyName); + } +} diff --git a/rules/Strict/NodeFactory/ExactCompareFactory.php b/rules/Strict/NodeFactory/ExactCompareFactory.php new file mode 100644 index 00000000000..3d1205d5a99 --- /dev/null +++ b/rules/Strict/NodeFactory/ExactCompareFactory.php @@ -0,0 +1,319 @@ +isString()->yes()) { + if ($treatAsNonEmpty || ! $isOnlyString) { + return new Identical($expr, new String_('')); + } + + $result = new BooleanOr(new Identical($expr, new String_('')), new Identical($expr, new String_('0'))); + } elseif ($exprType->isInteger()->yes()) { + return new Identical($expr, new Int_(0)); + } elseif ($exprType->isBoolean()->yes()) { + return new Identical($expr, $this->nodeFactory->createFalse()); + } elseif ($exprType->isArray()->yes()) { + return new Identical($expr, new Array_([])); + } elseif ($exprType->isNull()->yes()) { + return new Identical($expr, $this->nodeFactory->createNull()); + } elseif (! $exprType instanceof UnionType) { + return null; + } else { + $result = $this->createTruthyFromUnionType($exprType, $expr, $treatAsNonEmpty, false); + } + + if ($result instanceof BooleanOr && $expr instanceof CallLike && $result->left instanceof Identical && $result->right instanceof Identical) { + return new FuncCall(new Name('in_array'), [ + new Arg($expr), + new Arg(new Array_([new ArrayItem($result->left->right), new ArrayItem($result->right->right)])), + new Arg(new ConstFetch(new Name('true'))), + ]); + } + + if ($result instanceof BooleanOr + && $expr instanceof CallLike + && $result->left instanceof BooleanOr + && $result->left->left instanceof Identical + && $result->left->right instanceof Identical + && $result->right instanceof Identical) { + return new FuncCall(new Name('in_array'), [ + new Arg($expr), + new Arg(new Array_([ + new ArrayItem($result->left->left->right), + new ArrayItem($result->left->right->right), + new ArrayItem($result->right->right), + ])), + new Arg(new ConstFetch(new Name('true'))), + ]); + } + + return $result; + } + + public function createNotIdenticalFalsyCompare( + Type $exprType, + Expr $expr, + bool $treatAsNotEmpty, + bool $isOnlyString = true + ): Identical|Instanceof_|NotIdentical|BooleanAnd|BooleanNot|null { + $result = null; + + if ($exprType->isString()->yes()) { + if ($treatAsNotEmpty || ! $isOnlyString) { + return new NotIdentical($expr, new String_('')); + } + + $result = new BooleanAnd( + new NotIdentical($expr, new String_('')), + new NotIdentical($expr, new String_('0')) + ); + } elseif ($exprType->isInteger()->yes()) { + return new NotIdentical($expr, new Int_(0)); + } elseif ($exprType->isArray()->yes()) { + return new NotIdentical($expr, new Array_([])); + } elseif (! $exprType instanceof UnionType) { + return null; + } else { + $result = $this->createFromUnionType($exprType, $expr, $treatAsNotEmpty, false); + } + + if ($result instanceof BooleanAnd && $expr instanceof CallLike && $result->left instanceof NotIdentical && $result->right instanceof NotIdentical) { + return new BooleanNot(new FuncCall(new Name('in_array'), [ + new Arg($expr), + new Arg(new Array_([new ArrayItem($result->left->right), new ArrayItem($result->right->right)])), + new Arg(new ConstFetch(new Name('true'))), + ])); + } + + if ($result instanceof BooleanAnd + && $expr instanceof CallLike + && $result->left instanceof BooleanAnd + && $result->left->left instanceof NotIdentical + && $result->left->right instanceof NotIdentical + && $result->right instanceof NotIdentical) { + return new BooleanNot(new FuncCall(new Name('in_array'), [ + new Arg($expr), + new Arg(new Array_([ + new ArrayItem($result->left->left->right), + new ArrayItem($result->left->right->right), + new ArrayItem($result->right->right), + ])), + new Arg(new ConstFetch(new Name('true'))), + ])); + } + + return $result; + } + + private function createFromUnionType( + UnionType $unionType, + Expr $expr, + bool $treatAsNotEmpty, + bool $isOnlyString + ): Identical|Instanceof_|BooleanAnd|null { + $unionType = TypeCombinator::removeNull($unionType); + + if ($unionType->isBoolean()->yes()) { + return new Identical($expr, $this->nodeFactory->createTrue()); + } + + $className = ClassNameFromObjectTypeResolver::resolve($unionType); + if ($className !== null) { + return new Instanceof_($expr, new FullyQualified($className)); + } + + $nullConstFetch = $this->nodeFactory->createNull(); + $toNullNotIdentical = new NotIdentical($expr, $nullConstFetch); + + if ($unionType instanceof UnionType) { + return $this->resolveFromCleanedNullUnionType($unionType, $expr, $treatAsNotEmpty); + } + + $compareExpr = $this->createNotIdenticalFalsyCompare($unionType, $expr, $treatAsNotEmpty, $isOnlyString); + if (! $compareExpr instanceof Expr) { + return null; + } + + if ($treatAsNotEmpty) { + return new BooleanAnd($toNullNotIdentical, $compareExpr); + } + + if ($unionType->isString()->yes()) { + $booleanAnd = new BooleanAnd($toNullNotIdentical, $compareExpr); + + return new BooleanAnd($booleanAnd, new NotIdentical($expr, new String_('0'))); + } + + return new BooleanAnd($toNullNotIdentical, $compareExpr); + } + + private function resolveFromCleanedNullUnionType( + UnionType $unionType, + Expr $expr, + bool $treatAsNotEmpty + ): ?BooleanAnd { + $compareExprs = $this->collectCompareExprs($unionType, $expr, $treatAsNotEmpty, false); + return $this->createBooleanAnd($compareExprs); + } + + /** + * @return array + */ + private function collectCompareExprs( + UnionType $unionType, + Expr $expr, + bool $treatAsNonEmpty, + bool $identical = true + ): array { + $compareExprs = []; + foreach ($unionType->getTypes() as $unionedType) { + $compareExprs[] = $identical + ? $this->createIdenticalFalsyCompare($unionedType, $expr, $treatAsNonEmpty) + : $this->createNotIdenticalFalsyCompare($unionedType, $expr, $treatAsNonEmpty); + } + + return array_unique($compareExprs, SORT_REGULAR); + } + + private function cleanUpPossibleNullableUnionType(UnionType $unionType): Type + { + return count($unionType->getTypes()) === 2 + ? TypeCombinator::removeNull($unionType) + : $unionType; + } + + /** + * @param array $compareExprs + */ + private function createBooleanOr(array $compareExprs): ?BooleanOr + { + $truthyExpr = array_shift($compareExprs); + + foreach ($compareExprs as $compareExpr) { + if (! $compareExpr instanceof Expr) { + return null; + } + + if (! $truthyExpr instanceof Expr) { + return null; + } + + $truthyExpr = new BooleanOr($truthyExpr, $compareExpr); + } + + if (! $truthyExpr instanceof BooleanOr) { + return null; + } + + return $truthyExpr; + } + + /** + * @param array $compareExprs + */ + private function createBooleanAnd(array $compareExprs): ?BooleanAnd + { + $truthyExpr = array_shift($compareExprs); + + foreach ($compareExprs as $compareExpr) { + if (! $compareExpr instanceof Expr) { + return null; + } + + if (! $truthyExpr instanceof Expr) { + return null; + } + + $truthyExpr = new BooleanAnd($truthyExpr, $compareExpr); + } + + if (! $truthyExpr instanceof BooleanAnd) { + return null; + } + + return $truthyExpr; + } + + private function createTruthyFromUnionType( + UnionType $unionType, + Expr $expr, + bool $treatAsNonEmpty, + bool $isOnlyString + ): BooleanOr|NotIdentical|Identical|BooleanNot|null { + $unionType = $this->cleanUpPossibleNullableUnionType($unionType); + + if ($unionType instanceof UnionType) { + $compareExprs = $this->collectCompareExprs($unionType, $expr, $treatAsNonEmpty); + return $this->createBooleanOr($compareExprs); + } + + if ($unionType->isBoolean()->yes()) { + return new NotIdentical($expr, $this->nodeFactory->createTrue()); + } + + $className = ClassNameFromObjectTypeResolver::resolve($unionType); + if ($className !== null) { + return new BooleanNot(new Instanceof_($expr, new FullyQualified($className))); + } + + $toNullIdentical = new Identical($expr, $this->nodeFactory->createNull()); + + if ($treatAsNonEmpty) { + return $toNullIdentical; + } + + // assume we have to check empty string, integer and bools + $scalarFalsyIdentical = $this->createIdenticalFalsyCompare($unionType, $expr, $treatAsNonEmpty, $isOnlyString); + if (! $scalarFalsyIdentical instanceof Expr) { + return null; + } + + if ($unionType->isString()->yes()) { + $booleanOr = new BooleanOr($toNullIdentical, $scalarFalsyIdentical); + + return new BooleanOr($booleanOr, new Identical($expr, new String_('0'))); + } + + return new BooleanOr($toNullIdentical, $scalarFalsyIdentical); + } +} diff --git a/rules/Strict/Rector/AbstractFalsyScalarRuleFixerRector.php b/rules/Strict/Rector/AbstractFalsyScalarRuleFixerRector.php new file mode 100644 index 00000000000..ac4f08572c1 --- /dev/null +++ b/rules/Strict/Rector/AbstractFalsyScalarRuleFixerRector.php @@ -0,0 +1,34 @@ + $configuration + */ + public function configure(array $configuration): void + { + $treatAsNonEmpty = $configuration[self::TREAT_AS_NON_EMPTY] ?? (bool) current($configuration); + Assert::boolean($treatAsNonEmpty); + + $this->treatAsNonEmpty = $treatAsNonEmpty; + } +} diff --git a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php new file mode 100644 index 00000000000..4df8fdb9f21 --- /dev/null +++ b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php @@ -0,0 +1,160 @@ + false, + ] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Empty_::class, BooleanNot::class]; + } + + /** + * @param Empty_|BooleanNot $node + */ + public function refactor(Node $node): Expr|null + { + $scope = ScopeFetcher::fetch($node); + + if ($node instanceof BooleanNot) { + return $this->refactorBooleanNot($node, $scope); + } + + if ($node->expr instanceof ArrayDimFetch) { + return null; + } + + return $this->refactorEmpty($node, $scope, $this->treatAsNonEmpty); + } + + private function refactorBooleanNot(BooleanNot $booleanNot, Scope $scope): Expr|null + { + if (! $booleanNot->expr instanceof Empty_) { + return null; + } + + $empty = $booleanNot->expr; + if ($empty->expr instanceof ArrayDimFetch) { + return $this->createDimFetchBooleanAnd($empty->expr); + } + + if ($this->exprAnalyzer->isNonTypedFromParam($empty->expr)) { + return null; + } + + $emptyExprType = $scope->getNativeType($empty->expr); + + $result = $this->exactCompareFactory->createNotIdenticalFalsyCompare( + $emptyExprType, + $empty->expr, + $this->treatAsNonEmpty + ); + + if (! $result instanceof Expr) { + return null; + } + + if ($this->uninitializedPropertyAnalyzer->isUninitialized($empty->expr)) { + return new BooleanAnd(new Isset_([$empty->expr]), $result); + } + + return $result; + } + + private function refactorEmpty(Empty_ $empty, Scope $scope, bool $treatAsNonEmpty): Expr|null + { + if ($this->exprAnalyzer->isNonTypedFromParam($empty->expr)) { + return null; + } + + $exprType = $scope->getNativeType($empty->expr); + $result = $this->exactCompareFactory->createIdenticalFalsyCompare($exprType, $empty->expr, $treatAsNonEmpty); + if (! $result instanceof Expr) { + return null; + } + + if ($this->uninitializedPropertyAnalyzer->isUninitialized($empty->expr)) { + return new BooleanOr(new BooleanNot(new Isset_([$empty->expr])), $result); + } + + return $result; + } + + private function createDimFetchBooleanAnd(ArrayDimFetch $arrayDimFetch): ?BooleanAnd + { + $exprType = $this->nodeTypeResolver->getNativeType($arrayDimFetch); + + $isset = new Isset_([$arrayDimFetch]); + $compareExpr = $this->exactCompareFactory->createNotIdenticalFalsyCompare($exprType, $arrayDimFetch, false); + + if (! $compareExpr instanceof Expr) { + return null; + } + + return new BooleanAnd($isset, $compareExpr); + } +} diff --git a/rules/Transform/Enum/MagicPropertyHandler.php b/rules/Transform/Enum/MagicPropertyHandler.php new file mode 100644 index 00000000000..031c848d065 --- /dev/null +++ b/rules/Transform/Enum/MagicPropertyHandler.php @@ -0,0 +1,16 @@ +typeProvidingExprFromClassResolver->resolveTypeProvidingExprFromClass( $class, - $functionLike, - $objectType + $classMethod, + $objectType, ); - if ($expr !== null) { + if ($expr instanceof Expr) { if ($expr instanceof Variable) { - $this->addClassMethodParamForVariable($expr, $objectType, $functionLike); + $this->addClassMethodParamForVariable($expr, $objectType, $classMethod); } return $expr; } $propertyName = $this->propertyNaming->fqnToVariableName($objectType); - $propertyMetadata = new PropertyMetadata($propertyName, $objectType, Class_::MODIFIER_PRIVATE); - $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); + $this->classDependencyManipulator->addConstructorDependency( + $class, + new PropertyMetadata($propertyName, $objectType) + ); return $this->propertyFetchFactory->createFromType($objectType); } diff --git a/rules/Transform/NodeAnalyzer/SingletonClassMethodAnalyzer.php b/rules/Transform/NodeAnalyzer/SingletonClassMethodAnalyzer.php deleted file mode 100644 index 488961b7742..00000000000 --- a/rules/Transform/NodeAnalyzer/SingletonClassMethodAnalyzer.php +++ /dev/null @@ -1,114 +0,0 @@ -stmts; - if (count($stmts) !== 2) { - return null; - } - - $firstStmt = $stmts[0] ?? null; - if (! $firstStmt instanceof If_) { - return null; - } - - $staticPropertyFetch = $this->matchStaticPropertyFetchInIfCond($firstStmt->cond); - - if (count($firstStmt->stmts) !== 1) { - return null; - } - - if (! $firstStmt->stmts[0] instanceof Expression) { - return null; - } - - $stmt = $firstStmt->stmts[0]->expr; - - // create self and assign to static property - if (! $stmt instanceof Assign) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($staticPropertyFetch, $stmt->var)) { - return null; - } - - if (! $stmt->expr instanceof New_) { - return null; - } - - /** @var string $class */ - $class = $classMethod->getAttribute(AttributeKey::CLASS_NAME); - - // the "self" class is created - if (! $this->nodeTypeResolver->isObjectType($stmt->expr->class, new ObjectType($class))) { - return null; - } - - /** @var StaticPropertyFetch $staticPropertyFetch */ - return $staticPropertyFetch; - } - - private function matchStaticPropertyFetchInIfCond(Expr $expr): ?StaticPropertyFetch - { - // matching: "self::$static === null" - if ($expr instanceof Identical) { - if ($this->valueResolver->isNull($expr->left) && $expr->right instanceof StaticPropertyFetch) { - return $expr->right; - } - - if ($this->valueResolver->isNull($expr->right) && $expr->left instanceof StaticPropertyFetch) { - return $expr->left; - } - } - - // matching: "! self::$static" - if (! $expr instanceof BooleanNot) { - return null; - } - - $negatedExpr = $expr->expr; - if (! $negatedExpr instanceof StaticPropertyFetch) { - return null; - } - - return $negatedExpr; - } -} diff --git a/rules/Transform/NodeFactory/ClassMethodFactory.php b/rules/Transform/NodeFactory/ClassMethodFactory.php deleted file mode 100644 index 44d68f1b591..00000000000 --- a/rules/Transform/NodeFactory/ClassMethodFactory.php +++ /dev/null @@ -1,22 +0,0 @@ -makePublic(); - $methodBuilder->makeStatic(); - $methodBuilder->addStmts($function->stmts); - - return $methodBuilder->getNode(); - } -} diff --git a/rules/Transform/NodeFactory/PropertyFetchFactory.php b/rules/Transform/NodeFactory/PropertyFetchFactory.php index 3f8a7aa1de2..16b1d39ab83 100644 --- a/rules/Transform/NodeFactory/PropertyFetchFactory.php +++ b/rules/Transform/NodeFactory/PropertyFetchFactory.php @@ -9,7 +9,7 @@ use PHPStan\Type\ObjectType; use Rector\Naming\Naming\PropertyNaming; -final class PropertyFetchFactory +final readonly class PropertyFetchFactory { public function __construct( private PropertyNaming $propertyNaming diff --git a/rules/Transform/NodeFactory/ProvideConfigFilePathClassMethodFactory.php b/rules/Transform/NodeFactory/ProvideConfigFilePathClassMethodFactory.php deleted file mode 100644 index 1ebb70f0446..00000000000 --- a/rules/Transform/NodeFactory/ProvideConfigFilePathClassMethodFactory.php +++ /dev/null @@ -1,34 +0,0 @@ -nodeFactory->createPublicMethod('provideConfigFilePath'); - $classMethod->returnType = new Identifier('string'); - - $concat = new Concat(new Dir(), new String_('/config/configured_rule.php')); - $return = new Return_($concat); - - $classMethod->stmts[] = $return; - - return $classMethod; - } -} diff --git a/rules/Transform/NodeFactory/UnwrapClosureFactory.php b/rules/Transform/NodeFactory/UnwrapClosureFactory.php deleted file mode 100644 index a8e2c97666f..00000000000 --- a/rules/Transform/NodeFactory/UnwrapClosureFactory.php +++ /dev/null @@ -1,40 +0,0 @@ -value; - - if ($argValue instanceof Closure) { - $unwrappedNodes = $argValue->getStmts(); - - $lastStmtKey = array_key_last($argValue->stmts); - $lastStmt = $argValue->stmts[$lastStmtKey]; - - if ($lastStmt instanceof Return_ && $lastStmt->expr !== null) { - unset($unwrappedNodes[$lastStmtKey]); - $unwrappedNodes[] = new Assign($resultVariable, $lastStmt->expr); - } - - return $unwrappedNodes; - } - - throw new ShouldNotHappenException(); - } -} diff --git a/rules/Transform/NodeTypeAnalyzer/TypeProvidingExprFromClassResolver.php b/rules/Transform/NodeTypeAnalyzer/TypeProvidingExprFromClassResolver.php index 4f231ef1385..2b3350c354c 100644 --- a/rules/Transform/NodeTypeAnalyzer/TypeProvidingExprFromClassResolver.php +++ b/rules/Transform/NodeTypeAnalyzer/TypeProvidingExprFromClassResolver.php @@ -8,32 +8,27 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\FunctionLike; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Function_; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; -use PHPStan\Reflection\Php\PhpPropertyReflection; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use PHPStan\Type\TypeWithClassName; -use Rector\Core\ValueObject\MethodName; +use PHPStan\Type\TypeCombinator; use Rector\Naming\Naming\PropertyNaming; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; +use Rector\ValueObject\MethodName; -final class TypeProvidingExprFromClassResolver +final readonly class TypeProvidingExprFromClassResolver { public function __construct( - private TypeUnwrapper $typeUnwrapper, private ReflectionProvider $reflectionProvider, private NodeNameResolver $nodeNameResolver, - private PropertyNaming $propertyNaming + private PropertyNaming $propertyNaming, ) { } @@ -42,42 +37,36 @@ public function __construct( */ public function resolveTypeProvidingExprFromClass( Class_ $class, - ClassMethod | Function_ $functionLike, - ObjectType $objectType + ClassMethod $classMethod, + ObjectType $objectType, ): ?Expr { - $className = $class->getAttribute(AttributeKey::CLASS_NAME); - if ($className === null) { - return null; - } + $className = (string) $this->nodeNameResolver->getName($class); // A. match a method $classReflection = $this->reflectionProvider->getClass($className); $methodCallProvidingType = $this->resolveMethodCallProvidingType($classReflection, $objectType); - if ($methodCallProvidingType !== null) { + if ($methodCallProvidingType instanceof MethodCall) { return $methodCallProvidingType; } // B. match existing property - $scope = $class->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - $propertyFetch = $this->resolvePropertyFetchProvidingType($classReflection, $scope, $objectType); - if ($propertyFetch !== null) { + $propertyFetch = $this->resolvePropertyFetchProvidingType($classReflection, $objectType); + if ($propertyFetch instanceof PropertyFetch) { return $propertyFetch; } // C. param in constructor? - return $this->resolveConstructorParamProvidingType($functionLike, $objectType); + return $this->resolveConstructorParamProvidingType($classMethod, $objectType); } private function resolveMethodCallProvidingType( ClassReflection $classReflection, ObjectType $objectType ): ?MethodCall { - foreach ($classReflection->getNativeMethods() as $methodReflection) { - $functionVariant = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + $methodReflections = $this->getClassMethodReflections($classReflection); + + foreach ($methodReflections as $methodReflection) { + $functionVariant = ParametersAcceptorSelector::combineAcceptors($methodReflection->getVariants()); $returnType = $functionVariant->getReturnType(); if (! $this->isMatchingType($returnType, $objectType)) { @@ -93,14 +82,12 @@ private function resolveMethodCallProvidingType( private function resolvePropertyFetchProvidingType( ClassReflection $classReflection, - Scope $scope, ObjectType $objectType ): ?PropertyFetch { - $reflectionClass = $classReflection->getNativeReflection(); + $nativeReflectionClass = $classReflection->getNativeReflection(); - foreach ($reflectionClass->getProperties() as $reflectionProperty) { - /** @var PhpPropertyReflection $phpPropertyReflection */ - $phpPropertyReflection = $classReflection->getProperty($reflectionProperty->getName(), $scope); + foreach ($nativeReflectionClass->getProperties() as $reflectionProperty) { + $phpPropertyReflection = $classReflection->getNativeProperty($reflectionProperty->getName()); $readableType = $phpPropertyReflection->getReadableType(); if (! $this->isMatchingType($readableType, $objectType)) { @@ -113,13 +100,11 @@ private function resolvePropertyFetchProvidingType( return null; } - private function resolveConstructorParamProvidingType(FunctionLike $functionLike, ObjectType $objectType): ?Variable - { - if (! $functionLike instanceof ClassMethod) { - return null; - } - - if (! $this->nodeNameResolver->isName($functionLike, MethodName::CONSTRUCT)) { + private function resolveConstructorParamProvidingType( + ClassMethod $classMethod, + ObjectType $objectType + ): ?Variable { + if (! $this->nodeNameResolver->isName($classMethod, MethodName::CONSTRUCT)) { return null; } @@ -133,12 +118,28 @@ private function isMatchingType(Type $readableType, ObjectType $objectType): boo return false; } - $readableType = $this->typeUnwrapper->unwrapNullableType($readableType); + $readableType = TypeCombinator::removeNull($readableType); + $className = ClassNameFromObjectTypeResolver::resolve($readableType); - if (! $readableType instanceof TypeWithClassName) { + if ($className === null) { return false; } return $readableType->equals($objectType); } + + /** + * @return MethodReflection[] + */ + private function getClassMethodReflections(ClassReflection $classReflection): array + { + $nativeReflection = $classReflection->getNativeReflection(); + $methodReflections = []; + + foreach ($nativeReflection->getMethods() as $reflectionMethod) { + $methodReflections[] = $classReflection->getNativeMethod($reflectionMethod->getName()); + } + + return $methodReflections; + } } diff --git a/rules/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector.php b/rules/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector.php new file mode 100644 index 00000000000..bca3f48c32f --- /dev/null +++ b/rules/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector.php @@ -0,0 +1,226 @@ +get('key'); +$object->set('key', 'value'); +$object->has('key'); +$object->unset('key'); +CODE_SAMPLE + , + [new ArrayDimFetchToMethodCall(new ObjectType('SomeClass'), 'get', 'set', 'has', 'unset')], + ), + ]); + } + + public function getNodeTypes(): array + { + return [Assign::class, Isset_::class, Unset_::class, ArrayDimFetch::class]; + } + + /** + * @param ArrayDimFetch|Assign|Isset_|Unset_ $node + * @return ($node is Unset_ ? Stmt[] : ($node is Isset_ ? Expr : MethodCall|null)) + */ + public function refactor(Node $node): array|Expr|null + { + if ($node instanceof Unset_) { + return $this->handleUnset($node); + } + + if ($node instanceof Isset_) { + return $this->handleIsset($node); + } + + if ($node instanceof Assign) { + if (! $node->var instanceof ArrayDimFetch) { + return null; + } + + return $this->createExplicitMethodCall($node->var, MagicPropertyHandler::SET, $node->expr); + } + + // is part of assign, skip + if ($node->getAttribute(AttributeKey::IS_BEING_ASSIGNED)) { + return null; + } + + if ($node->getAttribute(AttributeKey::IS_ASSIGN_OP_VAR)) { + return null; + } + + // should be skipped as handled above + if ($node->getAttribute(AttributeKey::IS_UNSET_VAR)) { + return null; + } + + if ($node->getAttribute(AttributeKey::IS_ISSET_VAR)) { + return null; + } + + return $this->createExplicitMethodCall($node, MagicPropertyHandler::GET); + } + + public function configure(array $configuration): void + { + Assert::allIsInstanceOf($configuration, ArrayDimFetchToMethodCall::class); + + $this->arrayDimFetchToMethodCalls = $configuration; + } + + private function handleIsset(Isset_ $isset): Expr|null + { + $issets = []; + $exprs = []; + + foreach ($isset->vars as $var) { + if ($var instanceof ArrayDimFetch) { + $methodCall = $this->createExplicitMethodCall($var, 'exists'); + + if ($methodCall instanceof MethodCall) { + $exprs[] = $methodCall; + continue; + } + } + + $issets[] = $var; + } + + if ($exprs === []) { + // nothing to handle + return null; + } + + if ($issets !== []) { + $isset->vars = $issets; + array_unshift($exprs, $isset); + } + + return array_reduce( + $exprs, + fn (?Expr $carry, Expr $expr): Isset_|MethodCall|BooleanAnd => $carry instanceof Expr ? new BooleanAnd( + $carry, + $expr + ) : $expr, + ); + } + + /** + * @return Stmt[]|null + */ + private function handleUnset(Unset_ $unset): ?array + { + $unsets = []; + $stmts = []; + + foreach ($unset->vars as $var) { + if ($var instanceof ArrayDimFetch) { + $methodCall = $this->createExplicitMethodCall($var, 'unset'); + + if ($methodCall instanceof MethodCall) { + $stmts[] = new Expression($methodCall); + continue; + } + } + + $unsets[] = $var; + } + + // nothing to change + if ($stmts === []) { + return null; + } + + if ($unsets !== []) { + $unset->vars = $unsets; + array_unshift($stmts, $unset); + } + + return $stmts; + } + + /** + * @param MagicPropertyHandler::* $magicPropertyHandler + */ + private function createExplicitMethodCall( + ArrayDimFetch $arrayDimFetch, + string $magicPropertyHandler, + ?Expr $expr = null + ): ?MethodCall { + if (! $arrayDimFetch->dim instanceof Node) { + return null; + } + + foreach ($this->arrayDimFetchToMethodCalls as $arrayDimFetchToMethodCall) { + if (! $this->isObjectType($arrayDimFetch->var, $arrayDimFetchToMethodCall->getObjectType())) { + continue; + } + + $method = match ($magicPropertyHandler) { + MagicPropertyHandler::GET => $arrayDimFetchToMethodCall->getMethod(), + MagicPropertyHandler::SET => $arrayDimFetchToMethodCall->getSetMethod(), + MagicPropertyHandler::ISSET_ => $arrayDimFetchToMethodCall->getExistsMethod(), + MagicPropertyHandler::UNSET => $arrayDimFetchToMethodCall->getUnsetMethod(), + }; + + if ($method === null) { + continue; + } + + $args = [new Arg($arrayDimFetch->dim)]; + + if ($expr instanceof Expr) { + $args[] = new Arg($expr); + } + + return new MethodCall($arrayDimFetch->var, $method, $args); + } + + return null; + } +} diff --git a/rules/Transform/Rector/Assign/DimFetchAssignToMethodCallRector.php b/rules/Transform/Rector/Assign/DimFetchAssignToMethodCallRector.php deleted file mode 100644 index 08ea4e13937..00000000000 --- a/rules/Transform/Rector/Assign/DimFetchAssignToMethodCallRector.php +++ /dev/null @@ -1,143 +0,0 @@ -addMethod(...)', - [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -use Nette\Application\Routers\Route; -use Nette\Application\Routers\RouteList; - -class RouterFactory -{ - public static function createRouter() - { - $routeList = new RouteList(); - $routeList[] = new Route('...'); - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -use Nette\Application\Routers\RouteList; - -class RouterFactory -{ - public static function createRouter() - { - $routeList = new RouteList(); - $routeList->addRoute('...'); - } -} -CODE_SAMPLE -, - [ - self::DIM_FETCH_ASSIGN_TO_METHOD_CALL => [ - new DimFetchAssignToMethodCall( - 'Nette\Application\Routers\RouteList', - 'Nette\Application\Routers\Route', - 'addRoute' - ), - ], - ] - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Assign::class]; - } - - /** - * @param Assign $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->var instanceof ArrayDimFetch) { - return null; - } - - $arrayDimFetch = $node->var; - if (! $arrayDimFetch->var instanceof Variable) { - return null; - } - - if (! $node->expr instanceof New_) { - return null; - } - - $dimFetchAssignToMethodCall = $this->findDimFetchAssignToMethodCall($node); - if (! $dimFetchAssignToMethodCall instanceof DimFetchAssignToMethodCall) { - return null; - } - - return new MethodCall($arrayDimFetch->var, $dimFetchAssignToMethodCall->getAddMethod(), $node->expr->args); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $dimFetchAssignToMethodCalls = $configuration[self::DIM_FETCH_ASSIGN_TO_METHOD_CALL] ?? []; - Assert::allIsInstanceOf($dimFetchAssignToMethodCalls, DimFetchAssignToMethodCall::class); - $this->dimFetchAssignToMethodCalls = $dimFetchAssignToMethodCalls; - } - - private function findDimFetchAssignToMethodCall(Assign $assign): ?DimFetchAssignToMethodCall - { - /** @var ArrayDimFetch $arrayDimFetch */ - $arrayDimFetch = $assign->var; - - foreach ($this->dimFetchAssignToMethodCalls as $dimFetchAssignToMethodCall) { - if (! $this->isObjectType($arrayDimFetch->var, $dimFetchAssignToMethodCall->getListObjectType())) { - continue; - } - - if (! $this->isObjectType($assign->expr, $dimFetchAssignToMethodCall->getItemObjectType())) { - continue; - } - return $dimFetchAssignToMethodCall; - } - return null; - } -} diff --git a/rules/Transform/Rector/Assign/GetAndSetToMethodCallRector.php b/rules/Transform/Rector/Assign/GetAndSetToMethodCallRector.php deleted file mode 100644 index 5bcc6181d82..00000000000 --- a/rules/Transform/Rector/Assign/GetAndSetToMethodCallRector.php +++ /dev/null @@ -1,180 +0,0 @@ -someService = $someService; -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$container = new SomeContainer; -$container->setService("someService", $someService); -CODE_SAMPLE - , - [ - self::TYPE_TO_METHOD_CALLS => [ - new GetAndSetToMethodCall('SomeContainer', 'addService', 'getService'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Assign::class, PropertyFetch::class]; - } - - /** - * @param Assign|PropertyFetch $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof Assign) { - if ($node->var instanceof PropertyFetch) { - return $this->processMagicSet($node->expr, $node->var); - } - - return null; - } - - return $this->processPropertyFetch($node); - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $getAndSetToMethodCalls = $configuration[self::TYPE_TO_METHOD_CALLS] ?? []; - Assert::allIsAOf($getAndSetToMethodCalls, GetAndSetToMethodCall::class); - - $this->getAndSetToMethodCalls = $getAndSetToMethodCalls; - } - - private function processMagicSet(Expr $expr, PropertyFetch $propertyFetch): ?Node - { - foreach ($this->getAndSetToMethodCalls as $getAndSetToMethodCall) { - $objectType = $getAndSetToMethodCall->getObjectType(); - if ($this->shouldSkipPropertyFetch($propertyFetch, $objectType)) { - continue; - } - - return $this->createMethodCallNodeFromAssignNode( - $propertyFetch, - $expr, - $getAndSetToMethodCall->getSetMethod() - ); - } - - return null; - } - - private function processPropertyFetch(PropertyFetch $propertyFetch): ?MethodCall - { - $parentNode = $propertyFetch->getAttribute(AttributeKey::PARENT_NODE); - - foreach ($this->getAndSetToMethodCalls as $getAndSetToMethodCall) { - if ($this->shouldSkipPropertyFetch($propertyFetch, $getAndSetToMethodCall->getObjectType())) { - continue; - } - - // setter, skip - if (! $parentNode instanceof Assign) { - return $this->createMethodCallNodeFromPropertyFetchNode( - $propertyFetch, - $getAndSetToMethodCall->getGetMethod() - ); - } - - if ($parentNode->var !== $propertyFetch) { - return $this->createMethodCallNodeFromPropertyFetchNode( - $propertyFetch, - $getAndSetToMethodCall->getGetMethod() - ); - } - } - - return null; - } - - private function shouldSkipPropertyFetch(PropertyFetch $propertyFetch, ObjectType $objectType): bool - { - if (! $this->isObjectType($propertyFetch->var, $objectType)) { - return true; - } - - if (! $this->magicPropertyFetchAnalyzer->isMagicOnType($propertyFetch, $objectType)) { - return true; - } - - return $this->propertyFetchAnalyzer->isPropertyToSelf($propertyFetch); - } - - private function createMethodCallNodeFromAssignNode( - PropertyFetch $propertyFetch, - Expr $expr, - string $method - ): MethodCall { - $propertyName = $this->getName($propertyFetch->name); - return $this->nodeFactory->createMethodCall($propertyFetch->var, $method, [$propertyName, $expr]); - } - - private function createMethodCallNodeFromPropertyFetchNode( - PropertyFetch $propertyFetch, - string $method - ): MethodCall { - /** @var Variable $variableNode */ - $variableNode = $propertyFetch->var; - - return $this->nodeFactory->createMethodCall($variableNode, $method, [$this->getName($propertyFetch)]); - } -} diff --git a/rules/Transform/Rector/Assign/PropertyAssignToMethodCallRector.php b/rules/Transform/Rector/Assign/PropertyAssignToMethodCallRector.php deleted file mode 100644 index 7439111367f..00000000000 --- a/rules/Transform/Rector/Assign/PropertyAssignToMethodCallRector.php +++ /dev/null @@ -1,106 +0,0 @@ -oldProperty = false; -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someObject = new SomeClass; -$someObject->newMethodCall(false); -CODE_SAMPLE - , - [ - self::PROPERTY_ASSIGNS_TO_METHODS_CALLS => [ - new PropertyAssignToMethodCall('SomeClass', 'oldProperty', 'newMethodCall'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Assign::class]; - } - - /** - * @param Assign $node - */ - public function refactor(Node $node): ?Node - { - if (! $node->var instanceof PropertyFetch) { - return null; - } - - $propertyFetchNode = $node->var; - - /** @var Variable $propertyNode */ - $propertyNode = $propertyFetchNode->var; - - foreach ($this->propertyAssignsToMethodCalls as $propertyAssignToMethodCall) { - if (! $this->isObjectType($propertyFetchNode->var, $propertyAssignToMethodCall->getObjectType())) { - continue; - } - - if (! $this->isName($propertyFetchNode, $propertyAssignToMethodCall->getOldPropertyName())) { - continue; - } - - return $this->nodeFactory->createMethodCall( - $propertyNode, - $propertyAssignToMethodCall->getNewMethodName(), - [$node->expr] - ); - } - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $propertyAssignsToMethodCalls = $configuration[self::PROPERTY_ASSIGNS_TO_METHODS_CALLS] ?? []; - Assert::allIsInstanceOf($propertyAssignsToMethodCalls, PropertyAssignToMethodCall::class); - $this->propertyAssignsToMethodCalls = $propertyAssignsToMethodCalls; - } -} diff --git a/rules/Transform/Rector/Assign/PropertyFetchToMethodCallRector.php b/rules/Transform/Rector/Assign/PropertyFetchToMethodCallRector.php deleted file mode 100644 index 983d6562457..00000000000 --- a/rules/Transform/Rector/Assign/PropertyFetchToMethodCallRector.php +++ /dev/null @@ -1,172 +0,0 @@ - [ - new PropertyFetchToMethodCall('SomeObject', 'property', 'getProperty', 'setProperty'), - ], - ]; - - $secondConfiguration = [ - self::PROPERTIES_TO_METHOD_CALLS => [ - new PropertyFetchToMethodCall('SomeObject', 'property', 'getConfig', null, ['someArg']), - ], - ]; - return new RuleDefinition('Replaces properties assign calls be defined methods.', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -$result = $object->property; -$object->property = $value; -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$result = $object->getProperty(); -$object->setProperty($value); -CODE_SAMPLE - , - $firstConfiguration - ), - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -$result = $object->property; -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$result = $object->getProperty('someArg'); -CODE_SAMPLE - , - $secondConfiguration - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Assign::class, PropertyFetch::class]; - } - - /** - * @param PropertyFetch|Assign $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof Assign && $node->var instanceof PropertyFetch) { - return $this->processSetter($node); - } - - if ($node instanceof PropertyFetch) { - return $this->processGetter($node); - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $propertiesToMethodCalls = $configuration[self::PROPERTIES_TO_METHOD_CALLS] ?? []; - Assert::allIsInstanceOf($propertiesToMethodCalls, PropertyFetchToMethodCall::class); - $this->propertiesToMethodCalls = $propertiesToMethodCalls; - } - - private function processSetter(Assign $assign): ?Node - { - /** @var PropertyFetch $propertyFetchNode */ - $propertyFetchNode = $assign->var; - - $propertyToMethodCall = $this->matchPropertyFetchCandidate($propertyFetchNode); - if (! $propertyToMethodCall instanceof PropertyFetchToMethodCall) { - return null; - } - - if ($propertyToMethodCall->getNewSetMethod() === null) { - throw new ShouldNotHappenException(); - } - - $args = $this->nodeFactory->createArgs([$assign->expr]); - - /** @var Variable $variable */ - $variable = $propertyFetchNode->var; - - return $this->nodeFactory->createMethodCall($variable, $propertyToMethodCall->getNewSetMethod(), $args); - } - - private function processGetter(PropertyFetch $propertyFetch): ?Node - { - $propertyToMethodCall = $this->matchPropertyFetchCandidate($propertyFetch); - if (! $propertyToMethodCall instanceof PropertyFetchToMethodCall) { - return null; - } - - // simple method name - if ($propertyToMethodCall->getNewGetMethod() !== '') { - $methodCall = $this->nodeFactory->createMethodCall( - $propertyFetch->var, - $propertyToMethodCall->getNewGetMethod() - ); - - if ($propertyToMethodCall->getNewGetArguments() !== []) { - $args = $this->nodeFactory->createArgs($propertyToMethodCall->getNewGetArguments()); - $methodCall->args = $args; - } - - return $methodCall; - } - - return $propertyFetch; - } - - private function matchPropertyFetchCandidate(PropertyFetch $propertyFetch): ?PropertyFetchToMethodCall - { - foreach ($this->propertiesToMethodCalls as $propertyToMethodCall) { - if (! $this->isObjectType($propertyFetch->var, $propertyToMethodCall->getOldObjectType())) { - continue; - } - - if (! $this->isName($propertyFetch, $propertyToMethodCall->getOldProperty())) { - continue; - } - - return $propertyToMethodCall; - } - - return null; - } -} diff --git a/rules/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector.php b/rules/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector.php new file mode 100644 index 00000000000..9b9ebf88396 --- /dev/null +++ b/rules/Transform/Rector/Attribute/AttributeKeyToClassConstFetchRector.php @@ -0,0 +1,185 @@ + 'STRING', + ]), + ] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ + Class_::class, + Property::class, + Param::class, + ClassMethod::class, + Function_::class, + Closure::class, + ArrowFunction::class, + Interface_::class, + ]; + } + + /** + * @param Class_|Property|Param|ClassMethod|Function_|Closure|ArrowFunction|Interface_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->attrGroups === []) { + return null; + } + + $hasChanged = false; + foreach ($this->attributeKeysToClassConstFetches as $attributeKeyToClassConstFetch) { + foreach ($node->attrGroups as $attrGroup) { + if ($this->processToClassConstFetch($attrGroup, $attributeKeyToClassConstFetch)) { + $hasChanged = true; + } + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsAOf($configuration, AttributeKeyToClassConstFetch::class); + + $this->attributeKeysToClassConstFetches = $configuration; + } + + private function processToClassConstFetch( + AttributeGroup $attributeGroup, + AttributeKeyToClassConstFetch $attributeKeyToClassConstFetch + ): bool { + $hasChanged = false; + + foreach ($attributeGroup->attrs as $attribute) { + if (! $this->isName($attribute->name, $attributeKeyToClassConstFetch->getAttributeClass())) { + continue; + } + + foreach ($attribute->args as $arg) { + $argName = $arg->name; + if (! $argName instanceof Identifier) { + continue; + } + + if (! $this->isName($argName, $attributeKeyToClassConstFetch->getAttributeKey())) { + continue; + } + + if ($this->processArg($arg, $attributeKeyToClassConstFetch)) { + $hasChanged = true; + } + } + } + + return $hasChanged; + } + + private function processArg(Arg $arg, AttributeKeyToClassConstFetch $attributeKeyToClassConstFetch): bool + { + $value = $this->valueResolver->getValue($arg->value); + + $constName = $attributeKeyToClassConstFetch->getValuesToConstantsMap()[$value] ?? null; + if ($constName === null) { + return false; + } + + $classConstFetch = $this->nodeFactory->createClassConstFetch( + $attributeKeyToClassConstFetch->getConstantClass(), + $constName + ); + + if ( + $arg->value instanceof ClassConstFetch + && $this->getName($arg->value) === $this->getName($classConstFetch) + ) { + return false; + } + + $arg->value = $classConstFetch; + + return true; + } +} diff --git a/rules/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector.php b/rules/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector.php deleted file mode 100644 index 55d385b4806..00000000000 --- a/rules/Transform/Rector/ClassConstFetch/ClassConstFetchToValueRector.php +++ /dev/null @@ -1,88 +0,0 @@ - [ - new ClassConstFetchToValue('Nette\Configurator', 'DEVELOPMENT', 'development'), - new ClassConstFetchToValue('Nette\Configurator', 'PRODUCTION', 'production'), - ], - ]; - - return new RuleDefinition('Replaces constant by value', [ - new ConfiguredCodeSample( - '$value === Nette\Configurator::DEVELOPMENT', - '$value === "development"', - $configuration - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassConstFetch::class]; - } - - /** - * @param ClassConstFetch $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->classConstFetchesToValues as $classConstFetchToValue) { - if (! $this->isObjectType($node->class, $classConstFetchToValue->getObjectType())) { - continue; - } - - if (! $this->isName($node->name, $classConstFetchToValue->getConstant())) { - continue; - } - - return BuilderHelpers::normalizeValue($classConstFetchToValue->getValue()); - } - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $classConstFetchesToValues = $configuration[self::CLASS_CONST_FETCHES_TO_VALUES] ?? []; - Assert::allIsInstanceOf($classConstFetchesToValues, ClassConstFetchToValue::class); - - $this->classConstFetchesToValues = $classConstFetchesToValues; - } -} diff --git a/rules/Transform/Rector/ClassMethod/ReturnTypeWillChangeRector.php b/rules/Transform/Rector/ClassMethod/ReturnTypeWillChangeRector.php new file mode 100644 index 00000000000..a1b3619ab23 --- /dev/null +++ b/rules/Transform/Rector/ClassMethod/ReturnTypeWillChangeRector.php @@ -0,0 +1,87 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class, Interface_::class]; + } + + /** + * @param Class_|Interface_ $node + */ + public function refactor(Node $node): ?Node + { + throw new ShouldNotHappenException(sprintf( + 'Rule "%s" is deprecated as not used in any set, and discourages from type coverage and leads to worse code. Use type declaration set instead, to actually increase type coverage.', + self::class + )); + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::RETURN_TYPE_WILL_CHANGE_ATTRIBUTE; + } +} diff --git a/rules/Transform/Rector/ClassMethod/SingleToManyMethodRector.php b/rules/Transform/Rector/ClassMethod/SingleToManyMethodRector.php deleted file mode 100644 index 095697c8dc0..00000000000 --- a/rules/Transform/Rector/ClassMethod/SingleToManyMethodRector.php +++ /dev/null @@ -1,154 +0,0 @@ - [new SingleToManyMethod('SomeClass', 'getNode', 'getNodes')], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof ClassLike) { - return null; - } - - foreach ($this->singleToManyMethods as $singleToManyMethod) { - if (! $this->isObjectType($classLike, $singleToManyMethod->getObjectType())) { - continue; - } - - if (! $this->isName($node, $singleToManyMethod->getSingleMethodName())) { - continue; - } - - $node->name = new Identifier($singleToManyMethod->getManyMethodName()); - $this->keepOldReturnTypeInDocBlock($node); - - $node->returnType = new Identifier('array'); - $this->wrapReturnValueToArray($node); - - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $singleToManyMethods = $configuration[self::SINGLES_TO_MANY_METHODS] ?? []; - Assert::allIsInstanceOf($singleToManyMethods, SingleToManyMethod::class); - - $this->singleToManyMethods = $singleToManyMethods; - } - - private function keepOldReturnTypeInDocBlock(ClassMethod $classMethod): void - { - // keep old return type in the docblock - $oldReturnType = $classMethod->returnType; - if ($oldReturnType === null) { - return; - } - - $staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($oldReturnType); - $arrayType = new ArrayType(new MixedType(), $staticType); - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - $this->phpDocTypeChanger->changeReturnType($phpDocInfo, $arrayType); - } - - private function wrapReturnValueToArray(ClassMethod $classMethod): void - { - $this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) { - if (! $node instanceof Return_) { - return null; - } - - $node->expr = $this->nodeFactory->createArray([$node->expr]); - return null; - }); - } -} diff --git a/rules/Transform/Rector/ClassMethod/WrapReturnRector.php b/rules/Transform/Rector/ClassMethod/WrapReturnRector.php index 6282cb81fd0..26e5ba610ac 100644 --- a/rules/Transform/Rector/ClassMethod/WrapReturnRector.php +++ b/rules/Transform/Rector/ClassMethod/WrapReturnRector.php @@ -5,12 +5,14 @@ namespace Rector\Transform\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\ArrayItem; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Return_; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Transform\ValueObject\WrapReturn; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,11 +23,6 @@ */ final class WrapReturnRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const TYPE_METHOD_WRAPS = 'type_method_wraps'; - /** * @var WrapReturn[] */ @@ -55,9 +52,7 @@ public function getItem() } CODE_SAMPLE , - [ - self::TYPE_METHOD_WRAPS => [new WrapReturn('SomeClass', 'getItem', true)], - ] + [new WrapReturn('SomeClass', 'getItem', true)] ), ]); } @@ -67,59 +62,67 @@ public function getItem() */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { + $hasChanged = false; + foreach ($this->typeMethodWraps as $typeMethodWrap) { if (! $this->isObjectType($node, $typeMethodWrap->getObjectType())) { continue; } - if (! $this->isName($node, $typeMethodWrap->getMethod())) { - continue; - } + foreach ($node->getMethods() as $classMethod) { + if (! $this->isName($classMethod, $typeMethodWrap->getMethod())) { + continue; + } - if (! $node->stmts) { - continue; + if ($node->stmts === null) { + continue; + } + + if ($typeMethodWrap->isArrayWrap() && $this->wrap($classMethod)) { + $hasChanged = true; + } } + } - return $this->wrap($node, $typeMethodWrap->isArrayWrap()); + if ($hasChanged) { + return $node; } - return $node; + return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $typeMethodWraps = $configuration[self::TYPE_METHOD_WRAPS] ?? []; - Assert::allIsInstanceOf($typeMethodWraps, WrapReturn::class); - $this->typeMethodWraps = $typeMethodWraps; + Assert::allIsAOf($configuration, WrapReturn::class); + $this->typeMethodWraps = $configuration; } - private function wrap(ClassMethod $classMethod, bool $isArrayWrap): ?ClassMethod + private function wrap(ClassMethod $classMethod): bool { if (! is_iterable($classMethod->stmts)) { - return null; + return false; } - foreach ($classMethod->stmts as $key => $stmt) { - if ($stmt instanceof Return_ && $stmt->expr !== null) { - if ($isArrayWrap && ! $stmt->expr instanceof Array_) { - $stmt->expr = new Array_([new ArrayItem($stmt->expr)]); - } - - $classMethod->stmts[$key] = $stmt; + $hasChanged = false; + foreach ($classMethod->stmts as $stmt) { + if ($stmt instanceof Return_ && $stmt->expr instanceof Expr + && ! $stmt->expr instanceof Array_) { + $stmt->expr = new Array_([new ArrayItem($stmt->expr)]); + $hasChanged = true; } } - return $classMethod; + return $hasChanged; } } diff --git a/rules/Transform/Rector/Class_/AddInterfaceByParentRector.php b/rules/Transform/Rector/Class_/AddInterfaceByParentRector.php deleted file mode 100644 index e1283023ed0..00000000000 --- a/rules/Transform/Rector/Class_/AddInterfaceByParentRector.php +++ /dev/null @@ -1,110 +0,0 @@ - - */ - private array $interfaceByParent = []; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Add interface by parent', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -class SomeClass extends SomeParent -{ - -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -class SomeClass extends SomeParent implements SomeInterface -{ - -} -CODE_SAMPLE - , - [ - self::INTERFACE_BY_PARENT => [ - 'SomeParent' => 'SomeInterface', - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - /** @var Scope $scope */ - $scope = $node->getAttribute(AttributeKey::SCOPE); - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $parentClassReflection = $classReflection->getParentClass(); - if (! $parentClassReflection) { - return null; - } - - foreach ($this->interfaceByParent as $parentName => $interfaceName) { - if ($parentName !== $parentClassReflection->getName()) { - continue; - } - - foreach ($node->implements as $implement) { - if ($this->isName($implement, $interfaceName)) { - continue 2; - } - } - - $node->implements[] = new FullyQualified($interfaceName); - } - - return $node; - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->interfaceByParent = $configuration[self::INTERFACE_BY_PARENT] ?? []; - } -} diff --git a/rules/Transform/Rector/Class_/AddInterfaceByTraitRector.php b/rules/Transform/Rector/Class_/AddInterfaceByTraitRector.php index 4ebdb076478..e44a0c60a85 100644 --- a/rules/Transform/Rector/Class_/AddInterfaceByTraitRector.php +++ b/rules/Transform/Rector/Class_/AddInterfaceByTraitRector.php @@ -7,24 +7,20 @@ use PhpParser\Node; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** + * @api used in rector-doctrine * @see \Rector\Tests\Transform\Rector\Class_\AddInterfaceByTraitRector\AddInterfaceByTraitRectorTest */ final class AddInterfaceByTraitRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const INTERFACE_BY_TRAIT = 'interface_by_trait'; - /** * @var array */ @@ -40,18 +36,16 @@ class SomeClass use SomeTrait; } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass implements SomeInterface { use SomeTrait; } CODE_SAMPLE - , + , [ - self::INTERFACE_BY_TRAIT => [ - 'SomeTrait' => 'SomeInterface', - ], + 'SomeTrait' => 'SomeInterface', ] ), ]); @@ -70,35 +64,41 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - /** @var Scope $scope */ - $scope = $node->getAttribute(AttributeKey::SCOPE); + $scope = ScopeFetcher::fetch($node); $classReflection = $scope->getClassReflection(); if (! $classReflection instanceof ClassReflection) { return null; } + $hasChanged = false; foreach ($this->interfaceByTrait as $traitName => $interfaceName) { if (! $classReflection->hasTraitUse($traitName)) { continue; } - foreach ($node->implements as $implement) { - if ($this->isName($implement, $interfaceName)) { - continue 2; - } + if ($classReflection->implementsInterface($interfaceName)) { + continue; } $node->implements[] = new FullyQualified($interfaceName); + $hasChanged = true; + } + + if (! $hasChanged) { + return null; } return $node; } /** - * @param array> $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $this->interfaceByTrait = $configuration[self::INTERFACE_BY_TRAIT] ?? []; + Assert::allString(array_keys($configuration)); + Assert::allString($configuration); + + $this->interfaceByTrait = $configuration; } } diff --git a/rules/Transform/Rector/Class_/ChangeSingletonToServiceRector.php b/rules/Transform/Rector/Class_/ChangeSingletonToServiceRector.php deleted file mode 100644 index eecfab3beed..00000000000 --- a/rules/Transform/Rector/Class_/ChangeSingletonToServiceRector.php +++ /dev/null @@ -1,161 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->classAnalyzer->isAnonymousClass($node)) { - return null; - } - - $propertyAndClassMethodName = $this->matchStaticPropertyFetchAndGetSingletonMethodName($node); - if (! $propertyAndClassMethodName instanceof PropertyAndClassMethodName) { - return null; - } - - return $this->refactorClassStmts($node, $propertyAndClassMethodName); - } - - private function matchStaticPropertyFetchAndGetSingletonMethodName(Class_ $class): ?PropertyAndClassMethodName - { - foreach ($class->getMethods() as $classMethod) { - if (! $classMethod->isStatic()) { - continue; - } - - $staticPropertyFetch = $this->singletonClassMethodAnalyzer->matchStaticPropertyFetch($classMethod); - if (! $staticPropertyFetch instanceof StaticPropertyFetch) { - return null; - } - - /** @var string $propertyName */ - $propertyName = $this->getName($staticPropertyFetch); - - /** @var string $classMethodName */ - $classMethodName = $this->getName($classMethod); - - return new PropertyAndClassMethodName($propertyName, $classMethodName); - } - - return null; - } - - private function refactorClassStmts( - Class_ $class, - PropertyAndClassMethodName $propertyAndClassMethodName - ): Class_ { - foreach ($class->getMethods() as $classMethod) { - if ($this->isName($classMethod, $propertyAndClassMethodName->getClassMethodName())) { - $this->removeNodeFromStatements($class, $classMethod); - continue; - } - - if (! $this->isNames($classMethod, [MethodName::CONSTRUCT, MethodName::CLONE, '__wakeup'])) { - continue; - } - - if ($classMethod->isPublic()) { - continue; - } - - // remove non-public empty - if ($classMethod->stmts === []) { - $this->removeNodeFromStatements($class, $classMethod); - } else { - $this->visibilityManipulator->makePublic($classMethod); - } - } - - $this->removePropertyByName($class, $propertyAndClassMethodName->getPropertyName()); - - return $class; - } - - private function removePropertyByName(Class_ $class, string $propertyName): void - { - foreach ($class->getProperties() as $property) { - if (! $this->isName($property, $propertyName)) { - continue; - } - - $this->removeNodeFromStatements($class, $property); - } - } -} diff --git a/rules/Transform/Rector/Class_/MergeInterfacesRector.php b/rules/Transform/Rector/Class_/MergeInterfacesRector.php index 9582eeaafef..cd90f40b4dc 100644 --- a/rules/Transform/Rector/Class_/MergeInterfacesRector.php +++ b/rules/Transform/Rector/Class_/MergeInterfacesRector.php @@ -5,12 +5,14 @@ namespace Rector\Transform\Rector\Class_; use PhpParser\Node; -use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use PhpParser\Node\Stmt\Interface_; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * Covers cases like @@ -21,12 +23,6 @@ */ final class MergeInterfacesRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @api - * @var string - */ - public const OLD_TO_NEW_INTERFACES = 'old_to_new_interfaces'; - /** * @var array */ @@ -34,7 +30,7 @@ final class MergeInterfacesRector extends AbstractRector implements Configurable public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Merges old interface to a new one, that already has its methods', [ + return new RuleDefinition('Merge old interface to a new one, that already has its methods', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' class SomeClass implements SomeInterface, SomeOldInterface @@ -49,9 +45,7 @@ class SomeClass implements SomeInterface CODE_SAMPLE , [ - self::OLD_TO_NEW_INTERFACES => [ - 'SomeOldInterface' => 'SomeInterface', - ], + 'SomeOldInterface' => 'SomeInterface', ] ), ]); @@ -74,6 +68,7 @@ public function refactor(Node $node): ?Node return null; } + $hasChanged = false; foreach ($node->implements as $key => $implement) { $oldInterfaces = array_keys($this->oldToNewInterfaces); if (! $this->isNames($implement, $oldInterfaces)) { @@ -81,7 +76,12 @@ public function refactor(Node $node): ?Node } $interface = $this->getName($implement); - $node->implements[$key] = new Name($this->oldToNewInterfaces[$interface]); + $node->implements[$key] = new FullyQualified($this->oldToNewInterfaces[$interface]); + $hasChanged = true; + } + + if (! $hasChanged) { + return null; } $this->makeImplementsUnique($node); @@ -90,20 +90,26 @@ public function refactor(Node $node): ?Node } /** - * @param array> $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $this->oldToNewInterfaces = $configuration[self::OLD_TO_NEW_INTERFACES] ?? []; + Assert::allString(array_keys($configuration)); + Assert::allString($configuration); + + $this->oldToNewInterfaces = $configuration; } private function makeImplementsUnique(Class_ $class): void { $alreadyAddedNames = []; - foreach ($class->implements as $key => $name) { + /** @var array $implements */ + $implements = $class->implements; + + foreach ($implements as $key => $name) { $fqnName = $this->getName($name); if (in_array($fqnName, $alreadyAddedNames, true)) { - $this->nodeRemover->removeImplements($class, $key); + unset($class->implements[$key]); continue; } diff --git a/rules/Transform/Rector/Class_/ParentClassToTraitsRector.php b/rules/Transform/Rector/Class_/ParentClassToTraitsRector.php deleted file mode 100644 index ab858f9f7a7..00000000000 --- a/rules/Transform/Rector/Class_/ParentClassToTraitsRector.php +++ /dev/null @@ -1,121 +0,0 @@ - [ - new ParentClassToTraits('Nette\Object', ['Nette\SmartObject']), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node->extends === null) { - return null; - } - - if ($this->classAnalyzer->isAnonymousClass($node)) { - return null; - } - - foreach ($this->parentClassToTraits as $parentClassToTrait) { - if (! $this->isObjectType($node, $parentClassToTrait->getParentObjectType())) { - continue; - } - - foreach ($parentClassToTrait->getTraitNames() as $traitName) { - $this->classInsertManipulator->addAsFirstTrait($node, $traitName); - } - - $this->removeParentClass($node); - - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $parentClassToTraits = $configuration[self::PARENT_CLASS_TO_TRAITS] ?? []; - Assert::allIsInstanceOf($parentClassToTraits, ParentClassToTraits::class); - $this->parentClassToTraits = $parentClassToTraits; - } - - private function removeParentClass(Class_ $class): void - { - $class->extends = null; - } -} diff --git a/rules/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector.php b/rules/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector.php new file mode 100644 index 00000000000..7a6f3ab1e2e --- /dev/null +++ b/rules/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector.php @@ -0,0 +1,62 @@ +constFetchToClassConsts as $constFetchToClassConst) { + if (! $this->isName($node, $constFetchToClassConst->getOldConstName())) { + continue; + } + + return $this->nodeFactory->createClassConstFetch( + $constFetchToClassConst->getNewClassName(), + $constFetchToClassConst->getNewConstName() + ); + } + + return null; + } + + public function configure(array $configuration): void + { + Assert::allIsAOf($configuration, ConstFetchToClassConstFetch::class); + $this->constFetchToClassConsts = $configuration; + } +} diff --git a/rules/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector.php b/rules/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector.php deleted file mode 100644 index 7faf552955a..00000000000 --- a/rules/Transform/Rector/FuncCall/ArgumentFuncCallToMethodCallRector.php +++ /dev/null @@ -1,287 +0,0 @@ -viewFactory = $viewFactory; - } - - public function action() - { - $template = $this->viewFactory->make('template.blade'); - $viewFactory = $this->viewFactory; - } -} -CODE_SAMPLE - , - [ - self::FUNCTIONS_TO_METHOD_CALLS => [ - new ArgumentFuncCallToMethodCall('view', 'Illuminate\Contracts\View\Factory', 'make'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [FuncCall::class]; - } - - /** - * @param FuncCall $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkipFuncCall($node)) { - return null; - } - - /** @var Class_ $classLike */ - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - - foreach ($this->argumentFuncCallToMethodCalls as $argumentFuncCallToMethodCall) { - if (! $this->isName($node, $argumentFuncCallToMethodCall->getFunction())) { - continue; - } - - return $this->refactorFuncCallToMethodCall($argumentFuncCallToMethodCall, $classLike, $node); - } - - foreach ($this->arrayFunctionsToMethodCalls as $arrayFunctionToMethodCall) { - if (! $this->isName($node, $arrayFunctionToMethodCall->getFunction())) { - continue; - } - - return $this->refactorArrayFunctionToMethodCall($arrayFunctionToMethodCall, $node, $classLike); - } - - return null; - } - - /** - * @param mixed[] $configuration - */ - public function configure(array $configuration): void - { - $functionToMethodCalls = $configuration[self::FUNCTIONS_TO_METHOD_CALLS] ?? []; - Assert::allIsInstanceOf($functionToMethodCalls, ArgumentFuncCallToMethodCall::class); - $this->argumentFuncCallToMethodCalls = $functionToMethodCalls; - - $arrayFunctionsToMethodCalls = $configuration[self::ARRAY_FUNCTIONS_TO_METHOD_CALLS] ?? []; - Assert::allIsInstanceOf($arrayFunctionsToMethodCalls, ArrayFuncCallToMethodCall::class); - $this->arrayFunctionsToMethodCalls = $arrayFunctionsToMethodCalls; - } - - private function shouldSkipFuncCall(FuncCall $funcCall): bool - { - // we can inject only in injectable class method context - // we can inject only in injectable class method context - /** @var ClassMethod|null $classMethod */ - $classMethod = $funcCall->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return true; - } - - return $classMethod->isStatic(); - } - - /** - * @return PropertyFetch|MethodCall - */ - private function refactorFuncCallToMethodCall( - ArgumentFuncCallToMethodCall $argumentFuncCallToMethodCall, - Class_ $class, - FuncCall $funcCall - ): ?Node { - $fullyQualifiedObjectType = new FullyQualifiedObjectType($argumentFuncCallToMethodCall->getClass()); - $expectedName = $this->propertyNaming->getExpectedNameFromType($fullyQualifiedObjectType); - - if (! $expectedName instanceof ExpectedName) { - throw new ShouldNotHappenException(); - } - - $propertyMetadata = new PropertyMetadata( - $expectedName->getName(), - $fullyQualifiedObjectType, - Class_::MODIFIER_PRIVATE - ); - $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); - - $propertyFetchNode = $this->nodeFactory->createPropertyFetch('this', $expectedName->getName()); - - if ($funcCall->args === []) { - return $this->refactorEmptyFuncCallArgs($argumentFuncCallToMethodCall, $propertyFetchNode); - } - - if ($this->isFunctionToMethodCallWithArgs($funcCall, $argumentFuncCallToMethodCall)) { - $methodName = $argumentFuncCallToMethodCall->getMethodIfArgs(); - if (! is_string($methodName)) { - throw new ShouldNotHappenException(); - } - - return new MethodCall($propertyFetchNode, $methodName, $funcCall->args); - } - - return null; - } - - /** - * @return PropertyFetch|MethodCall|null - */ - private function refactorArrayFunctionToMethodCall( - ArrayFuncCallToMethodCall $arrayFuncCallToMethodCall, - FuncCall $funcCall, - Class_ $class - ): ?Node { - $propertyName = $this->propertyNaming->fqnToVariableName($arrayFuncCallToMethodCall->getClass()); - $propertyFetch = $this->nodeFactory->createPropertyFetch('this', $propertyName); - - $fullyQualifiedObjectType = new FullyQualifiedObjectType($arrayFuncCallToMethodCall->getClass()); - - $propertyMetadata = new PropertyMetadata($propertyName, $fullyQualifiedObjectType, Class_::MODIFIER_PRIVATE); - $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); - - return $this->createMethodCallArrayFunctionToMethodCall( - $funcCall, - $arrayFuncCallToMethodCall, - $propertyFetch - ); - } - - private function refactorEmptyFuncCallArgs( - ArgumentFuncCallToMethodCall $argumentFuncCallToMethodCall, - PropertyFetch $propertyFetch - ): MethodCall | PropertyFetch { - if ($argumentFuncCallToMethodCall->getMethodIfNoArgs()) { - $methodName = $argumentFuncCallToMethodCall->getMethodIfNoArgs(); - if (! is_string($methodName)) { - throw new ShouldNotHappenException(); - } - - return new MethodCall($propertyFetch, $methodName); - } - - return $propertyFetch; - } - - private function isFunctionToMethodCallWithArgs( - FuncCall $funcCall, - ArgumentFuncCallToMethodCall $argumentFuncCallToMethodCall - ): bool { - if ($argumentFuncCallToMethodCall->getMethodIfArgs() === null) { - return false; - } - - return count($funcCall->args) >= 1; - } - - /** - * @return PropertyFetch|MethodCall|null - */ - private function createMethodCallArrayFunctionToMethodCall( - FuncCall $funcCall, - ArrayFuncCallToMethodCall $arrayFuncCallToMethodCall, - PropertyFetch $propertyFetch - ): ?Node { - if ($funcCall->args === []) { - return $propertyFetch; - } - - if ($arrayFuncCallToMethodCall->getArrayMethod() && $this->arrayTypeAnalyzer->isArrayType( - $funcCall->args[0]->value - )) { - return new MethodCall($propertyFetch, $arrayFuncCallToMethodCall->getArrayMethod(), $funcCall->args); - } - if ($arrayFuncCallToMethodCall->getNonArrayMethod() === '') { - return null; - } - if ($this->arrayTypeAnalyzer->isArrayType($funcCall->args[0]->value)) { - return null; - } - return new MethodCall($propertyFetch, $arrayFuncCallToMethodCall->getNonArrayMethod(), $funcCall->args); - } -} diff --git a/rules/Transform/Rector/FuncCall/FuncCallToConstFetchRector.php b/rules/Transform/Rector/FuncCall/FuncCallToConstFetchRector.php index 5d0a0bbabd2..c6f3dae7575 100644 --- a/rules/Transform/Rector/FuncCall/FuncCallToConstFetchRector.php +++ b/rules/Transform/Rector/FuncCall/FuncCallToConstFetchRector.php @@ -8,21 +8,17 @@ use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\Transform\Rector\FuncCall\FuncCallToConstFetchRector\FunctionCallToConstantRectorTest */ final class FuncCallToConstFetchRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const FUNCTIONS_TO_CONSTANTS = 'functions_to_constants'; - /** * @var string[] */ @@ -53,9 +49,7 @@ public function run() CODE_SAMPLE , [ - self::FUNCTIONS_TO_CONSTANTS => [ - 'php_sapi_name' => 'PHP_SAPI', - ], + 'php_sapi_name' => 'PHP_SAPI', ] ), @@ -76,9 +70,10 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { $functionName = $this->getName($node); - if (! $functionName) { + if (! is_string($functionName)) { return null; } + if (! array_key_exists($functionName, $this->functionsToConstants)) { return null; } @@ -87,10 +82,14 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $this->functionsToConstants = $configuration[self::FUNCTIONS_TO_CONSTANTS] ?? []; + Assert::allString($configuration); + Assert::allString(array_keys($configuration)); + + /** @var array $configuration */ + $this->functionsToConstants = $configuration; } } diff --git a/rules/Transform/Rector/FuncCall/FuncCallToMethodCallRector.php b/rules/Transform/Rector/FuncCall/FuncCallToMethodCallRector.php index 480db1c5563..08fadf2e746 100644 --- a/rules/Transform/Rector/FuncCall/FuncCallToMethodCallRector.php +++ b/rules/Transform/Rector/FuncCall/FuncCallToMethodCallRector.php @@ -7,10 +7,8 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Transform\NodeAnalyzer\FuncCallStaticCallToMethodCallAnalyzer; use Rector\Transform\ValueObject\FuncCallToMethodCall; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; @@ -22,24 +20,19 @@ */ final class FuncCallToMethodCallRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const FUNC_CALL_TO_CLASS_METHOD_CALL = 'function_to_class_to_method_call'; - /** * @var FuncCallToMethodCall[] */ private array $funcNameToMethodCallNames = []; public function __construct( - private FuncCallStaticCallToMethodCallAnalyzer $funcCallStaticCallToMethodCallAnalyzer + private readonly FuncCallStaticCallToMethodCallAnalyzer $funcCallStaticCallToMethodCallAnalyzer ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Turns defined function calls to local method calls.', [ + return new RuleDefinition('Turn defined function calls to local method calls', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -71,11 +64,7 @@ public function run() } CODE_SAMPLE , - [ - self::FUNC_CALL_TO_CLASS_METHOD_CALL => [ - new FuncCallToMethodCall('view', 'Namespaced\SomeRenderer', 'render'), - ], - ] + [new FuncCallToMethodCall('view', 'Namespaced\SomeRenderer', 'render')] ), ]); } @@ -85,57 +74,73 @@ public function run() */ public function getNodeTypes(): array { - return [FuncCall::class]; + return [Class_::class]; } /** - * @param FuncCall $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } - - $classMethod = $node->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return null; - } + $hasChanged = false; + $class = $node; - if ($classMethod->isStatic()) { - return null; - } + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->isStatic()) { + continue; + } - foreach ($this->funcNameToMethodCallNames as $funcNameToMethodCallName) { - if (! $this->isName($node->name, $funcNameToMethodCallName->getOldFuncName())) { + if ($classMethod->isAbstract()) { continue; } - $expr = $this->funcCallStaticCallToMethodCallAnalyzer->matchTypeProvidingExpr( - $classLike, + $this->traverseNodesWithCallable($classMethod, function (Node $node) use ( + $class, $classMethod, - $funcNameToMethodCallName->getNewObjectType() - ); - - return $this->nodeFactory->createMethodCall( - $expr, - $funcNameToMethodCallName->getNewMethodName(), - $node->args - ); + &$hasChanged + ): ?Node { + if (! $node instanceof FuncCall) { + return null; + } + + foreach ($this->funcNameToMethodCallNames as $funcNameToMethodCallName) { + if (! $this->isName($node->name, $funcNameToMethodCallName->getOldFuncName())) { + continue; + } + + $expr = $this->funcCallStaticCallToMethodCallAnalyzer->matchTypeProvidingExpr( + $class, + $classMethod, + $funcNameToMethodCallName->getNewObjectType(), + ); + + $hasChanged = true; + + return $this->nodeFactory->createMethodCall( + $expr, + $funcNameToMethodCallName->getNewMethodName(), + $node->args + ); + } + + return null; + }); + } + + if ($hasChanged) { + return $node; } return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $funcCallsToClassMethodCalls = $configuration[self::FUNC_CALL_TO_CLASS_METHOD_CALL] ?? []; - Assert::allIsInstanceOf($funcCallsToClassMethodCalls, FuncCallToMethodCall::class); + Assert::allIsAOf($configuration, FuncCallToMethodCall::class); - $this->funcNameToMethodCallNames = $funcCallsToClassMethodCalls; + $this->funcNameToMethodCallNames = $configuration; } } diff --git a/rules/Transform/Rector/FuncCall/FuncCallToNewRector.php b/rules/Transform/Rector/FuncCall/FuncCallToNewRector.php index fbaaa582699..f3da0832956 100644 --- a/rules/Transform/Rector/FuncCall/FuncCallToNewRector.php +++ b/rules/Transform/Rector/FuncCall/FuncCallToNewRector.php @@ -8,21 +8,17 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\New_; use PhpParser\Node\Name\FullyQualified; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\Transform\Rector\FuncCall\FuncCallToNewRector\FuncCallToNewRectorTest */ final class FuncCallToNewRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const FUNCTIONS_TO_NEWS = 'functions_to_news'; - /** * @var string[] */ @@ -51,11 +47,9 @@ public function run() } } CODE_SAMPLE -, + , [ - self::FUNCTIONS_TO_NEWS => [ - 'collection' => ['Collection'], - ], + 'collection' => 'Collection', ] ), ]); @@ -86,10 +80,12 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $this->functionToNew = $configuration[self::FUNCTIONS_TO_NEWS] ?? []; + Assert::allString($configuration); + + $this->functionToNew = $configuration; } } diff --git a/rules/Transform/Rector/FuncCall/FuncCallToStaticCallRector.php b/rules/Transform/Rector/FuncCall/FuncCallToStaticCallRector.php index 11c84876573..7bb95ad0929 100644 --- a/rules/Transform/Rector/FuncCall/FuncCallToStaticCallRector.php +++ b/rules/Transform/Rector/FuncCall/FuncCallToStaticCallRector.php @@ -6,8 +6,8 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Transform\ValueObject\FuncCallToStaticCall; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,11 +18,6 @@ */ final class FuncCallToStaticCallRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const FUNC_CALLS_TO_STATIC_CALLS = 'func_calls_to_static_calls'; - /** * @var FuncCallToStaticCall[] */ @@ -30,15 +25,11 @@ final class FuncCallToStaticCallRector extends AbstractRector implements Configu public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Turns defined function call to static method call.', [ + return new RuleDefinition('Turn defined function call to static method call', [ new ConfiguredCodeSample( 'view("...", []);', 'SomeClass::render("...", []);', - [ - self::FUNC_CALLS_TO_STATIC_CALLS => [ - new FuncCallToStaticCall('view', 'SomeStaticClass', 'render'), - ], - ] + [new FuncCallToStaticCall('view', 'SomeStaticClass', 'render')] ), ]); } @@ -72,12 +63,12 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $funcCallsToStaticCalls = $configuration[self::FUNC_CALLS_TO_STATIC_CALLS] ?? []; - Assert::allIsInstanceOf($funcCallsToStaticCalls, FuncCallToStaticCall::class); - $this->funcCallsToStaticCalls = $funcCallsToStaticCalls; + Assert::allIsAOf($configuration, FuncCallToStaticCall::class); + + $this->funcCallsToStaticCalls = $configuration; } } diff --git a/rules/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector.php b/rules/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector.php deleted file mode 100644 index 3babb31e001..00000000000 --- a/rules/Transform/Rector/Isset_/UnsetAndIssetToMethodCallRector.php +++ /dev/null @@ -1,133 +0,0 @@ -hasService("someKey"); -$container->removeService("someKey"); -CODE_SAMPLE - , - [ - self::ISSET_UNSET_TO_METHOD_CALL => [$unsetAndIssetToMethodCall], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Isset_::class, Unset_::class]; - } - - /** - * @param Isset_|Unset_ $node - */ - public function refactor(Node $node): ?Node - { - foreach ($node->vars as $arrayDimFetch) { - if (! $arrayDimFetch instanceof ArrayDimFetch) { - continue; - } - - foreach ($this->issetUnsetToMethodCalls as $issetUnsetToMethodCall) { - if (! $this->isObjectType($arrayDimFetch->var, $issetUnsetToMethodCall->getObjectType())) { - continue; - } - - $newNode = $this->processArrayDimFetchNode($node, $arrayDimFetch, $issetUnsetToMethodCall); - if ($newNode !== null) { - return $newNode; - } - } - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $issetUnsetToMethodCalls = $configuration[self::ISSET_UNSET_TO_METHOD_CALL] ?? []; - Assert::allIsInstanceOf($issetUnsetToMethodCalls, UnsetAndIssetToMethodCall::class); - - $this->issetUnsetToMethodCalls = $issetUnsetToMethodCalls; - } - - private function processArrayDimFetchNode( - Node $node, - ArrayDimFetch $arrayDimFetch, - UnsetAndIssetToMethodCall $unsetAndIssetToMethodCall - ): ?Node { - if ($node instanceof Isset_) { - if ($unsetAndIssetToMethodCall->getIssetMethodCall() === '') { - return null; - } - - return $this->nodeFactory->createMethodCall( - $arrayDimFetch->var, - $unsetAndIssetToMethodCall->getIssetMethodCall(), - [$arrayDimFetch->dim] - ); - } - - if ($node instanceof Unset_) { - if ($unsetAndIssetToMethodCall->getUnsedMethodCall() === '') { - return null; - } - - return $this->nodeFactory->createMethodCall( - $arrayDimFetch->var, - $unsetAndIssetToMethodCall->getUnsedMethodCall(), - [$arrayDimFetch->dim] - ); - } - - return null; - } -} diff --git a/rules/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector.php b/rules/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector.php deleted file mode 100644 index 0b5b7b26949..00000000000 --- a/rules/Transform/Rector/MethodCall/CallableInMethodCallToVariableRector.php +++ /dev/null @@ -1,130 +0,0 @@ -save($key, function () use ($container) { - return 100; - }); - } -} -CODE_SAMPLE -, - <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run() - { - /** @var \Nette\Caching\Cache $cache */ - $result = 100; - $cache->save($key, $result); - } -} -CODE_SAMPLE -, - [ - self::CALLABLE_IN_METHOD_CALL_TO_VARIABLE => [ - new CallableInMethodCallToVariable('Nette\Caching\Cache', 'save', 1), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?MethodCall - { - foreach ($this->callableInMethodCallToVariable as $singleCallableInMethodCallToVariable) { - if (! $this->isObjectType($node->var, $singleCallableInMethodCallToVariable->getObjectType())) { - continue; - } - - if (! isset($node->args[$singleCallableInMethodCallToVariable->getArgumentPosition()])) { - continue; - } - - $arg = $node->args[$singleCallableInMethodCallToVariable->getArgumentPosition()]; - $argValueType = $this->getStaticType($arg->value); - if (! $argValueType instanceof ClosureType) { - continue; - } - - $resultVariable = new Variable('result'); - - $unwrappedNodes = $this->unwrapClosureFactory->createAssign($resultVariable, $arg); - - $arg->value = $resultVariable; - $this->addNodesBeforeNode($unwrappedNodes, $node); - - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $callableInMethodCallToVariable = $configuration[self::CALLABLE_IN_METHOD_CALL_TO_VARIABLE] ?? []; - Assert::allIsInstanceOf($callableInMethodCallToVariable, CallableInMethodCallToVariable::class); - - $this->callableInMethodCallToVariable = $callableInMethodCallToVariable; - } -} diff --git a/rules/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector.php b/rules/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector.php deleted file mode 100644 index 8f1c4eb4a6d..00000000000 --- a/rules/Transform/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector.php +++ /dev/null @@ -1,103 +0,0 @@ -setInject(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$serviceDefinition = new Nette\DI\ServiceDefinition; -$serviceDefinition->addTag('inject'); -CODE_SAMPLE - , - [ - self::METHOD_CALL_RENAMES_WITH_ADDED_ARGUMENTS => [ - new MethodCallToAnotherMethodCallWithArguments( - 'Nette\DI\ServiceDefinition', - 'setInject', - 'addTag', - ['inject'] - ), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->methodCallRenamesWithAddedArguments as $methodCallRenameWithAddedArgument) { - if (! $this->isObjectType($node->var, $methodCallRenameWithAddedArgument->getObjectType())) { - continue; - } - - if (! $this->isName($node->name, $methodCallRenameWithAddedArgument->getOldMethod())) { - continue; - } - - $node->name = new Identifier($methodCallRenameWithAddedArgument->getNewMethod()); - $node->args = $this->nodeFactory->createArgs($methodCallRenameWithAddedArgument->getNewArguments()); - - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $methodCallRenamesWithAddedArguments = $configuration[self::METHOD_CALL_RENAMES_WITH_ADDED_ARGUMENTS] ?? []; - Assert::allIsInstanceOf( - $methodCallRenamesWithAddedArguments, - MethodCallToAnotherMethodCallWithArguments::class - ); - $this->methodCallRenamesWithAddedArguments = $methodCallRenamesWithAddedArguments; - } -} diff --git a/rules/Transform/Rector/MethodCall/MethodCallToFuncCallRector.php b/rules/Transform/Rector/MethodCall/MethodCallToFuncCallRector.php new file mode 100644 index 00000000000..057f1082ddd --- /dev/null +++ b/rules/Transform/Rector/MethodCall/MethodCallToFuncCallRector.php @@ -0,0 +1,100 @@ +render('some_template'); + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + public function show() + { + return view('some_template'); + } +} +CODE_SAMPLE + , + [new MethodCallToFuncCall('SomeClass', 'render', 'view')] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + /** + * @param MethodCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + foreach ($this->methodCallsToFuncCalls as $methodCallToFuncCall) { + if (! $this->isName($node->name, $methodCallToFuncCall->getMethodName())) { + continue; + } + + if (! $this->isObjectType($node->var, new ObjectType($methodCallToFuncCall->getObjectType()))) { + continue; + } + + return new FuncCall(new FullyQualified($methodCallToFuncCall->getFunctionName()), $node->getArgs()); + } + + return null; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsInstanceOf($configuration, MethodCallToFuncCall::class); + $this->methodCallsToFuncCalls = $configuration; + } +} diff --git a/rules/Transform/Rector/MethodCall/MethodCallToMethodCallRector.php b/rules/Transform/Rector/MethodCall/MethodCallToMethodCallRector.php deleted file mode 100644 index 4138640b2a3..00000000000 --- a/rules/Transform/Rector/MethodCall/MethodCallToMethodCallRector.php +++ /dev/null @@ -1,174 +0,0 @@ -firstDependency->go(); - } -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function __construct( - private SecondDependency $secondDependency - ) { - } - - public function run() - { - $this->secondDependency->away(); - } -} -CODE_SAMPLE - , - [ - self::METHOD_CALLS_TO_METHOD_CALLS => [ - new MethodCallToMethodCall('FirstDependency', 'go', 'SecondDependency', 'away'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->methodCallsToMethodsCalls as $methodCallToMethodCall) { - if (! $node->var instanceof PropertyFetch) { - continue; - } - - if (! $this->isMatch($node, $methodCallToMethodCall)) { - continue; - } - - $propertyFetch = $node->var; - - $class = $node->getAttribute(AttributeKey::CLASS_NODE); - $newObjectType = new ObjectType($methodCallToMethodCall->getNewType()); - - $newPropertyName = $this->matchNewPropertyName($methodCallToMethodCall, $class); - if ($newPropertyName === null) { - continue; - } - - $propertyMetadata = new PropertyMetadata($newPropertyName, $newObjectType, Class_::MODIFIER_PRIVATE); - $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); - - // rename property - $node->var = new PropertyFetch($propertyFetch->var, $newPropertyName); - // rename method - $node->name = new Identifier($methodCallToMethodCall->getNewMethod()); - - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $methodCallsToMethodsCalls = $configuration[self::METHOD_CALLS_TO_METHOD_CALLS] ?? []; - Assert::allIsAOf($methodCallsToMethodsCalls, MethodCallToMethodCall::class); - $this->methodCallsToMethodsCalls = $methodCallsToMethodsCalls; - } - - private function isMatch(MethodCall $methodCall, MethodCallToMethodCall $methodCallToMethodCall): bool - { - if (! $this->isObjectType($methodCall->var, new ObjectType($methodCallToMethodCall->getOldType()))) { - return false; - } - - return $this->isName($methodCall->name, $methodCallToMethodCall->getOldMethod()); - } - - private function matchNewPropertyName(MethodCallToMethodCall $methodCallToMethodCall, Class_ $class): ?string - { - $newPropertyName = $this->propertyNaming->fqnToVariableName($methodCallToMethodCall->getNewType()); - - $propertyMetadata = new PropertyMetadata( - $newPropertyName, - new ObjectType($methodCallToMethodCall->getNewType()), - Class_::MODIFIER_PRIVATE - ); - - $classContextProperty = $this->propertyPresenceChecker->getClassContextProperty($class, $propertyMetadata); - if ($classContextProperty === null) { - return $newPropertyName; - } - - // re-use existing proeprty name - return $this->getName($classContextProperty); - } -} diff --git a/rules/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector.php b/rules/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector.php deleted file mode 100644 index bf631daf177..00000000000 --- a/rules/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector.php +++ /dev/null @@ -1,94 +0,0 @@ - - */ - private array $methodCallToPropertyFetchCollection = []; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Turns method call "$this->something()" to property fetch "$this->something"', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $this->someMethod(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function run() - { - $this->someProperty; - } -} -CODE_SAMPLE - , - [ - self::METHOD_CALL_TO_PROPERTY_FETCHES => [ - 'someMethod' => 'someProperty', - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->methodCallToPropertyFetchCollection as $methodName => $propertyName) { - if (! $this->isName($node->name, $methodName)) { - continue; - } - - return $this->nodeFactory->createPropertyFetch('this', $propertyName); - } - - return null; - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->methodCallToPropertyFetchCollection = $configuration[self::METHOD_CALL_TO_PROPERTY_FETCHES] ?? []; - } -} diff --git a/rules/Transform/Rector/MethodCall/MethodCallToStaticCallRector.php b/rules/Transform/Rector/MethodCall/MethodCallToStaticCallRector.php index c07702c39e6..34e403ada4a 100644 --- a/rules/Transform/Rector/MethodCall/MethodCallToStaticCallRector.php +++ b/rules/Transform/Rector/MethodCall/MethodCallToStaticCallRector.php @@ -6,8 +6,8 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Transform\ValueObject\MethodCallToStaticCall; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,11 +18,6 @@ */ final class MethodCallToStaticCallRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const METHOD_CALLS_TO_STATIC_CALLS = 'method_calls_to_static_calls'; - /** * @var MethodCallToStaticCall[] */ @@ -48,7 +43,7 @@ public function loadConfiguration() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' final class SomeClass { @@ -65,12 +60,8 @@ public function loadConfiguration() } } CODE_SAMPLE -, - [ - self::METHOD_CALLS_TO_STATIC_CALLS => [ - new MethodCallToStaticCall('AnotherDependency', 'process', 'StaticCaller', 'anotherMethod'), - ], - ] + , + [new MethodCallToStaticCall('AnotherDependency', 'process', 'StaticCaller', 'anotherMethod')] ), ]); } @@ -89,11 +80,11 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { foreach ($this->methodCallsToStaticCalls as $methodCallToStaticCall) { - if (! $this->isObjectType($node->var, $methodCallToStaticCall->getOldObjectType())) { + if (! $this->isName($node->name, $methodCallToStaticCall->getOldMethod())) { continue; } - if (! $this->isName($node->name, $methodCallToStaticCall->getOldMethod())) { + if (! $this->isObjectType($node->var, $methodCallToStaticCall->getOldObjectType())) { continue; } @@ -108,12 +99,12 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $methodCallsToStaticCalls = $configuration[self::METHOD_CALLS_TO_STATIC_CALLS] ?? []; - Assert::allIsInstanceOf($methodCallsToStaticCalls, MethodCallToStaticCall::class); - $this->methodCallsToStaticCalls = $methodCallsToStaticCalls; + Assert::allIsAOf($configuration, MethodCallToStaticCall::class); + + $this->methodCallsToStaticCalls = $configuration; } } diff --git a/rules/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector.php b/rules/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector.php deleted file mode 100644 index 94f13545a40..00000000000 --- a/rules/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector.php +++ /dev/null @@ -1,106 +0,0 @@ -someMethodCall(); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run(SomeTypeToReplace $someTypeToReplace) - { - $this->someProperty->someMethodCall(); - } -} -CODE_SAMPLE - , - [ - self::PARENT_CALLS_TO_PROPERTIES => [ - new ReplaceParentCallByPropertyCall('SomeTypeToReplace', 'someMethodCall', 'someProperty'), - ], - ] - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->parentCallToProperties as $parentCallToProperty) { - if (! $this->isObjectType($node->var, $parentCallToProperty->getObjectType())) { - continue; - } - - if (! $this->isName($node->name, $parentCallToProperty->getMethod())) { - continue; - } - - $node->var = $this->nodeFactory->createPropertyFetch('this', $parentCallToProperty->getProperty()); - return $node; - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $parentCallToProperties = $configuration[self::PARENT_CALLS_TO_PROPERTIES] ?? []; - Assert::allIsInstanceOf($parentCallToProperties, ReplaceParentCallByPropertyCall::class); - - $this->parentCallToProperties = $parentCallToProperties; - } -} diff --git a/rules/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector.php b/rules/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector.php deleted file mode 100644 index d2a9b8d8ba8..00000000000 --- a/rules/Transform/Rector/MethodCall/ServiceGetterToConstructorInjectionRector.php +++ /dev/null @@ -1,180 +0,0 @@ -firstService = $firstService; - } - - public function run() - { - $anotherService = $this->firstService->getAnotherService(); - $anotherService->run(); - } -} - -class FirstService -{ - /** - * @var AnotherService - */ - private $anotherService; - - public function __construct(AnotherService $anotherService) - { - $this->anotherService = $anotherService; - } - - public function getAnotherService(): AnotherService - { - return $this->anotherService; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - /** - * @var FirstService - */ - private $firstService; - - /** - * @var AnotherService - */ - private $anotherService; - - public function __construct(FirstService $firstService, AnotherService $anotherService) - { - $this->firstService = $firstService; - $this->anotherService = $anotherService; - } - - public function run() - { - $anotherService = $this->anotherService; - $anotherService->run(); - } -} -CODE_SAMPLE - , - [ - self::METHOD_CALL_TO_SERVICES => [ - new ServiceGetterToConstructorInjection('FirstService', 'getAnotherService', 'AnotherService'), - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - */ - public function refactor(Node $node): ?Node - { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } - - if ($this->classAnalyzer->isAnonymousClass($classLike)) { - return null; - } - - foreach ($this->methodCallToServices as $methodCallToService) { - if (! $this->isObjectType($node->var, $methodCallToService->getOldObjectType())) { - continue; - } - - if (! $this->isName($node->name, $methodCallToService->getOldMethod())) { - continue; - } - - $serviceObjectType = new ObjectType($methodCallToService->getServiceType()); - - $propertyName = $this->propertyNaming->fqnToVariableName($serviceObjectType); - - $propertyMetadata = new PropertyMetadata($propertyName, $serviceObjectType, Class_::MODIFIER_PRIVATE); - $this->propertyToAddCollector->addPropertyToClass($classLike, $propertyMetadata); - - return new PropertyFetch(new Variable('this'), new Identifier($propertyName)); - } - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $methodCallToServices = $configuration[self::METHOD_CALL_TO_SERVICES] ?? []; - Assert::allIsInstanceOf($methodCallToServices, ServiceGetterToConstructorInjection::class); - $this->methodCallToServices = $methodCallToServices; - } -} diff --git a/rules/Transform/Rector/New_/NewArgToMethodCallRector.php b/rules/Transform/Rector/New_/NewArgToMethodCallRector.php deleted file mode 100644 index 1ee09b1633c..00000000000 --- a/rules/Transform/Rector/New_/NewArgToMethodCallRector.php +++ /dev/null @@ -1,110 +0,0 @@ -usePutenv(); - } -} -CODE_SAMPLE -, - [ - self::NEW_ARGS_TO_METHOD_CALLS => [new NewArgToMethodCall('Dotenv', true, 'usePutenv')], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [New_::class]; - } - - /** - * @param New_ $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->newArgsToMethodCalls as $newArgToMethodCall) { - if (! $this->isObjectType($node->class, $newArgToMethodCall->getObjectType())) { - continue; - } - - if (! isset($node->args[0])) { - return null; - } - - $firstArgValue = $node->args[0]->value; - if (! $this->valueResolver->isValue($firstArgValue, $newArgToMethodCall->getValue())) { - continue; - } - - unset($node->args[0]); - - return new MethodCall($node, 'usePutenv'); - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $newArgsToMethodCalls = $configuration[self::NEW_ARGS_TO_METHOD_CALLS] ?? []; - Assert::allIsInstanceOf($newArgsToMethodCalls, NewArgToMethodCall::class); - $this->newArgsToMethodCalls = $newArgsToMethodCalls; - } -} diff --git a/rules/Transform/Rector/New_/NewToConstructorInjectionRector.php b/rules/Transform/Rector/New_/NewToConstructorInjectionRector.php deleted file mode 100644 index 7b3715eca76..00000000000 --- a/rules/Transform/Rector/New_/NewToConstructorInjectionRector.php +++ /dev/null @@ -1,192 +0,0 @@ -validate(1000); - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var Validator - */ - private $validator; - - public function __construct(Validator $validator) - { - $this->validator = $validator; - } - - public function run() - { - $this->validator->validate(1000); - } -} -CODE_SAMPLE - , - [ - self::TYPES_TO_CONSTRUCTOR_INJECTION => ['Validator'], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [New_::class, Assign::class, MethodCall::class]; - } - - /** - * @param New_|Assign|MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof MethodCall) { - return $this->refactorMethodCall($node); - } - - if ($node instanceof Assign) { - $this->refactorAssign($node); - } - - if ($node instanceof New_) { - $this->refactorNew($node); - } - - return null; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $typesToConstructorInjections = $configuration[self::TYPES_TO_CONSTRUCTOR_INJECTION] ?? []; - foreach ($typesToConstructorInjections as $typeToConstructorInjection) { - $this->constructorInjectionObjectTypes[] = new ObjectType($typeToConstructorInjection); - } - } - - private function refactorMethodCall(MethodCall $methodCall): ?MethodCall - { - foreach ($this->constructorInjectionObjectTypes as $constructorInjectionObjectType) { - if (! $methodCall->var instanceof Variable) { - continue; - } - - if (! $this->isObjectType($methodCall->var, $constructorInjectionObjectType)) { - continue; - } - - if (! $this->nodeTypeResolver->isObjectType($methodCall->var, $constructorInjectionObjectType)) { - continue; - } - - $methodCall->var = $this->propertyFetchFactory->createFromType($constructorInjectionObjectType); - return $methodCall; - } - - return null; - } - - private function refactorAssign(Assign $assign): void - { - if (! $assign->expr instanceof New_) { - return; - } - - foreach ($this->constructorInjectionObjectTypes as $constructorInjectionObjectType) { - if (! $this->isObjectType($assign->expr, $constructorInjectionObjectType)) { - continue; - } - - $this->assignRemover->removeAssignNode($assign); - } - } - - private function refactorNew(New_ $new): void - { - foreach ($this->constructorInjectionObjectTypes as $constructorInjectionObjectType) { - if (! $this->isObjectType($new->class, $constructorInjectionObjectType)) { - continue; - } - - $classLike = $new->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - continue; - } - - $expectedPropertyName = $this->propertyNaming->getExpectedNameFromType($constructorInjectionObjectType); - if (! $expectedPropertyName instanceof ExpectedName) { - continue; - } - - $propertyMetadata = new PropertyMetadata( - $expectedPropertyName->getName(), - $constructorInjectionObjectType, - Class_::MODIFIER_PRIVATE - ); - $this->propertyToAddCollector->addPropertyToClass($classLike, $propertyMetadata); - } - } -} diff --git a/rules/Transform/Rector/New_/NewToMethodCallRector.php b/rules/Transform/Rector/New_/NewToMethodCallRector.php deleted file mode 100644 index 2eb813b6b2b..00000000000 --- a/rules/Transform/Rector/New_/NewToMethodCallRector.php +++ /dev/null @@ -1,155 +0,0 @@ -myClassFactory->create($argument); - } -} -CODE_SAMPLE - , - [ - self::NEWS_TO_METHOD_CALLS => [new NewToMethodCall('MyClass', 'MyClassFactory', 'create')], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [New_::class]; - } - - /** - * @param New_ $node - */ - public function refactor(Node $node): ?Node - { - foreach ($this->newsToMethodCalls as $newsToMethodCall) { - if (! $this->isObjectType($node, $newsToMethodCall->getNewObjectType())) { - continue; - } - - $serviceObjectType = $newsToMethodCall->getServiceObjectType(); - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - if ($className === $serviceObjectType->getClassName()) { - continue; - } - - /** @var Class_ $class */ - $class = $node->getAttribute(AttributeKey::CLASS_NODE); - - $propertyName = $this->getExistingFactoryPropertyName( - $class, - $newsToMethodCall->getServiceObjectType() - ); - - if ($propertyName === null) { - $serviceObjectType = $newsToMethodCall->getServiceObjectType(); - $propertyName = $this->classNaming->getShortName($serviceObjectType->getClassName()); - $propertyName = lcfirst($propertyName); - - $propertyMetadata = new PropertyMetadata( - $propertyName, - $newsToMethodCall->getServiceObjectType(), - Class_::MODIFIER_PRIVATE - ); - $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); - } - - $propertyFetch = new PropertyFetch(new Variable('this'), $propertyName); - - return new MethodCall($propertyFetch, $newsToMethodCall->getServiceMethod(), $node->args); - } - - return $node; - } - - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $newsToMethodCalls = $configuration[self::NEWS_TO_METHOD_CALLS] ?? []; - Assert::allIsInstanceOf($newsToMethodCalls, NewToMethodCall::class); - $this->newsToMethodCalls = $newsToMethodCalls; - } - - private function getExistingFactoryPropertyName(Class_ $class, ObjectType $factoryObjectType): ?string - { - foreach ($class->getProperties() as $property) { - if (! $this->isObjectType($property, $factoryObjectType)) { - continue; - } - - return $this->getName($property); - } - - return null; - } -} diff --git a/rules/Transform/Rector/New_/NewToStaticCallRector.php b/rules/Transform/Rector/New_/NewToStaticCallRector.php index 638980a76c0..c020235e946 100644 --- a/rules/Transform/Rector/New_/NewToStaticCallRector.php +++ b/rules/Transform/Rector/New_/NewToStaticCallRector.php @@ -6,8 +6,8 @@ use PhpParser\Node; use PhpParser\Node\Expr\New_; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Transform\ValueObject\NewToStaticCall; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,11 +18,6 @@ */ final class NewToStaticCallRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const TYPE_TO_STATIC_CALLS = 'type_to_static_calls'; - /** * @var NewToStaticCall[] */ @@ -52,9 +47,7 @@ public function run() } CODE_SAMPLE , - [ - self::TYPE_TO_STATIC_CALLS => [new NewToStaticCall('Cookie', 'Cookie', 'create')], - ] + [new NewToStaticCall('Cookie', 'Cookie', 'create')] ), ]); } @@ -88,12 +81,12 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $typeToStaticCalls = $configuration[self::TYPE_TO_STATIC_CALLS] ?? []; - Assert::allIsInstanceOf($typeToStaticCalls, NewToStaticCall::class); - $this->typeToStaticCalls = $typeToStaticCalls; + Assert::allIsAOf($configuration, NewToStaticCall::class); + + $this->typeToStaticCalls = $configuration; } } diff --git a/rules/Transform/Rector/Scalar/ScalarValueToConstFetchRector.php b/rules/Transform/Rector/Scalar/ScalarValueToConstFetchRector.php new file mode 100644 index 00000000000..9a1e6a93070 --- /dev/null +++ b/rules/Transform/Rector/Scalar/ScalarValueToConstFetchRector.php @@ -0,0 +1,76 @@ +scalarValueToConstFetches as $scalarValueToConstFetch) { + if ($node->value === $scalarValueToConstFetch->getScalar()->value) { + return $scalarValueToConstFetch->getConstFetch(); + } + } + + return null; + } + + public function configure(array $configuration): void + { + Assert::allIsAOf($configuration, ScalarValueToConstFetch::class); + + $this->scalarValueToConstFetches = $configuration; + } +} diff --git a/rules/Transform/Rector/StaticCall/StaticCallToFuncCallRector.php b/rules/Transform/Rector/StaticCall/StaticCallToFuncCallRector.php index 56918d72b54..a69378e5460 100644 --- a/rules/Transform/Rector/StaticCall/StaticCallToFuncCallRector.php +++ b/rules/Transform/Rector/StaticCall/StaticCallToFuncCallRector.php @@ -8,8 +8,8 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name\FullyQualified; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Transform\ValueObject\StaticCallToFuncCall; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -21,29 +21,17 @@ final class StaticCallToFuncCallRector extends AbstractRector implements ConfigurableRectorInterface { /** - * @var string + * @var StaticCallToFuncCall[] */ - public const STATIC_CALLS_TO_FUNCTIONS = 'static_calls_to_functions'; - - /** - * @param StaticCallToFuncCall[] $staticCallsToFunctions - */ - public function __construct( - private array $staticCallsToFunctions = [] - ) { - } + private array $staticCallsToFunctions = []; public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Turns static call to function call.', [ + return new RuleDefinition('Turn static call to function call', [ new ConfiguredCodeSample( 'OldClass::oldMethod("args");', 'new_function("args");', - [ - self::STATIC_CALLS_TO_FUNCTIONS => [ - new StaticCallToFuncCall('OldClass', 'oldMethod', 'new_function'), - ], - ] + [new StaticCallToFuncCall('OldClass', 'oldMethod', 'new_function')] ), ]); } @@ -62,11 +50,11 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { foreach ($this->staticCallsToFunctions as $staticCallToFunction) { - if (! $this->isObjectType($node->class, $staticCallToFunction->getObjectType())) { + if (! $this->isName($node->name, $staticCallToFunction->getMethod())) { continue; } - if (! $this->isName($node->name, $staticCallToFunction->getMethod())) { + if (! $this->isObjectType($node->class, $staticCallToFunction->getObjectType())) { continue; } @@ -77,12 +65,12 @@ public function refactor(Node $node): ?Node } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $staticCallsToFunctions = $configuration[self::STATIC_CALLS_TO_FUNCTIONS] ?? []; - Assert::allIsInstanceOf($staticCallsToFunctions, StaticCallToFuncCall::class); - $this->staticCallsToFunctions = $staticCallsToFunctions; + Assert::allIsAOf($configuration, StaticCallToFuncCall::class); + + $this->staticCallsToFunctions = $configuration; } } diff --git a/rules/Transform/Rector/StaticCall/StaticCallToMethodCallRector.php b/rules/Transform/Rector/StaticCall/StaticCallToMethodCallRector.php index bf290f32eae..5b26e209e8b 100644 --- a/rules/Transform/Rector/StaticCall/StaticCallToMethodCallRector.php +++ b/rules/Transform/Rector/StaticCall/StaticCallToMethodCallRector.php @@ -10,11 +10,9 @@ use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Exception\ShouldNotHappenException; +use Rector\Rector\AbstractRector; use Rector\Transform\NodeAnalyzer\FuncCallStaticCallToMethodCallAnalyzer; use Rector\Transform\ValueObject\StaticCallToMethodCall; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; @@ -22,23 +20,19 @@ use Webmozart\Assert\Assert; /** + * @note used in Laravel Rector https://github.com/driftingly/rector-laravel/blob/f46812350e0b4ef535c82d3759e86bb5c6abb651/config/sets/laravel-static-to-injection.php#L23 + * * @see \Rector\Tests\Transform\Rector\StaticCall\StaticCallToMethodCallRector\StaticCallToMethodCallRectorTest */ final class StaticCallToMethodCallRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @api - * @var string - */ - public const STATIC_CALLS_TO_METHOD_CALLS = 'static_calls_to_method_calls'; - /** * @var StaticCallToMethodCall[] */ private array $staticCallsToMethodCalls = []; public function __construct( - private FuncCallStaticCallToMethodCallAnalyzer $funcCallStaticCallToMethodCallAnalyzer + private readonly FuncCallStaticCallToMethodCallAnalyzer $funcCallStaticCallToMethodCallAnalyzer ) { } @@ -57,9 +51,9 @@ public function run() } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' -use Symplify\SmartFileSystem\SmartFileSystem; +use App\Custom\SmartFileSystem; class SomeClass { @@ -79,16 +73,14 @@ public function run() } } CODE_SAMPLE - , + , [ - self::STATIC_CALLS_TO_METHOD_CALLS => [ - new StaticCallToMethodCall( - 'Nette\Utils\FileSystem', - 'write', - 'Symplify\SmartFileSystem\SmartFileSystem', - 'dumpFile' - ), - ], + new StaticCallToMethodCall( + 'Nette\Utils\FileSystem', + 'write', + 'App\Custom\SmartFileSystem', + 'dumpFile' + ), ] ), ]); @@ -99,63 +91,85 @@ public function run() */ public function getNodeTypes(): array { - return [StaticCall::class]; + return [Class_::class]; } /** - * @param StaticCall $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } + $class = $node; + $hasChanged = false; - $classMethod = $node->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return null; - } + foreach ($node->getMethods() as $classMethod) { + $this->traverseNodesWithCallable($classMethod, function (Node $node) use ( + $class, + $classMethod, + &$hasChanged + ): null|MethodCall|StaticCall { + if (! $node instanceof StaticCall) { + return null; + } - foreach ($this->staticCallsToMethodCalls as $staticCallToMethodCall) { - if (! $staticCallToMethodCall->isStaticCallMatch($node)) { - continue; - } + foreach ($this->staticCallsToMethodCalls as $staticCallToMethodCall) { + if (! $staticCallToMethodCall->isStaticCallMatch($node)) { + continue; + } - if ($classMethod->isStatic()) { - return $this->refactorToInstanceCall($node, $staticCallToMethodCall); - } + if ($classMethod->isStatic()) { + return $this->refactorToInstanceCall($node, $staticCallToMethodCall); + } - $expr = $this->funcCallStaticCallToMethodCallAnalyzer->matchTypeProvidingExpr( - $classLike, - $classMethod, - $staticCallToMethodCall->getClassObjectType() - ); + $expr = $this->funcCallStaticCallToMethodCallAnalyzer->matchTypeProvidingExpr( + $class, + $classMethod, + $staticCallToMethodCall->getClassObjectType(), + ); + + $methodName = $this->getMethodName($node, $staticCallToMethodCall); - if ($staticCallToMethodCall->getMethodName() === '*') { - $methodName = $this->getName($node->name); - } else { - $methodName = $staticCallToMethodCall->getMethodName(); - } + $hasChanged = true; - if (! is_string($methodName)) { - throw new ShouldNotHappenException(); - } + return new MethodCall($expr, $methodName, $node->args); + } - return new MethodCall($expr, $methodName, $node->args); + return $node; + }); } - return $node; + if ($hasChanged) { + return $node; + } + + return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $staticCallsToMethodCalls = $configuration[self::STATIC_CALLS_TO_METHOD_CALLS] ?? []; - Assert::allIsInstanceOf($staticCallsToMethodCalls, StaticCallToMethodCall::class); - $this->staticCallsToMethodCalls = $staticCallsToMethodCalls; + Assert::allIsAOf($configuration, StaticCallToMethodCall::class); + + $this->staticCallsToMethodCalls = $configuration; + } + + private function getMethodName( + StaticCall $staticCall, + StaticCallToMethodCall $staticCallToMethodCall + ): string { + if ($staticCallToMethodCall->getMethodName() === '*') { + $methodName = $this->getName($staticCall->name); + } else { + $methodName = $staticCallToMethodCall->getMethodName(); + } + + if (! is_string($methodName)) { + throw new ShouldNotHappenException(); + } + + return $methodName; } private function refactorToInstanceCall( @@ -163,6 +177,9 @@ private function refactorToInstanceCall( StaticCallToMethodCall $staticCallToMethodCall ): MethodCall { $new = new New_(new FullyQualified($staticCallToMethodCall->getClassType())); - return new MethodCall($new, $staticCallToMethodCall->getMethodName(), $staticCall->args); + + $methodName = $this->getMethodName($staticCall, $staticCallToMethodCall); + + return new MethodCall($new, $methodName, $staticCall->args); } } diff --git a/rules/Transform/Rector/StaticCall/StaticCallToNewRector.php b/rules/Transform/Rector/StaticCall/StaticCallToNewRector.php index 4e9f545245e..30aff7724b7 100644 --- a/rules/Transform/Rector/StaticCall/StaticCallToNewRector.php +++ b/rules/Transform/Rector/StaticCall/StaticCallToNewRector.php @@ -8,25 +8,18 @@ use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name\FullyQualified; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Rector\AbstractRector; use Rector\Transform\ValueObject\StaticCallToNew; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; /** - * @changelog https://github.com/symfony/symfony/pull/35308 - * * @see \Rector\Tests\Transform\Rector\StaticCall\StaticCallToNewRector\StaticCallToNewRectorTest */ final class StaticCallToNewRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const STATIC_CALLS_TO_NEWS = 'static_calls_to_news'; - /** * @var StaticCallToNew[] */ @@ -41,24 +34,22 @@ class SomeClass { public function run() { - $dotenv = JsonResponse::create(true); + $dotenv = JsonResponse::create(['foo' => 'bar'], Response::HTTP_OK); } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { public function run() { - $dotenv = new JsonResponse(); + $dotenv = new JsonResponse(['foo' => 'bar'], Response::HTTP_OK); } } CODE_SAMPLE -, - [ - self::STATIC_CALLS_TO_NEWS => [new StaticCallToNew('JsonResponse', 'create')], - ] + , + [new StaticCallToNew('JsonResponse', 'create')] ), ]); } @@ -72,16 +63,16 @@ public function getNodeTypes(): array } /** - * @param Node\Expr\StaticCall $node + * @param StaticCall $node */ public function refactor(Node $node): ?Node { - foreach ($this->staticCallsToNews as $staticCallToNews) { - if (! $this->isName($node->class, $staticCallToNews->getClass())) { + foreach ($this->staticCallsToNews as $staticCallToNew) { + if (! $this->isName($node->class, $staticCallToNew->getClass())) { continue; } - if (! $this->isName($node->name, $staticCallToNews->getMethod())) { + if (! $this->isName($node->name, $staticCallToNew->getMethod())) { continue; } @@ -90,19 +81,19 @@ public function refactor(Node $node): ?Node continue; } - return new New_(new FullyQualified($class)); + return new New_(new FullyQualified($class), $node->args); } - return $node; + return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $staticCallsToNews = $configuration[self::STATIC_CALLS_TO_NEWS] ?? []; - Assert::allIsAOf($staticCallsToNews, StaticCallToNew::class); - $this->staticCallsToNews = $staticCallsToNews; + Assert::allIsAOf($configuration, StaticCallToNew::class); + + $this->staticCallsToNews = $configuration; } } diff --git a/rules/Transform/Rector/String_/StringToClassConstantRector.php b/rules/Transform/Rector/String_/StringToClassConstantRector.php index 5fcece8d027..f1e4a00f5f5 100644 --- a/rules/Transform/Rector/String_/StringToClassConstantRector.php +++ b/rules/Transform/Rector/String_/StringToClassConstantRector.php @@ -6,8 +6,9 @@ use PhpParser\Node; use PhpParser\Node\Scalar\String_; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Rector\AbstractRector; use Rector\Transform\ValueObject\StringToClassConstant; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,19 +19,19 @@ */ final class StringToClassConstantRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const STRINGS_TO_CLASS_CONSTANTS = 'strings_to_class_constants'; - /** * @var StringToClassConstant[] */ private array $stringsToClassConstants = []; + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Changes strings to specific constants', [ + return new RuleDefinition('Change strings to specific constants', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' final class SomeSubscriber @@ -52,11 +53,7 @@ public static function getSubscribedEvents() } CODE_SAMPLE , - [ - self::STRINGS_TO_CLASS_CONSTANTS => [ - new StringToClassConstant('compiler.post_dump', 'Yet\AnotherClass', 'CONSTANT'), - ], - ] + [new StringToClassConstant('compiler.post_dump', 'Yet\AnotherClass', 'CONSTANT')] ), ]); } @@ -85,16 +82,16 @@ public function refactor(Node $node): ?Node ); } - return $node; + return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $stringToClassConstants = $configuration[self::STRINGS_TO_CLASS_CONSTANTS] ?? []; - Assert::allIsInstanceOf($stringToClassConstants, StringToClassConstant::class); - $this->stringsToClassConstants = $stringToClassConstants; + Assert::allIsAOf($configuration, StringToClassConstant::class); + + $this->stringsToClassConstants = $configuration; } } diff --git a/rules/Transform/Rector/String_/ToStringToMethodCallRector.php b/rules/Transform/Rector/String_/ToStringToMethodCallRector.php deleted file mode 100644 index 899717dfe1b..00000000000 --- a/rules/Transform/Rector/String_/ToStringToMethodCallRector.php +++ /dev/null @@ -1,117 +0,0 @@ - - */ - private array $methodNamesByType = []; - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Turns defined code uses of "__toString()" method to specific method calls.', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -$someValue = new SomeObject; -$result = (string) $someValue; -$result = $someValue->__toString(); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$someValue = new SomeObject; -$result = $someValue->getPath(); -$result = $someValue->getPath(); -CODE_SAMPLE - , - [ - self::METHOD_NAMES_BY_TYPE => [ - 'SomeObject' => 'getPath', - ], - ] - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [String_::class, MethodCall::class]; - } - - /** - * @param String_|MethodCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node instanceof String_) { - return $this->processStringNode($node); - } - - return $this->processMethodCall($node); - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->methodNamesByType = $configuration[self::METHOD_NAMES_BY_TYPE] ?? []; - } - - private function processStringNode(String_ $string): ?Node - { - foreach ($this->methodNamesByType as $type => $methodName) { - if (! $this->isObjectType($string->expr, new ObjectType($type))) { - continue; - } - - return $this->nodeFactory->createMethodCall($string->expr, $methodName); - } - - return null; - } - - private function processMethodCall(MethodCall $methodCall): ?Node - { - foreach ($this->methodNamesByType as $type => $methodName) { - if (! $this->isObjectType($methodCall->var, new ObjectType($type))) { - continue; - } - - if (! $this->isName($methodCall->name, '__toString')) { - continue; - } - - $methodCall->name = new Identifier($methodName); - - return $methodCall; - } - - return null; - } -} diff --git a/rules/Transform/ValueObject/ArgumentFuncCallToMethodCall.php b/rules/Transform/ValueObject/ArgumentFuncCallToMethodCall.php deleted file mode 100644 index 0ba6315f8e1..00000000000 --- a/rules/Transform/ValueObject/ArgumentFuncCallToMethodCall.php +++ /dev/null @@ -1,36 +0,0 @@ -function; - } - - public function getClass(): string - { - return $this->class; - } - - public function getMethodIfNoArgs(): ?string - { - return $this->methodIfNoArgs; - } - - public function getMethodIfArgs(): ?string - { - return $this->methodIfArgs; - } -} diff --git a/rules/Transform/ValueObject/ArrayDimFetchToMethodCall.php b/rules/Transform/ValueObject/ArrayDimFetchToMethodCall.php new file mode 100644 index 00000000000..7a19f9415d6 --- /dev/null +++ b/rules/Transform/ValueObject/ArrayDimFetchToMethodCall.php @@ -0,0 +1,46 @@ +objectType; + } + + public function getMethod(): string + { + return $this->method; + } + + public function getSetMethod(): ?string + { + return $this->setMethod; + } + + public function getExistsMethod(): ?string + { + return $this->existsMethod; + } + + public function getUnsetMethod(): ?string + { + return $this->unsetMethod; + } +} diff --git a/rules/Transform/ValueObject/ArrayFuncCallToMethodCall.php b/rules/Transform/ValueObject/ArrayFuncCallToMethodCall.php deleted file mode 100644 index 332931f1740..00000000000 --- a/rules/Transform/ValueObject/ArrayFuncCallToMethodCall.php +++ /dev/null @@ -1,36 +0,0 @@ -function; - } - - public function getClass(): string - { - return $this->class; - } - - public function getArrayMethod(): string - { - return $this->arrayMethod; - } - - public function getNonArrayMethod(): string - { - return $this->nonArrayMethod; - } -} diff --git a/rules/Transform/ValueObject/AttributeKeyToClassConstFetch.php b/rules/Transform/ValueObject/AttributeKeyToClassConstFetch.php new file mode 100644 index 00000000000..f611ab31170 --- /dev/null +++ b/rules/Transform/ValueObject/AttributeKeyToClassConstFetch.php @@ -0,0 +1,42 @@ + $valuesToConstantsMap + */ + public function __construct( + private string $attributeClass, + private string $attributeKey, + private string $constantClass, + private array $valuesToConstantsMap + ) { + } + + public function getAttributeClass(): string + { + return $this->attributeClass; + } + + public function getAttributeKey(): string + { + return $this->attributeKey; + } + + public function getConstantClass(): string + { + return $this->constantClass; + } + + /** + * @return array + */ + public function getValuesToConstantsMap(): array + { + return $this->valuesToConstantsMap; + } +} diff --git a/rules/Transform/ValueObject/CallableInMethodCallToVariable.php b/rules/Transform/ValueObject/CallableInMethodCallToVariable.php deleted file mode 100644 index 80cc463ac4f..00000000000 --- a/rules/Transform/ValueObject/CallableInMethodCallToVariable.php +++ /dev/null @@ -1,32 +0,0 @@ -classType); - } - - public function getMethodName(): string - { - return $this->methodName; - } - - public function getArgumentPosition(): int - { - return $this->argumentPosition; - } -} diff --git a/rules/Transform/ValueObject/ClassConstFetchToValue.php b/rules/Transform/ValueObject/ClassConstFetchToValue.php deleted file mode 100644 index 02ae5be16d4..00000000000 --- a/rules/Transform/ValueObject/ClassConstFetchToValue.php +++ /dev/null @@ -1,38 +0,0 @@ -class); - } - - public function getConstant(): string - { - return $this->constant; - } - - /** - * @return mixed - */ - public function getValue() - { - return $this->value; - } -} diff --git a/rules/Transform/ValueObject/ClassMethodReference.php b/rules/Transform/ValueObject/ClassMethodReference.php new file mode 100644 index 00000000000..3bbf1b63b61 --- /dev/null +++ b/rules/Transform/ValueObject/ClassMethodReference.php @@ -0,0 +1,31 @@ +class; + } + + public function getMethod(): string + { + return $this->method; + } +} diff --git a/rules/Transform/ValueObject/ConstFetchToClassConstFetch.php b/rules/Transform/ValueObject/ConstFetchToClassConstFetch.php new file mode 100644 index 00000000000..dac185a81d5 --- /dev/null +++ b/rules/Transform/ValueObject/ConstFetchToClassConstFetch.php @@ -0,0 +1,35 @@ +oldConstName); + RectorAssert::className($this->newClassName); + RectorAssert::constantName($this->newConstName); + } + + public function getOldConstName(): string + { + return $this->oldConstName; + } + + public function getNewClassName(): string + { + return $this->newClassName; + } + + public function getNewConstName(): string + { + return $this->newConstName; + } +} diff --git a/rules/Transform/ValueObject/DimFetchAssignToMethodCall.php b/rules/Transform/ValueObject/DimFetchAssignToMethodCall.php deleted file mode 100644 index 7501bc30258..00000000000 --- a/rules/Transform/ValueObject/DimFetchAssignToMethodCall.php +++ /dev/null @@ -1,32 +0,0 @@ -listClass); - } - - public function getItemObjectType(): ObjectType - { - return new ObjectType($this->itemClass); - } - - public function getAddMethod(): string - { - return $this->addMethod; - } -} diff --git a/rules/Transform/ValueObject/FuncCallToMethodCall.php b/rules/Transform/ValueObject/FuncCallToMethodCall.php index e0ff0ecb54d..98b93b933b4 100644 --- a/rules/Transform/ValueObject/FuncCallToMethodCall.php +++ b/rules/Transform/ValueObject/FuncCallToMethodCall.php @@ -5,14 +5,19 @@ namespace Rector\Transform\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class FuncCallToMethodCall +final readonly class FuncCallToMethodCall { public function __construct( private string $oldFuncName, private string $newClassName, private string $newMethodName ) { + RectorAssert::functionName($oldFuncName); + + RectorAssert::className($newClassName); + RectorAssert::methodName($newMethodName); } public function getOldFuncName(): string diff --git a/rules/Transform/ValueObject/FuncCallToStaticCall.php b/rules/Transform/ValueObject/FuncCallToStaticCall.php index e5bb6989a02..4be740252a2 100644 --- a/rules/Transform/ValueObject/FuncCallToStaticCall.php +++ b/rules/Transform/ValueObject/FuncCallToStaticCall.php @@ -4,13 +4,19 @@ namespace Rector\Transform\ValueObject; -final class FuncCallToStaticCall +use Rector\Validation\RectorAssert; + +final readonly class FuncCallToStaticCall { public function __construct( private string $oldFuncName, private string $newClassName, private string $newMethodName ) { + RectorAssert::functionName($oldFuncName); + + RectorAssert::className($newClassName); + RectorAssert::methodName($newMethodName); } public function getOldFuncName(): string diff --git a/rules/Transform/ValueObject/FunctionToStaticCall.php b/rules/Transform/ValueObject/FunctionToStaticCall.php deleted file mode 100644 index 2867592b6f4..00000000000 --- a/rules/Transform/ValueObject/FunctionToStaticCall.php +++ /dev/null @@ -1,30 +0,0 @@ -class; - } - - public function getMethod(): string - { - return $this->method; - } - - public function getFunction(): string - { - return $this->function; - } -} diff --git a/rules/Transform/ValueObject/GetAndSetToMethodCall.php b/rules/Transform/ValueObject/GetAndSetToMethodCall.php deleted file mode 100644 index ee98e003d20..00000000000 --- a/rules/Transform/ValueObject/GetAndSetToMethodCall.php +++ /dev/null @@ -1,35 +0,0 @@ -getMethod; - } - - public function getSetMethod(): string - { - return $this->setMethod; - } - - public function getObjectType(): ObjectType - { - return new ObjectType($this->classType); - } -} diff --git a/rules/Transform/ValueObject/MethodCallToAnotherMethodCallWithArguments.php b/rules/Transform/ValueObject/MethodCallToAnotherMethodCallWithArguments.php deleted file mode 100644 index 0d414792555..00000000000 --- a/rules/Transform/ValueObject/MethodCallToAnotherMethodCallWithArguments.php +++ /dev/null @@ -1,44 +0,0 @@ -type); - } - - public function getOldMethod(): string - { - return $this->oldMethod; - } - - public function getNewMethod(): string - { - return $this->newMethod; - } - - /** - * @return mixed[] - */ - public function getNewArguments(): array - { - return $this->newArguments; - } -} diff --git a/rules/Transform/ValueObject/MethodCallToFuncCall.php b/rules/Transform/ValueObject/MethodCallToFuncCall.php new file mode 100644 index 00000000000..953a73869ef --- /dev/null +++ b/rules/Transform/ValueObject/MethodCallToFuncCall.php @@ -0,0 +1,30 @@ +objectType; + } + + public function getMethodName(): string + { + return $this->methodName; + } + + public function getFunctionName(): string + { + return $this->functionName; + } +} diff --git a/rules/Transform/ValueObject/MethodCallToMethodCall.php b/rules/Transform/ValueObject/MethodCallToMethodCall.php deleted file mode 100644 index 35a639fcf6c..00000000000 --- a/rules/Transform/ValueObject/MethodCallToMethodCall.php +++ /dev/null @@ -1,40 +0,0 @@ -oldType; - } - - public function getOldMethod(): string - { - return $this->oldMethod; - } - - public function getNewType(): string - { - return $this->newType; - } - - public function getNewMethod(): string - { - return $this->newMethod; - } -} diff --git a/rules/Transform/ValueObject/MethodCallToStaticCall.php b/rules/Transform/ValueObject/MethodCallToStaticCall.php index 6784af02ccd..d91ee08ab6e 100644 --- a/rules/Transform/ValueObject/MethodCallToStaticCall.php +++ b/rules/Transform/ValueObject/MethodCallToStaticCall.php @@ -5,8 +5,9 @@ namespace Rector\Transform\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class MethodCallToStaticCall +final readonly class MethodCallToStaticCall { public function __construct( private string $oldClass, @@ -14,6 +15,11 @@ public function __construct( private string $newClass, private string $newMethod ) { + RectorAssert::className($oldClass); + RectorAssert::className($oldMethod); + + RectorAssert::className($newClass); + RectorAssert::className($newMethod); } public function getOldObjectType(): ObjectType diff --git a/rules/Transform/ValueObject/NewArgToMethodCall.php b/rules/Transform/ValueObject/NewArgToMethodCall.php deleted file mode 100644 index 0d215e57338..00000000000 --- a/rules/Transform/ValueObject/NewArgToMethodCall.php +++ /dev/null @@ -1,38 +0,0 @@ -type); - } - - /** - * @return mixed - */ - public function getValue() - { - return $this->value; - } - - public function getMethodCall(): string - { - return $this->methodCall; - } -} diff --git a/rules/Transform/ValueObject/NewToMethodCall.php b/rules/Transform/ValueObject/NewToMethodCall.php deleted file mode 100644 index 9b7625abc99..00000000000 --- a/rules/Transform/ValueObject/NewToMethodCall.php +++ /dev/null @@ -1,32 +0,0 @@ -newType); - } - - public function getServiceObjectType(): ObjectType - { - return new ObjectType($this->serviceType); - } - - public function getServiceMethod(): string - { - return $this->serviceMethod; - } -} diff --git a/rules/Transform/ValueObject/NewToStaticCall.php b/rules/Transform/ValueObject/NewToStaticCall.php index 11353af8dfe..bac0603487d 100644 --- a/rules/Transform/ValueObject/NewToStaticCall.php +++ b/rules/Transform/ValueObject/NewToStaticCall.php @@ -5,14 +5,19 @@ namespace Rector\Transform\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class NewToStaticCall +final readonly class NewToStaticCall { public function __construct( private string $type, private string $staticCallClass, private string $staticCallMethod ) { + RectorAssert::className($type); + + RectorAssert::className($staticCallClass); + RectorAssert::methodName($staticCallMethod); } public function getObjectType(): ObjectType diff --git a/rules/Transform/ValueObject/ParentClassToTraits.php b/rules/Transform/ValueObject/ParentClassToTraits.php deleted file mode 100644 index 21cdd0da910..00000000000 --- a/rules/Transform/ValueObject/ParentClassToTraits.php +++ /dev/null @@ -1,33 +0,0 @@ -parentType); - } - - /** - * @return string[] - */ - public function getTraitNames(): array - { - // keep the Trait order the way it is in config - return array_reverse($this->traitNames); - } -} diff --git a/rules/Transform/ValueObject/PropertyAndClassMethodName.php b/rules/Transform/ValueObject/PropertyAndClassMethodName.php deleted file mode 100644 index 5097df36a54..00000000000 --- a/rules/Transform/ValueObject/PropertyAndClassMethodName.php +++ /dev/null @@ -1,24 +0,0 @@ -propertyName; - } - - public function getClassMethodName(): string - { - return $this->classMethodName; - } -} diff --git a/rules/Transform/ValueObject/PropertyAssignToMethodCall.php b/rules/Transform/ValueObject/PropertyAssignToMethodCall.php deleted file mode 100644 index d452e6e6941..00000000000 --- a/rules/Transform/ValueObject/PropertyAssignToMethodCall.php +++ /dev/null @@ -1,32 +0,0 @@ -class); - } - - public function getOldPropertyName(): string - { - return $this->oldPropertyName; - } - - public function getNewMethodName(): string - { - return $this->newMethodName; - } -} diff --git a/rules/Transform/ValueObject/PropertyFetchToMethodCall.php b/rules/Transform/ValueObject/PropertyFetchToMethodCall.php deleted file mode 100644 index d2f2f87292f..00000000000 --- a/rules/Transform/ValueObject/PropertyFetchToMethodCall.php +++ /dev/null @@ -1,50 +0,0 @@ -oldType); - } - - public function getOldProperty(): string - { - return $this->oldProperty; - } - - public function getNewGetMethod(): string - { - return $this->newGetMethod; - } - - public function getNewSetMethod(): ?string - { - return $this->newSetMethod; - } - - /** - * @return mixed[] - */ - public function getNewGetArguments(): array - { - return $this->newGetArguments; - } -} diff --git a/rules/Transform/ValueObject/ReplaceParentCallByPropertyCall.php b/rules/Transform/ValueObject/ReplaceParentCallByPropertyCall.php deleted file mode 100644 index 99d07a8a37c..00000000000 --- a/rules/Transform/ValueObject/ReplaceParentCallByPropertyCall.php +++ /dev/null @@ -1,32 +0,0 @@ -class); - } - - public function getMethod(): string - { - return $this->method; - } - - public function getProperty(): string - { - return $this->property; - } -} diff --git a/rules/Transform/ValueObject/ScalarValueToConstFetch.php b/rules/Transform/ValueObject/ScalarValueToConstFetch.php new file mode 100644 index 00000000000..91084ffd106 --- /dev/null +++ b/rules/Transform/ValueObject/ScalarValueToConstFetch.php @@ -0,0 +1,30 @@ +scalar; + } + + public function getConstFetch(): ConstFetch|ClassConstFetch + { + return $this->constFetch; + } +} diff --git a/rules/Transform/ValueObject/ServiceGetterToConstructorInjection.php b/rules/Transform/ValueObject/ServiceGetterToConstructorInjection.php deleted file mode 100644 index 65c4f9ebc2a..00000000000 --- a/rules/Transform/ValueObject/ServiceGetterToConstructorInjection.php +++ /dev/null @@ -1,32 +0,0 @@ -oldType); - } - - public function getOldMethod(): string - { - return $this->oldMethod; - } - - public function getServiceType(): string - { - return $this->serviceType; - } -} diff --git a/rules/Transform/ValueObject/SingleToManyMethod.php b/rules/Transform/ValueObject/SingleToManyMethod.php deleted file mode 100644 index 83ed429bfd9..00000000000 --- a/rules/Transform/ValueObject/SingleToManyMethod.php +++ /dev/null @@ -1,32 +0,0 @@ -class); - } - - public function getSingleMethodName(): string - { - return $this->singleMethodName; - } - - public function getManyMethodName(): string - { - return $this->manyMethodName; - } -} diff --git a/rules/Transform/ValueObject/StaticCallToFuncCall.php b/rules/Transform/ValueObject/StaticCallToFuncCall.php index 7cc9d3e8c13..c557515a5d9 100644 --- a/rules/Transform/ValueObject/StaticCallToFuncCall.php +++ b/rules/Transform/ValueObject/StaticCallToFuncCall.php @@ -5,14 +5,19 @@ namespace Rector\Transform\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class StaticCallToFuncCall +final readonly class StaticCallToFuncCall { public function __construct( private string $class, private string $method, private string $function ) { + RectorAssert::className($class); + RectorAssert::methodName($method); + + RectorAssert::functionName($function); } public function getObjectType(): ObjectType diff --git a/rules/Transform/ValueObject/StaticCallToMethodCall.php b/rules/Transform/ValueObject/StaticCallToMethodCall.php index 7de83d0f777..e6cfd1b5300 100644 --- a/rules/Transform/ValueObject/StaticCallToMethodCall.php +++ b/rules/Transform/ValueObject/StaticCallToMethodCall.php @@ -8,8 +8,9 @@ use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class StaticCallToMethodCall +final readonly class StaticCallToMethodCall { public function __construct( private string $staticClass, @@ -17,6 +18,18 @@ public function __construct( private string $classType, private string $methodName ) { + RectorAssert::className($staticClass); + + // special char to match all method names + if ($staticMethod !== '*') { + RectorAssert::methodName($staticMethod); + } + + RectorAssert::className($classType); + + if ($methodName !== '*') { + RectorAssert::methodName($methodName); + } } public function getClassObjectType(): ObjectType diff --git a/rules/Transform/ValueObject/StaticCallToNew.php b/rules/Transform/ValueObject/StaticCallToNew.php index 7b0e0edfcbc..c3129b674f2 100644 --- a/rules/Transform/ValueObject/StaticCallToNew.php +++ b/rules/Transform/ValueObject/StaticCallToNew.php @@ -4,12 +4,16 @@ namespace Rector\Transform\ValueObject; -final class StaticCallToNew +use Rector\Validation\RectorAssert; + +final readonly class StaticCallToNew { public function __construct( private string $class, private string $method ) { + RectorAssert::className($class); + RectorAssert::methodName($method); } public function getClass(): string diff --git a/rules/Transform/ValueObject/StringToClassConstant.php b/rules/Transform/ValueObject/StringToClassConstant.php index c71f9b8f9e0..012d6855fb5 100644 --- a/rules/Transform/ValueObject/StringToClassConstant.php +++ b/rules/Transform/ValueObject/StringToClassConstant.php @@ -4,13 +4,16 @@ namespace Rector\Transform\ValueObject; -final class StringToClassConstant +use Rector\Validation\RectorAssert; + +final readonly class StringToClassConstant { public function __construct( private string $string, private string $class, private string $constant ) { + RectorAssert::className($class); } public function getString(): string diff --git a/rules/Transform/ValueObject/UnsetAndIssetToMethodCall.php b/rules/Transform/ValueObject/UnsetAndIssetToMethodCall.php deleted file mode 100644 index b89d0435a3e..00000000000 --- a/rules/Transform/ValueObject/UnsetAndIssetToMethodCall.php +++ /dev/null @@ -1,32 +0,0 @@ -type); - } - - public function getIssetMethodCall(): string - { - return $this->issetMethodCall; - } - - public function getUnsedMethodCall(): string - { - return $this->unsedMethodCall; - } -} diff --git a/rules/Transform/ValueObject/VariableMethodCallToServiceCall.php b/rules/Transform/ValueObject/VariableMethodCallToServiceCall.php deleted file mode 100644 index 0f357243eb9..00000000000 --- a/rules/Transform/ValueObject/VariableMethodCallToServiceCall.php +++ /dev/null @@ -1,50 +0,0 @@ -variableType); - } - - public function getMethodName(): string - { - return $this->methodName; - } - - /** - * @return mixed - */ - public function getArgumentValue() - { - return $this->argumentValue; - } - - public function getServiceType(): string - { - return $this->serviceType; - } - - public function getServiceMethodName(): string - { - return $this->serviceMethodName; - } -} diff --git a/rules/Transform/ValueObject/WrapReturn.php b/rules/Transform/ValueObject/WrapReturn.php index 19ae552fd69..5782ff77143 100644 --- a/rules/Transform/ValueObject/WrapReturn.php +++ b/rules/Transform/ValueObject/WrapReturn.php @@ -5,14 +5,16 @@ namespace Rector\Transform\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class WrapReturn +final readonly class WrapReturn { public function __construct( private string $type, private string $method, private bool $isArrayWrap ) { + RectorAssert::className($type); } public function getObjectType(): ObjectType diff --git a/rules/TypeDeclaration/AlreadyAssignDetector/ConstructorAssignDetector.php b/rules/TypeDeclaration/AlreadyAssignDetector/ConstructorAssignDetector.php index 2939741becf..2061e95a62b 100644 --- a/rules/TypeDeclaration/AlreadyAssignDetector/ConstructorAssignDetector.php +++ b/rules/TypeDeclaration/AlreadyAssignDetector/ConstructorAssignDetector.php @@ -7,33 +7,46 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\Expression; -use PhpParser\NodeTraverser; +use PhpParser\Node\Stmt\If_; +use PhpParser\NodeFinder; +use PhpParser\NodeVisitor; use PHPStan\Type\ObjectType; -use Rector\Core\ValueObject\MethodName; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; +use Rector\NodeDecorator\StatementDepthAttributeDecorator; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Comparing\NodeComparator; use Rector\TypeDeclaration\Matcher\PropertyAssignMatcher; use Rector\TypeDeclaration\NodeAnalyzer\AutowiredClassMethodOrPropertyAnalyzer; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\ValueObject\MethodName; -final class ConstructorAssignDetector +final readonly class ConstructorAssignDetector { - /** - * @var string - */ - private const IS_FIRST_LEVEL_STATEMENT = 'first_level_stmt'; - public function __construct( private NodeTypeResolver $nodeTypeResolver, private PropertyAssignMatcher $propertyAssignMatcher, private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private AutowiredClassMethodOrPropertyAnalyzer $autowiredClassMethodOrPropertyAnalyzer + private AutowiredClassMethodOrPropertyAnalyzer $autowiredClassMethodOrPropertyAnalyzer, + private PropertyFetchAnalyzer $propertyFetchAnalyzer, + private NodeComparator $nodeComparator ) { } - public function isPropertyAssigned(ClassLike $classLike, string $propertyName): bool + public function isPropertyAssignedConditionally(Class_ $class, string $propertyName): bool + { + return $this->isPropertyAssigned($class, $propertyName, true); + } + + public function isPropertyAssigned(ClassLike $classLike, string $propertyName, bool $allowConditional = false): bool { $initializeClassMethods = $this->matchInitializeClassMethod($classLike); if ($initializeClassMethods === []) { @@ -42,12 +55,17 @@ public function isPropertyAssigned(ClassLike $classLike, string $propertyName): $isAssignedInConstructor = false; - $this->decorateFirstLevelStatementAttribute($initializeClassMethods); + StatementDepthAttributeDecorator::decorateClassMethods($initializeClassMethods); foreach ($initializeClassMethods as $initializeClassMethod) { $this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $initializeClassMethod->stmts, function ( Node $node - ) use ($propertyName, &$isAssignedInConstructor): ?int { + ) use ($propertyName, &$isAssignedInConstructor, $allowConditional): ?int { + if ($this->isIfElseAssign($node, $propertyName)) { + $isAssignedInConstructor = true; + return NodeVisitor::STOP_TRAVERSAL; + } + $expr = $this->matchAssignExprToPropertyName($node, $propertyName); if (! $expr instanceof Expr) { return null; @@ -55,45 +73,78 @@ public function isPropertyAssigned(ClassLike $classLike, string $propertyName): /** @var Assign $assign */ $assign = $node; - $isFirstLevelStatement = $assign->getAttribute(self::IS_FIRST_LEVEL_STATEMENT); + + // is merged in assign? + if ($this->isPropertyUsedInAssign($assign, $propertyName)) { + $isAssignedInConstructor = false; + return NodeVisitor::STOP_TRAVERSAL; + } + + $isFirstLevelStatement = $assign->getAttribute(AttributeKey::IS_FIRST_LEVEL_STATEMENT); // cannot be nested if ($isFirstLevelStatement !== true) { + if ($allowConditional) { + $isAssignedInConstructor = true; + return NodeVisitor::STOP_TRAVERSAL; + } + return null; } $isAssignedInConstructor = true; - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + return NodeVisitor::STOP_TRAVERSAL; }); } + if (! $isAssignedInConstructor) { + return $this->propertyFetchAnalyzer->isFilledViaMethodCallInConstructStmts($classLike, $propertyName); + } + return $isAssignedInConstructor; } - private function matchAssignExprToPropertyName(Node $node, string $propertyName): ?Expr + /** + * @param Stmt[] $stmts + */ + private function isAssignedInStmts(array $stmts, string $propertyName): bool { - if (! $node instanceof Assign) { - return null; + $isAssigned = false; + foreach ($stmts as $stmt) { + // non Expression can be on next stmt + if (! $stmt instanceof Expression) { + $isAssigned = false; + break; + } + + if ($this->matchAssignExprToPropertyName($stmt->expr, $propertyName) instanceof Expr) { + $isAssigned = true; + } } - return $this->propertyAssignMatcher->matchPropertyAssignExpr($node, $propertyName); + return $isAssigned; } - /** - * @param ClassMethod[] $classMethods - */ - private function decorateFirstLevelStatementAttribute(array $classMethods): void + private function isIfElseAssign(Node $node, string $propertyName): bool { - foreach ($classMethods as $classMethod) { - foreach ((array) $classMethod->stmts as $methodStmt) { - $methodStmt->setAttribute(self::IS_FIRST_LEVEL_STATEMENT, true); + if (! $node instanceof If_ || $node->elseifs !== [] || ! $node->else instanceof Else_) { + return false; + } - if ($methodStmt instanceof Expression) { - $methodStmt->expr->setAttribute(self::IS_FIRST_LEVEL_STATEMENT, true); - } - } + return $this->isAssignedInStmts($node->stmts, $propertyName) && $this->isAssignedInStmts( + $node->else->stmts, + $propertyName + ); + } + + private function matchAssignExprToPropertyName(Node $node, string $propertyName): ?Expr + { + if (! $node instanceof Assign) { + return null; } + + return $this->propertyAssignMatcher->matchPropertyAssignExpr($node, $propertyName); } /** @@ -131,4 +182,25 @@ private function matchInitializeClassMethod(ClassLike $classLike): array return $initializingClassMethods; } + + private function isPropertyUsedInAssign(Assign $assign, string $propertyName): bool + { + $nodeFinder = new NodeFinder(); + $var = $assign->var; + return (bool) $nodeFinder->findFirst($assign->expr, function (Node $node) use ($propertyName, $var): ?bool { + if (! $node instanceof PropertyFetch) { + return null; + } + + if (! $node->name instanceof Identifier) { + return null; + } + + if ($node->name->toString() !== $propertyName) { + return null; + } + + return $this->nodeComparator->areNodesEqual($node, $var); + }); + } } diff --git a/rules/TypeDeclaration/AlreadyAssignDetector/NullTypeAssignDetector.php b/rules/TypeDeclaration/AlreadyAssignDetector/NullTypeAssignDetector.php index 8cd10d048e2..4da8def998d 100644 --- a/rules/TypeDeclaration/AlreadyAssignDetector/NullTypeAssignDetector.php +++ b/rules/TypeDeclaration/AlreadyAssignDetector/NullTypeAssignDetector.php @@ -8,20 +8,18 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Stmt\ClassLike; -use PhpParser\NodeTraverser; -use Rector\NodeNestingScope\ScopeNestingComparator; +use PhpParser\NodeVisitor; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; use Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer; use Rector\TypeDeclaration\Matcher\PropertyAssignMatcher; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; /** * Should add extra null type */ -final class NullTypeAssignDetector +final readonly class NullTypeAssignDetector { public function __construct( - private ScopeNestingComparator $scopeNestingComparator, private DoctrineTypeAnalyzer $doctrineTypeAnalyzer, private NodeTypeResolver $nodeTypeResolver, private PropertyAssignMatcher $propertyAssignMatcher, @@ -42,16 +40,11 @@ public function detect(ClassLike $classLike, string $propertyName): bool return null; } - if ($this->scopeNestingComparator->isNodeConditionallyScoped($expr)) { - $needsNullType = true; - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - // not in doctrine property - $staticType = $this->nodeTypeResolver->getStaticType($expr); + $staticType = $this->nodeTypeResolver->getType($expr); if ($this->doctrineTypeAnalyzer->isDoctrineCollectionWithIterableUnionType($staticType)) { $needsNullType = false; - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } return null; diff --git a/rules/TypeDeclaration/AlreadyAssignDetector/PropertyDefaultAssignDetector.php b/rules/TypeDeclaration/AlreadyAssignDetector/PropertyDefaultAssignDetector.php index b6829d185bf..9b62c3360df 100644 --- a/rules/TypeDeclaration/AlreadyAssignDetector/PropertyDefaultAssignDetector.php +++ b/rules/TypeDeclaration/AlreadyAssignDetector/PropertyDefaultAssignDetector.php @@ -4,6 +4,7 @@ namespace Rector\TypeDeclaration\AlreadyAssignDetector; +use PhpParser\Node\Expr; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Property; @@ -16,6 +17,6 @@ public function detect(ClassLike $classLike, string $propertyName): bool return false; } - return $property->props[0]->default !== null; + return $property->props[0]->default instanceof Expr; } } diff --git a/rules/TypeDeclaration/Contract/TypeInferer/ParamTypeInfererInterface.php b/rules/TypeDeclaration/Contract/TypeInferer/ParamTypeInfererInterface.php deleted file mode 100644 index de6f08c466d..00000000000 --- a/rules/TypeDeclaration/Contract/TypeInferer/ParamTypeInfererInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -> + */ + public const array ARRAY_AND_CALLBACK_POSITIONS = [ + 'array_walk' => [ + 'array' => 0, + 'callback' => 1, + ], + 'array_map' => [ + 'array' => 1, + 'callback' => 0, + ], + 'usort' => [ + 'array' => 0, + 'callback' => 1, + ], + 'array_filter' => [ + 'array' => 0, + 'callback' => 1, + ], + ]; +} diff --git a/rules/TypeDeclaration/Enum/TypeStrictness.php b/rules/TypeDeclaration/Enum/TypeStrictness.php deleted file mode 100644 index 5773066f524..00000000000 --- a/rules/TypeDeclaration/Enum/TypeStrictness.php +++ /dev/null @@ -1,25 +0,0 @@ -getPriority(), - PHP_EOL, - $firstPriorityAwareTypeInferer::class, - PHP_EOL, - $secondPriorityAwareTypeInferer::class, - PHP_EOL - ); - - parent::__construct($message); - } -} diff --git a/rules/TypeDeclaration/FunctionLikeReturnTypeResolver.php b/rules/TypeDeclaration/FunctionLikeReturnTypeResolver.php index a49f0943ea3..40cf0eadc14 100644 --- a/rules/TypeDeclaration/FunctionLikeReturnTypeResolver.php +++ b/rules/TypeDeclaration/FunctionLikeReturnTypeResolver.php @@ -4,21 +4,21 @@ namespace Rector\TypeDeclaration; -use PhpParser\Node\FunctionLike; +use PhpParser\Node\Stmt\ClassMethod; use PHPStan\Type\MixedType; use PHPStan\Type\Type; use Rector\StaticTypeMapper\StaticTypeMapper; -final class FunctionLikeReturnTypeResolver +final readonly class FunctionLikeReturnTypeResolver { public function __construct( private StaticTypeMapper $staticTypeMapper ) { } - public function resolveFunctionLikeReturnTypeToPHPStanType(FunctionLike $functionLike): Type + public function resolveFunctionLikeReturnTypeToPHPStanType(ClassMethod $classMethod): Type { - $functionReturnType = $functionLike->getReturnType(); + $functionReturnType = $classMethod->getReturnType(); if ($functionReturnType === null) { return new MixedType(); } diff --git a/rules/TypeDeclaration/Guard/ParamTypeAddGuard.php b/rules/TypeDeclaration/Guard/ParamTypeAddGuard.php new file mode 100644 index 00000000000..ee49d4dfe25 --- /dev/null +++ b/rules/TypeDeclaration/Guard/ParamTypeAddGuard.php @@ -0,0 +1,73 @@ +nodeNameResolver->getName($param); + + $isLegal = true; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + (array) $functionLike->getStmts(), + function (Node $subNode) use (&$isLegal, $paramName): ?int { + if ($subNode instanceof Assign && $subNode->var instanceof Variable && $this->nodeNameResolver->isName( + $subNode->var, + $paramName + )) { + $isLegal = false; + return NodeVisitor::STOP_TRAVERSAL; + } + + if ($subNode instanceof If_ && (bool) $this->betterNodeFinder->findFirst( + $subNode->cond, + fn (Node $node): bool => $node instanceof Variable && $this->nodeNameResolver->isName( + $node, + $paramName + ) + )) { + $isLegal = false; + return NodeVisitor::STOP_TRAVERSAL; + } + + if ($subNode instanceof Ternary && (bool) $this->betterNodeFinder->findFirst( + $subNode, + fn (Node $node): bool => $node instanceof Variable && $this->nodeNameResolver->isName( + $node, + $paramName + ) + )) { + $isLegal = false; + return NodeVisitor::STOP_TRAVERSAL; + } + + return null; + } + ); + + return $isLegal; + } +} diff --git a/rules/TypeDeclaration/Guard/PropertyTypeOverrideGuard.php b/rules/TypeDeclaration/Guard/PropertyTypeOverrideGuard.php new file mode 100644 index 00000000000..46fc3ed9738 --- /dev/null +++ b/rules/TypeDeclaration/Guard/PropertyTypeOverrideGuard.php @@ -0,0 +1,42 @@ +makePropertyTypedGuard->isLegal($property, $classReflection)) { + return false; + } + + $propertyName = $this->nodeNameResolver->getName($property); + foreach ($classReflection->getParents() as $parentClassReflection) { + $nativeReflectionClass = $parentClassReflection->getNativeReflection(); + + if (! $nativeReflectionClass->hasProperty($propertyName)) { + continue; + } + + $parentPropertyReflection = $nativeReflectionClass->getProperty($propertyName); + + // empty type override is not allowed + return $parentPropertyReflection->getType() !== null; + } + + return true; + } +} diff --git a/rules/TypeDeclaration/Matcher/PropertyAssignMatcher.php b/rules/TypeDeclaration/Matcher/PropertyAssignMatcher.php index 2aedad7dcbc..ba086cd1fc0 100644 --- a/rules/TypeDeclaration/Matcher/PropertyAssignMatcher.php +++ b/rules/TypeDeclaration/Matcher/PropertyAssignMatcher.php @@ -7,13 +7,11 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; -use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer; -use Rector\NodeNameResolver\NodeNameResolver; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; -final class PropertyAssignMatcher +final readonly class PropertyAssignMatcher { public function __construct( - private NodeNameResolver $nodeNameResolver, private PropertyFetchAnalyzer $propertyFetchAnalyzer ) { } @@ -25,19 +23,16 @@ public function __construct( */ public function matchPropertyAssignExpr(Assign $assign, string $propertyName): ?Expr { - if ($this->propertyFetchAnalyzer->isPropertyFetch($assign->var)) { - if (! $this->nodeNameResolver->isName($assign->var, $propertyName)) { - return null; - } - + $assignVar = $assign->var; + if ($this->propertyFetchAnalyzer->isLocalPropertyFetchName($assignVar, $propertyName)) { return $assign->expr; } - if ($assign->var instanceof ArrayDimFetch && $this->propertyFetchAnalyzer->isPropertyFetch($assign->var->var)) { - if (! $this->nodeNameResolver->isName($assign->var->var, $propertyName)) { - return null; - } + if (! $assignVar instanceof ArrayDimFetch) { + return null; + } + if ($this->propertyFetchAnalyzer->isLocalPropertyFetchName($assignVar->var, $propertyName)) { return $assign->expr; } diff --git a/rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php index 75df1aa988f..ab6391ecf28 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php +++ b/rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php @@ -5,20 +5,41 @@ namespace Rector\TypeDeclaration\NodeAnalyzer; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\NodeNameResolver\NodeNameResolver; -use Symfony\Contracts\Service\Attribute\Required; +use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; -final class AutowiredClassMethodOrPropertyAnalyzer +final readonly class AutowiredClassMethodOrPropertyAnalyzer { public function __construct( private PhpDocInfoFactory $phpDocInfoFactory, - private NodeNameResolver $nodeNameResolver + private PhpAttributeAnalyzer $phpAttributeAnalyzer ) { } + public function matchAutowiredMethodInClass(Class_ $class): ?ClassMethod + { + foreach ($class->getMethods() as $classMethod) { + if (! $classMethod->isPublic()) { + continue; + } + + if ($classMethod->isMagic()) { + continue; + } + + if (! $this->detect($classMethod)) { + continue; + } + + return $classMethod; + } + + return null; + } + public function detect(ClassMethod | Param | Property $node): bool { $nodePhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); @@ -26,17 +47,9 @@ public function detect(ClassMethod | Param | Property $node): bool return true; } - foreach ($node->attrGroups as $attrGroup) { - foreach ($attrGroup->attrs as $attribute) { - if ($this->nodeNameResolver->isNames( - $attribute->name, - [Required::class, 'Nette\DI\Attributes\Inject'] - )) { - return true; - } - } - } - - return false; + return $this->phpAttributeAnalyzer->hasPhpAttributes( + $node, + ['Symfony\Contracts\Service\Attribute\Required', 'Nette\DI\Attributes\Inject'] + ); } } diff --git a/rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php b/rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php index bea09fb60d8..857b8fcfeaa 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php +++ b/rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php @@ -5,79 +5,99 @@ namespace Rector\TypeDeclaration\NodeAnalyzer; use PhpParser\Node\Arg; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Identifier; +use PhpParser\Node\VariadicPlaceholder; +use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Type\ArrayType; +use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\MixedType; -use PHPStan\Type\NullType; +use PHPStan\Type\NeverType; use PHPStan\Type\ObjectType; use PHPStan\Type\ThisType; use PHPStan\Type\Type; -use PHPStan\Type\TypeWithClassName; use PHPStan\Type\UnionType; -use Rector\NodeCollector\ValueObject\ArrayCallable; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; -use Rector\TypeDeclaration\Enum\TypeStrictness; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; -final class CallTypesResolver +final readonly class CallTypesResolver { public function __construct( private NodeTypeResolver $nodeTypeResolver, - private TypeFactory $typeFactory + private TypeFactory $typeFactory, + private ReflectionProvider $reflectionProvider ) { } /** - * @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls - * @return Type[] + * @param MethodCall[]|StaticCall[] $calls + * @return array */ public function resolveStrictTypesFromCalls(array $calls): array { - return $this->resolveTypesFromCalls($calls, TypeStrictness::STRICTNESS_TYPE_DECLARATION()); - } + $staticTypesByArgumentPosition = []; - /** - * @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls - * @return Type[] - */ - public function resolveWeakTypesFromCalls(array $calls): array - { - return $this->resolveTypesFromCalls($calls, TypeStrictness::STRICTNESS_DOCBLOCK()); + foreach ($calls as $call) { + foreach ($call->args as $position => $arg) { + if ($this->shouldSkipArg($arg)) { + return []; + } + + /** @var Arg $arg */ + $staticTypesByArgumentPosition[$position][] = $this->resolveStrictArgValueType($arg); + } + } + + // unite to single type + return $this->unionToSingleType($staticTypesByArgumentPosition); } /** - * @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls - * @return Type[] + * @param MethodCall[]|StaticCall[] $calls + * @return array */ - private function resolveTypesFromCalls(array $calls, TypeStrictness $typeStrictness): array + public function resolveTypesFromCalls(array $calls): array { $staticTypesByArgumentPosition = []; foreach ($calls as $call) { - if (! $call instanceof StaticCall && ! $call instanceof MethodCall) { - continue; - } - foreach ($call->args as $position => $arg) { - $argValueType = $this->resolveArgValueType($typeStrictness, $arg); - $staticTypesByArgumentPosition[$position][] = $argValueType; + if ($this->shouldSkipArg($arg)) { + return []; + } + + /** @var Arg $arg */ + if ($this->isEmptyArray($arg->value)) { + // skip empty array, as it doesn't add any value + continue; + } + + $positionOrName = $arg->name instanceof Identifier ? $arg->name->toString() : $position; + + $staticTypesByArgumentPosition[$positionOrName][] = $this->resolveArgValueType($arg); } } // unite to single type - return $this->unionToSingleType($staticTypesByArgumentPosition); + return $this->unionToSingleType($staticTypesByArgumentPosition, true); } - private function resolveArgValueType(TypeStrictness $typeStrictness, Arg $arg): Type + private function resolveStrictArgValueType(Arg $arg): Type { - if ($typeStrictness->equals(TypeStrictness::STRICTNESS_TYPE_DECLARATION())) { - $argValueType = $this->nodeTypeResolver->getNativeType($arg->value); - } else { - $argValueType = $this->nodeTypeResolver->resolve($arg->value); - } + $argValueType = $this->nodeTypeResolver->getNativeType($arg->value); - // "self" in another object is not correct, this make it independent - return $this->correctSelfType($argValueType); + return $this->normalizeType($argValueType); + } + + private function resolveArgValueType(Arg $arg): Type + { + $argValueType = $this->nodeTypeResolver->getType($arg->value); + + return $this->normalizeType($argValueType); } private function correctSelfType(Type $argValueType): Type @@ -93,23 +113,35 @@ private function correctSelfType(Type $argValueType): Type * @param array $staticTypesByArgumentPosition * @return array */ - private function unionToSingleType(array $staticTypesByArgumentPosition): array + private function unionToSingleType(array $staticTypesByArgumentPosition, bool $removeMixedArray = false): array { $staticTypeByArgumentPosition = []; + foreach ($staticTypesByArgumentPosition as $position => $staticTypes) { + if ($removeMixedArray) { + $staticTypes = array_filter( + $staticTypes, + fn (Type $type): bool => ! $this->isArrayMixedMixedType($type) + ); + } + $unionedType = $this->typeFactory->createMixedPassedOrUnionType($staticTypes); - // narrow parents to most child type - $unionedType = $this->narrowParentObjectTreeToSingleObjectChildType($unionedType); - $staticTypeByArgumentPosition[$position] = $unionedType; - } - if (count($staticTypeByArgumentPosition) !== 1) { - return $staticTypeByArgumentPosition; - } - if (! $staticTypeByArgumentPosition[0] instanceof NullType) { - return $staticTypeByArgumentPosition; + $staticTypeByArgumentPosition[$position] = $this->narrowParentObjectTreeToSingleObjectChildType( + $unionedType + ); + + if ($removeMixedArray && $staticTypeByArgumentPosition[$position] instanceof UnionType) { + foreach ($staticTypeByArgumentPosition[$position]->getTypes() as $subType) { + if ($subType instanceof ArrayType && $this->isArrayMixedMixedType($subType)) { + $staticTypeByArgumentPosition[$position] = new MixedType(); + continue 2; + } + } + } } - return [new MixedType()]; + + return $staticTypeByArgumentPosition; } private function narrowParentObjectTreeToSingleObjectChildType(Type $type): Type @@ -122,11 +154,10 @@ private function narrowParentObjectTreeToSingleObjectChildType(Type $type): Type return $type; } - /** @var TypeWithClassName $firstUnionedType */ $firstUnionedType = $type->getTypes()[0]; - foreach ($type->getTypes() as $unionedType) { - if (! $unionedType instanceof TypeWithClassName) { + $className = ClassNameFromObjectTypeResolver::resolve($unionedType); + if ($className === null) { return $type; } @@ -141,11 +172,64 @@ private function narrowParentObjectTreeToSingleObjectChildType(Type $type): Type private function isTypeWithClassNameOnly(UnionType $unionType): bool { foreach ($unionType->getTypes() as $unionedType) { - if (! $unionedType instanceof TypeWithClassName) { + $className = ClassNameFromObjectTypeResolver::resolve($unionedType); + if ($className === null) { return false; } } return true; } + + private function normalizeType(Type $argValueType): MixedType|ObjectType|Type + { + // "self" in another object is not correct, this make it independent + $argValueType = $this->correctSelfType($argValueType); + + if (! $argValueType instanceof ObjectType) { + return $argValueType; + } + + // fix false positive generic type on string + if (! $this->reflectionProvider->hasClass($argValueType->getClassName())) { + return new MixedType(); + } + + return $argValueType; + } + + /** + * There is first class callable usage, or argument unpack, or named expr + * simply returns array marks as unknown as can be anything and in any position + */ + private function shouldSkipArg(Arg|VariadicPlaceholder $arg): bool + { + if ($arg instanceof VariadicPlaceholder) { + return true; + } + + return $arg->unpack; + } + + private function isEmptyArray(Expr $expr): bool + { + if (! $expr instanceof Array_) { + return false; + } + + return $expr->items === []; + } + + private function isArrayMixedMixedType(Type $type): bool + { + if (! $type instanceof ArrayType && ! $type instanceof ConstantArrayType) { + return false; + } + + if (! $type->getItemType() instanceof MixedType && ! $type->getItemType() instanceof NeverType) { + return false; + } + + return $type->getKeyType() instanceof MixedType || $type->getKeyType() instanceof NeverType; + } } diff --git a/rules/TypeDeclaration/NodeAnalyzer/CallerParamMatcher.php b/rules/TypeDeclaration/NodeAnalyzer/CallerParamMatcher.php index 12b438baa22..d51f69eaa78 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/CallerParamMatcher.php +++ b/rules/TypeDeclaration/NodeAnalyzer/CallerParamMatcher.php @@ -4,11 +4,16 @@ namespace Rector\TypeDeclaration\NodeAnalyzer; +use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\ComplexType; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; +use PhpParser\Node\IntersectionType; use PhpParser\Node\Name; use PhpParser\Node\NullableType; use PhpParser\Node\Param; @@ -16,43 +21,68 @@ use PhpParser\Node\UnionType; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\Core\PhpParser\AstResolver; +use PHPStan\Type\MixedType; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\NodeTypeResolver\TypeComparator\TypeComparator; +use Rector\PhpParser\AstResolver; +use Rector\StaticTypeMapper\StaticTypeMapper; -final class CallerParamMatcher +final readonly class CallerParamMatcher { public function __construct( private NodeNameResolver $nodeNameResolver, - private AstResolver $astResolver + private AstResolver $astResolver, + private StaticTypeMapper $staticTypeMapper, + private TypeComparator $typeComparator ) { } public function matchCallParamType( - StaticCall | MethodCall | FuncCall $call, Param $param, - Scope $scope - ): null | Identifier | Name | NullableType | UnionType { - $callParam = $this->matchCallParam($call, $param, $scope); - if (! $callParam instanceof Param) { + Param $callParam + ): null | Identifier | Name | NullableType | UnionType | ComplexType { + if (! $callParam->type instanceof Node) { return null; } - return $callParam->type; - } + if (! $param->default instanceof Expr && ! $callParam->default instanceof Expr) { + // skip as mixed is not helpful and possibly requires more precise change elsewhere + if ($this->isCallParamMixed($callParam->type)) { + return null; + } - public function matchCallParam(StaticCall | MethodCall | FuncCall $call, Param $param, Scope $scope): ?Param - { - $callArgPosition = $this->matchCallArgPosition($call, $param); - if ($callArgPosition === null) { + return $callParam->type; + } + + $default = $param->default ?? $callParam->default; + if (! $default instanceof Expr) { return null; } - $classMethodOrFunction = $this->astResolver->resolveClassMethodOrFunctionFromCall($call, $scope); - if ($classMethodOrFunction === null) { + $callParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($callParam->type); + $defaultType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($default); + + if ($this->typeComparator->areTypesEqual($callParamType, $defaultType)) { + return $callParam->type; + } + + if ($this->typeComparator->isSubtype($defaultType, $callParamType)) { + return $callParam->type; + } + + if (! $defaultType->isNull()->yes()) { return null; } - return $classMethodOrFunction->params[$callArgPosition] ?? null; + if ($callParam->type instanceof Name || $callParam->type instanceof Identifier) { + return new NullableType($callParam->type); + } + + if ($callParam->type instanceof IntersectionType || $callParam->type instanceof UnionType) { + return new UnionType([...$callParam->type->types, new Identifier('null')]); + } + + return null; } public function matchParentParam(StaticCall $parentStaticCall, Param $param, Scope $scope): ?Param @@ -71,11 +101,30 @@ public function matchParentParam(StaticCall $parentStaticCall, Param $param, Sco return $this->resolveParentMethodParam($scope, $methodName, $parentStaticCallArgPosition); } + public function matchCallParam(StaticCall | MethodCall | FuncCall $call, Param $param): ?Param + { + $callArgPosition = $this->matchCallArgPosition($call, $param); + if ($callArgPosition === null) { + return null; + } + + $classMethodOrFunction = $this->astResolver->resolveClassMethodOrFunctionFromCall($call); + if ($classMethodOrFunction === null) { + return null; + } + + return $classMethodOrFunction->params[$callArgPosition] ?? null; + } + private function matchCallArgPosition(StaticCall | MethodCall | FuncCall $call, Param $param): int | null { $paramName = $this->nodeNameResolver->getName($param); foreach ($call->args as $argPosition => $arg) { + if (! $arg instanceof Arg) { + continue; + } + if (! $arg->value instanceof Variable) { continue; } @@ -112,4 +161,10 @@ private function resolveParentMethodParam(Scope $scope, string $methodName, int return null; } + + private function isCallParamMixed(Node $node): bool + { + $callParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($node); + return $callParamType instanceof MixedType; + } } diff --git a/rules/TypeDeclaration/NodeAnalyzer/ClassMethodAndPropertyAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/ClassMethodAndPropertyAnalyzer.php index 1fee9804149..5ccea80d790 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/ClassMethodAndPropertyAnalyzer.php +++ b/rules/TypeDeclaration/NodeAnalyzer/ClassMethodAndPropertyAnalyzer.php @@ -4,22 +4,24 @@ namespace Rector\TypeDeclaration\NodeAnalyzer; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; use Rector\NodeNameResolver\NodeNameResolver; -final class ClassMethodAndPropertyAnalyzer +final readonly class ClassMethodAndPropertyAnalyzer { public function __construct( private NodeNameResolver $nodeNameResolver ) { } - public function hasClassMethodOnlyStatementReturnOfPropertyFetch( - ClassMethod $classMethod, - string $propertyName - ): bool { + public function hasPropertyFetchReturn(ClassMethod $classMethod, string $propertyName): bool + { $stmts = (array) $classMethod->stmts; if (count($stmts) !== 1) { return false; @@ -30,7 +32,6 @@ public function hasClassMethodOnlyStatementReturnOfPropertyFetch( return false; } - /** @var Return_ $return */ $return = $onlyClassMethodStmt; if (! $return->expr instanceof PropertyFetch) { @@ -39,4 +40,71 @@ public function hasClassMethodOnlyStatementReturnOfPropertyFetch( return $this->nodeNameResolver->isName($return->expr, $propertyName); } + + public function hasOnlyPropertyAssign(ClassMethod $classMethod, string $propertyName): bool + { + $stmts = (array) $classMethod->stmts; + if (count($stmts) !== 1) { + return false; + } + + $onlyClassMethodStmt = $stmts[0]; + return $this->isLocalPropertyVariableAssign($onlyClassMethodStmt, $propertyName); + } + + public function hasPropertyAssignWithReturnThis(ClassMethod $classMethod): bool + { + $stmts = (array) $classMethod->stmts; + if (count($stmts) !== 2) { + return false; + } + + $possibleAssignStmt = $stmts[0]; + $possibleReturnThis = $stmts[1]; + + if (! $this->isLocalPropertyVariableAssign($possibleAssignStmt, null)) { + return false; + } + + if (! $possibleReturnThis instanceof Return_) { + return false; + + } + + $returnExpr = $possibleReturnThis->expr; + if (! $returnExpr instanceof Variable) { + return false; + } + + return $this->nodeNameResolver->isName($returnExpr, 'this'); + } + + private function isLocalPropertyVariableAssign(Stmt $onlyClassMethodStmt, ?string $propertyName): bool + { + if (! $onlyClassMethodStmt instanceof Expression) { + return false; + } + + if (! $onlyClassMethodStmt->expr instanceof Assign) { + return false; + } + + $assign = $onlyClassMethodStmt->expr; + + $assignVar = $assign->var; + if (! $assignVar instanceof PropertyFetch) { + return false; + } + + $propertyFetch = $assignVar; + if (! $this->nodeNameResolver->isName($propertyFetch->var, 'this')) { + return false; + } + + if ($propertyName) { + return $this->nodeNameResolver->isName($propertyFetch->name, $propertyName); + } + + return true; + } } diff --git a/rules/TypeDeclaration/NodeAnalyzer/ClassMethodParamTypeCompleter.php b/rules/TypeDeclaration/NodeAnalyzer/ClassMethodParamTypeCompleter.php index 0272c1e6c9d..16092575b30 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/ClassMethodParamTypeCompleter.php +++ b/rules/TypeDeclaration/NodeAnalyzer/ClassMethodParamTypeCompleter.php @@ -5,45 +5,61 @@ namespace Rector\TypeDeclaration\NodeAnalyzer; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; -use PHPStan\Type\CallableType; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; +use PHPStan\Type\UnionType; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\VendorLocker\NodeVendorLocker\ClassMethodParamVendorLockResolver; -final class ClassMethodParamTypeCompleter +final readonly class ClassMethodParamTypeCompleter { public function __construct( private StaticTypeMapper $staticTypeMapper, - private ClassMethodParamVendorLockResolver $classMethodParamVendorLockResolver + private ClassMethodParamVendorLockResolver $classMethodParamVendorLockResolver, ) { } /** * @param array $classParameterTypes */ - public function complete(ClassMethod $classMethod, array $classParameterTypes): ?ClassMethod + public function complete(ClassMethod $classMethod, array $classParameterTypes, int $maxUnionTypes): ?ClassMethod { $hasChanged = false; foreach ($classParameterTypes as $position => $argumentStaticType) { - if ($this->shouldSkipArgumentStaticType($classMethod, $argumentStaticType, $position)) { + /** @var Type $argumentStaticType */ + if ($this->shouldSkipArgumentStaticType($classMethod, $argumentStaticType, $position, $maxUnionTypes)) { continue; } $phpParserTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( $argumentStaticType, - TypeKind::PARAM() + TypeKind::PARAM ); + if (! $phpParserTypeNode instanceof Node) { continue; } + // check default override + $param = $classMethod->params[$position]; + + if (! $this->isAcceptedByDefault($param, $argumentStaticType)) { + continue; + } + + // skip if param type already filled + if ($param->type instanceof Node) { + continue; + } + // update parameter - $classMethod->params[$position]->type = $phpParserTypeNode; + $param->type = $phpParserTypeNode; $hasChanged = true; } @@ -57,12 +73,18 @@ public function complete(ClassMethod $classMethod, array $classParameterTypes): private function shouldSkipArgumentStaticType( ClassMethod $classMethod, Type $argumentStaticType, - int $position + int $position, + int $maxUnionTypes ): bool { if ($argumentStaticType instanceof MixedType) { return true; } + // skip mixed in union type + if ($argumentStaticType instanceof UnionType && $argumentStaticType->isSuperTypeOf(new MixedType())->yes()) { + return true; + } + if (! isset($classMethod->params[$position])) { return true; } @@ -76,23 +98,38 @@ private function shouldSkipArgumentStaticType( return false; } - $parameterStaticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($parameter->type); + $currentParameterStaticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($parameter->type); + if ($this->isClosureAndCallableType($currentParameterStaticType, $argumentStaticType)) { + return true; + } + + // too many union types + if ($this->isTooDetailedUnionType($currentParameterStaticType, $argumentStaticType, $maxUnionTypes)) { + return true; + } + + // current type already accepts the one added + if ($currentParameterStaticType->accepts($argumentStaticType, true)->yes()) { + return true; + } - if ($this->isClosureAndCallableType($parameterStaticType, $argumentStaticType)) { + // avoid overriding more precise type + if ($argumentStaticType->isSuperTypeOf($currentParameterStaticType)->yes()) { return true; } // already completed → skip - return $parameterStaticType->equals($argumentStaticType); + return $currentParameterStaticType->equals($argumentStaticType); } private function isClosureAndCallableType(Type $parameterStaticType, Type $argumentStaticType): bool { - if ($parameterStaticType instanceof CallableType && $this->isClosureObjectType($argumentStaticType)) { + if ($parameterStaticType->isCallable()->yes() && $this->isClosureObjectType($argumentStaticType)) { return true; } - return $argumentStaticType instanceof CallableType && $this->isClosureObjectType($parameterStaticType); + return $argumentStaticType->isCallable() + ->yes() && $this->isClosureObjectType($parameterStaticType); } private function isClosureObjectType(Type $type): bool @@ -103,4 +140,29 @@ private function isClosureObjectType(Type $type): bool return $type->getClassName() === 'Closure'; } + + private function isTooDetailedUnionType(Type $currentType, Type $newType, int $maxUnionTypes): bool + { + if ($currentType instanceof MixedType) { + return false; + } + + if (! $newType instanceof UnionType) { + return false; + } + + return count($newType->getTypes()) > $maxUnionTypes; + } + + private function isAcceptedByDefault(Param $param, Type $argumentStaticType): bool + { + if (! $param->default instanceof Expr) { + return true; + } + + $defaultExpr = $param->default; + $defaultStaticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($defaultExpr); + return $argumentStaticType->accepts($defaultStaticType, false) + ->yes(); + } } diff --git a/rules/TypeDeclaration/NodeAnalyzer/DeclareStrictTypeFinder.php b/rules/TypeDeclaration/NodeAnalyzer/DeclareStrictTypeFinder.php new file mode 100644 index 00000000000..2d03733e272 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/DeclareStrictTypeFinder.php @@ -0,0 +1,30 @@ +stmts as $stmt) { + if (! $stmt instanceof Declare_) { + continue; + } + + foreach ($stmt->declares as $declare) { + if ($declare->key->toString() === 'strict_types') { + return true; + } + } + } + + return false; + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/JMSTypeAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/JMSTypeAnalyzer.php new file mode 100644 index 00000000000..fdeee4f3777 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/JMSTypeAnalyzer.php @@ -0,0 +1,74 @@ +getProperties() as $property) { + if ($property->type instanceof Node) { + continue; + } + + if ($this->attributeFinder->hasAttributeByClasses($property, [ClassName::JMS_TYPE])) { + return true; + } + } + + return false; + } + + public function hasPropertyJMSTypeAttribute(Property $property): bool + { + if (! $this->phpAttributeAnalyzer->hasPhpAttribute($property, ClassName::JMS_TYPE)) { + return false; + } + + // most likely collection, not sole type + return ! $this->phpAttributeAnalyzer->hasPhpAttributes( + $property, + array_merge(CollectionMapping::TO_MANY_CLASSES, CollectionMapping::TO_ONE_CLASSES) + ); + } + + public function resolveTypeAttributeValue(Property $property): ?string + { + $jmsTypeAttribute = $this->attributeFinder->findAttributeByClass($property, ClassName::JMS_TYPE); + if (! $jmsTypeAttribute instanceof Attribute) { + return null; + } + + $typeValue = $this->valueResolver->getValue($jmsTypeAttribute->args[0]->value); + if (! is_string($typeValue)) { + return null; + } + + if (StringUtils::isMatch($typeValue, '#DateTime\<(.*?)\>#')) { + // special case for DateTime, which is not a scalar type + return 'DateTime'; + } + + return $typeValue; + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/NeverFuncCallAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/NeverFuncCallAnalyzer.php new file mode 100644 index 00000000000..0ca20bab991 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/NeverFuncCallAnalyzer.php @@ -0,0 +1,49 @@ +stmts as $stmt) { + if ($this->isWithNeverTypeExpr($stmt)) { + return true; + } + } + + return false; + } + + public function isWithNeverTypeExpr(Stmt $stmt, bool $withNativeNeverType = true): bool + { + if ($stmt instanceof Expression) { + $stmt = $stmt->expr; + } + + if ($stmt instanceof Stmt) { + return false; + } + + $stmtType = $withNativeNeverType + ? $this->nodeTypeResolver->getNativeType($stmt) + : $this->nodeTypeResolver->getType($stmt); + + return $stmtType instanceof NeverType; + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/ParamAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/ParamAnalyzer.php new file mode 100644 index 00000000000..4bfa562c01d --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/ParamAnalyzer.php @@ -0,0 +1,31 @@ +getParams() as $param) { + $paramName = $this->nodeNameResolver->getName($param); + if ('$' . $paramName !== $desiredParamName) { + continue; + } + + return $param; + } + + return null; + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/ReturnAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/ReturnAnalyzer.php new file mode 100644 index 00000000000..246af63b648 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/ReturnAnalyzer.php @@ -0,0 +1,44 @@ +stmts === null) { + return false; + } + + // void or combined with yield/yield from + if ($returns === []) { + return false; + } + + // possible void + foreach ($returns as $return) { + if (! $return->expr instanceof Expr) { + return false; + } + } + + // possible silent void + return ! $this->silentVoidResolver->hasSilentVoid($functionLike); + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/ReturnFilter/ExclusiveNativeCallLikeReturnMatcher.php b/rules/TypeDeclaration/NodeAnalyzer/ReturnFilter/ExclusiveNativeCallLikeReturnMatcher.php new file mode 100644 index 00000000000..f9c44a93a38 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/ReturnFilter/ExclusiveNativeCallLikeReturnMatcher.php @@ -0,0 +1,64 @@ +|null + */ + public function match(array $returns): array|null + { + $callLikes = []; + + foreach ($returns as $return) { + // we need exact expr return + $returnExpr = $return->expr; + if (! $returnExpr instanceof StaticCall && ! $returnExpr instanceof MethodCall && ! $returnExpr instanceof FuncCall) { + return null; + } + + $functionLikeReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($returnExpr); + + if (! $functionLikeReflection instanceof FunctionReflection && ! $functionLikeReflection instanceof MethodReflection) { + return null; + } + + // is native func call? + if (! $this->isNativeCallLike($functionLikeReflection)) { + return null; + } + + $callLikes[] = $returnExpr; + } + + return $callLikes; + } + + private function isNativeCallLike(MethodReflection|FunctionReflection $functionLikeReflection): bool + { + if ($functionLikeReflection instanceof FunctionReflection) { + return $functionLikeReflection->isBuiltin(); + } + + // is native method call? + $classReflection = $functionLikeReflection->getDeclaringClass(); + return $classReflection->isBuiltin(); + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/ReturnStrictTypeAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/ReturnStrictTypeAnalyzer.php deleted file mode 100644 index 1d63ecb7d7f..00000000000 --- a/rules/TypeDeclaration/NodeAnalyzer/ReturnStrictTypeAnalyzer.php +++ /dev/null @@ -1,75 +0,0 @@ - - */ - public function collectStrictReturnTypes(array $returns): array - { - $returnedStrictTypeNodes = []; - - foreach ($returns as $return) { - if ($return->expr === null) { - return []; - } - - $returnedExpr = $return->expr; - - if ($returnedExpr instanceof MethodCall || $returnedExpr instanceof StaticCall || $returnedExpr instanceof FuncCall) { - $returnNode = $this->resolveMethodCallReturnNode($returnedExpr); - } else { - return []; - } - - if (! $returnNode instanceof Node) { - return []; - } - - $returnedStrictTypeNodes[] = $returnNode; - } - - return $this->typeNodeUnwrapper->uniquateNodes($returnedStrictTypeNodes); - } - - private function resolveMethodCallReturnNode(MethodCall | StaticCall | FuncCall $call): ?Node - { - $methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($call); - if ($methodReflection === null) { - return null; - } - - $parametersAcceptor = $methodReflection->getVariants()[0]; - $returnType = $parametersAcceptor->getReturnType(); - if ($returnType instanceof MixedType) { - return null; - } - - return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN()); - } -} diff --git a/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictNativeFunctionReturnTypeAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictNativeFunctionReturnTypeAnalyzer.php new file mode 100644 index 00000000000..9494de33cec --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictNativeFunctionReturnTypeAnalyzer.php @@ -0,0 +1,39 @@ +stmts === null) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($functionLike); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($functionLike, $returns)) { + return null; + } + + return $this->exclusiveNativeCallLikeReturnMatcher->match($returns); + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictReturnNewAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictReturnNewAnalyzer.php new file mode 100644 index 00000000000..1d8166cbf07 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictReturnNewAnalyzer.php @@ -0,0 +1,149 @@ +stmts === null) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($functionLike); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($functionLike, $returns)) { + return null; + } + + // in case of more returns, we need to check if they all return the same variable + + $createdVariablesToTypes = $this->resolveCreatedVariablesToTypes($functionLike); + + $alwaysReturnedClassNames = []; + + foreach ($returns as $return) { + // exact one return of variable + if (! $return->expr instanceof Variable) { + return null; + } + + $returnType = $this->nodeTypeResolver->getNativeType($return->expr); + if ($returnType instanceof ObjectWithoutClassType) { + $alwaysReturnedClassNames[] = 'object'; + continue; + } + + if (! $returnType instanceof ObjectType) { + return null; + } + + $returnedVariableName = $this->nodeNameResolver->getName($return->expr); + + $className = $createdVariablesToTypes[$returnedVariableName] ?? null; + if (! is_string($className)) { + return null; + } + + if ($returnType->getClassName() !== $className) { + return null; + } + + $alwaysReturnedClassNames[] = $className; + } + + $uniqueAlwaysReturnedClasses = array_unique($alwaysReturnedClassNames); + if (count($uniqueAlwaysReturnedClasses) !== 1) { + return null; + } + + return $uniqueAlwaysReturnedClasses[0]; + } + + /** + * @return array + */ + private function resolveCreatedVariablesToTypes(ClassMethod|Function_|Closure $functionLike): array + { + $createdVariablesToTypes = []; + + // what new is assigned to it? + foreach ((array) $functionLike->stmts as $stmt) { + $assignToVariable = $this->matchAssignToVariable($stmt); + if (! $assignToVariable instanceof AssignToVariable) { + continue; + } + + $assignedExpr = $assignToVariable->getAssignedExpr(); + $variableName = $assignToVariable->getVariableName(); + + if (! $assignedExpr instanceof New_) { + // possible variable override by another type! - unset it + if (isset($createdVariablesToTypes[$variableName])) { + unset($createdVariablesToTypes[$variableName]); + } + + continue; + } + + $className = $this->nodeNameResolver->getName($assignedExpr->class); + if (! is_string($className)) { + continue; + } + + $createdVariablesToTypes[$variableName] = $className; + } + + return $createdVariablesToTypes; + } + + private function matchAssignToVariable(Stmt $stmt): ?AssignToVariable + { + if (! $stmt instanceof Expression) { + return null; + } + + if (! $stmt->expr instanceof Assign) { + return null; + } + + $assign = $stmt->expr; + $assignedVar = $assign->var; + + if (! $assignedVar instanceof Variable) { + return null; + } + + $variableName = $this->nodeNameResolver->getName($assignedVar); + if (! is_string($variableName)) { + return null; + } + + return new AssignToVariable($variableName, $assign->expr); + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/StrictTypeSafetyChecker.php b/rules/TypeDeclaration/NodeAnalyzer/StrictTypeSafetyChecker.php new file mode 100644 index 00000000000..1a7e787db3f --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/StrictTypeSafetyChecker.php @@ -0,0 +1,209 @@ +betterNodeFinder->findInstanceOf($fileNode->stmts, CallLike::class); + foreach ($callLikes as $callLike) { + if (! $this->isCallLikeSafe($callLike)) { + return false; + } + } + + $attributes = $this->betterNodeFinder->findInstanceOf($fileNode->stmts, Attribute::class); + foreach ($attributes as $attribute) { + if (! $this->isAttributeSafe($attribute)) { + return false; + } + } + + $functionLikes = $this->betterNodeFinder->findInstanceOf($fileNode->stmts, FunctionLike::class); + foreach ($functionLikes as $functionLike) { + if (! $this->areFunctionReturnsTypeSafe($functionLike)) { + return false; + } + } + + $assigns = $this->betterNodeFinder->findInstanceOf($fileNode->stmts, Assign::class); + foreach ($assigns as $assign) { + if (! $this->isPropertyAssignSafe($assign)) { + return false; + } + } + + return true; + } + + private function isCallLikeSafe(CallLike $callLike): bool + { + if ($callLike->isFirstClassCallable()) { + return true; + } + + $reflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($callLike); + if (! $reflection instanceof FunctionReflection && ! $reflection instanceof MethodReflection) { + return false; + } + + $scope = ScopeFetcher::fetch($callLike); + $parameters = ParametersAcceptorSelectorVariantsWrapper::select($reflection, $callLike, $scope) + ->getParameters(); + + return $this->areArgsSafe($callLike->getArgs(), $parameters); + } + + private function isAttributeSafe(Attribute $attribute): bool + { + $reflection = $this->reflectionResolver->resolveConstructorReflectionFromAttribute($attribute); + if (! $reflection instanceof MethodReflection) { + return false; + } + + $parameters = ParametersAcceptorSelector::combineAcceptors($reflection->getVariants())->getParameters(); + + return $this->areArgsSafe($attribute->args, $parameters); + } + + /** + * @param Arg[] $args + * @param ParameterReflection[] $parameters + */ + private function areArgsSafe(array $args, array $parameters): bool + { + foreach ($args as $position => $arg) { + if ($arg->unpack) { + return false; + } + + $parameterReflection = null; + + if ($arg->name !== null) { + foreach ($parameters as $parameter) { + if ($parameter->getName() === $arg->name->name) { + $parameterReflection = $parameter; + break; + } + } + } elseif (isset($parameters[$position])) { + $parameterReflection = $parameters[$position]; + } else { + $lastParameter = end($parameters); + if ($lastParameter !== false && $lastParameter->isVariadic()) { + $parameterReflection = $lastParameter; + } + } + + if ($parameterReflection === null) { + return false; + } + + $parameterType = $parameterReflection->getType(); + $argType = $this->nodeTypeResolver->getNativeType($arg->value); + + if (! $this->isTypeSafeForStrictMode($parameterType, $argType)) { + return false; + } + } + + return true; + } + + private function areFunctionReturnsTypeSafe(FunctionLike $functionLike): bool + { + if ($functionLike->getReturnType() === null) { + return true; + } + + $declaredReturnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($functionLike->getReturnType()); + + if ( + $declaredReturnType instanceof MixedType + || $declaredReturnType instanceof NeverType + || $declaredReturnType->isVoid() + ->yes() + ) { + return true; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($functionLike); + + foreach ($returns as $return) { + if ($return->expr === null) { + continue; + } + + $returnExprType = $this->nodeTypeResolver->getNativeType($return->expr); + + if (! $this->isTypeSafeForStrictMode($declaredReturnType, $returnExprType)) { + return false; + } + } + + return true; + } + + private function isPropertyAssignSafe(Assign $assign): bool + { + if (! $assign->var instanceof PropertyFetch && ! $assign->var instanceof StaticPropertyFetch) { + return true; + } + + $propertyReflection = $this->reflectionResolver->resolvePropertyReflectionFromPropertyFetch($assign->var); + if (! $propertyReflection instanceof PhpPropertyReflection) { + return false; + } + + $propertyType = $propertyReflection->getNativeType(); + $assignedType = $this->nodeTypeResolver->getNativeType($assign->expr); + + return $this->isTypeSafeForStrictMode($propertyType, $assignedType); + } + + private function isTypeSafeForStrictMode(Type $declaredType, Type $valueType): bool + { + // need to be strict with mixed to avoid false positives + if ($valueType instanceof MixedType) { + $valueType = new StrictMixedType(); + } + + return $declaredType->accepts($valueType, strictTypes: true) + ->yes(); + } +} diff --git a/rules/TypeDeclaration/NodeAnalyzer/TypeNodeUnwrapper.php b/rules/TypeDeclaration/NodeAnalyzer/TypeNodeUnwrapper.php index ccb35c69a6f..6d39e5b324d 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/TypeNodeUnwrapper.php +++ b/rules/TypeDeclaration/NodeAnalyzer/TypeNodeUnwrapper.php @@ -6,12 +6,13 @@ use PhpParser\Node; use PhpParser\Node\Identifier; +use PhpParser\Node\IntersectionType; use PhpParser\Node\Name; use PhpParser\Node\NullableType; use PhpParser\Node\UnionType; -use Rector\Core\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Comparing\NodeComparator; -final class TypeNodeUnwrapper +final readonly class TypeNodeUnwrapper { public function __construct( private NodeComparator $nodeComparator @@ -19,7 +20,7 @@ public function __construct( } /** - * @param array $typeNodes + * @param array $typeNodes * @return array */ public function unwrapNullableUnionTypes(array $typeNodes): array @@ -28,10 +29,12 @@ public function unwrapNullableUnionTypes(array $typeNodes): array foreach ($typeNodes as $typeNode) { if ($typeNode instanceof UnionType) { - $unwrappedTypeNodes = array_merge($unwrappedTypeNodes, $typeNode->types); + $unwrappedTypeNodes = [...$unwrappedTypeNodes, ...$this->unwrapNullableUnionTypes($typeNode->types)]; } elseif ($typeNode instanceof NullableType) { $unwrappedTypeNodes[] = $typeNode->type; $unwrappedTypeNodes[] = new Identifier('null'); + } elseif ($typeNode instanceof IntersectionType) { + $unwrappedTypeNodes = [...$unwrappedTypeNodes, ...$this->unwrapNullableUnionTypes($typeNode->types)]; } else { $unwrappedTypeNodes[] = $typeNode; } @@ -41,8 +44,10 @@ public function unwrapNullableUnionTypes(array $typeNodes): array } /** - * @param Node[] $nodes - * @return Node[] + * @template TNode as Node + * + * @param TNode[] $nodes + * @return TNode[] */ public function uniquateNodes(array $nodes): array { diff --git a/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php b/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php new file mode 100644 index 00000000000..91f2f697a38 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php @@ -0,0 +1,106 @@ +expr]; + } else { + $stmts = (array) $functionLike->stmts; + } + + $funcCalls = $this->betterNodeFinder->findInstancesOfScoped($stmts, FuncCall::class); + $funcCalls = array_values( + array_filter($funcCalls, fn (FuncCall $funcCall): bool => $this->nodeNameResolver->isName( + $funcCall->name, + 'sprintf' + )) + ); + + if (count($funcCalls) !== 1) { + return false; + } + + $funcCall = $funcCalls[0]; + + if ($funcCall->isFirstClassCallable()) { + return false; + } + + $args = $funcCall->getArgs(); + if (count($args) < 2) { + return false; + } + + /** @var Arg $messageArg */ + $messageArg = array_shift($args); + + $messageValue = $this->valueResolver->getValue($messageArg->value); + if (! is_string($messageValue)) { + return false; + } + + // match all %s, %d types by position + $masks = Strings::match($messageValue, '#%[sd]#'); + + foreach ($args as $position => $arg) { + if (! $arg->value instanceof Variable) { + continue; + } + + if (! $this-> nodeNameResolver->isName($arg->value, $variableName)) { + continue; + } + + if (! isset($masks[$position])) { + continue; + } + + $knownMaskOnPosition = $masks[$position]; + if ($knownMaskOnPosition !== $mask) { + continue; + } + + $type = $this->nodeTypeResolver->getNativeType($arg->value); + if ($type instanceof MixedType && $type->getSubtractedType() instanceof UnionType) { + continue; + } + + return true; + } + + return false; + } +} diff --git a/rules/TypeDeclaration/NodeFactory/JMSTypePropertyTypeFactory.php b/rules/TypeDeclaration/NodeFactory/JMSTypePropertyTypeFactory.php new file mode 100644 index 00000000000..7a3b839277b --- /dev/null +++ b/rules/TypeDeclaration/NodeFactory/JMSTypePropertyTypeFactory.php @@ -0,0 +1,83 @@ +scalarStringToTypeMapper->mapScalarStringToType($typeValue); + if ($type instanceof MixedType) { + // fallback to object type + $type = new ObjectType($typeValue); + } + + $node = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PROPERTY); + + if ($node instanceof FullyQualified && ! $this->reflectionProvider->hasClass($node->toString())) { + return null; + } + + return $node; + } + + public function createScalarTypeNode(string $typeValue, Property $property): ?Node + { + if ($typeValue === 'float') { + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNode($property); + // fallback to string, as most likely string representation of float + if ($propertyPhpDocInfo instanceof PhpDocInfo && $propertyPhpDocInfo->getVarType() instanceof StringType) { + $this->varTagRemover->removeVarTag($property); + return new Identifier('string'); + } + } + + if ($typeValue === 'string') { + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNode($property); + // fallback to string, as most likely string representation of float + if ($propertyPhpDocInfo instanceof PhpDocInfo && $propertyPhpDocInfo->getVarType() instanceof FloatType) { + $this->varTagRemover->removeVarTag($property); + return new Identifier('float'); + } + } + + $type = $this->scalarStringToTypeMapper->mapScalarStringToType($typeValue); + if ($type instanceof MixedType) { + return null; + } + + return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PROPERTY); + } +} diff --git a/rules/TypeDeclaration/NodeManipulator/AddNeverReturnType.php b/rules/TypeDeclaration/NodeManipulator/AddNeverReturnType.php new file mode 100644 index 00000000000..7e1ec4759f4 --- /dev/null +++ b/rules/TypeDeclaration/NodeManipulator/AddNeverReturnType.php @@ -0,0 +1,101 @@ +shouldSkip($node, $scope)) { + return null; + } + + $node->returnType = new Identifier('never'); + + return $node; + } + + private function shouldSkip(ClassMethod | Function_ | Closure $node, Scope $scope): bool + { + // already has return type, and non-void + // it can be "never" return itself, or other return type + if ($node->returnType instanceof Node && ! $this->nodeNameResolver->isName($node->returnType, 'void')) { + return true; + } + + if ($this->hasReturnOrYields($node)) { + return true; + } + + if (! $this->hasNeverNodesOrNeverFuncCalls($node)) { + return true; + } + + if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $node, + $scope + )) { + return true; + } + + if (! $node->returnType instanceof Node) { + return false; + } + + // skip as most likely intentional + return ! $this->classModifierChecker->isInsideFinalClass($node) && $this->nodeNameResolver->isName( + $node->returnType, + 'void' + ); + } + + private function hasReturnOrYields(ClassMethod|Function_|Closure $node): bool + { + return $this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped( + $node, + [Return_::class, Yield_::class, YieldFrom::class, ...ControlStructure::CONDITIONAL_NODE_SCOPE_TYPES] + ); + } + + private function hasNeverNodesOrNeverFuncCalls(ClassMethod|Function_|Closure $node): bool + { + $hasNeverNodes = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped( + $node, + fn (Node $subNode): bool => $subNode instanceof Expression && $subNode->expr instanceof Throw_ + ); + if ($hasNeverNodes) { + return true; + } + + return $this->neverFuncCallAnalyzer->hasNeverFuncCall($node); + } +} diff --git a/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromCast.php b/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromCast.php new file mode 100644 index 00000000000..6bcbd520f22 --- /dev/null +++ b/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromCast.php @@ -0,0 +1,65 @@ +returnType instanceof Node) { + return null; + } + + if ($functionLike instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $functionLike, + $scope + )) { + return null; + } + + $hasNonCastReturn = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped( + $functionLike, + static fn (Node $subNode): bool => $subNode instanceof Return_ && ! $subNode->expr instanceof Cast + ); + + if ($hasNonCastReturn) { + return null; + } + + $returnType = $this->returnTypeInferer->inferFunctionLike($functionLike); + if ($returnType instanceof UnionType) { + return null; + } + + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + return null; + } + + $functionLike->returnType = $returnTypeNode; + return $functionLike; + } +} diff --git a/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromParam.php b/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromParam.php new file mode 100644 index 00000000000..bce482f80cd --- /dev/null +++ b/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromParam.php @@ -0,0 +1,173 @@ +stmts === null) { + return null; + } + + if ($this->shouldSkipNode($functionLike, $scope)) { + return null; + } + + $return = $this->findCurrentScopeReturn($functionLike->stmts); + if (! $return instanceof Return_ || ! $return->expr instanceof Expr) { + return null; + } + + $returnName = $this->nodeNameResolver->getName($return->expr); + $stmts = $functionLike->stmts; + + foreach ($functionLike->getParams() as $param) { + if (! $param->type instanceof Node) { + continue; + } + + if ($this->shouldSkipParam($param, $stmts)) { + continue; + } + + $paramName = $this->nodeNameResolver->getName($param); + if ($returnName !== $paramName) { + continue; + } + + $functionLike->returnType = $param->type; + return $functionLike; + } + + return null; + } + + /** + * @param Stmt[] $stmts + */ + private function findCurrentScopeReturn(array $stmts): ?Return_ + { + $return = null; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmts, static function (Node $node) use ( + &$return + ): ?int { + // skip scope nesting + if ($node instanceof Class_ || $node instanceof FunctionLike) { + $return = null; + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $node instanceof Return_) { + return null; + } + + if (! $node->expr instanceof Variable) { + $return = null; + return NodeVisitor::STOP_TRAVERSAL; + } + + $return = $node; + return null; + }); + + return $return; + } + + /** + * @param Stmt[] $stmts + */ + private function shouldSkipParam(Param $param, array $stmts): bool + { + $paramName = $this->nodeNameResolver->getName($param); + $isParamModified = false; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmts, function (Node $node) use ( + $paramName, + &$isParamModified + ): int|null { + // skip scope nesting + if ($node instanceof Class_ || $node instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($node instanceof AssignRef && $this->nodeNameResolver->isName($node->expr, $paramName)) { + $isParamModified = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + if (! $node instanceof Assign) { + return null; + } + + if (! $node->var instanceof Variable) { + return null; + } + + if (! $this->nodeNameResolver->isName($node->var, $paramName)) { + return null; + } + + $isParamModified = true; + return NodeVisitor::STOP_TRAVERSAL; + }); + + return $isParamModified; + } + + private function shouldSkipNode(ClassMethod|Function_ $functionLike, Scope $scope): bool + { + // type is already known, skip + if ($functionLike->returnType instanceof Node) { + return true; + } + + if ($functionLike instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $functionLike, + $scope + )) { + return true; + } + + $returnType = $this->returnTypeInferer->inferFunctionLike($functionLike); + if ($returnType instanceof MixedType) { + return true; + } + + $returnType = TypeCombinator::removeNull($returnType); + return $returnType instanceof UnionType; + } +} diff --git a/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromStrictNativeCall.php b/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromStrictNativeCall.php new file mode 100644 index 00000000000..7ed504bcb5d --- /dev/null +++ b/rules/TypeDeclaration/NodeManipulator/AddReturnTypeFromStrictNativeCall.php @@ -0,0 +1,75 @@ +returnType instanceof Node) { + return null; + } + + if ($functionLike instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $functionLike, + $scope + )) { + return null; + } + + $nativeCallLikes = $this->strictNativeFunctionReturnTypeAnalyzer->matchAlwaysReturnNativeCallLikes( + $functionLike + ); + if ($nativeCallLikes === null) { + return null; + } + + $callLikeTypes = []; + foreach ($nativeCallLikes as $nativeCallLike) { + $callLikeTypes[] = $this->nodeTypeResolver->getType($nativeCallLike); + } + + $returnType = $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($callLikeTypes); + if ($returnType instanceof MixedType) { + return null; + } + + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + return null; + } + + $functionLike->returnType = $returnTypeNode; + return $functionLike; + } +} diff --git a/rules/TypeDeclaration/NodeManipulator/AddUnionReturnType.php b/rules/TypeDeclaration/NodeManipulator/AddUnionReturnType.php new file mode 100644 index 00000000000..1a0014e94d3 --- /dev/null +++ b/rules/TypeDeclaration/NodeManipulator/AddUnionReturnType.php @@ -0,0 +1,69 @@ +stmts === null) { + return null; + } + + // type is already known + if ($node->returnType instanceof Node) { + return null; + } + + if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $node, + $scope + )) { + return null; + } + + $inferReturnType = $this->returnTypeInferer->inferFunctionLike($node); + if (! $inferReturnType instanceof UnionType) { + return null; + } + + $returnType = $this->unionTypeMapper->mapToPhpParserNode($inferReturnType, TypeKind::RETURN); + if (! $returnType instanceof Node) { + return null; + } + + // handled by another PHP 7.1 rule with broader scope + if ($returnType instanceof NullableType) { + return null; + } + + $node->returnType = $returnType; + return $node; + } +} diff --git a/rules/TypeDeclaration/NodeTypeAnalyzer/CallTypeAnalyzer.php b/rules/TypeDeclaration/NodeTypeAnalyzer/CallTypeAnalyzer.php deleted file mode 100644 index b2d0ef91f2e..00000000000 --- a/rules/TypeDeclaration/NodeTypeAnalyzer/CallTypeAnalyzer.php +++ /dev/null @@ -1,42 +0,0 @@ -reflectionResolver->resolveFunctionLikeReflectionFromCall($call); - if ($methodReflection === null) { - return []; - } - - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); - - $parameterTypes = []; - - /** @var ParameterReflection $parameterReflection */ - foreach ($parametersAcceptor->getParameters() as $parameterReflection) { - $parameterTypes[] = $parameterReflection->getType(); - } - - return $parameterTypes; - } -} diff --git a/rules/TypeDeclaration/NodeTypeAnalyzer/DetailedTypeAnalyzer.php b/rules/TypeDeclaration/NodeTypeAnalyzer/DetailedTypeAnalyzer.php index b90b93c3654..d44fa1ce1bc 100644 --- a/rules/TypeDeclaration/NodeTypeAnalyzer/DetailedTypeAnalyzer.php +++ b/rules/TypeDeclaration/NodeTypeAnalyzer/DetailedTypeAnalyzer.php @@ -13,9 +13,8 @@ final class DetailedTypeAnalyzer { /** * Use this constant to avoid overly detailed long-dragging union types across whole universe - * @var int */ - private const MAX_NUMBER_OF_TYPES = 3; + private const int MAX_NUMBER_OF_TYPES = 3; public function isTooDetailed(Type $type): bool { diff --git a/rules/TypeDeclaration/NodeTypeAnalyzer/PropertyTypeDecorator.php b/rules/TypeDeclaration/NodeTypeAnalyzer/PropertyTypeDecorator.php new file mode 100644 index 00000000000..3ca5b32bcf2 --- /dev/null +++ b/rules/TypeDeclaration/NodeTypeAnalyzer/PropertyTypeDecorator.php @@ -0,0 +1,84 @@ +phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES)) { + $property->type = $typeNode; + return; + } + + if ($changeVarTypeFallback) { + $this->phpDocTypeChanger->changeVarType($property, $phpDocInfo, $unionType); + } + + return; + } + + $property->type = $typeNode; + + $propertyProperty = $property->props[0]; + + // add null default + if (! $propertyProperty->default instanceof Expr) { + $propertyProperty->default = $this->nodeFactory->createNull(); + } + + // has array with defined type? add docs + if (! $this->isDocBlockRequired($unionType)) { + return; + } + + if (! $changeVarTypeFallback) { + return; + } + + $this->phpDocTypeChanger->changeVarType($property, $phpDocInfo, $unionType); + } + + private function isDocBlockRequired(UnionType $unionType): bool + { + foreach ($unionType->getTypes() as $unionedType) { + if ($unionedType->isArray()->yes()) { + $describedArray = $unionedType->describe(VerbosityLevel::value()); + if ($describedArray !== 'array') { + return true; + } + } + } + + return false; + } +} diff --git a/rules/TypeDeclaration/NodeTypeAnalyzer/TraitTypeAnalyzer.php b/rules/TypeDeclaration/NodeTypeAnalyzer/TraitTypeAnalyzer.php deleted file mode 100644 index 5728248747a..00000000000 --- a/rules/TypeDeclaration/NodeTypeAnalyzer/TraitTypeAnalyzer.php +++ /dev/null @@ -1,35 +0,0 @@ -nodeTypeResolver->getFullyQualifiedClassName($type); - - if (! $this->reflectionProvider->hasClass($fullyQualifiedName)) { - return false; - } - - $classReflection = $this->reflectionProvider->getClass($fullyQualifiedName); - return $classReflection->isTrait(); - } -} diff --git a/rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php b/rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php new file mode 100644 index 00000000000..10e30330fe7 --- /dev/null +++ b/rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php @@ -0,0 +1,287 @@ +getClassName(), '\\'); + if (str_starts_with($objectType->getClassName(), '\\')) { + return new FullyQualifiedObjectType($className); + } + + $uses = $this->useImportsResolver->resolve(); + + if (! $withPreslash) { + $aliasedObjectType = $this->matchAliasedObjectType($objectType, $uses); + if ($aliasedObjectType instanceof AliasedObjectType) { + return $aliasedObjectType; + } + + $shortenedObjectType = $this->matchShortenedObjectType($objectType, $uses); + if ($shortenedObjectType !== null) { + return $shortenedObjectType; + } + } + + if ($this->reflectionProvider->hasClass($className)) { + return new FullyQualifiedObjectType($className); + } + + // probably in same namespace + $namespaceName = null; + if ($scope instanceof Scope) { + $namespaceName = $scope->getNamespace(); + if ($namespaceName !== null) { + $newClassName = $namespaceName . '\\' . $className; + if ($this->reflectionProvider->hasClass($newClassName)) { + return new FullyQualifiedObjectType($newClassName); + } + } + + $classReflection = $scope->getClassReflection(); + if ($classReflection instanceof ClassReflection) { + $templateTags = $classReflection->getTemplateTags(); + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($node); + $templateTypeScope = $nameScope->getTemplateTypeScope(); + + if (! $templateTypeScope instanceof TemplateTypeScope) { + // invalid type + return $this->resolveNamespacedNonExistingObjectType($namespaceName, $className, $withPreslash); + } + + $currentTemplateTag = $templateTags[$className] ?? null; + if ($currentTemplateTag === null) { + // invalid type + return $this->resolveNamespacedNonExistingObjectType($namespaceName, $className, $withPreslash); + } + + return TemplateTypeFactory::create( + $templateTypeScope, + $currentTemplateTag->getName(), + $currentTemplateTag->getBound(), + $currentTemplateTag->getVariance() + ); + } + } + + // invalid type + return $this->resolveNamespacedNonExistingObjectType($namespaceName, $className, $withPreslash); + } + + private function resolveNamespacedNonExistingObjectType( + ?string $namespacedName, + string $className, + bool $withPreslash + ): NonExistingObjectType { + if ($namespacedName === null) { + return new NonExistingObjectType($className); + } + + if ($withPreslash) { + return new NonExistingObjectType($className); + } + + if (str_contains($className, '\\')) { + return new NonExistingObjectType($className); + } + + return new NonExistingObjectType($namespacedName . '\\' . $className); + } + + /** + * @param array $uses + */ + private function matchAliasedObjectType(ObjectType $objectType, array $uses): ?AliasedObjectType + { + if ($uses === []) { + return null; + } + + $className = $objectType->getClassName(); + + foreach ($uses as $use) { + $prefix = $this->useImportsResolver->resolvePrefix($use); + foreach ($use->uses as $useUse) { + if (! $useUse->alias instanceof Identifier) { + continue; + } + + $useName = $prefix . $useUse->name->toString(); + $alias = $useUse->alias->toString(); + $fullyQualifiedName = $prefix . $useUse->name->toString(); + + $processAliasedObject = $this->processAliasedObject( + $alias, + $className, + $useName, + $fullyQualifiedName + ); + + if ($processAliasedObject instanceof AliasedObjectType) { + return $processAliasedObject; + } + } + } + + return null; + } + + private function processAliasedObject( + string $alias, + string $className, + string $useName, + string $fullyQualifiedName + ): ?AliasedObjectType { + // A. is alias in use statement matching this class alias + if ($alias === $className) { + return new AliasedObjectType($className, $fullyQualifiedName); + } + + // B. is aliased classes matching the class name + if ($useName === $className) { + return new AliasedObjectType($className, $fullyQualifiedName); + } + + if (str_starts_with($className, $alias . '\\')) { + return new AliasedObjectType($className, $fullyQualifiedName . ltrim($className, $alias)); + } + + return null; + } + + /** + * @param array $uses + */ + private function matchShortenedObjectType( + ObjectType $objectType, + array $uses + ): ShortenedObjectType|ShortenedGenericObjectType|null { + if ($uses === []) { + return null; + } + + foreach ($uses as $use) { + $prefix = $use instanceof GroupUse + ? $use->prefix . '\\' + : ''; + foreach ($use->uses as $useUse) { + if ($useUse->alias instanceof Identifier) { + continue; + } + + $partialNamespaceObjectType = $this->matchPartialNamespaceObjectType($prefix, $objectType, $useUse); + if ($partialNamespaceObjectType instanceof ShortenedObjectType) { + return $partialNamespaceObjectType; + } + + $partialNamespaceObjectType = $this->matchClassWithLastUseImportPart($prefix, $objectType, $useUse); + if ($partialNamespaceObjectType instanceof FullyQualifiedObjectType) { + // keep Generic items + if ($objectType instanceof GenericObjectType) { + return new ShortenedGenericObjectType( + $objectType->getClassName(), + $objectType->getTypes(), + $partialNamespaceObjectType->getClassName() + ); + } + + return $partialNamespaceObjectType->getShortNameType(); + } + + if ($partialNamespaceObjectType instanceof ShortenedObjectType) { + return $partialNamespaceObjectType; + } + } + } + + return null; + } + + private function matchPartialNamespaceObjectType( + string $prefix, + ObjectType $objectType, + UseItem $useItem + ): ?ShortenedObjectType { + if ($objectType->getClassName() === $useItem->name->getLast()) { + return new ShortenedObjectType($objectType->getClassName(), $prefix . $useItem->name->toString()); + } + + // partial namespace + if (! \str_starts_with($objectType->getClassName(), $useItem->name->getLast() . '\\')) { + return null; + } + + $classNameWithoutLastUsePart = Strings::after($objectType->getClassName(), '\\', 1); + + $connectedClassName = $prefix . $useItem->name->toString() . '\\' . $classNameWithoutLastUsePart; + + if ($objectType->getClassName() === $connectedClassName) { + return null; + } + + return new ShortenedObjectType($objectType->getClassName(), $connectedClassName); + } + + /** + * @return FullyQualifiedObjectType|ShortenedObjectType|null + */ + private function matchClassWithLastUseImportPart( + string $prefix, + ObjectType $objectType, + UseItem $useItem + ): ?ObjectType { + if ($useItem->name->getLast() !== $objectType->getClassName()) { + return null; + } + + if (! $this->reflectionProvider->hasClass($prefix . $useItem->name->toString())) { + return null; + } + + if ($objectType->getClassName() === $prefix . $useItem->name->toString()) { + return new FullyQualifiedObjectType($objectType->getClassName()); + } + + return new ShortenedObjectType($objectType->getClassName(), $prefix . $useItem->name->toString()); + } +} diff --git a/rules/TypeDeclaration/PHPStan/Type/ObjectTypeSpecifier.php b/rules/TypeDeclaration/PHPStan/Type/ObjectTypeSpecifier.php deleted file mode 100644 index 21ee2cec27a..00000000000 --- a/rules/TypeDeclaration/PHPStan/Type/ObjectTypeSpecifier.php +++ /dev/null @@ -1,225 +0,0 @@ -getAttribute(AttributeKey::USE_NODES); - if ($uses === null) { - return $objectType; - } - - $aliasedObjectType = $this->matchAliasedObjectType($node, $objectType); - if ($aliasedObjectType !== null) { - return $aliasedObjectType; - } - - $shortenedObjectType = $this->matchShortenedObjectType($node, $objectType); - if ($shortenedObjectType !== null) { - return $shortenedObjectType; - } - - $sameNamespacedObjectType = $this->matchSameNamespacedObjectType($node, $objectType); - if ($sameNamespacedObjectType !== null) { - return $sameNamespacedObjectType; - } - - $className = ltrim($objectType->getClassName(), '\\'); - - if ($this->reflectionProvider->hasClass($className)) { - return new FullyQualifiedObjectType($className); - } - - // invalid type - return new MixedType(); - } - - private function matchAliasedObjectType(Node $node, ObjectType $objectType): ?AliasedObjectType - { - /** @var Use_[]|null $uses */ - $uses = $node->getAttribute(AttributeKey::USE_NODES); - if ($uses === null) { - return null; - } - - $className = $objectType->getClassName(); - - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - - foreach ($uses as $use) { - foreach ($use->uses as $useUse) { - if ($useUse->alias === null) { - continue; - } - - $useName = $useUse->name->toString(); - $alias = $useUse->alias->toString(); - $fullyQualifiedName = $useUse->name->toString(); - - $processAliasedObject = $this->processAliasedObject( - $alias, - $className, - $useName, - $parent, - $fullyQualifiedName - ); - if ($processAliasedObject instanceof AliasedObjectType) { - return $processAliasedObject; - } - } - } - - return null; - } - - private function processAliasedObject( - string $alias, - string $className, - string $useName, - ?Node $parentNode, - string $fullyQualifiedName - ): ?AliasedObjectType { - // A. is alias in use statement matching this class alias - if ($alias === $className) { - return new AliasedObjectType($alias, $fullyQualifiedName); - } - - // B. is aliased classes matching the class name and parent node is MethodCall/StaticCall - if ($useName === $className && ($parentNode instanceof MethodCall || $parentNode instanceof StaticCall)) { - return new AliasedObjectType($useName, $fullyQualifiedName); - } - - // C. is aliased classes matching the class name - if ($useName === $className) { - return new AliasedObjectType($alias, $fullyQualifiedName); - } - - return null; - } - - private function matchShortenedObjectType(Node $node, ObjectType $objectType): ?ShortenedObjectType - { - /** @var Use_[]|null $uses */ - $uses = $node->getAttribute(AttributeKey::USE_NODES); - if ($uses === null) { - return null; - } - - foreach ($uses as $use) { - foreach ($use->uses as $useUse) { - if ($useUse->alias !== null) { - continue; - } - - $partialNamespaceObjectType = $this->matchPartialNamespaceObjectType($objectType, $useUse); - if ($partialNamespaceObjectType !== null) { - return $partialNamespaceObjectType; - } - - $partialNamespaceObjectType = $this->matchClassWithLastUseImportPart($objectType, $useUse); - if ($partialNamespaceObjectType instanceof FullyQualifiedObjectType) { - return $partialNamespaceObjectType->getShortNameType(); - } - - if ($partialNamespaceObjectType instanceof ShortenedObjectType) { - return $partialNamespaceObjectType; - } - } - } - - return null; - } - - private function matchSameNamespacedObjectType(Node $node, ObjectType $objectType): ?ObjectType - { - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - $namespaceName = $scope->getNamespace(); - if ($namespaceName === null) { - return null; - } - - $namespacedObject = $namespaceName . '\\' . ltrim($objectType->getClassName(), '\\'); - - if ($this->reflectionProvider->hasClass($namespacedObject)) { - return new FullyQualifiedObjectType($namespacedObject); - } - - return null; - } - - private function matchPartialNamespaceObjectType(ObjectType $objectType, UseUse $useUse): ?ShortenedObjectType - { - // partial namespace - if (! \str_starts_with($objectType->getClassName(), $useUse->name->getLast() . '\\')) { - return null; - } - - $classNameWithoutLastUsePart = Strings::after($objectType->getClassName(), '\\', 1); - - $connectedClassName = $useUse->name->toString() . '\\' . $classNameWithoutLastUsePart; - if (! $this->reflectionProvider->hasClass($connectedClassName)) { - return null; - } - - if ($objectType->getClassName() === $connectedClassName) { - return null; - } - - return new ShortenedObjectType($objectType->getClassName(), $connectedClassName); - } - - /** - * @return FullyQualifiedObjectType|ShortenedObjectType|null - */ - private function matchClassWithLastUseImportPart(ObjectType $objectType, UseUse $useUse): ?ObjectType - { - if ($useUse->name->getLast() !== $objectType->getClassName()) { - return null; - } - - if (! $this->reflectionProvider->hasClass($useUse->name->toString())) { - return null; - } - - if ($objectType->getClassName() === $useUse->name->toString()) { - return new FullyQualifiedObjectType($objectType->getClassName()); - } - - return new ShortenedObjectType($objectType->getClassName(), $useUse->name->toString()); - } -} diff --git a/rules/TypeDeclaration/PhpDoc/ShortClassExpander.php b/rules/TypeDeclaration/PhpDoc/ShortClassExpander.php deleted file mode 100644 index 688afcb8c51..00000000000 --- a/rules/TypeDeclaration/PhpDoc/ShortClassExpander.php +++ /dev/null @@ -1,63 +0,0 @@ -getCleanedUpTargetEntity($targetEntity); - if ($this->reflectionProvider->hasClass($targetEntity)) { - return $targetEntity; - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return $targetEntity; - } - - $namespacedTargetEntity = $scope->getNamespace() . '\\' . $targetEntity; - if ($this->reflectionProvider->hasClass($namespacedTargetEntity)) { - return $namespacedTargetEntity; - } - - $resolvedType = $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType( - $node, - new ObjectType($targetEntity) - ); - if ($resolvedType instanceof ShortenedObjectType) { - return $resolvedType->getFullyQualifiedName(); - } - - // probably tested class - return $targetEntity; - } - - private function getCleanedUpTargetEntity(string $targetEntity): string - { - return Strings::replace($targetEntity, self::CLASS_CONST_REGEX, ''); - } -} diff --git a/rules/TypeDeclaration/PhpDocParser/NonInformativeReturnTagRemover.php b/rules/TypeDeclaration/PhpDocParser/NonInformativeReturnTagRemover.php deleted file mode 100644 index 3c6572c2fb3..00000000000 --- a/rules/TypeDeclaration/PhpDocParser/NonInformativeReturnTagRemover.php +++ /dev/null @@ -1,239 +0,0 @@ -, string[]> - */ - private const USELESS_DOC_NAMES_BY_TYPE_CLASS = [ - IterableType::class => ['iterable'], - CallableType::class => ['callable'], - VoidType::class => ['void'], - ArrayType::class => ['array'], - SelfObjectType::class => ['self'], - ParentStaticType::class => ['parent'], - BooleanType::class => ['bool', 'boolean'], - ObjectWithoutClassType::class => ['object'], - ]; - - public function __construct( - private PhpDocInfoFactory $phpDocInfoFactory, - private TypeChecker $typeChecker - ) { - } - - public function removeReturnTagIfNotUseful(ClassMethod | Function_ $functionLike): void - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - - $returnTagValueNode = $phpDocInfo->getReturnTagValue(); - if (! $returnTagValueNode instanceof ReturnTagValueNode) { - return; - } - - // useful - if ($returnTagValueNode->description !== '') { - return; - } - - $returnType = $phpDocInfo->getReturnType(); - - // is bare type - if ($this->typeChecker->isInstanceOf($returnType, [FloatType::class, StringType::class, IntegerType::class])) { - $phpDocInfo->removeByType(ReturnTagValueNode::class); - return; - } - - $this->removeNonUniqueUselessDocNames($returnType, $returnTagValueNode, $phpDocInfo); - $this->removeShortObjectType($returnType, $returnTagValueNode, $phpDocInfo); - $this->removeNullableType($returnType, $returnTagValueNode, $phpDocInfo); - $this->removeFullyQualifiedObjectType($returnType, $returnTagValueNode, $phpDocInfo); - } - - private function removeNonUniqueUselessDocNames( - Type $returnType, - ReturnTagValueNode $returnTagValueNode, - PhpDocInfo $phpDocInfo - ): void { - foreach (self::USELESS_DOC_NAMES_BY_TYPE_CLASS as $typeClass => $uselessDocNames) { - if (! is_a($returnType, $typeClass, true)) { - continue; - } - - if (! $this->isIdentifierWithValues($returnTagValueNode->type, $uselessDocNames)) { - continue; - } - - $phpDocInfo->removeByType(ReturnTagValueNode::class); - return; - } - } - - private function removeShortObjectType( - Type $returnType, - ReturnTagValueNode $returnTagValueNode, - PhpDocInfo $phpDocInfo - ): void { - if (! $returnType instanceof ShortenedObjectType) { - return; - } - - if (! $this->isIdentifierWithValues($returnTagValueNode->type, [$returnType->getShortName()])) { - return; - } - - $phpDocInfo->removeByType(ReturnTagValueNode::class); - } - - private function removeNullableType( - Type $returnType, - ReturnTagValueNode $returnTagValueNode, - PhpDocInfo $phpDocInfo - ): void { - $nullabledReturnType = $this->matchNullabledType($returnType); - if (! $nullabledReturnType instanceof Type) { - return; - } - - $nullabledReturnTagValueNode = $this->matchNullabledReturnTagValueNode($returnTagValueNode); - if (! $nullabledReturnTagValueNode instanceof TypeNode) { - return; - } - - if (! $nullabledReturnType instanceof FullyQualifiedObjectType) { - return; - } - - if (! $nullabledReturnTagValueNode instanceof IdentifierTypeNode) { - return; - } - - if (! \str_ends_with($nullabledReturnType->getClassName(), $nullabledReturnTagValueNode->name)) { - return; - } - - $phpDocInfo->removeByType(ReturnTagValueNode::class); - } - - private function removeFullyQualifiedObjectType( - Type $returnType, - ReturnTagValueNode $returnTagValueNode, - PhpDocInfo $phpDocInfo - ): void { - if (! $returnType instanceof FullyQualifiedObjectType) { - return; - } - - if (! $returnTagValueNode->type instanceof IdentifierTypeNode) { - return; - } - - $className = $returnType->getClassName(); - $returnTagValueNodeType = (string) $returnTagValueNode->type; - - if ($this->isClassNameAndPartMatch($className, $returnTagValueNodeType)) { - $phpDocInfo->removeByType(ReturnTagValueNode::class); - } - } - - /** - * @param string[] $values - */ - private function isIdentifierWithValues(TypeNode $typeNode, array $values): bool - { - if (! $typeNode instanceof IdentifierTypeNode) { - return false; - } - - return in_array($typeNode->name, $values, true); - } - - private function matchNullabledType(Type $returnType): ?Type - { - if (! $returnType instanceof UnionType) { - return null; - } - - if (! $returnType->isSuperTypeOf(new NullType())->yes()) { - return null; - } - - if (count($returnType->getTypes()) !== 2) { - return null; - } - - foreach ($returnType->getTypes() as $unionedReturnType) { - if ($unionedReturnType instanceof NullType) { - continue; - } - - return $unionedReturnType; - } - - return null; - } - - private function matchNullabledReturnTagValueNode(ReturnTagValueNode $returnTagValueNode): ?TypeNode - { - if (! $returnTagValueNode->type instanceof UnionTypeNode) { - return null; - } - - if (count($returnTagValueNode->type->types) !== 2) { - return null; - } - - foreach ($returnTagValueNode->type->types as $unionedReturnTagValueNode) { - if ($this->isIdentifierWithValues($unionedReturnTagValueNode, ['null'])) { - continue; - } - - return $unionedReturnTagValueNode; - } - - return null; - } - - private function isClassNameAndPartMatch(string $className, string $returnTagValueNodeType): bool - { - if ($className === $returnTagValueNodeType) { - return true; - } - - if ('\\' . $className === $returnTagValueNodeType) { - return true; - } - - return \str_ends_with($className, '\\' . $returnTagValueNodeType); - } -} diff --git a/rules/TypeDeclaration/PhpDocParser/ParamPhpDocNodeFactory.php b/rules/TypeDeclaration/PhpDocParser/ParamPhpDocNodeFactory.php index 5252eddea70..8b79ed8ff03 100644 --- a/rules/TypeDeclaration/PhpDocParser/ParamPhpDocNodeFactory.php +++ b/rules/TypeDeclaration/PhpDocParser/ParamPhpDocNodeFactory.php @@ -5,24 +5,25 @@ namespace Rector\TypeDeclaration\PhpDocParser; use PhpParser\Node\Param; +use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; -use Rector\BetterPhpDocParser\ValueObject\PhpDoc\VariadicAwareParamTagValueNode; use Rector\NodeNameResolver\NodeNameResolver; -final class ParamPhpDocNodeFactory +final readonly class ParamPhpDocNodeFactory { public function __construct( private NodeNameResolver $nodeNameResolver ) { } - public function create(TypeNode $typeNode, Param $param): VariadicAwareParamTagValueNode + public function create(TypeNode $typeNode, Param $param): ParamTagValueNode { - return new VariadicAwareParamTagValueNode( + return new ParamTagValueNode( $typeNode, $param->variadic, '$' . $this->nodeNameResolver->getName($param), - '' + '', + false ); } } diff --git a/rules/TypeDeclaration/PhpParserTypeAnalyzer.php b/rules/TypeDeclaration/PhpParserTypeAnalyzer.php deleted file mode 100644 index 9858f527d0f..00000000000 --- a/rules/TypeDeclaration/PhpParserTypeAnalyzer.php +++ /dev/null @@ -1,55 +0,0 @@ -isUnionType($possibleSubtype, $possibleParentType)) { - return false; - } - - // possible - https://3v4l.org/ZuJCh - if ($possibleSubtype instanceof NullableType && ! $possibleParentType instanceof NullableType) { - return $this->isCovariantSubtypeOf($possibleSubtype->type, $possibleParentType); - } - - // not possible - https://3v4l.org/iNDTc - if (! $possibleSubtype instanceof NullableType && $possibleParentType instanceof NullableType) { - return false; - } - - $subtypeType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($possibleParentType); - $parentType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($possibleSubtype); - - return $parentType->isSuperTypeOf($subtypeType) - ->yes(); - } - - private function isUnionType(Node $possibleSubtype, Node $possibleParentType): bool - { - if ($possibleSubtype instanceof UnionType) { - return true; - } - - return $possibleParentType instanceof UnionType; - } -} diff --git a/rules/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector.php b/rules/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector.php new file mode 100644 index 00000000000..8ef89b529e6 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector.php @@ -0,0 +1,87 @@ + []; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +fn (): array => []; +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ArrowFunction::class]; + } + + /** + * @param ArrowFunction $node + */ + public function refactor(Node $node): ?Node + { + if ($node->returnType instanceof Node) { + return null; + } + + $type = $this->nodeTypeResolver->getNativeType($node->expr); + + // not valid to add explicit type in PHP + if ($type->isVoid()->yes()) { + return null; + } + + $docblockType = $this->getType($node->expr); + if ($type instanceof MixedType && $docblockType instanceof NullType) { + return null; + } + + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + return null; + } + + $node->returnType = $returnTypeNode; + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARROW_FUNCTION; + } +} diff --git a/rules/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector.php b/rules/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector.php new file mode 100644 index 00000000000..9778e9ed261 --- /dev/null +++ b/rules/TypeDeclaration/Rector/BooleanAnd/BinaryOpNullableToInstanceofRector.php @@ -0,0 +1,147 @@ +someMethod()) { + return 'yes'; + } + + return 'no'; +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +function someFunction(?SomeClass $someClass) +{ + if ($someClass instanceof SomeClass && $someClass->someMethod()) { + return 'yes'; + } + + return 'no'; +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [BooleanAnd::class, BooleanOr::class]; + } + + /** + * @param BooleanAnd|BooleanOr $node + */ + public function refactor(Node $node): ?Node + { + if ($node->left instanceof Assign || $node->right instanceof Assign) { + return null; + } + + if ($node instanceof BooleanOr) { + return $this->processNegationBooleanOr($node); + } + + return $this->processNullableInstance($node); + } + + private function processNullableInstance(BooleanAnd|BooleanOr $node): null|BooleanAnd|BooleanOr + { + $nullableObjectType = $this->nullableTypeAnalyzer->resolveNullableObjectType($node->left); + + $hasChanged = false; + if ($nullableObjectType instanceof ObjectType) { + $node->left = $this->createExprInstanceof($node->left, $nullableObjectType); + + $hasChanged = true; + } + + $nullableObjectType = $this->nullableTypeAnalyzer->resolveNullableObjectType($node->right); + + if ($nullableObjectType instanceof ObjectType) { + $node->right = $this->createExprInstanceof($node->right, $nullableObjectType); + + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function processNegationBooleanOr(BooleanOr $booleanOr): ?BooleanOr + { + $hasChanged = false; + if ($booleanOr->left instanceof BooleanNot) { + $nullableObjectType = $this->nullableTypeAnalyzer->resolveNullableObjectType($booleanOr->left->expr); + + if ($nullableObjectType instanceof ObjectType) { + $booleanOr->left->expr = $this->createExprInstanceof($booleanOr->left->expr, $nullableObjectType); + $hasChanged = true; + } + } + + if ($booleanOr->right instanceof BooleanNot) { + $nullableObjectType = $this->nullableTypeAnalyzer->resolveNullableObjectType($booleanOr->right->expr); + + if ($nullableObjectType instanceof ObjectType) { + $booleanOr->right->expr = $this->createExprInstanceof($booleanOr->right->expr, $nullableObjectType); + + $hasChanged = true; + } + } + + if ($hasChanged) { + return $booleanOr; + } + + /** @var BooleanOr|null $result */ + $result = $this->processNullableInstance($booleanOr); + return $result; + } + + private function createExprInstanceof(Expr $expr, ObjectType $objectType): Instanceof_ + { + $fullyQualified = new FullyQualified($objectType->getClassName()); + return new Instanceof_($expr, $fullyQualified); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector.php deleted file mode 100644 index 66936c97dc8..00000000000 --- a/rules/TypeDeclaration/Rector/ClassMethod/AddArrayParamDocTypeRector.php +++ /dev/null @@ -1,166 +0,0 @@ -values = $values; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var int[] - */ - private $values; - - /** - * @param int[] $values - */ - public function __construct(array $values) - { - $this->values = $values; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - if ($node->getParams() === []) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - foreach ($node->getParams() as $param) { - if ($this->shouldSkipParam($param)) { - continue; - } - - $paramType = $this->paramTypeInferer->inferParam($param); - if ($paramType instanceof MixedType) { - continue; - } - - if ($this->paramAnalyzer->isNullable($param) && ! $paramType instanceof UnionType) { - $paramType = new UnionType([$paramType, new NullType()]); - } - - $paramName = $this->getName($param); - - $this->phpDocTypeChanger->changeParamType($phpDocInfo, $paramType, $param, $paramName); - } - - if ($phpDocInfo->hasChanged()) { - $node->setAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED, true); - $this->paramTagRemover->removeParamTagsIfUseless($phpDocInfo, $node); - return $node; - } - - return null; - } - - private function shouldSkipParam(Param $param): bool - { - // type missing at all - if ($param->type === null) { - return true; - } - - // not an array type - $paramType = $this->nodeTypeResolver->resolve($param->type); - - // weird case for maybe interface - if ($paramType->isIterable()->maybe() && ($paramType instanceof ObjectType)) { - return true; - } - - $isArrayable = $paramType->isIterable() - ->yes() || $paramType->isArray() - ->yes() || ($paramType->isIterable()->maybe() || $paramType->isArray()->maybe()); - if (! $isArrayable) { - return true; - } - - return $this->isArrayExplicitMixed($paramType); - } - - private function isArrayExplicitMixed(Type $type): bool - { - if (! $type instanceof ArrayType) { - return false; - } - - $iterableValueType = $type->getIterableValueType(); - if (! $iterableValueType instanceof MixedType) { - return false; - } - - return $iterableValueType->isExplicitMixed(); - } -} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector.php deleted file mode 100644 index e61be3d5998..00000000000 --- a/rules/TypeDeclaration/Rector/ClassMethod/AddArrayReturnDocTypeRector.php +++ /dev/null @@ -1,264 +0,0 @@ -values; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var int[] - */ - private $values; - - /** - * @return int[] - */ - public function getValues(): array - { - return $this->values; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ?Node - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - if ($this->shouldSkip($node, $phpDocInfo)) { - return null; - } - - $inferredReturnType = $this->returnTypeInferer->inferFunctionLikeWithExcludedInferers( - $node, - [ReturnTypeDeclarationReturnTypeInferer::class] - ); - - $inferredReturnType = $this->normalizeTypeToRespectArrayScalarType->normalizeToArray( - $inferredReturnType, - $node->returnType - ); - - $currentReturnType = $phpDocInfo->getReturnType(); - - if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethodOldTypeWithNewType( - $currentReturnType, - $inferredReturnType - )) { - return null; - } - - if ($this->shouldSkipType($inferredReturnType, $currentReturnType, $node, $phpDocInfo)) { - return null; - } - - $this->phpDocTypeChanger->changeReturnType($phpDocInfo, $inferredReturnType); - - if ($phpDocInfo->hasChanged()) { - $node->setAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED, true); - $this->returnTagRemover->removeReturnTagIfUseless($phpDocInfo, $node); - return $node; - } - - return null; - } - - private function shouldSkip(ClassMethod $classMethod, PhpDocInfo $phpDocInfo): bool - { - if ($this->shouldSkipClassMethod($classMethod)) { - return true; - } - - if ($this->hasArrayShapeNode($classMethod)) { - return true; - } - - $currentPhpDocReturnType = $phpDocInfo->getReturnType(); - if ($currentPhpDocReturnType instanceof ArrayType && $currentPhpDocReturnType->getItemType() instanceof MixedType) { - return true; - } - - if ($this->hasInheritDoc($classMethod)) { - return true; - } - - return $currentPhpDocReturnType instanceof IterableType; - } - - /** - * @deprecated - * @todo merge to - * @see \Rector\TypeDeclaration\TypeAlreadyAddedChecker\ReturnTypeAlreadyAddedChecker - */ - private function shouldSkipType( - Type $newType, - Type $currentType, - ClassMethod $classMethod, - PhpDocInfo $phpDocInfo - ): bool { - if ($newType instanceof ArrayType && $this->shouldSkipArrayType($newType, $classMethod, $phpDocInfo)) { - return true; - } - - // not an array type - if ($newType instanceof VoidType) { - return true; - } - - if ($this->advancedArrayAnalyzer->isMoreSpecificArrayTypeOverride($newType, $classMethod, $phpDocInfo)) { - return true; - } - - if ($this->isGenericTypeToMixedTypeOverride($newType, $currentType)) { - return true; - } - - return $this->detailedTypeAnalyzer->isTooDetailed($newType); - } - - private function shouldSkipClassMethod(ClassMethod $classMethod): bool - { - if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($classMethod)) { - return true; - } - - if ($classMethod->returnType === null) { - return false; - } - - return ! $this->isNames($classMethod->returnType, ['array', 'iterable', 'Iterator']); - } - - private function hasArrayShapeNode(ClassMethod $classMethod): bool - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - - $returnTagValueNode = $phpDocInfo->getReturnTagValue(); - if (! $returnTagValueNode instanceof ReturnTagValueNode) { - return false; - } - - if ($returnTagValueNode->type instanceof GenericTypeNode) { - return true; - } - - if ($returnTagValueNode->type instanceof ArrayShapeNode) { - return true; - } - - if (! $returnTagValueNode->type instanceof ArrayTypeNode) { - return false; - } - - return $returnTagValueNode->type->type instanceof ArrayShapeNode; - } - - private function hasInheritDoc(ClassMethod $classMethod): bool - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - return $phpDocInfo->hasInheritDoc(); - } - - private function shouldSkipArrayType(ArrayType $arrayType, ClassMethod $classMethod, PhpDocInfo $phpDocInfo): bool - { - if ($this->advancedArrayAnalyzer->isNewAndCurrentTypeBothCallable($arrayType, $phpDocInfo)) { - return true; - } - - if ($this->advancedArrayAnalyzer->isClassStringArrayByStringArrayOverride($arrayType, $classMethod)) { - return true; - } - - return $this->advancedArrayAnalyzer->isMixedOfSpecificOverride($arrayType, $phpDocInfo); - } - - private function isGenericTypeToMixedTypeOverride(Type $newType, Type $currentType): bool - { - if ($newType instanceof GenericObjectType && $currentType instanceof MixedType) { - $types = $newType->getTypes(); - if ($types[0] instanceof MixedType && $types[1] instanceof ArrayType) { - return true; - } - } - - return false; - } -} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php index 5829fca4978..c7c3fa92aaf 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php @@ -5,89 +5,68 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\PhpParser\NodeFinder\LocalMethodCallFinder; -use Rector\Core\Rector\AbstractRector; +use Rector\PhpParser\NodeFinder\LocalMethodCallFinder; +use Rector\Rector\AbstractRector; use Rector\TypeDeclaration\NodeAnalyzer\CallTypesResolver; use Rector\TypeDeclaration\NodeAnalyzer\ClassMethodParamTypeCompleter; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; +use Rector\TypeDeclarationDocblocks\PrivateMethodFlagger; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://github.com/symplify/phpstan-rules/blob/master/docs/rules_overview.md#checktypehintcallertyperule - * * @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector\AddMethodCallBasedStrictParamTypeRectorTest */ -final class AddMethodCallBasedStrictParamTypeRector extends AbstractRector implements ConfigurableRectorInterface +final class AddMethodCallBasedStrictParamTypeRector extends AbstractRector { - /** - * @var string - */ - public const TRUST_DOC_BLOCKS = 'trust_doc_blocks'; - - private bool $shouldTrustDocBlocks = false; + private const int MAX_UNION_TYPES = 3; public function __construct( - private CallTypesResolver $callTypesResolver, - private ClassMethodParamTypeCompleter $classMethodParamTypeCompleter, - private LocalMethodCallFinder $localMethodCallFinder, + private readonly CallTypesResolver $callTypesResolver, + private readonly ClassMethodParamTypeCompleter $classMethodParamTypeCompleter, + private readonly LocalMethodCallFinder $localMethodCallFinder, + private readonly PrivateMethodFlagger $privateMethodFlagger ) { } public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Change param type to strict type of passed expression', [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - public function getById($id) - { - } -} - -class CallerClass + return new RuleDefinition( + 'Change private method param type to strict type, based on passed strict types', + [ + new CodeSample( + <<<'CODE_SAMPLE' +final class SomeClass { - public function run(SomeClass $someClass) + public function run(int $value) { - $someClass->getById($this->getId()); + $this->resolve($value); } - public function getId(): int + private function resolve($value) { - return 1000; } } CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function getById(int $id) - { - } -} - -class CallerClass + , + <<<'CODE_SAMPLE' +final class SomeClass { - public function run(SomeClass $someClass) + public function run(int $value) { - $someClass->getById($this->getId()); + $this->resolve($value); } - public function getId(): int + private function resolve(int $value) { - return 1000; } } CODE_SAMPLE - , - [ - self::TRUST_DOC_BLOCKS => false, - ] - ), - ]); + ), + + ] + ); } /** @@ -95,38 +74,47 @@ public function getId(): int */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($node->params === []) { - return null; - } + $hasChanged = false; - if (! $node->isPrivate()) { - return null; - } + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->params === []) { + continue; + } + + if (! $this->privateMethodFlagger->isClassMethodPrivate($node, $classMethod)) { + continue; + } - $methodCalls = $this->localMethodCallFinder->match($node); + if ($classMethod->isPublic()) { + continue; + } - if ($this->shouldTrustDocBlocks) { - $classMethodParameterTypes = $this->callTypesResolver->resolveWeakTypesFromCalls($methodCalls); - } else { + $methodCalls = $this->localMethodCallFinder->match($node, $classMethod); $classMethodParameterTypes = $this->callTypesResolver->resolveStrictTypesFromCalls($methodCalls); + + $classMethod = $this->classMethodParamTypeCompleter->complete( + $classMethod, + $classMethodParameterTypes, + self::MAX_UNION_TYPES + ); + + if ($classMethod instanceof ClassMethod) { + $hasChanged = true; + } } - return $this->classMethodParamTypeCompleter->complete($node, $classMethodParameterTypes); - } + if ($hasChanged) { + return $node; + } - /** - * @param array $configuration - */ - public function configure(array $configuration): void - { - $this->shouldTrustDocBlocks = $configuration[self::TRUST_DOC_BLOCKS] ?? false; + return null; } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector.php new file mode 100644 index 00000000000..1c3dafbb847 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector.php @@ -0,0 +1,273 @@ +value; + }); +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +/** + * @param stdClass[] $items + */ +function process(array $items): void +{ + array_walk($items, function (stdClass $item) { + echo $item->value; + }); +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): null|ClassMethod|Function_ + { + if ($node->params === []) { + return null; + } + + if ($node->stmts === null) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $variableNamesWithArrayType = $this->collectVariableNamesWithArrayType($node, $phpDocInfo); + + if ($variableNamesWithArrayType === []) { + return null; + } + + $paramsWithType = []; + $this->traverseNodesWithCallable( + $node->stmts, + function (Node $subNode) use ($variableNamesWithArrayType, $node, &$paramsWithType): null|int { + if ($subNode instanceof Class_ || $subNode instanceof Function_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $subNode instanceof FuncCall) { + return null; + } + + if (! $this->isNames($subNode, array_keys(NativeFuncCallPositions::ARRAY_AND_CALLBACK_POSITIONS))) { + return null; + } + + if ($subNode->isFirstClassCallable()) { + return null; + } + + $args = $subNode->getArgs(); + if (count($args) < 2) { + return null; + } + + $funcCallName = (string) $this->getName($subNode); + + $arrayArg = $subNode->getArg( + 'array', + NativeFuncCallPositions::ARRAY_AND_CALLBACK_POSITIONS[$funcCallName]['array'] + ); + if (! $arrayArg instanceof Arg) { + return null; + } + + $arrayArgValue = $arrayArg->value; + if (! $arrayArgValue instanceof Variable) { + return null; + } + + // defined on param provided + if (! $this->isNames($arrayArgValue, $variableNamesWithArrayType)) { + return null; + } + + $arrayArgValueType = $this->nodeTypeResolver->getNativeType($arrayArgValue); + + // type changed, eg: by reassign + if (! $arrayArgValueType->isArray()->yes()) { + return null; + } + + $callbackArg = $subNode->getArg( + 'callback', + NativeFuncCallPositions::ARRAY_AND_CALLBACK_POSITIONS[$funcCallName]['callback'] + ); + if (! $callbackArg instanceof Arg) { + return null; + } + + $callbackArgValue = $callbackArg->value; + if (! $callbackArgValue instanceof ArrowFunction && ! $callbackArgValue instanceof Closure) { + return null; + } + + // no params or more than 2 params + if ($callbackArgValue->params === [] || count($callbackArgValue->params) > 2) { + return null; + } + + foreach ($callbackArgValue->params as $callbackArgValueParam) { + // not typed + if (! $callbackArgValueParam->type instanceof Node) { + return null; + } + } + + if (isset($callbackArgValue->params[1]) && ! $this->nodeComparator->areNodesEqual( + $callbackArgValue->params[0]->type, + $callbackArgValue->params[1]->type + )) { + return null; + } + + if (! $callbackArgValue->params[0]->type instanceof Node) { + return null; + } + + $arrayArgValueName = (string) $this->getName($arrayArgValue); + $paramToUpdate = $this->getParamByName($node, $arrayArgValueName); + + if (! $paramToUpdate instanceof Param) { + return null; + } + + $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($callbackArgValue->params[0]->type); + if ($paramType instanceof MixedType) { + return null; + } + + $paramsWithType[$this->getName($paramToUpdate)] = array_unique( + array_merge($paramsWithType[$this->getName($paramToUpdate)] ?? [], [$paramType]), + SORT_REGULAR + ); + return null; + } + ); + + $hasChanged = false; + foreach ($paramsWithType as $paramName => $type) { + $type = count($type) > 1 ? TypeCombinator::union(...$type) : current($type); + + /** @var Param $paramByName */ + $paramByName = $this->getParamByName($node, $paramName); + $this->phpDocTypeChanger->changeParamType( + $node, + $phpDocInfo, + new ArrayType(new MixedType(), $type), + $paramByName, + $paramName + ); + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + private function getParamByName(ClassMethod|Function_ $node, string $paramName): ?Param + { + foreach ($node->params as $param) { + if ($this->isName($param, $paramName)) { + return $param; + } + } + + return null; + } + + /** + * @return string[] + */ + private function collectVariableNamesWithArrayType(ClassMethod|Function_ $node, PhpDocInfo $phpDocInfo): array + { + $variableNamesWithArrayType = []; + + foreach ($node->params as $param) { + if (! $param->type instanceof Identifier) { + continue; + } + + if ($param->type->toString() !== 'array') { + continue; + } + + if (! $param->var instanceof Variable) { + continue; + } + + $paramName = $this->getName($param); + $paramTag = $phpDocInfo->getParamTagValueByName($paramName); + if ($paramTag instanceof ParamTagValueNode) { + continue; + } + + $variableNamesWithArrayType[] = $paramName; + } + + return $variableNamesWithArrayType; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector.php new file mode 100644 index 00000000000..0d8848691a4 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddParamFromDimFetchKeyUseRector.php @@ -0,0 +1,151 @@ + 'John', + 'age' => 30, + ]; + + return $data[$key]; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + public function get(string $key) + { + $data = [ + 'name' => 'John', + 'age' => 30, + ]; + + return $data[$key]; + } +} +CODE_SAMPLE + ), + + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->params === []) { + continue; + } + + if ($this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($classMethod)) { + continue; + } + + foreach ($classMethod->getParams() as $param) { + if ($param->type instanceof Node) { + continue; + } + + $paramName = $this->getName($param); + + $dimFetches = $this->arrayDimFetchFinder->findByDimName($classMethod, $paramName); + if ($dimFetches === []) { + continue; + } + + if (! $this->paramTypeAddGuard->isLegal($param, $classMethod)) { + continue; + } + + foreach ($dimFetches as $dimFetch) { + $dimFetchType = $this->getType($dimFetch->var); + + if (! $dimFetchType instanceof ArrayType && ! $dimFetchType instanceof ConstantArrayType) { + continue 2; + } + + if ($dimFetch->dim instanceof Variable) { + $type = $this->nodeTypeResolver->getType($dimFetch->dim); + if ($type instanceof UnionType) { + continue 2; + } + } + } + + $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $dimFetchType->getKeyType(), + TypeKind::PARAM + ); + + if (! $paramTypeNode instanceof Node) { + continue; + } + + $param->type = $paramTypeNode; + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector.php new file mode 100644 index 00000000000..38f59b982f0 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddParamStringTypeFromSprintfUseRector.php @@ -0,0 +1,118 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class]; + } + + /** + * @param ClassMethod|Function_|Closure|ArrowFunction $node + */ + public function refactor(Node $node): ClassMethod|Function_|Closure|ArrowFunction|null + { + if ($node instanceof ClassMethod && $node->stmts === null) { + return null; + } + + if ($node->getParams() === []) { + return null; + } + + if ($node instanceof ClassMethod && $this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($node)) { + return null; + } + + $hasChanged = false; + foreach ($node->getParams() as $param) { + if ($param->type instanceof Node) { + continue; + } + + // skip non string default value + if ($param->default instanceof Expr && ! $param->default instanceof String_) { + continue; + } + + if (! $this->paramTypeAddGuard->isLegal($param, $node)) { + continue; + } + + $variableName = $this->getName($param); + if (! $this->variableInSprintfMaskMatcher->matchMask($node, $variableName, '%s')) { + continue; + } + + $param->type = new Identifier('string'); + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php new file mode 100644 index 00000000000..6c0d198254b --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php @@ -0,0 +1,158 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if (! $classMethod->isPublic()) { + continue; + } + + if ($classMethod->getParams() === []) { + continue; + } + + $dataProviderNodes = $this->dataProviderMethodsFinder->findDataProviderNodes($node, $classMethod); + if ($dataProviderNodes->getClassMethods() === []) { + continue; + } + + $hasClassMethodChanged = $this->refactorClassMethod($classMethod, $dataProviderNodes); + if ($hasClassMethodChanged) { + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function refactorClassMethod(ClassMethod $classMethod, DataProviderNodes $dataProviderNodes): bool + { + $hasChanged = false; + + foreach ($classMethod->getParams() as $parameterPosition => $param) { + if ($param->type instanceof Node) { + continue; + } + + if ($param->variadic) { + continue; + } + + $paramTypeDeclaration = $this->parameterTypeFromDataProviderResolver->resolve( + $parameterPosition, + $dataProviderNodes->getClassMethods() + ); + + if ($paramTypeDeclaration instanceof MixedType) { + continue; + } + + $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramTypeDeclaration, TypeKind::PARAM); + if ($typeNode instanceof Node) { + $param->type = $typeNode; + $hasChanged = true; + } + } + + return $hasChanged; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector.php index 380cba48d3c..924d31de9bf 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector.php @@ -5,20 +5,21 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Name; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Interface_; -use PhpParser\Node\Stmt\Trait_; use PHPStan\Type\MixedType; use PHPStan\Type\StringType; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; +use Rector\Php\PhpVersionProvider; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\TypeDeclaration\ValueObject\AddParamTypeDeclaration; +use Rector\ValueObject\PhpVersionFeature; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; @@ -28,27 +29,22 @@ */ final class AddParamTypeDeclarationRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const PARAMETER_TYPEHINTS = 'parameter_typehints'; - /** * @var AddParamTypeDeclaration[] */ - private array $parameterTypehints = []; + private array $addParamTypeDeclarations = []; + + private bool $hasChanged = false; public function __construct( - private TypeComparator $typeComparator + private readonly TypeComparator $typeComparator, + private readonly PhpVersionProvider $phpVersionProvider, + private readonly StaticTypeMapper $staticTypeMapper, ) { } public function getRuleDefinition(): RuleDefinition { - $configuration = [ - self::PARAMETER_TYPEHINTS => [new AddParamTypeDeclaration('SomeClass', 'process', 0, new StringType())], - ]; - return new RuleDefinition('Add param types where needed', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' @@ -59,7 +55,7 @@ public function process($name) } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeClass { @@ -68,8 +64,8 @@ public function process(string $name) } } CODE_SAMPLE - , - $configuration + , + [new AddParamTypeDeclaration('SomeClass', 'process', 0, new StringType())] ), ]); } @@ -79,31 +75,37 @@ public function process(string $name) */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class, Interface_::class]; } /** - * @param ClassMethod $node + * @param Class_|Interface_ $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { - return null; - } + $this->hasChanged = false; - /** @var ClassLike $classLike */ - $classLike = $node->getAttribute(AttributeKey::CLASS_NODE); - - foreach ($this->parameterTypehints as $parameterTypehint) { - if (! $this->isObjectType($classLike, $parameterTypehint->getObjectType())) { + foreach ($node->getMethods() as $classMethod) { + if ($this->shouldSkip($node, $classMethod)) { continue; } - if (! $this->isName($node, $parameterTypehint->getMethodName())) { - continue; + $methodName = $this->getName($classMethod); + foreach ($this->addParamTypeDeclarations as $addParamTypeDeclaration) { + if (! $this->nodeNameResolver->isStringName($methodName, $addParamTypeDeclaration->getMethodName())) { + continue; + } + + if (! $this->isObjectType($node, $addParamTypeDeclaration->getObjectType())) { + continue; + } + + $this->refactorClassMethodWithTypehintByParameterPosition($classMethod, $addParamTypeDeclaration); } + } - $this->refactorClassMethodWithTypehintByParameterPosition($node, $parameterTypehint); + if (! $this->hasChanged) { + return null; } return $node; @@ -114,44 +116,24 @@ public function refactor(Node $node): ?Node */ public function configure(array $configuration): void { - $parameterTypehints = $configuration[self::PARAMETER_TYPEHINTS] ?? []; - Assert::allIsInstanceOf($parameterTypehints, AddParamTypeDeclaration::class); - $this->parameterTypehints = $parameterTypehints; + Assert::allIsAOf($configuration, AddParamTypeDeclaration::class); + + $this->addParamTypeDeclarations = $configuration; } - private function shouldSkip(ClassMethod $classMethod): bool + private function shouldSkip(Class_|Interface_ $classLike, ClassMethod $classMethod): bool { // skip class methods without args if ($classMethod->params === []) { return true; } - $classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof ClassLike) { - return true; - } - - // skip traits - if ($classLike instanceof Trait_) { - return true; - } - // skip class without parents/interfaces - if ($classLike instanceof Class_) { - if ($classLike->implements !== []) { - return false; - } - - if ($classLike->extends !== null) { - return false; - } - - return true; + if ($classLike instanceof Class_ && $classLike->implements !== []) { + return false; } - // skip interface without parents - /** @var Interface_ $classLike */ - return ! (bool) $classLike->extends; + return ! $classLike->extends instanceof Name; } private function refactorClassMethodWithTypehintByParameterPosition( @@ -169,24 +151,31 @@ private function refactorClassMethodWithTypehintByParameterPosition( private function refactorParameter(Param $param, AddParamTypeDeclaration $addParamTypeDeclaration): void { // already set → no change - if ($param->type !== null) { + if ($param->type instanceof Node) { $currentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); if ($this->typeComparator->areTypesEqual($currentParamType, $addParamTypeDeclaration->getParamType())) { return; } } + $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $addParamTypeDeclaration->getParamType(), + TypeKind::PARAM + ); + + $this->hasChanged = true; + // remove it if ($addParamTypeDeclaration->getParamType() instanceof MixedType) { + if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::MIXED_TYPE)) { + $param->type = $paramTypeNode; + return; + } + $param->type = null; return; } - $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $addParamTypeDeclaration->getParamType(), - TypeKind::PARAM() - ); - - $param->type = $returnTypeNode; + $param->type = $paramTypeNode; } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector.php new file mode 100644 index 00000000000..ae587099264 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector.php @@ -0,0 +1,175 @@ +name = $name; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private string $name; + + public function setName(string $name) + { + $this->name = $name; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?ClassMethod + { + $hasChanged = false; + + foreach ($node->params as $param) { + // already known type → skip + if ($param->type instanceof Node) { + continue; + } + + if ($param->variadic) { + continue; + } + + if (! $this->paramTypeAddGuard->isLegal($param, $node)) { + continue; + } + + $paramName = $this->getName($param); + $propertyStaticTypes = $this->resolvePropertyStaticTypesByParamName($node, $paramName); + $possibleParamType = $this->typeFactory->createMixedPassedOrUnionType($propertyStaticTypes); + + $paramType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($possibleParamType, TypeKind::PARAM); + if (! $paramType instanceof Node) { + continue; + } + + if ($this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($node)) { + return null; + } + + $param->type = $paramType; + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_PROPERTIES; + } + + /** + * @return Type[] + */ + private function resolvePropertyStaticTypesByParamName(ClassMethod $classMethod, string $paramName): array + { + $propertyStaticTypes = []; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod, function (Node $node) use ( + $paramName, + &$propertyStaticTypes + ): ?int { + if ($node instanceof Class_ || $node instanceof Function_) { + // skip anonymous classes and inner function + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $node instanceof Assign) { + return null; + } + + if (! $this->propertyFetchAnalyzer->isVariableAssignToThisPropertyFetch($node, $paramName)) { + return null; + } + + $exprType = $this->nodeTypeResolver->getNativeType($node->expr); + + $nodeExprType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($exprType, TypeKind::PARAM); + + $varType = $this->nodeTypeResolver->getNativeType($node->var); + $nodeVarType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($varType, TypeKind::PROPERTY); + + if ($nodeExprType instanceof Node && ! $this->nodeComparator->areNodesEqual($nodeExprType, $nodeVarType)) { + return null; + } + + $propertyStaticTypes[] = $varType; + + return null; + }); + + return $propertyStaticTypes; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector.php new file mode 100644 index 00000000000..4471a3658d2 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector.php @@ -0,0 +1,174 @@ +id; + }, $items); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + /** + * @return int[] + */ + public function getItems(array $items) + { + return array_map(function ($item): int { + return $item->id; + }, $items); + } +} +CODE_SAMPLE + )], + ); + } + + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): null|Function_|ClassMethod + { + $returnsScoped = $this->betterNodeFinder->findReturnsScoped($node); + + if ($this->hasNonArrayReturnType($node)) { + return null; + } + + // nothing to return? skip it + if ($returnsScoped === []) { + return null; + } + + // only returns with expr and no void + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returnsScoped)) { + return null; + } + + $closureReturnTypes = []; + + foreach ($returnsScoped as $returnScoped) { + if (! $returnScoped->expr instanceof FuncCall) { + return null; + } + + $arrayMapClosure = $this->matchArrayMapClosure($returnScoped->expr); + if (! $arrayMapClosure instanceof FunctionLike) { + return null; + } + + if (! $arrayMapClosure->returnType instanceof Node) { + return null; + } + + $closureReturnTypes[] = $this->staticTypeMapper->mapPhpParserNodePHPStanType($arrayMapClosure->returnType); + } + + $returnType = $this->typeFactory->createMixedPassedOrUnionType($closureReturnTypes); + $arrayType = new ArrayType(new MixedType(), $returnType); + + $functionLikePhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + $returnOriginalType = $functionLikePhpDocInfo->getReturnType(); + + if ($returnOriginalType instanceof ArrayType && ! $returnOriginalType->getItemType() instanceof MixedType) { + return null; + } + + if ($returnOriginalType instanceof IntersectionType) { + return null; + } + + $hasChanged = $this->phpDocTypeChanger->changeReturnType($node, $functionLikePhpDocInfo, $arrayType); + if ($hasChanged) { + return $node; + } + + return null; + } + + private function hasNonArrayReturnType(ClassMethod|Function_ $functionLike): bool + { + if (! $functionLike->returnType instanceof Identifier) { + return false; + } + + return $functionLike->returnType->toLowerString() !== 'array'; + } + + private function matchArrayMapClosure(FuncCall $funcCall): Closure|ArrowFunction|null + { + if (! $this->isName($funcCall, 'array_map')) { + return null; + } + + if ($funcCall->isFirstClassCallable()) { + return null; + } + + // lets infer strict array_map() type + $firstArg = $funcCall->getArgs()[0]; + if (! $firstArg->value instanceof Closure && ! $firstArg->value instanceof ArrowFunction) { + return null; + } + + return $firstArg->value; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector.php new file mode 100644 index 00000000000..3a965bff1ac --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnDocblockForScalarArrayFromAssignsRector.php @@ -0,0 +1,269 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $returnType = $phpDocInfo->getReturnType(); + + if (! $returnType instanceof MixedType || $returnType->isExplicitMixed()) { + return null; + } + + if ($node->returnType instanceof Node && ! $this->isName($node->returnType, 'array')) { + return null; + } + + $returnsScoped = $this->betterNodeFinder->findReturnsScoped($node); + + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returnsScoped)) { + return null; + } + + $returnedVariableNames = $this->extractReturnedVariableNames($returnsScoped); + if ($returnedVariableNames === []) { + return null; + } + + $scalarArrayTypes = []; + foreach ($returnedVariableNames as $returnedVariableName) { + $scalarType = $this->resolveScalarArrayTypeForVariable($node, $returnedVariableName); + + if ($scalarType instanceof Type) { + $scalarArrayTypes[] = $scalarType; + } else { + return null; + } + } + + $firstScalarType = $scalarArrayTypes[0]; + foreach ($scalarArrayTypes as $scalarArrayType) { + if (! $firstScalarType->equals($scalarArrayType)) { + return null; + } + } + + $arrayType = new ArrayType(new MixedType(), $firstScalarType); + + $hasChanged = $this->phpDocTypeChanger->changeReturnType($node, $phpDocInfo, $arrayType); + if ($hasChanged) { + return $node; + } + + return null; + } + + /** + * @param Return_[] $returnsScoped + * @return string[] + */ + private function extractReturnedVariableNames(array $returnsScoped): array + { + $variableNames = []; + + foreach ($returnsScoped as $returnScoped) { + if (! $returnScoped->expr instanceof Variable) { + continue; + } + + $variableName = $this->getName($returnScoped->expr); + if ($variableName !== null) { + $variableNames[] = $variableName; + } + } + + return array_unique($variableNames); + } + + private function resolveScalarArrayTypeForVariable(ClassMethod|Function_ $node, string $variableName): ?Type + { + $assigns = $this->betterNodeFinder->findInstancesOfScoped([$node], Assign::class); + + $scalarTypes = []; + $arrayHasInitialized = false; + $arrayHasDimAssigns = false; + + foreach ($assigns as $assign) { + if ($assign->var instanceof Variable && $this->isName( + $assign->var, + $variableName + ) && ($assign->expr instanceof Array_ && $assign->expr->items === [])) { + $arrayHasInitialized = true; + continue; + } + + if (! $assign->var instanceof ArrayDimFetch) { + continue; + } + + $arrayDimFetch = $assign->var; + if (! $arrayDimFetch->var instanceof Variable) { + continue; + } + + if (! $this->isName($arrayDimFetch->var, $variableName)) { + continue; + } + + if ($arrayDimFetch->dim instanceof Expr) { + continue; + } + + $arrayHasDimAssigns = true; + + $scalarType = $this->resolveScalarType($assign->expr); + + if ($scalarType instanceof Type) { + $scalarTypes[] = $scalarType; + } else { + return null; + } + } + + if (! $arrayHasInitialized || ! $arrayHasDimAssigns) { + return null; + } + + if ($scalarTypes === []) { + return null; + } + + $firstType = $scalarTypes[0]; + foreach ($scalarTypes as $scalarType) { + if (! $firstType->equals($scalarType)) { + return null; + } + } + + return $firstType; + } + + private function resolveScalarType(Expr $expr): ?Type + { + if ($expr instanceof String_) { + return new StringType(); + } + + if ($expr instanceof Int_) { + return new IntegerType(); + } + + if ($expr instanceof DNumber) { + return new FloatType(); + } + + $exprType = $this->nodeTypeResolver->getNativeType($expr); + if ($exprType->isScalar()->yes()) { + return $exprType; + } + + return null; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector.php new file mode 100644 index 00000000000..aa96bd590b5 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector.php @@ -0,0 +1,187 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($this->isNames($classMethod, [MethodName::CONSTRUCT, MethodName::DESTRUCT])) { + continue; + } + + $parentClassMethodReturnType = $this->getReturnTypeRecursive($classMethod); + if (! $parentClassMethodReturnType instanceof Type) { + continue; + } + + $changedClassMethod = $this->processClassMethodReturnType( + $node, + $classMethod, + $parentClassMethodReturnType + ); + if (! $changedClassMethod instanceof ClassMethod) { + continue; + } + + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function getReturnTypeRecursive(ClassMethod $classMethod): ?Type + { + $returnType = $classMethod->getReturnType(); + if ($returnType instanceof Node) { + return $this->staticTypeMapper->mapPhpParserNodePHPStanType($returnType); + } + + $parentMethodReflection = $this->parentClassMethodTypeOverrideGuard->getParentClassMethod($classMethod); + while ($parentMethodReflection instanceof MethodReflection) { + if ($parentMethodReflection->isPrivate()) { + return null; + } + + $parameterAcceptor = ParametersAcceptorSelector::combineAcceptors($parentMethodReflection->getVariants()); + + $parentReturnType = $parameterAcceptor->getNativeReturnType(); + if (! $parentReturnType instanceof MixedType) { + return $parentReturnType; + } + + if ($parentReturnType->isExplicitMixed()) { + return $parentReturnType; + } + + $parentMethodReflection = $this->parentClassMethodTypeOverrideGuard->getParentClassMethod( + $parentMethodReflection + ); + } + + return null; + } + + private function processClassMethodReturnType( + Class_ $class, + ClassMethod $classMethod, + Type $parentType + ): ?ClassMethod { + if ($parentType instanceof MixedType) { + $className = (string) $this->getName($class); + $currentObjectType = new ObjectType($className); + if (! $parentType->equals($currentObjectType) && $classMethod->returnType instanceof Node) { + return null; + } + } + + if ($parentType instanceof MixedType && ! $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::MIXED_TYPE + )) { + return null; + } + + // already set and sub type or equal → no change + if ($this->parentClassMethodTypeOverrideGuard->shouldSkipReturnTypeChange($classMethod, $parentType)) { + return null; + } + + $classMethod->returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $parentType, + TypeKind::RETURN + ); + + return $classMethod; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php index 66b04c72d21..0c2cd4568eb 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php @@ -5,15 +5,24 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; +use PHPStan\Reflection\ClassReflection; use PHPStan\Type\ArrayType; use PHPStan\Type\MixedType; +use PHPStan\Type\ObjectType; +use PHPStan\Type\StaticType; use PHPStan\Type\Type; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\TypeComparator\TypeComparator; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Php\PhpVersionProvider; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\StaticTypeMapper\ValueObject\Type\SimpleStaticType; use Rector\TypeDeclaration\ValueObject\AddReturnTypeDeclaration; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VendorLocker\ParentClassMethodTypeOverrideGuard; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; @@ -23,29 +32,24 @@ */ final class AddReturnTypeDeclarationRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const METHOD_RETURN_TYPES = 'method_return_types'; - /** * @var AddReturnTypeDeclaration[] */ private array $methodReturnTypes = []; + private bool $hasChanged = false; + public function __construct( - private TypeComparator $typeComparator + private readonly PhpVersionProvider $phpVersionProvider, + private readonly ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard, + private readonly StaticTypeMapper $staticTypeMapper, + private readonly ReflectionResolver $reflectionResolver ) { } public function getRuleDefinition(): RuleDefinition { - $arrayType = new ArrayType(new MixedType(), new MixedType()); - $configuration = [ - self::METHOD_RETURN_TYPES => [new AddReturnTypeDeclaration('SomeClass', 'getData', $arrayType)], - ]; - - return new RuleDefinition('Changes defined return typehint of method and class.', [ + return new RuleDefinition('Change defined return typehint of method and class', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' class SomeClass @@ -65,7 +69,7 @@ public function getData(): array } CODE_SAMPLE , - $configuration + [new AddReturnTypeDeclaration('SomeClass', 'getData', new ArrayType(new MixedType(), new MixedType()))] ), ]); } @@ -75,59 +79,91 @@ public function getData(): array */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { + $this->hasChanged = false; + foreach ($this->methodReturnTypes as $methodReturnType) { - if (! $this->isObjectType($node, $methodReturnType->getObjectType())) { + $objectType = $methodReturnType->getObjectType(); + if (! $this->isObjectType($node, $objectType)) { continue; } - if (! $this->isName($node, $methodReturnType->getMethod())) { - continue; + foreach ($node->getMethods() as $classMethod) { + if (! $this->isName($classMethod, $methodReturnType->getMethod())) { + continue; + } + + $this->processClassMethodNodeWithTypehints( + $classMethod, + $node, + $methodReturnType->getReturnType(), + $objectType + ); } + } - $this->processClassMethodNodeWithTypehints($node, $methodReturnType->getReturnType()); - - return $node; + if (! $this->hasChanged) { + return null; } - return null; + return $node; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $methodReturnTypes = $configuration[self::METHOD_RETURN_TYPES] ?? []; - Assert::allIsInstanceOf($methodReturnTypes, AddReturnTypeDeclaration::class); + Assert::allIsAOf($configuration, AddReturnTypeDeclaration::class); - $this->methodReturnTypes = $methodReturnTypes; + $this->methodReturnTypes = $configuration; } - private function processClassMethodNodeWithTypehints(ClassMethod $classMethod, Type $newType): void - { - // remove it + private function processClassMethodNodeWithTypehints( + ClassMethod $classMethod, + Class_ $class, + Type $newType, + ObjectType $objectType + ): void { if ($newType instanceof MixedType) { + $className = (string) $this->getName($class); + $currentObjectType = new ObjectType($className); + + if (! $objectType->equals($currentObjectType) && $classMethod->returnType instanceof Node) { + return; + } + } + + // remove it + if ($newType instanceof MixedType && ! $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::MIXED_TYPE + )) { $classMethod->returnType = null; return; } - // already set → no change - if ($classMethod->returnType !== null) { - $currentReturnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($classMethod->returnType); - if ($this->typeComparator->areTypesEqual($currentReturnType, $newType)) { + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if ($classMethod->returnType instanceof Node && $newType instanceof SimpleStaticType) { + if (! $classReflection instanceof ClassReflection) { return; } + + $newType = new StaticType($classReflection); + } + + // already set and sub type or equal → no change + if ($this->parentClassMethodTypeOverrideGuard->shouldSkipReturnTypeChange($classMethod, $newType)) { + return; } - $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($newType, TypeKind::RETURN()); - $classMethod->returnType = $returnTypeNode; + $classMethod->returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($newType, TypeKind::RETURN); + $this->hasChanged = true; } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector.php new file mode 100644 index 00000000000..f9d6e1ec866 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeFromTryCatchTypeRector.php @@ -0,0 +1,176 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { + return null; + } + + // already known type + if ($node->returnType instanceof Node) { + return null; + } + + $tryReturnType = null; + $catchReturnTypes = []; + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + foreach ((array) $node->stmts as $classMethodStmt) { + if (! $classMethodStmt instanceof TryCatch) { + continue; + } + + // skip if there is no catch + if ($classMethodStmt->catches === []) { + continue; + } + + $tryCatch = $classMethodStmt; + $tryReturnType = $this->matchReturnType($tryCatch); + + foreach ($tryCatch->catches as $catch) { + $currentCatchType = $this->matchReturnType($catch); + + // each catch must have type + if (! $currentCatchType instanceof Type) { + return null; + } + + $catchReturnTypes[] = $currentCatchType; + } + + if ($tryCatch->finally instanceof Finally_) { + $finallyReturnType = $this->matchReturnType($tryCatch->finally); + if ($finallyReturnType instanceof Type) { + $catchReturnTypes[] = $finallyReturnType; + } + } + } + + if (! $tryReturnType instanceof Type) { + return null; + } + + foreach ($catchReturnTypes as $catchReturnType) { + if (! $this->typeComparator->areTypesEqual($catchReturnType, $tryReturnType)) { + return null; + } + } + + $returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($tryReturnType, TypeKind::RETURN); + if (! $returnType instanceof Node) { + return null; + } + + $node->returnType = $returnType; + return $node; + } + + private function matchReturnType(TryCatch|Catch_|Finally_ $tryOrCatchOrFinally): ?Type + { + foreach ($tryOrCatchOrFinally->stmts as $stmt) { + if (! $stmt instanceof Return_) { + continue; + } + + if (! $stmt->expr instanceof Expr) { + continue; + } + + return $this->nodeTypeResolver->getNativeType($stmt->expr); + } + + return null; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector.php index 16ba793393d..88fc60ba5fb 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector.php @@ -5,13 +5,14 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; -use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\Throw_; use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Function_; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use PhpParser\Node\Stmt\Expression; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ClassModifierChecker; use Rector\TypeDeclaration\TypeInferer\SilentVoidResolver; +use Rector\ValueObject\PhpVersionFeature; use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnVendorLockResolver; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -23,8 +24,9 @@ final class AddVoidReturnTypeWhereNoReturnRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private SilentVoidResolver $silentVoidResolver, - private ClassMethodReturnVendorLockResolver $classMethodReturnVendorLockResolver + private readonly SilentVoidResolver $silentVoidResolver, + private readonly ClassMethodReturnVendorLockResolver $classMethodReturnVendorLockResolver, + private readonly ClassModifierChecker $classModifierChecker, ) { } @@ -63,19 +65,20 @@ public function getValues(): void */ public function getNodeTypes(): array { - return [ClassMethod::class, Function_::class, Closure::class]; + return [ClassMethod::class]; } /** - * @param ClassMethod|Function_|Closure $node + * @param ClassMethod $node */ public function refactor(Node $node): ?Node { - if ($node->returnType !== null) { + // already has return type → skip + if ($node->returnType instanceof Node) { return null; } - if ($node instanceof ClassMethod && ($node->isMagic() || $node->isAbstract())) { + if ($this->shouldSkipClassMethod($node)) { return null; } @@ -83,7 +86,7 @@ public function refactor(Node $node): ?Node return null; } - if ($node instanceof ClassMethod && $this->classMethodReturnVendorLockResolver->isVendorLocked($node)) { + if ($this->classMethodReturnVendorLockResolver->isVendorLocked($node)) { return null; } @@ -95,4 +98,50 @@ public function provideMinPhpVersion(): int { return PhpVersionFeature::VOID_TYPE; } + + private function shouldSkipClassMethod(ClassMethod $classMethod): bool + { + if ($classMethod->isAbstract()) { + return true; + } + + // is not final and has only exception? possibly implemented by child + if ($this->isNotFinalAndHasExceptionOnly($classMethod)) { + return true; + } + + // possibly required by child implementation + if ($this->isNotFinalAndEmpty($classMethod)) { + return true; + } + + if ($classMethod->isProtected()) { + return ! $this->classModifierChecker->isInsideFinalClass($classMethod); + } + + return $this->classModifierChecker->isInsideAbstractClass($classMethod) && $classMethod->getStmts() === []; + } + + private function isNotFinalAndHasExceptionOnly(ClassMethod $classMethod): bool + { + if ($this->classModifierChecker->isInsideFinalClass($classMethod)) { + return false; + } + + if (count((array) $classMethod->stmts) !== 1) { + return false; + } + + $onlyStmt = $classMethod->stmts[0] ?? null; + return $onlyStmt instanceof Expression && $onlyStmt->expr instanceof Throw_; + } + + private function isNotFinalAndEmpty(ClassMethod $classMethod): bool + { + if ($this->classModifierChecker->isInsideFinalClass($classMethod)) { + return false; + } + + return $classMethod->stmts === []; + } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector.php new file mode 100644 index 00000000000..f0ded98f930 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector.php @@ -0,0 +1,143 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkip($node, $scope)) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + if (! $this->hasOnlyBooleanConstExprs($returns)) { + return null; + } + + $node->returnType = new Identifier('bool'); + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + /** + * @param ClassMethod|Function_|Closure $node + */ + private function shouldSkip(Node $node, Scope $scope): bool + { + // already has the type, skip + if ($node->returnType instanceof Node) { + return true; + } + + return $node instanceof ClassMethod + && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope); + } + + /** + * @param Return_[] $returns + */ + private function hasOnlyBooleanConstExprs(array $returns): bool + { + foreach ($returns as $return) { + if (! $return->expr instanceof ConstFetch) { + return false; + } + + if (! $this->valueResolver->isTrueOrFalse($return->expr)) { + return false; + } + } + + return true; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php new file mode 100644 index 00000000000..567b14895f6 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php @@ -0,0 +1,199 @@ + $second; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function resolve($first, $second): bool + { + return $first > $second; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @funcCall array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkip($node, $scope)) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + // handled in another rule + if ($this->hasOnlyBooleanConstExprs($returns)) { + return null; + } + + // handled in another rule + if (! $this->hasOnlyBoolScalarReturnExprs($returns)) { + return null; + } + + $node->returnType = new Identifier('bool'); + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + /** + * @param ClassMethod|Function_|Closure $node + */ + private function shouldSkip(Node $node, Scope $scope): bool + { + // already has the type, skip + if ($node->returnType instanceof Node) { + return true; + } + + return $node instanceof ClassMethod + && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope); + } + + /** + * @param Return_[] $returns + */ + private function hasOnlyBoolScalarReturnExprs(array $returns): bool + { + foreach ($returns as $return) { + if (! $return->expr instanceof Expr) { + return false; + } + + if ($this->exprAnalyzer->isBoolExpr($return->expr)) { + continue; + } + + if ($return->expr instanceof FuncCall && $this->isNativeBooleanReturnTypeFuncCall($return->expr)) { + continue; + } + + return false; + } + + return true; + } + + private function isNativeBooleanReturnTypeFuncCall(FuncCall $funcCall): bool + { + $functionName = $this->getName($funcCall); + + if (! is_string($functionName)) { + return false; + } + + $name = new Name($functionName); + if (! $this->reflectionProvider->hasFunction($name, null)) { + return false; + } + + $functionReflection = $this->reflectionProvider->getFunction($name, null); + if (! $functionReflection->isBuiltin()) { + return false; + } + + foreach ($functionReflection->getVariants() as $extendedParametersAcceptor) { + if (! $extendedParametersAcceptor->getNativeReturnType()->isBoolean()->yes()) { + return false; + } + } + + return true; + } + + /** + * @param Return_[] $returns + */ + private function hasOnlyBooleanConstExprs(array $returns): bool + { + foreach ($returns as $return) { + if (! $return->expr instanceof ConstFetch) { + return false; + } + + if (! $this->valueResolver->isTrueOrFalse($return->expr)) { + return false; + } + } + + return true; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector.php new file mode 100644 index 00000000000..34b4c225ee9 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector.php @@ -0,0 +1,142 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if (! $classMethod->isMagic()) { + continue; + } + + if ($this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($classMethod)) { + continue; + } + + if ($this->isNames($classMethod, [MethodName::CALL, MethodName::CALL_STATIC])) { + $secondParam = $classMethod->getParams()[1]; + if (! $secondParam->type instanceof Node) { + $secondParam->type = new Name('array'); + $hasChanged = true; + } + } + + // first arg string + if ($this->isNames( + $classMethod, + [ + MethodName::CALL, + MethodName::CALL_STATIC, + MethodName::__SET, + MethodName::__GET, + MethodName::ISSET, + MethodName::UNSET, + ] + )) { + $firstParam = $classMethod->getParams()[0]; + if (! $firstParam->type instanceof Node) { + $firstParam->type = new Identifier('string'); + $hasChanged = true; + } + } + + if ($this->isName($classMethod, MethodName::__GET) && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::MIXED_TYPE + ) && ! $classMethod->returnType instanceof Node) { + $classMethod->returnType = new Identifier('mixed'); + $hasChanged = true; + } + + if ($this->isName($classMethod, MethodName::__SET) && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::MIXED_TYPE + )) { + $secondParam = $classMethod->getParams()[1]; + if (! $secondParam->type instanceof Node) { + $secondParam->type = new Identifier('mixed'); + $hasChanged = true; + } + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector.php new file mode 100644 index 00000000000..bafc1688487 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector.php @@ -0,0 +1,311 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $returnType = $node->returnType; + if (! $returnType instanceof Identifier && ! $returnType instanceof FullyQualified) { + return null; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + if (! $classReflection->isFinalByKeyword() && ! $node->isFinal()) { + return null; + } + + $actualReturnClass = $this->getActualReturnedClass($node); + if ($actualReturnClass === null) { + return null; + } + + $declaredType = $returnType->toString(); + + // already most narrow type + if ($declaredType === $actualReturnClass) { + return null; + } + + // non-existing class + if ($declaredType !== 'object') { + if (! $this->reflectionProvider->hasClass($declaredType)) { + return null; + } + + $declaredTypeClassReflection = $this->reflectionProvider->getClass($declaredType); + + // already last final object + if ($declaredTypeClassReflection->isFinalByKeyword()) { + return null; + } + + // this rule narrows only object or class types, not interfaces + if (! $declaredTypeClassReflection->isClass()) { + return null; + } + } + + if (! $this->isNarrowingValid($node, $declaredType, $actualReturnClass)) { + return null; + } + + if (! $this->isNarrowingValidFromParent($node, $actualReturnClass)) { + return null; + } + + $node->returnType = new FullyQualified($actualReturnClass); + $this->updateDocblock($node, $actualReturnClass); + + return $node; + } + + private function updateDocblock(ClassMethod $classMethod, string $actualReturnClass): void + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); + if (! $phpDocInfo instanceof PhpDocInfo) { + return; + } + + $returnTagValueNode = $phpDocInfo->getReturnTagValue(); + if (! $returnTagValueNode instanceof ReturnTagValueNode) { + return; + } + + if ($returnTagValueNode->type instanceof IdentifierTypeNode) { + $oldType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( + $returnTagValueNode->type, + $classMethod + ); + } else { + return; + } + + if ($oldType instanceof ObjectType) { + $objectType = new ObjectType($actualReturnClass); + if ($this->typeComparator->areTypesEqual($oldType, $objectType)) { + return; + } + } + + $returnTagValueNode->type = new FullyQualifiedIdentifierTypeNode($actualReturnClass); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); + } + + private function isNarrowingValid(ClassMethod $classMethod, string $declaredType, string $actualType): bool + { + if ($declaredType === 'object') { + return true; + } + + $actualObjectType = new ObjectType($actualType); + $declaredObjectType = new ObjectType($declaredType); + + if (! $declaredObjectType->isSuperTypeOf($actualObjectType) + ->yes()) { + return false; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); + if (! $phpDocInfo instanceof PhpDocInfo) { + return true; + } + + $returnType = $phpDocInfo->getReturnType(); + return ! $returnType instanceof GenericObjectType; + } + + private function isNarrowingValidFromParent(ClassMethod $classMethod, string $actualReturnClass): bool + { + if ($classMethod->isPrivate()) { + return true; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if (! $classReflection instanceof ClassReflection) { + return true; + } + + $ancestors = array_filter( + $classReflection->getAncestors(), + fn (ClassReflection $ancestorClassReflection): bool => $classReflection->getName() !== $ancestorClassReflection->getName() + ); + + $methodName = $this->getName($classMethod); + + foreach ($ancestors as $ancestor) { + if ($ancestor->getFileName() === null) { + continue; + } + + if (! $ancestor->hasNativeMethod($methodName)) { + continue; + } + + $parentClassMethod = $this->astResolver->resolveClassMethod($ancestor->getName(), $methodName); + if (! $parentClassMethod instanceof ClassMethod) { + continue; + } + + $parentReturnType = $parentClassMethod->returnType; + if (! $parentReturnType instanceof Identifier && ! $parentReturnType instanceof FullyQualified) { + continue; + } + + $parentReturnTypeName = $parentReturnType->toString(); + + if (! $this->isNarrowingValid($parentClassMethod, $parentReturnTypeName, $actualReturnClass)) { + return false; + } + } + + return true; + } + + private function getActualReturnedClass(ClassMethod $classMethod): ?string + { + $returnStatements = $this->betterNodeFinder->findReturnsScoped($classMethod); + if ($returnStatements === []) { + return null; + } + + $returnedClass = null; + + foreach ($returnStatements as $returnStatement) { + if ($returnStatement->expr === null) { + return null; + } + + $returnType = $this->nodeTypeResolver->getNativeType($returnStatement->expr); + + if (! $returnType->isObject()->yes()) { + return null; + } + + $classNames = $returnType->getObjectClassNames(); + + if (count($classNames) !== 1) { + return null; + } + + $className = $classNames[0]; + + if ($returnedClass === null) { + $returnedClass = $className; + } elseif ($returnedClass !== $className) { + return null; + } + } + + return $returnedClass; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector.php new file mode 100644 index 00000000000..89da603b8d0 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector.php @@ -0,0 +1,179 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkip($node, $scope)) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + // handled by another rule + if ($this->isAlwaysNumeric($returns)) { + return null; + } + + $isAlwaysIntType = true; + $isAlwaysFloatType = true; + + foreach ($returns as $return) { + if (! $return->expr instanceof Expr) { + return null; + } + + $exprType = $this->nodeTypeResolver->getNativeType($return->expr); + if (! $exprType->isInteger()->yes()) { + $isAlwaysIntType = false; + } + + if (! $exprType->isFloat()->yes()) { + $isAlwaysFloatType = false; + } + } + + if ($isAlwaysFloatType) { + $node->returnType = new Identifier('float'); + return $node; + } + + if ($isAlwaysIntType) { + $node->returnType = new Identifier('int'); + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + private function shouldSkip(ClassMethod|Function_ $functionLike, Scope $scope): bool + { + // type is already known, skip + if ($functionLike->returnType instanceof Node) { + return true; + } + + // empty, nothing to find + if ($functionLike->stmts === null || $functionLike->stmts === []) { + return true; + } + + if (! $functionLike instanceof ClassMethod) { + return false; + } + + return $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($functionLike, $scope); + } + + /** + * @param Return_[] $returns + */ + private function isAlwaysNumeric(array $returns): bool + { + $isAlwaysFloat = true; + $isAlwaysInt = true; + + foreach ($returns as $return) { + $expr = $return->expr; + if ($expr instanceof UnaryMinus) { + $expr = $expr->expr; + } + + if (! $expr instanceof Float_) { + $isAlwaysFloat = false; + } + + if (! $expr instanceof Int_) { + $isAlwaysInt = false; + } + } + + if ($isAlwaysFloat) { + return true; + } + + return $isAlwaysInt; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector.php new file mode 100644 index 00000000000..af0bc81aecc --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector.php @@ -0,0 +1,142 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkip($node, $scope)) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + $isAlwaysInt = true; + $isAlwaysFloat = true; + + foreach ($returns as $return) { + $expr = $return->expr; + if ($expr instanceof UnaryMinus) { + $expr = $expr->expr; + } + + if (! $expr instanceof Float_) { + $isAlwaysFloat = false; + } + + if (! $expr instanceof Int_) { + $isAlwaysInt = false; + } + } + + if ($isAlwaysFloat) { + $node->returnType = new Identifier('float'); + return $node; + } + + if ($isAlwaysInt) { + $node->returnType = new Identifier('int'); + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + private function shouldSkip(ClassMethod|Function_ $functionLike, Scope $scope): bool + { + // type is already known, skip + if ($functionLike->returnType instanceof Node) { + return true; + } + + // empty, nothing to find + if ($functionLike->stmts === null || $functionLike->stmts === []) { + return true; + } + + if (! $functionLike instanceof ClassMethod) { + return false; + } + + return $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($functionLike, $scope); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php index bf99ab1545f..73db69cfc6c 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php @@ -5,24 +5,24 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Expr\ArrowFunction; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Identifier; -use PhpParser\Node\Name; -use PhpParser\Node\NullableType; use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\If_; -use PhpParser\Node\UnionType; -use PHPStan\Analyser\Scope; -use PHPStan\Reflection\ClassReflection; -use Rector\Core\Rector\AbstractRector; -use Rector\Defluent\ConflictGuard\ParentClassMethodTypeOverrideGuard; +use PhpParser\Node\Stmt\Function_; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\StaticTypeMapper\Mapper\PhpParserNodeMapper; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\TypeDeclaration\Guard\ParamTypeAddGuard; use Rector\TypeDeclaration\NodeAnalyzer\CallerParamMatcher; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\VendorLocker\ParentClassMethodTypeOverrideGuard; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -32,9 +32,13 @@ final class ParamTypeByMethodCallTypeRector extends AbstractRector { public function __construct( - private CallerParamMatcher $callerParamMatcher, - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard + private readonly CallerParamMatcher $callerParamMatcher, + private readonly ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard, + private readonly ParamTypeAddGuard $paramTypeAddGuard, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly PhpParserNodeMapper $phpParserNodeMapper, + private readonly StaticTypeMapper $staticTypeMapper, + private readonly TypeFactory $typeFactory ) { } @@ -63,7 +67,7 @@ public function go($value) } } CODE_SAMPLE -, + , <<<'CODE_SAMPLE' class SomeTypedService { @@ -94,43 +98,44 @@ public function go(string $value) */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class]; } /** - * @param ClassMethod $node + * @param ClassMethod|Function_|Closure|ArrowFunction $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkipClassMethod($node)) { + if ($node->params === []) { + return null; + } + + // has params with at least one missing type + if (! $this->hasAtLeastOneParamWithoutType($node)) { + return null; + } + + if ($node instanceof ClassMethod && $this->shouldSkipClassMethod($node)) { return null; } /** @var array $callers */ - $callers = $this->betterNodeFinder->findInstancesOf( - (array) $node->stmts, + $callers = $this->betterNodeFinder->findInstancesOfScoped( + [$node], [StaticCall::class, MethodCall::class, FuncCall::class] ); - $hasChanged = false; - $scope = $node->getAttribute(AttributeKey::SCOPE); - - foreach ($node->params as $param) { - if ($this->shouldSkipParam($param, $node)) { - continue; - } - - foreach ($callers as $caller) { - $paramType = $this->callerParamMatcher->matchCallParamType($caller, $param, $scope); - if ($paramType === null) { - continue; - } + // keep only callers with args + $callersWithArgs = array_filter( + $callers, + fn (StaticCall|MethodCall|FuncCall $caller): bool => $caller->args !== [] + ); - $this->mirrorParamType($param, $paramType); - $hasChanged = true; - } + if ($callersWithArgs === []) { + return null; } + $hasChanged = $this->refactorFunctionLike($node, $callersWithArgs); if ($hasChanged) { return $node; } @@ -140,79 +145,103 @@ public function refactor(Node $node): ?Node private function shouldSkipClassMethod(ClassMethod $classMethod): bool { - if ($classMethod->params === []) { - return true; + $isMissingParameterTypes = false; + foreach ($classMethod->params as $param) { + if ($param->type instanceof Node) { + continue; + } + + if ($param->variadic) { + continue; + } + + $isMissingParameterTypes = true; } - if ($this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($classMethod)) { + if ($isMissingParameterTypes === false) { return true; } - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { + return $this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($classMethod); + } + + private function shouldSkipParam( + Param $param, + ClassMethod|Function_|Closure|ArrowFunction $functionLike + ): bool { + // already has type, skip + if ($param->type instanceof Node) { return true; } - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { + if ($param->variadic) { return true; } - return ! $classReflection->isClass(); + return ! $this->paramTypeAddGuard->isLegal($param, $functionLike); } - private function mirrorParamType( - Param $decoratedParam, - Identifier | Name | NullableType | UnionType $paramType - ): void { - // mimic type - $newParamType = $paramType; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - $newParamType, - function (Node $node): void { - // original attributes have to removed to avoid tokens crashing from origin positions - $node->setAttributes([]); + /** + * @param array $callers + */ + private function refactorFunctionLike( + ClassMethod|Closure|Function_|ArrowFunction $functionLike, + array $callers + ): bool { + $hasChanged = false; + + foreach ($functionLike->params as $param) { + if ($this->shouldSkipParam($param, $functionLike)) { + continue; } - ); - $decoratedParam->type = $newParamType; - } + $paramTypes = []; + foreach ($callers as $caller) { + $matchCallParam = $this->callerParamMatcher->matchCallParam($caller, $param); - /** - * Should skip param because one of them is conditional types? - */ - private function isParamConditioned(Param $param, ClassMethod $classMethod): bool - { - $paramName = $this->nodeNameResolver->getName($param->var); - if ($paramName === null) { - return false; - } + // nothing to do with param, continue + if (! $matchCallParam instanceof Param) { + continue; + } - /** @var Variable[] $variables */ - $variables = $this->betterNodeFinder->findInstanceOf((array) $classMethod->stmts, Variable::class); + $paramType = $this->callerParamMatcher->matchCallParamType($param, $matchCallParam); + if (! $paramType instanceof Node) { + $paramTypes = []; + break; + } + + if ($caller->getAttribute(AttributeKey::IS_RIGHT_AND)) { + $paramTypes = []; + break; + } - foreach ($variables as $variable) { - if (! $this->isName($variable, $paramName)) { + $paramTypes[] = $this->phpParserNodeMapper->mapToPHPStanType($paramType); + } + + if ($paramTypes === []) { continue; } - $conditional = $this->betterNodeFinder->findParentType($variable, If_::class); - if ($conditional instanceof If_) { - return true; + $type = $this->typeFactory->createMixedPassedOrUnionType($paramTypes); + $paramNodeType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM); + + if ($paramNodeType instanceof Node) { + $param->type = $paramNodeType; + $hasChanged = true; } } - return false; + return $hasChanged; } - private function shouldSkipParam(Param $param, ClassMethod $classMethod): bool + private function hasAtLeastOneParamWithoutType(ClassMethod|Function_|Closure|ArrowFunction $functionLike): bool { - if ($this->isParamConditioned($param, $classMethod)) { - return true; + foreach ($functionLike->params as $param) { + if (! $param->type instanceof Node) { + return true; + } } - // already has type, skip - return $param->type !== null; + return false; } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector.php index 49b40ddfeb7..3641552ac02 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector.php @@ -5,13 +5,19 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use Rector\Core\Rector\AbstractRector; +use Rector\Enum\ObjectReference; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; use Rector\TypeDeclaration\NodeAnalyzer\CallerParamMatcher; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -22,7 +28,9 @@ final class ParamTypeByParentCallTypeRector extends AbstractRector { public function __construct( - private CallerParamMatcher $callerParamMatcher + private readonly CallerParamMatcher $callerParamMatcher, + private readonly ReflectionResolver $reflectionResolver, + private readonly BetterNodeFinder $betterNodeFinder ) { } @@ -73,53 +81,68 @@ public function __construct(string $name) */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { - return null; - } - - $parentStaticCall = $this->findParentStaticCall($node); - if (! $parentStaticCall instanceof StaticCall) { - return null; - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { + // no parent calls available + if ($node->extends === null) { return null; } $hasChanged = false; - foreach ($node->params as $param) { - // already has type, skip - if ($param->type !== null) { + foreach ($node->getMethods() as $classMethod) { + if ($this->shouldSkip($classMethod)) { continue; } - $parentParam = $this->callerParamMatcher->matchParentParam($parentStaticCall, $param, $scope); - if (! $parentParam instanceof Param) { + $parentStaticCall = $this->findParentStaticCall($classMethod); + if (! $parentStaticCall instanceof StaticCall) { continue; } - if ($parentParam->type === null) { - continue; - } + $scope = ScopeFetcher::fetch($classMethod); + + foreach ($classMethod->params as $param) { + // already has type, skip + if ($param->type !== null) { + continue; + } - // mimic type - $paramType = $parentParam->type; + if ($param->variadic) { + continue; + } - // original attributes have to removed to avoid tokens crashing from origin positions - $paramType->setAttributes([]); + if ($this->isParamUsedInSpreadArg($classMethod, $param)) { + continue; + } - $param->type = $paramType; - $hasChanged = true; + $parentParam = $this->callerParamMatcher->matchParentParam($parentStaticCall, $param, $scope); + if (! $parentParam instanceof Param) { + continue; + } + + if (! $parentParam->type instanceof Node) { + continue; + } + + // mimic type + $paramType = $parentParam->type; + + // original attributes have to removed to avoid tokens crashing from origin positions + $this->traverseNodesWithCallable($paramType, static function (Node $node): null { + $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); + return null; + }); + + $param->type = $paramType; + $hasChanged = true; + } } if ($hasChanged) { @@ -137,7 +160,7 @@ private function findParentStaticCall(ClassMethod $classMethod): ?StaticCall $staticCalls = $this->betterNodeFinder->findInstanceOf($classMethod, StaticCall::class); foreach ($staticCalls as $staticCall) { - if (! $this->isName($staticCall->class, 'parent')) { + if (! $this->isName($staticCall->class, ObjectReference::PARENT)) { continue; } @@ -153,20 +176,49 @@ private function findParentStaticCall(ClassMethod $classMethod): ?StaticCall private function shouldSkip(ClassMethod $classMethod): bool { - if ($classMethod->params === []) { + if (! $this->hasAtLeastOneParamWithoutType($classMethod)) { return true; } - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return true; - } - - $classReflection = $scope->getClassReflection(); + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); if (! $classReflection instanceof ClassReflection) { return true; } return ! $classReflection->isClass(); } + + private function isParamUsedInSpreadArg(ClassMethod $classMethod, Param $param): bool + { + /** @var Arg[] $args */ + $args = $this->betterNodeFinder->findInstancesOfScoped((array) $classMethod->stmts, Arg::class); + + $paramName = $this->getName($param); + foreach ($args as $arg) { + if (! $arg->unpack) { + continue; + } + + if ($arg->value instanceof Variable && $this->isName($arg->value, $paramName)) { + return true; + } + } + + return false; + } + + private function hasAtLeastOneParamWithoutType(ClassMethod $classMethod): bool + { + foreach ($classMethod->getParams() as $param) { + if ($param->variadic) { + continue; + } + + if ($param->type === null) { + return true; + } + } + + return false; + } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector.php index 7bad843846d..b0fc49e4443 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnNeverTypeRector.php @@ -5,32 +5,25 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; -use PhpParser\Node\Expr\Yield_; -use PhpParser\Node\Name; -use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Function_; -use PhpParser\Node\Stmt\Return_; -use PhpParser\Node\Stmt\Throw_; -use PHPStan\Type\NeverType; -use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\Defluent\ConflictGuard\ParentClassMethodTypeOverrideGuard; +use Rector\PHPStan\ScopeFetcher; +use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; +use Rector\Rector\AbstractRector; +use Rector\TypeDeclaration\NodeManipulator\AddNeverReturnType; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @changelog https://wiki.php.net/rfc/noreturn_type - * * @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\ReturnNeverTypeRectorTest */ -final class ReturnNeverTypeRector extends AbstractRector +final class ReturnNeverTypeRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard, - private PhpDocTypeChanger $phpDocTypeChanger + private readonly AddNeverReturnType $addNeverReturnType, + private readonly TestsNodeAnalyzer $testsNodeAnalyzer ) { } @@ -52,10 +45,7 @@ public function run() <<<'CODE_SAMPLE' final class SomeClass { - /** - * @return never - */ - public function run() + public function run(): never { throw new InvalidException(); } @@ -78,69 +68,33 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($this->shouldSkip($node)) { + if ($this->isTestClassMethodWithFilledReturnType($node)) { return null; } - if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::NEVER_TYPE)) { - // never-type supported natively - $node->returnType = new Name('never'); - } else { - // static anlysis based never type - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $this->phpDocTypeChanger->changeReturnType($phpDocInfo, new NeverType()); - } - - return $node; + $scope = ScopeFetcher::fetch($node); + return $this->addNeverReturnType->add($node, $scope); } - private function shouldSkip(ClassMethod | Function_ $node): bool + public function provideMinPhpVersion(): int { - $returns = $this->betterNodeFinder->findInstanceOf($node, Return_::class); - if ($returns !== []) { - return true; - } - - $notNeverNodes = $this->betterNodeFinder->findInstanceOf($node, Yield_::class); - if ($notNeverNodes !== []) { - return true; - } - - $neverNodes = $this->betterNodeFinder->findInstancesOf($node, [Node\Expr\Throw_::class, Throw_::class]); - - $hasNeverFuncCall = $this->hasNeverFuncCall($node); - if ($neverNodes === [] && ! $hasNeverFuncCall) { - return true; - } - - if ($node instanceof ClassMethod && ! $this->parentClassMethodTypeOverrideGuard->isReturnTypeChangeAllowed( - $node - )) { - return true; - } - - return $node->returnType && $this->isName($node->returnType, 'never'); + return PhpVersionFeature::NEVER_TYPE; } - private function hasNeverFuncCall(ClassMethod | Function_ $functionLike): bool + private function isTestClassMethodWithFilledReturnType(ClassMethod|Function_ $callLike): bool { - $hasNeverType = false; - - foreach ((array) $functionLike->stmts as $stmt) { - if ($stmt instanceof Expression) { - $stmt = $stmt->expr; - } + if (! $callLike instanceof ClassMethod) { + return false; + } - if ($stmt instanceof Stmt) { - continue; - } + if (! $callLike->isPublic()) { + return false; + } - $stmtType = $this->getStaticType($stmt); - if ($stmtType instanceof NeverType) { - $hasNeverType = true; - } + if (! $this->testsNodeAnalyzer->isInTestClass($callLike)) { + return false; } - return $hasNeverType; + return $callLike->returnType instanceof Node; } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector.php new file mode 100644 index 00000000000..580032d088b --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector.php @@ -0,0 +1,124 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NULLABLE_TYPE; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + // empty body, nothing to resolve + if ($node->stmts === null || $node->stmts === []) { + return null; + } + + // type is already known, skip + if ($node->returnType instanceof Node) { + return null; + } + + if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $node, + $scope + )) { + return null; + } + + $inferReturnType = $this->returnTypeInferer->inferFunctionLike($node); + if (! $inferReturnType instanceof UnionType) { + return null; + } + + $returnType = $this->unionTypeMapper->mapToPhpParserNode($inferReturnType, TypeKind::RETURN); + if (! $returnType instanceof Node) { + return null; + } + + // handled by union PHP 8.0 rule + if (! $returnType instanceof NullableType) { + return null; + } + + $node->returnType = $returnType; + return $node; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector.php new file mode 100644 index 00000000000..5bcfa0f3f0d --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromMockObjectRector.php @@ -0,0 +1,153 @@ +createMock(SomeClass::class); + return $someMock; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeTest extends TestCase +{ + public function createSomeMock(): \PHPUnit\Framework\MockObject\MockObject + { + $someMock = $this->createMock(SomeClass::class); + return $someMock; + } +} +CODE_SAMPLE + ), + + ]); + } + + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + // type is already known + if ($node->returnType instanceof Node) { + return null; + } + + if (! $this->isInsideTestCaseClass($scope)) { + return null; + } + + if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { + return null; + } + + // we need exactly 1 return + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (count($returns) !== 1) { + return null; + } + + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + /** @var Expr $expr */ + $expr = $returns[0]->expr; + $returnType = $this->nodeTypeResolver->getNativeType($expr); + + if (! $this->isMockObjectType($returnType)) { + return null; + } + + $node->returnType = new FullyQualified(ClassName::MOCK_OBJECT); + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + private function isIntersectionWithMockObjectType(Type $type): bool + { + if (! $type instanceof IntersectionType) { + return false; + } + + if (count($type->getTypes()) !== 2) { + return false; + } + + return in_array(ClassName::MOCK_OBJECT, $type->getObjectClassNames()); + } + + private function isMockObjectType(Type $returnType): bool + { + if ($returnType instanceof ObjectType && $returnType->isInstanceOf(ClassName::MOCK_OBJECT)->yes()) { + return true; + } + + return $this->isIntersectionWithMockObjectType($returnType); + } + + private function isInsideTestCaseClass(Scope $scope): bool + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + // is phpunit test case? + return $classReflection->is(ClassName::TEST_CASE_CLASS); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector.php new file mode 100644 index 00000000000..ef38170132d --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnCastRector.php @@ -0,0 +1,86 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + return $this->addReturnTypeFromCast->add($node, $scope); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector.php new file mode 100644 index 00000000000..bfd15cd8eab --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnDirectArrayRector.php @@ -0,0 +1,128 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + // already has return type, skip + if ($node->returnType instanceof Node) { + return null; + } + + if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $node, + $scope + )) { + return null; + } + + if (! $this->hasReturnArray($node)) { + return null; + } + + $type = $this->returnTypeInferer->inferFunctionLike($node); + if (! $type->isArray()->yes()) { + return null; + } + + $node->returnType = new Identifier('array'); + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + private function hasReturnArray(ClassMethod|Function_ $functionLike): bool + { + $stmts = $functionLike->stmts; + + if (! is_array($stmts)) { + return false; + } + + foreach ($stmts as $stmt) { + if (! $stmt instanceof Return_) { + continue; + } + + if (! $stmt->expr instanceof Array_) { + continue; + } + + return true; + } + + return false; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector.php index a6787ccbd48..37019622852 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector.php @@ -5,17 +5,37 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; -use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\New_; +use PhpParser\Node\Identifier; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Return_; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use PHPStan\Type\ObjectWithoutClassType; +use PHPStan\Type\StaticType; +use PHPStan\Type\Type; +use Rector\Enum\ObjectReference; +use Rector\Exception\ShouldNotHappenException; +use Rector\NodeAnalyzer\ClassAnalyzer; +use Rector\NodeTypeResolver\NodeTypeResolver\NewTypeResolver; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\StaticTypeMapper\ValueObject\Type\SelfStaticType; +use Rector\Symfony\CodeQuality\Enum\ResponseClass; +use Rector\Symfony\TypeAnalyzer\ControllerAnalyzer; +use Rector\TypeDeclaration\NodeAnalyzer\ReturnAnalyzer; +use Rector\TypeDeclaration\NodeAnalyzer\ReturnTypeAnalyzer\StrictReturnNewAnalyzer; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -26,7 +46,17 @@ final class ReturnTypeFromReturnNewRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private TypeFactory $typeFactory + private readonly TypeFactory $typeFactory, + private readonly ReflectionProvider $reflectionProvider, + private readonly ReflectionResolver $reflectionResolver, + private readonly StrictReturnNewAnalyzer $strictReturnNewAnalyzer, + private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, + private readonly ClassAnalyzer $classAnalyzer, + private readonly NewTypeResolver $newTypeResolver, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly StaticTypeMapper $staticTypeMapper, + private readonly ReturnAnalyzer $returnAnalyzer, + private readonly ControllerAnalyzer $controllerAnalyzer ) { } @@ -37,9 +67,9 @@ public function getRuleDefinition(): RuleDefinition <<<'CODE_SAMPLE' final class SomeClass { - public function action() + public function create() { - return new Response(); + return new Project(); } } CODE_SAMPLE @@ -47,9 +77,9 @@ public function action() <<<'CODE_SAMPLE' final class SomeClass { - public function action(): Response + public function create(): Project { - return new Response(); + return new Project(); } } CODE_SAMPLE @@ -62,53 +92,160 @@ public function action(): Response */ public function getNodeTypes(): array { - return [ClassMethod::class, Function_::class, ArrowFunction::class]; + return [ClassMethod::class, Function_::class]; } /** - * @param ClassMethod|Function_|ArrowFunction $node + * @param ClassMethod|Function_ $node */ public function refactor(Node $node): ?Node { - if ($node->returnType !== null) { + $scope = ScopeFetcher::fetch($node); + // already filled + if ($node->returnType instanceof Node) { return null; } - if ($node instanceof ArrowFunction) { - $returns = [new Return_($node->expr)]; - } else { - /** @var Return_[] $returns */ - $returns = $this->betterNodeFinder->findInstanceOf((array) $node->stmts, Return_::class); + if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $node, + $scope + )) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + $returnedNewClassName = $this->strictReturnNewAnalyzer->matchAlwaysReturnVariableNew($node); + + if ($returnedNewClassName === 'object') { + $node->returnType = new Identifier('object'); + return $node; + } + + if (is_string($returnedNewClassName)) { + $node->returnType = new FullyQualified($returnedNewClassName); + + return $node; + } + + return $this->refactorDirectReturnNew($node, $returns); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + private function createObjectTypeFromNew(New_ $new): ObjectType|ObjectWithoutClassType|StaticType|null + { + if ($this->classAnalyzer->isAnonymousClass($new->class)) { + $newType = $this->newTypeResolver->resolve($new); + if (! $newType instanceof ObjectWithoutClassType) { + return null; + } + + return $newType; + } + + if (! $new->class instanceof Name) { + return null; + } + + $className = $this->getName($new->class); + if ($className === ObjectReference::STATIC || $className === ObjectReference::SELF) { + $classReflection = $this->reflectionResolver->resolveClassReflection($new); + if (! $classReflection instanceof ClassReflection) { + throw new ShouldNotHappenException(); + } + + if ($className === ObjectReference::SELF) { + return new SelfStaticType($classReflection); + } + + return new StaticType($classReflection); } - if ($returns === []) { + if (! $this->reflectionProvider->hasClass($className)) { return null; } + $classReflection = $this->reflectionProvider->getClass($className); + return new ObjectType($className, null, $classReflection); + } + + /** + * @template TFunctionLike as ClassMethod|Function_ + * + * @param TFunctionLike $functionLike + * @param Return_[] $returns + * @return TFunctionLike|null + */ + private function refactorDirectReturnNew( + ClassMethod|Function_ $functionLike, + array $returns + ): null|Function_|ClassMethod { + $newTypes = $this->resolveReturnNewType($returns); + if ($newTypes === null) { + return null; + } + + $returnType = $this->typeFactory->createMixedPassedOrUnionType($newTypes); + + /** handled by @see \Rector\Symfony\CodeQuality\Rector\ClassMethod\ResponseReturnTypeControllerActionRector earlier */ + if ($this->isResponseInsideController($returnType, $functionLike)) { + return null; + } + + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + return null; + } + + $functionLike->returnType = $returnTypeNode; + + return $functionLike; + } + + /** + * @param Return_[] $returns + * @return Type[]|null + */ + private function resolveReturnNewType(array $returns): ?array + { $newTypes = []; foreach ($returns as $return) { if (! $return->expr instanceof New_) { return null; } - $new = $return->expr; - if (! $new->class instanceof Name) { + $newType = $this->createObjectTypeFromNew($return->expr); + if (! $newType instanceof Type) { return null; } - $className = $this->getName($new->class); - $newTypes[] = new ObjectType($className); + $newTypes[] = $newType; } - $returnType = $this->typeFactory->createMixedPassedOrUnionType($newTypes); - $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN()); - $node->returnType = $returnTypeNode; - - return $node; + return $newTypes; } - public function provideMinPhpVersion(): int + private function isResponseInsideController(Type $returnType, ClassMethod|Function_ $functionLike): bool { - return PhpVersionFeature::SCALAR_TYPES; + if (! $functionLike instanceof ClassMethod) { + return false; + } + + if (! $returnType instanceof ObjectType) { + return false; + } + + if (! $returnType->isInstanceOf(ResponseClass::BASIC)->yes()) { + return false; + } + + return $this->controllerAnalyzer->isInsideController($functionLike); } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector.php new file mode 100644 index 00000000000..a1aadb061b0 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictConstantReturnRector.php @@ -0,0 +1,138 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($node->returnType instanceof Node) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { + return null; + } + + $matchedType = $this->matchAlwaysReturnConstFetch($returns); + if (! $matchedType instanceof Type) { + return null; + } + + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($matchedType, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + return null; + } + + $node->returnType = $returnTypeNode; + + return $node; + } + + /** + * @return PhpVersion::* + */ + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_70; + } + + /** + * @param Return_[] $returns + */ + private function matchAlwaysReturnConstFetch(array $returns): ?Type + { + $classConstFetchTypes = []; + + foreach ($returns as $return) { + if (! $return->expr instanceof ClassConstFetch && ! $return->expr instanceof ConstFetch) { + return null; + } + + $classConstFetchTypes[] = $this->nodeTypeResolver->getType($return->expr); + } + + return $this->typeFactory->createMixedPassedOrUnionType($classConstFetchTypes); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector.php new file mode 100644 index 00000000000..7c7a4d8e4b7 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictFluentReturnRector.php @@ -0,0 +1,139 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::HAS_RETURN_TYPE; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + // already typed → skip + if ($node->returnType instanceof Node) { + return null; + } + + if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { + return null; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $returnType = $this->returnTypeInferer->inferFunctionLike($node); + + if ($returnType instanceof StaticType && $returnType->getStaticObjectType()->getClassName() === $classReflection->getName()) { + return $this->processAddReturnSelfOrStatic($node, $classReflection); + } + + if ($returnType instanceof ObjectType && $returnType->getClassName() === $classReflection->getName()) { + $node->returnType = new Name('self'); + return $node; + } + + if (! $returnType instanceof ThisType) { + return null; + } + + return $this->processAddReturnSelfOrStatic($node, $classReflection); + } + + private function processAddReturnSelfOrStatic( + ClassMethod $classMethod, + ClassReflection $classReflection + ): ClassMethod { + $classMethod->returnType = $this->shouldSelf($classReflection) + ? new Name('self') + : new Name('static'); + + return $classMethod; + } + + private function shouldSelf(ClassReflection $classReflection): bool + { + if ($classReflection->isAnonymous()) { + return true; + } + + if ($classReflection->isFinalByKeyword()) { + return true; + } + + return ! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::STATIC_RETURN_TYPE); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector.php new file mode 100644 index 00000000000..dc110215b7d --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNativeCallRector.php @@ -0,0 +1,77 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + return $this->addReturnTypeFromStrictNativeCall->add($node, $scope); + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_70; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector.php new file mode 100644 index 00000000000..d0372b24e59 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictNewArrayRector.php @@ -0,0 +1,287 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkip($node, $scope)) { + return null; + } + + // 1. is variable instantiated with array + $stmts = $node->stmts; + if ($stmts === null) { + return null; + } + + $variables = $this->matchArrayAssignedVariable($stmts); + if ($variables === []) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + $variables = $this->matchVariableNotOverriddenByNonArray($node, $variables); + if ($variables === []) { + return null; + } + + if (count($returns) > 1) { + $returnType = $this->returnTypeInferer->inferFunctionLike($node); + return $this->processAddArrayReturnType($node, $returnType); + } + + $onlyReturn = $returns[0]; + if (! $onlyReturn->expr instanceof Variable) { + return null; + } + + if (! $this->nodeComparator->isNodeEqual($onlyReturn->expr, $variables)) { + return null; + } + + $returnType = $this->nodeTypeResolver->getNativeType($onlyReturn->expr); + return $this->processAddArrayReturnType($node, $returnType); + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_70; + } + + private function processAddArrayReturnType( + ClassMethod|Function_|Closure $node, + Type $returnType + ): ClassMethod|Function_|Closure|null { + if (! $returnType->isArray()->yes()) { + return null; + } + + // always returns array + $node->returnType = new Identifier('array'); + + // add more precise array type if suitable + if ($this->shouldAddReturnArrayDocType($returnType)) { + $this->changeReturnType($node, $returnType); + } + + return $node; + } + + private function shouldSkip(ClassMethod|Function_|Closure $node, Scope $scope): bool + { + if ($node->returnType instanceof Node) { + return true; + } + + return $node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $node, + $scope + ); + } + + private function changeReturnType(ClassMethod|Function_|Closure $node, Type $arrayType): void + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + // skip already filled type, on purpose + if (! $phpDocInfo->getReturnType() instanceof MixedType) { + return; + } + + // can handle only exactly 1-type array + if ($arrayType instanceof ConstantArrayType && count($arrayType->getValueTypes()) !== 1) { + return; + } + + $itemType = $arrayType->getIterableValueType(); + if ($itemType instanceof IntersectionType) { + $narrowArrayType = $arrayType; + } else { + $narrowArrayType = new ArrayType(new MixedType(), $itemType); + } + + if ($arrayType->isList()->yes()) { + $narrowArrayType = TypeCombinator::intersect($narrowArrayType, new AccessoryArrayListType()); + } + + $this->phpDocTypeChanger->changeReturnType($node, $phpDocInfo, $narrowArrayType); + } + + /** + * @param Variable[] $variables + * @return Variable[] + */ + private function matchVariableNotOverriddenByNonArray( + ClassMethod|Function_ $functionLike, + array $variables + ): array { + // is variable overridden? + /** @var Assign[] $assigns */ + $assigns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, Assign::class); + foreach ($assigns as $assign) { + if (! $assign->var instanceof Variable) { + continue; + } + + foreach ($variables as $key => $variable) { + if (! $this->nodeNameResolver->areNamesEqual($assign->var, $variable)) { + continue; + } + + if ($assign->expr instanceof Array_) { + continue; + } + + $nativeType = $this->nodeTypeResolver->getNativeType($assign->expr); + if (! $nativeType->isArray()->yes()) { + unset($variables[$key]); + } + } + } + + return $variables; + } + + /** + * @param Stmt[] $stmts + * @return Variable[] + */ + private function matchArrayAssignedVariable(array $stmts): array + { + $variables = []; + foreach ($stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof Assign) { + continue; + } + + $assign = $stmt->expr; + if (! $assign->var instanceof Variable) { + continue; + } + + $nativeType = $this->nodeTypeResolver->getNativeType($assign->expr); + if ($nativeType->isArray()->yes()) { + $variables[] = $assign->var; + } + } + + return $variables; + } + + private function shouldAddReturnArrayDocType(Type $arrayType): bool + { + if ($arrayType instanceof ConstantArrayType) { + if ($arrayType->getIterableValueType() instanceof NeverType) { + return false; + } + + // handle only simple arrays + if (! $arrayType->getIterableKeyType()->isInteger()->yes()) { + return false; + } + } + + return true; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector.php new file mode 100644 index 00000000000..4b253c834d3 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictParamRector.php @@ -0,0 +1,77 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NULLABLE_TYPE; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + return $this->addReturnTypeFromParam->add($node, $scope); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector.php index 49c7e6cda9d..5d0c483d83f 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector.php @@ -5,9 +5,8 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; -use PhpParser\Node\Expr\ArrowFunction; +use PhpParser\Node\ComplexType; use PhpParser\Node\Expr\Closure; -use PhpParser\Node\FunctionLike; use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; @@ -16,28 +15,39 @@ use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\UnionType as PhpParserUnionType; -use PHPStan\Type\NullType; +use PHPStan\Analyser\Scope; use PHPStan\Type\ObjectType; use PHPStan\Type\UnionType; -use PHPStan\Type\VoidType; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; -use Rector\TypeDeclaration\NodeAnalyzer\ReturnStrictTypeAnalyzer; +use Rector\Php\PhpVersionProvider; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\TypeDeclaration\NodeAnalyzer\ReturnAnalyzer; use Rector\TypeDeclaration\NodeAnalyzer\TypeNodeUnwrapper; +use Rector\TypeDeclaration\TypeAnalyzer\ReturnStrictTypeAnalyzer; use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector\ReturnTypeFromStrictTypedCallRectorTest */ -final class ReturnTypeFromStrictTypedCallRector extends AbstractRector +final class ReturnTypeFromStrictTypedCallRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private TypeNodeUnwrapper $typeNodeUnwrapper, - private ReturnStrictTypeAnalyzer $returnStrictTypeAnalyzer, - private ReturnTypeInferer $returnTypeInferer + private readonly TypeNodeUnwrapper $typeNodeUnwrapper, + private readonly ReturnStrictTypeAnalyzer $returnStrictTypeAnalyzer, + private readonly ReturnTypeInferer $returnTypeInferer, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly PhpVersionProvider $phpVersionProvider, + private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, + private readonly ReturnAnalyzer $returnAnalyzer, + private readonly StaticTypeMapper $staticTypeMapper ) { } @@ -83,88 +93,75 @@ private function getNumber(): int */ public function getNodeTypes(): array { - return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class]; + return [ClassMethod::class, Function_::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; } /** - * @param ClassMethod|Function_|Closure|ArrowFunction $node + * @param ClassMethod|Function_ $node */ public function refactor(Node $node): ?Node { - if ($this->isSkipped($node)) { + $scope = ScopeFetcher::fetch($node); + // already filled → skip + if ($node->returnType instanceof Node) { return null; } - if ($node instanceof ArrowFunction) { - return $this->processArrowFunction($node); + if ($node->stmts === null) { + return null; } - /** @var Return_[] $returns */ - $returns = $this->betterNodeFinder->find((array) $node->stmts, function (Node $n) use ($node): bool { - $currentFunctionLike = $this->betterNodeFinder->findParentType($n, FunctionLike::class); - - if ($currentFunctionLike === $node) { - return $n instanceof Return_; - } - - $currentReturn = $this->betterNodeFinder->findParentType($n, Return_::class); - if (! $currentReturn instanceof Return_) { - return false; - } - - $currentFunctionLike = $this->betterNodeFinder->findParentType($currentReturn, FunctionLike::class); - if ($currentFunctionLike !== $node) { - return false; - } + if ($this->shouldSkip($node, $scope)) { + return null; + } - return $n instanceof Return_; - }); + $currentScopeReturns = $this->betterNodeFinder->findReturnsScoped($node); - $returnedStrictTypes = $this->returnStrictTypeAnalyzer->collectStrictReturnTypes($returns); + $returnedStrictTypes = $this->returnStrictTypeAnalyzer->collectStrictReturnTypes($currentScopeReturns, $scope); if ($returnedStrictTypes === []) { return null; } + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $currentScopeReturns)) { + return null; + } + if (count($returnedStrictTypes) === 1) { - return $this->refactorSingleReturnType($returns[0], $returnedStrictTypes[0], $node); + return $this->refactorSingleReturnType($currentScopeReturns[0], $returnedStrictTypes[0], $node); } if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES)) { /** @var PhpParserUnionType[] $returnedStrictTypes */ $unwrappedTypes = $this->typeNodeUnwrapper->unwrapNullableUnionTypes($returnedStrictTypes); - $returnType = new PhpParserUnionType($unwrappedTypes); - $node->returnType = $returnType; - return $node; - } - - return null; - } + $unionType = new PhpParserUnionType($unwrappedTypes); - private function processArrowFunction(ArrowFunction $arrowFunction): ?ArrowFunction - { - $resolvedType = $this->nodeTypeResolver->resolve($arrowFunction->expr); + $type = $this->staticTypeMapper->mapPhpParserNodePHPStanType($unionType); + $returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::RETURN); - // void type is not accepted for arrow functions - https://www.php.net/manual/en/functions.arrow.php#125673 - if ($resolvedType instanceof VoidType) { - return null; - } + // verify type transformed into node + if (! $returnType instanceof Node) { + return null; + } - $returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($resolvedType, TypeKind::RETURN()); + $node->returnType = $unionType; - if (! $returnType instanceof Node) { - return null; + return $node; } - $arrowFunction->returnType = $returnType; - return $arrowFunction; + return null; } - private function isUnionPossibleReturnsVoid(ClassMethod | Function_ | Closure | ArrowFunction $node): bool + private function isUnionPossibleReturnsVoid(ClassMethod | Function_ | Closure $node): bool { $inferReturnType = $this->returnTypeInferer->inferFunctionLike($node); if ($inferReturnType instanceof UnionType) { foreach ($inferReturnType->getTypes() as $type) { - if ($type instanceof VoidType) { + if ($type->isVoid()->yes()) { return true; } } @@ -179,7 +176,7 @@ private function processSingleUnionType( NullableType $nullableType ): Closure | ClassMethod | Function_ { $types = $unionType->getTypes(); - $returnType = $types[0] instanceof ObjectType && $types[1] instanceof NullType + $returnType = $types[0] instanceof ObjectType && $types[1]->isNull()->yes() ? new NullableType(new FullyQualified($types[0]->getClassName())) : $nullableType; @@ -187,30 +184,28 @@ private function processSingleUnionType( return $node; } - private function isSkipped(ClassMethod | Function_ | Closure | ArrowFunction $node): bool + private function shouldSkip(ClassMethod | Function_ | Closure $node, Scope $scope): bool { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + if ($node->returnType instanceof Node) { return true; } - if ($node->returnType !== null) { + if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $node, + $scope + )) { return true; } - if (! $node instanceof ClassMethod) { - return $this->isUnionPossibleReturnsVoid($node); - } - if (! $node->isMagic()) { - return $this->isUnionPossibleReturnsVoid($node); - } - return true; + + return $this->isUnionPossibleReturnsVoid($node); } private function refactorSingleReturnType( Return_ $return, - Identifier | Name | NullableType | PhpParserUnionType $returnedStrictTypeNode, + Identifier|Name|NullableType|ComplexType $returnedStrictTypeNode, ClassMethod | Function_ | Closure $functionLike ): Closure | ClassMethod | Function_ { - $resolvedType = $this->nodeTypeResolver->resolve($return); + $resolvedType = $this->nodeTypeResolver->getType($return); if ($resolvedType instanceof UnionType) { if (! $returnedStrictTypeNode instanceof NullableType) { diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector.php index 6d86f80d4ac..0032b240dc7 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictTypedPropertyRector.php @@ -5,17 +5,23 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Return_; use PHPStan\Reflection\Php\PhpPropertyReflection; use PHPStan\Type\MixedType; use PHPStan\Type\Type; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\Reflection\ReflectionResolver; -use Rector\Core\ValueObject\PhpVersionFeature; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PHPStan\ScopeFetcher; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\TypeDeclaration\NodeAnalyzer\ReturnAnalyzer; +use Rector\ValueObject\PhpVersionFeature; +use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -26,8 +32,12 @@ final class ReturnTypeFromStrictTypedPropertyRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private TypeFactory $typeFactory, - private ReflectionResolver $reflectionResolver + private readonly TypeFactory $typeFactory, + private readonly ReflectionResolver $reflectionResolver, + private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, + private readonly BetterNodeFinder $betterNodeFinder, + private readonly StaticTypeMapper $staticTypeMapper, + private readonly ReturnAnalyzer $returnAnalyzer ) { } @@ -76,7 +86,12 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($node->returnType !== null) { + if ($node->returnType instanceof Node) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { return null; } @@ -91,7 +106,7 @@ public function refactor(Node $node): ?Node return null; } - $propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyType, TypeKind::RETURN()); + $propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyType, TypeKind::RETURN); if (! $propertyTypeNode instanceof Node) { return null; } @@ -111,17 +126,16 @@ public function provideMinPhpVersion(): int */ private function resolveReturnPropertyType(ClassMethod $classMethod): array { - /** @var Return_[] $returns */ - $returns = $this->betterNodeFinder->findInstanceOf($classMethod, Return_::class); + $returns = $this->betterNodeFinder->findReturnsScoped($classMethod); $propertyTypes = []; foreach ($returns as $return) { - if ($return->expr === null) { + if (! $return->expr instanceof Expr) { return []; } - if (! $return->expr instanceof PropertyFetch) { + if (! $return->expr instanceof PropertyFetch && ! $return->expr instanceof StaticPropertyFetch) { return []; } @@ -137,7 +151,11 @@ private function resolveReturnPropertyType(ClassMethod $classMethod): array return []; } - $propertyTypes[] = $phpPropertyReflection->getNativeType(); + $propertyTypes[] = $this->nodeTypeResolver->getNativeType($return->expr); + } + + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($classMethod, $returns)) { + return []; } return $propertyTypes; diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector.php new file mode 100644 index 00000000000..f9c00821391 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromSymfonySerializerRector.php @@ -0,0 +1,135 @@ +serializer->deserialize($data, SomeType::class, 'json'); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private \Symfony\Component\Serializer\Serializer $serializer; + + public function resolveEntity($data): SomeType + { + return $this->serializer->deserialize($data, SomeType::class, 'json'); + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::HAS_RETURN_TYPE; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + if ($node->returnType instanceof Node) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { + return null; + } + + if (count($node->stmts) !== 1) { + return null; + } + + if (! $node->stmts[0] instanceof Return_ || ! $node->stmts[0]->expr instanceof MethodCall) { + return null; + } + + $returnExpr = $node->stmts[0]->expr; + if (! $this->isName($returnExpr->name, 'deserialize')) { + return null; + } + + if ($returnExpr->isFirstClassCallable()) { + return null; + } + + if (! $this->isObjectType($returnExpr->var, new ObjectType('Symfony\Component\Serializer\Serializer'))) { + return null; + } + + $args = $returnExpr->getArgs(); + if ($this->argsAnalyzer->hasNamedArg($args)) { + return null; + } + + if (count($args) !== 3) { + return null; + } + + $type = $this->valueResolver->getValue($args[1]->value); + if (! is_string($type)) { + return null; + } + + $node->returnType = new FullyQualified($type); + return $node; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector.php new file mode 100644 index 00000000000..0e560c7ae00 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnUnionTypeRector.php @@ -0,0 +1,92 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::UNION_TYPES; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + return $this->addUnionReturnType->add($node, $scope); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php b/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php new file mode 100644 index 00000000000..9f48e7fdffa --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php @@ -0,0 +1,309 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class, Closure::class]; + } + + /** + * @param ClassMethod|Function_|Closure $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + if ($node instanceof ClassMethod && $this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($node)) { + return null; + } + + foreach ($node->getParams() as $param) { + if ($param->type instanceof Node) { + continue; + } + + if ($param->variadic) { + continue; + } + + if ($param->default instanceof Expr && ! $this->getType($param->default)->isArray()->yes()) { + continue; + } + + if (! $this->isParamAccessedArrayDimFetch($param, $node)) { + continue; + } + + $param->type = new Identifier('array'); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function isParamAccessedArrayDimFetch(Param $param, ClassMethod|Function_|Closure $functionLike): bool + { + if ($functionLike->stmts === null) { + return false; + } + + $paramName = $this->getName($param); + + $isParamAccessedArrayDimFetch = false; + $this->traverseNodesWithCallable($functionLike->stmts, function (Node $node) use ( + $paramName, + &$isParamAccessedArrayDimFetch, + ): int|null { + if ($node instanceof Class_ || $node instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($this->shouldStop($node, $paramName)) { + // force set to false to avoid too early replaced + $isParamAccessedArrayDimFetch = false; + return NodeVisitor::STOP_TRAVERSAL; + } + + if (! $node instanceof ArrayDimFetch) { + return null; + } + + if (! $node->dim instanceof Expr) { + return null; + } + + if (! $node->var instanceof Variable) { + return null; + } + + if (! $this->isName($node->var, $paramName)) { + return null; + } + + // skip possible strings + $variableType = $this->getType($node->var); + if ($variableType->isString()->yes()) { + // force set to false to avoid too early replaced + $isParamAccessedArrayDimFetch = false; + return NodeVisitor::STOP_TRAVERSAL; + } + + // skip integer in possibly string type as string can be accessed via int + $dimType = $this->getType($node->dim); + if ($dimType->isInteger()->yes() && $variableType->isString()->maybe()) { + return null; + } + + $variableType = $this->typeFactory->createMixedPassedOrUnionType([$variableType]); + if ($variableType instanceof UnionType) { + $isParamAccessedArrayDimFetch = false; + return NodeVisitor::STOP_TRAVERSAL; + } + + if ($this->isArrayAccess($variableType)) { + $isParamAccessedArrayDimFetch = false; + return NodeVisitor::STOP_TRAVERSAL; + } + + $isParamAccessedArrayDimFetch = true; + return null; + }); + + return $isParamAccessedArrayDimFetch; + } + + private function isEchoed(Node $node, string $paramName): bool + { + if (! $node instanceof Echo_) { + return false; + } + + foreach ($node->exprs as $expr) { + if ($expr instanceof Variable && $this->isName($expr, $paramName)) { + return true; + } + } + + return false; + } + + private function shouldStop(Node $node, string $paramName): bool + { + $nodeToCheck = null; + + if ($node instanceof FuncCall && ! $node->isFirstClassCallable() + && $this->isNames($node, ['is_array', 'is_string', 'is_int', 'is_bool', 'is_float'])) { + $firstArg = $node->getArgs()[0]; + $nodeToCheck = $firstArg->value; + } + + if ($node instanceof Expression) { + $nodeToCheck = $node->expr; + } + + if ($node instanceof Coalesce) { + $nodeToCheck = $node->left; + } + + if ($node instanceof AssignOpCoalesce) { + $nodeToCheck = $node->var; + } + + if ($this->isMethodCall($paramName, $nodeToCheck)) { + return true; + } + + if ($nodeToCheck instanceof Variable && $this->isName($nodeToCheck, $paramName)) { + return true; + } + + if ($this->isEmptyOrEchoedOrCasted($node, $paramName)) { + return true; + } + + return $this->isReassignAndUseAsArg($node, $paramName); + } + + private function isReassignAndUseAsArg(Node $node, string $paramName): bool + { + if (! $node instanceof Assign) { + return false; + } + + if (! $node->var instanceof Variable) { + return false; + } + + if (! $this->isName($node->var, $paramName)) { + return false; + } + + if (! $node->expr instanceof CallLike) { + return false; + } + + if ($node->expr->isFirstClassCallable()) { + return false; + } + + foreach ($node->expr->getArgs() as $arg) { + if ($arg->value instanceof Variable && $this->isName($arg->value, $paramName)) { + return true; + } + } + + return false; + } + + private function isEmptyOrEchoedOrCasted(Node $node, string $paramName): bool + { + if ($node instanceof Empty_ && $node->expr instanceof Variable && $this->isName($node->expr, $paramName)) { + return true; + } + + if ($this->isEchoed($node, $paramName)) { + return true; + } + + return $node instanceof Array_ && $node->expr instanceof Variable && $this->isName($node->expr, $paramName); + } + + private function isMethodCall(string $paramName, ?Node $node): bool + { + if ($node instanceof MethodCall) { + return $node->var instanceof Variable && $this->isName($node->var, $paramName); + } + + return false; + } + + private function isArrayAccess(Type $type): bool + { + if (! $type instanceof ObjectType) { + return false; + } + + return $this->typeComparator->isSubtype($type, new ObjectType('ArrayAccess')); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php b/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php new file mode 100644 index 00000000000..1f09f021414 --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php @@ -0,0 +1,223 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class, Closure::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + /** + * @param ClassMethod|Function_|Closure $node + */ + public function refactor(Node $node): ?Node + { + if ($node instanceof ClassMethod && $this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($node)) { + return null; + } + + $hasChanged = false; + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + foreach ($node->getParams() as $param) { + if ($param->type instanceof Node) { + continue; + } + + $variableConcattedFromParam = $this->resolveVariableConcattedFromParam($param, $node); + if (! $variableConcattedFromParam instanceof Variable) { + continue; + } + + $paramDocType = $phpDocInfo->getParamType($this->getName($param)); + if (! $paramDocType instanceof MixedType && ! $paramDocType->isString()->yes()) { + continue; + } + + $nativeType = $this->nodeTypeResolver->getNativeType($variableConcattedFromParam); + if (! $nativeType instanceof MixedType) { + continue; + } + + $subtractedType = $nativeType->getSubtractedType(); + if (! $subtractedType instanceof Type) { + $param->type = new Identifier('string'); + $hasChanged = true; + + continue; + } + + if (TypeCombinator::containsNull($subtractedType)) { + $param->type = new NullableType(new Identifier('string')); + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function resolveVariableConcattedFromParam( + Param $param, + ClassMethod|Function_|Closure $functionLike + ): ?Variable { + if ($functionLike->stmts === null) { + return null; + } + + if ($param->default instanceof Expr && ! $this->getType($param->default)->isString()->yes()) { + return null; + } + + $paramName = $this->getName($param); + $variableConcatted = null; + + $this->traverseNodesWithCallable($functionLike->stmts, function (Node $node) use ( + $paramName, + &$variableConcatted, + ): int|null { + // skip nested class and function nodes + if ($node instanceof FunctionLike || $node instanceof Class_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($node instanceof Assign && $node->var instanceof Variable && $this->isName($node->var, $paramName)) { + $variableConcatted = null; + return NodeVisitor::STOP_TRAVERSAL; + } + + $expr = $this->resolveAssignConcatVariable($node, $paramName); + if ($expr instanceof Variable) { + $variableConcatted = $expr; + } + + $variableBinaryConcat = $this->resolveBinaryConcatVariable($node, $paramName); + if ($variableBinaryConcat instanceof Variable) { + $variableConcatted = $variableBinaryConcat; + } + + return null; + }); + + return $variableConcatted; + } + + private function isVariableWithSameParam(Expr $expr, string $paramName): bool + { + if (! $expr instanceof Variable) { + return false; + } + + return $this->isName($expr, $paramName); + } + + private function resolveAssignConcatVariable(Node $node, string $paramName): ?Expr + { + if (! $node instanceof Concat) { + return null; + } + + if ($this->isVariableWithSameParam($node->var, $paramName)) { + return $node->var; + } + + if ($this->isVariableWithSameParam($node->expr, $paramName)) { + return $node->expr; + } + + return null; + } + + private function resolveBinaryConcatVariable(Node $node, string $paramName): ?Expr + { + if (! $node instanceof Expr\BinaryOp\Concat) { + return null; + } + + if ($this->isVariableWithSameParam($node->left, $paramName)) { + return $node->left; + } + + if ($this->isVariableWithSameParam($node->right, $paramName)) { + return $node->right; + } + + return null; + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector.php new file mode 100644 index 00000000000..dbe9080cc1d --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector.php @@ -0,0 +1,124 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + // already added → skip + if ($node->returnType instanceof Node) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + foreach ($returns as $return) { + // we need exact string "value" return + if (! $return->expr instanceof String_ && ! $return->expr instanceof InterpolatedString) { + return null; + } + } + + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkipClassMethodForOverride($node, $scope)) { + return null; + } + + $node->returnType = new Identifier('string'); + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_70; + } + + private function shouldSkipClassMethodForOverride(ClassMethod|Function_ $functionLike, Scope $scope): bool + { + if (! $functionLike instanceof ClassMethod) { + return false; + } + + return $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($functionLike, $scope); + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php new file mode 100644 index 00000000000..dfad709348b --- /dev/null +++ b/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php @@ -0,0 +1,164 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + // already added → skip + if ($node->returnType instanceof Node) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + // handled by another rule + if ($this->hasAlwaysStringScalarReturn($returns)) { + return null; + } + + // anything that return strict string, but no strings only + if (! $this->isAlwaysStringStrictType($returns)) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkipClassMethodForOverride($node, $scope)) { + return null; + } + + $node->returnType = new Identifier('string'); + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_70; + } + + private function shouldSkipClassMethodForOverride(ClassMethod|Function_ $functionLike, Scope $scope): bool + { + if (! $functionLike instanceof ClassMethod) { + return false; + } + + return $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($functionLike, $scope); + } + + /** + * @param Return_[] $returns + */ + private function hasAlwaysStringScalarReturn(array $returns): bool + { + foreach ($returns as $return) { + // we need exact string "value" return + if (! $return->expr instanceof String_ && ! $return->expr instanceof InterpolatedString) { + return false; + } + } + + return true; + } + + /** + * @param Return_[] $returns + */ + private function isAlwaysStringStrictType(array $returns): bool + { + foreach ($returns as $return) { + // void return + if (! $return->expr instanceof Expr) { + return false; + } + + $exprType = $this->nodeTypeResolver->getNativeType($return->expr); + if (! $exprType->isString()->yes()) { + return false; + } + } + + return true; + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php b/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php new file mode 100644 index 00000000000..d9982405e35 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php @@ -0,0 +1,110 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $hasChanged = false; + foreach ($node->getMethods() as $classMethod) { + // has type already + if ($classMethod->returnType instanceof Node) { + continue; + } + + if (! $this->testsNodeAnalyzer->isTestClassMethod($classMethod)) { + continue; + } + + if ($classMethod->isAbstract()) { + continue; + } + + if (! $this->silentVoidResolver->hasExclusiveVoid($classMethod)) { + continue; + } + + $classMethod->returnType = new Identifier('void'); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::VOID_TYPE; + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php b/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php new file mode 100644 index 00000000000..3ec0cb6d1ad --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php @@ -0,0 +1,216 @@ + + */ +final class SomeRepository extends EntityRepository +{ + public function getActiveItem() + { + return $this->findOneBy([ + 'something' + ]); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use Doctrine\ORM\EntityRepository; + +/** + * @extends EntityRepository + */ +final class SomeRepository extends EntityRepository +{ + public function getActiveItem(): ?SomeType + { + return $this->findOneBy([ + 'something' + ]); + } +} +CODE_SAMPLE + ), + + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isObjectType($node, new ObjectType('Doctrine\ORM\EntityRepository'))) { + return null; + } + + $entityClassName = $this->resolveEntityClassnameFromPhpDoc($node); + if ($entityClassName === null) { + return null; + } + + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($this->shouldSkipClassMethod($classMethod)) { + continue; + } + + if ($this->containsMethodCallNamed($classMethod, 'getOneOrNullResult')) { + $classMethod->returnType = $this->createNullableType($entityClassName); + } elseif ($this->containsMethodCallNamed($classMethod, 'findOneBy')) { + $classMethod->returnType = $this->createNullableType($entityClassName); + } + + if ($this->containsMethodCallNamed($classMethod, 'findBy')) { + $classMethod->returnType = new Identifier('array'); + // add docblock with type + + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + + $arrayTypeNode = new ArrayTypeNode(new IdentifierTypeNode($entityClassName)); + $classMethodPhpDocInfo->addTagValueNode(new ReturnTagValueNode($arrayTypeNode, '')); + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); + } + + $hasChanged = true; + // try to figure out the return type + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function resolveEntityClassnameFromPhpDoc(Class_ $class): ?string + { + $classPhpDocInfo = $this->phpDocInfoFactory->createFromNode($class); + + // we need a way to resolve entity type... 1st idea is from @extends docblock + if (! $classPhpDocInfo instanceof PhpDocInfo) { + return null; + } + + $extendsTagValuePhpDocNodes = $classPhpDocInfo->getTagsByName('extends'); + + if ($extendsTagValuePhpDocNodes === []) { + return null; + } + + $extendsTagValueNode = $extendsTagValuePhpDocNodes[0]->value; + if (! $extendsTagValueNode instanceof ExtendsTagValueNode) { + return null; + } + + $genericTypeNode = $extendsTagValueNode->type; + if ($genericTypeNode->type->name !== 'EntityRepository') { + return null; + } + + $entityGenericType = $genericTypeNode->genericTypes[0]; + if (! $entityGenericType instanceof IdentifierTypeNode) { + return null; + } + + // skip if value is used in generics + if (in_array($entityGenericType->name, $classPhpDocInfo->getTemplateNames(), true)) { + return null; + } + + return $entityGenericType->name; + } + + private function containsMethodCallNamed(ClassMethod $classMethod, string $desiredMethodName): bool + { + return (bool) $this->nodeFinder->findFirst((array) $classMethod->stmts, static function (Node $node) use ( + $desiredMethodName + ): bool { + if (! $node instanceof MethodCall) { + return false; + } + + if (! $node->name instanceof Identifier) { + return false; + } + + $currentMethodCallName = $node->name->toString(); + return $currentMethodCallName === $desiredMethodName; + }); + } + + private function shouldSkipClassMethod(ClassMethod $classMethod): bool + { + if (! $classMethod->isPublic()) { + return true; + } + + if ($classMethod->isStatic()) { + return true; + } + + return $classMethod->returnType instanceof Node; + } + + private function createNullableType(string $entityClassName): NullableType + { + $name = new Name($entityClassName); + return new NullableType($name); + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php b/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php new file mode 100644 index 00000000000..08d6768dfff --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php @@ -0,0 +1,124 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + foreach ($node->getProperties() as $property) { + if (! $property->type instanceof Node) { + continue; + } + + if (! $property->type instanceof FullyQualified) { + continue; + } + + if ($property->type->toString() !== 'DateTime') { + continue; + } + + if (! $property->isPrivate()) { + continue; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($property); + if (! $phpDocInfo instanceof PhpDocInfo) { + continue; + } + + $varType = $phpDocInfo->getVarType(); + $className = $varType instanceof TypeWithClassName + ? $this->nodeTypeResolver->getFullyQualifiedClassName($varType) + : null; + if ($className === 'DateTimeInterface') { + $varTagvalueNode = $phpDocInfo->getVarTagValueNode(); + if ($varTagvalueNode instanceof VarTagValueNode && $varTagvalueNode->description === '') { + $phpDocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); + } + + $property->type = new FullyQualified('DateTimeInterface'); + $hasChanged = true; + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_PROPERTIES; + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector.php b/rules/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector.php new file mode 100644 index 00000000000..30edfc962c9 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/ObjectTypedPropertyFromJMSSerializerAttributeTypeRector.php @@ -0,0 +1,135 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ATTRIBUTES; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->jmsTypeAnalyzer->hasAtLeastOneUntypedPropertyUsingJmsAttribute($node)) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + if ($this->shouldSkipProperty($property, $classReflection)) { + continue; + } + + $typeValue = $this->jmsTypeAnalyzer->resolveTypeAttributeValue($property); + if (! is_string($typeValue)) { + continue; + } + + $propertyTypeNode = $this->jmsTypePropertyTypeFactory->createObjectTypeNode($typeValue); + if (! $propertyTypeNode instanceof FullyQualified) { + continue; + } + + $property->type = new NullableType($propertyTypeNode); + $property->props[0]->default = $this->nodeFactory->createNull(); + + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function shouldSkipProperty(Property $property, ClassReflection $classReflection): bool + { + if ($property->type instanceof Node || $property->props[0]->default instanceof Expr) { + return true; + } + + if (! $this->jmsTypeAnalyzer->hasPropertyJMSTypeAttribute($property)) { + return true; + } + + return ! $this->makePropertyTypedGuard->isLegal($property, $classReflection); + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php b/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php new file mode 100644 index 00000000000..e27356c026e --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php @@ -0,0 +1,279 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private string $name = 'John'; + + public function setName(string $name): void + { + $this->name = $name; + } + + public function getName(): string + { + return $this->name; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + $classReflection = null; + + foreach ($node->getProperties() as $property) { + if ($property->type instanceof Node) { + continue; + } + + if (! $property->isPrivate()) { + continue; + } + + $getterSetterPropertyType = $this->matchGetterSetterIdenticalType($property, $node); + if (! $getterSetterPropertyType instanceof Type) { + continue; + } + + $hasPropertyDefaultNull = $this->hasPropertyDefaultNull($property); + + if (! $hasPropertyDefaultNull && ! $this->isDefaultExprTypeCompatible( + $property, + $getterSetterPropertyType + )) { + continue; + } + + if (! $classReflection instanceof ClassReflection) { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + } + + if (! $classReflection instanceof ClassReflection) { + return null; + } + + if (! $this->makePropertyTypedGuard->isLegal($property, $classReflection, false)) { + continue; + } + + $propertyTypeDeclaration = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $getterSetterPropertyType, + TypeKind::PROPERTY + ); + + if (! $propertyTypeDeclaration instanceof Node) { + continue; + } + + $this->decorateDefaultExpr($getterSetterPropertyType, $property, $hasPropertyDefaultNull); + + $property->type = $propertyTypeDeclaration; + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_PROPERTIES; + } + + private function matchGetterSetterIdenticalType(Property $property, Class_ $class): ?Type + { + $getterBasedStrictType = $this->getterTypeDeclarationPropertyTypeInferer->inferProperty($property, $class); + if (! $getterBasedStrictType instanceof Type) { + return null; + } + + $setterBasedStrictType = $this->setterTypeDeclarationPropertyTypeInferer->inferProperty($property, $class); + if (! $setterBasedStrictType instanceof Type) { + return null; + } + + // single type + if ($setterBasedStrictType->equals($getterBasedStrictType)) { + return $setterBasedStrictType; + } + + if ($getterBasedStrictType instanceof UnionType) { + $getterBasedStrictTypes = $getterBasedStrictType->getTypes(); + } else { + $getterBasedStrictTypes = [$getterBasedStrictType]; + } + + if ($setterBasedStrictType instanceof UnionType) { + $setterBasedStrictTypes = $setterBasedStrictType->getTypes(); + } else { + $setterBasedStrictTypes = [$setterBasedStrictType]; + } + + return new UnionType([...$setterBasedStrictTypes, ...$getterBasedStrictTypes]); + } + + private function isDefaultExprTypeCompatible(Property $property, Type $getterSetterPropertyType): bool + { + $defaultExpr = $property->props[0]->default ?? null; + + // make sure default value is not a conflicting type + if (! $defaultExpr instanceof Node) { + // no value = no problem :) + return true; + } + + $defaultExprType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($defaultExpr); + + // avoid constant vs variable type conflicts + if ($defaultExprType instanceof FloatType && $getterSetterPropertyType instanceof FloatType) { + return true; + } + + if ($defaultExprType instanceof IntegerType && $getterSetterPropertyType instanceof IntegerType) { + return true; + } + + if ($defaultExprType instanceof StringType && $getterSetterPropertyType instanceof StringType) { + return true; + } + + return $defaultExprType->equals($getterSetterPropertyType); + } + + private function decorateDefaultExpr( + Type $getterSetterPropertyType, + Property $property, + bool $hasPropertyDefaultNull + ): void { + if (! TypeCombinator::containsNull($getterSetterPropertyType)) { + if ($getterSetterPropertyType instanceof FloatType) { + if (! $property->props[0]->default instanceof Expr) { + // string is used, we need default value + $property->props[0]->default = new Float_(0.0); + } + } elseif ($getterSetterPropertyType instanceof IntegerType) { + if (! $property->props[0]->default instanceof Expr) { + // string is used, we need default value + $property->props[0]->default = new Int_(0); + } + } + + if ($hasPropertyDefaultNull) { + if ($getterSetterPropertyType instanceof StringType) { + // string is used, we need default value + $property->props[0]->default = new String_(''); + } else { + // reset to nothing + $property->props[0]->default = null; + } + } + + return; + } + + $propertyProperty = $property->props[0]; + + // already set → skip it + if ($propertyProperty->default instanceof Expr) { + return; + } + + $propertyProperty->default = new ConstFetch(new Name('null')); + } + + private function hasPropertyDefaultNull(Property $property): bool + { + $defaultExpr = $property->props[0]->default ?? null; + if (! $defaultExpr instanceof ConstFetch) { + return false; + } + + return $defaultExpr->name->toLowerString() === 'null'; + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php b/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php new file mode 100644 index 00000000000..86d6c9ddb50 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php @@ -0,0 +1,152 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + if ($this->shouldSkip($node, $scope)) { + return null; + } + + if ($node->stmts === null) { + return null; + } + + $returns = $this->betterNodeFinder->findReturnsScoped($node); + if (count($returns) !== 1) { + return null; + } + + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returns)) { + return null; + } + + $return = $returns[0]; + if (! $return->expr instanceof Ternary) { + return null; + } + + $ternary = $return->expr; + + $returnScope = $return->expr->getAttribute(AttributeKey::SCOPE); + if (! $returnScope instanceof Scope) { + return null; + } + + $nativeTernaryType = $returnScope->getNativeType($ternary); + if ($nativeTernaryType instanceof MixedType) { + return null; + } + + $ternaryType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($ternary); + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($ternaryType, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + return null; + } + + $node->returnType = $returnTypeNode; + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + private function shouldSkip(ClassMethod|Function_ $functionLike, Scope $scope): bool + { + // type is already filled, skip + if ($functionLike->returnType instanceof Node) { + return true; + } + + $returnType = $this->returnTypeInferer->inferFunctionLike($functionLike); + $returnType = TypeCombinator::removeNull($returnType); + if ($returnType instanceof UnionType) { + return true; + } + + return $functionLike instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $functionLike, + $scope + ); + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector.php b/rules/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector.php new file mode 100644 index 00000000000..ed6f499c15a --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/ScalarTypedPropertyFromJMSSerializerAttributeTypeRector.php @@ -0,0 +1,134 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ATTRIBUTES; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->jmsTypeAnalyzer->hasAtLeastOneUntypedPropertyUsingJmsAttribute($node)) { + return null; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + if ($this->shouldSkipProperty($property, $classReflection)) { + continue; + } + + $typeValue = $this->jmsTypeAnalyzer->resolveTypeAttributeValue($property); + if (! is_string($typeValue)) { + continue; + } + + $propertyTypeNode = $this->jmsTypePropertyTypeFactory->createScalarTypeNode($typeValue, $property); + if (! $propertyTypeNode instanceof Identifier) { + continue; + } + + $property->type = new NullableType($propertyTypeNode); + $property->props[0]->default = $this->nodeFactory->createNull(); + + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function shouldSkipProperty(Property $property, ClassReflection $classReflection): bool + { + if ($property->type instanceof Node || $property->props[0]->default instanceof Expr) { + return true; + } + + if (! $this->jmsTypeAnalyzer->hasPropertyJMSTypeAttribute($property)) { + return true; + } + + return ! $this->makePropertyTypedGuard->isLegal($property, $classReflection); + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector.php b/rules/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector.php new file mode 100644 index 00000000000..ff03419ece2 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/TypedPropertyFromCreateMockAssignRector.php @@ -0,0 +1,157 @@ +someProperty = $this->createMock(SomeMockedClass::class); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +final class SomeTest extends TestCase +{ + private MockObject $someProperty; + + protected function setUp(): void + { + $this->someProperty = $this->createMock(SomeMockedClass::class); + } +} +CODE_SAMPLE + ), + + ] + ); + } + + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isObjectType($node, new ObjectType(ClassName::TEST_CASE_CLASS))) { + return null; + } + + $hasChanged = false; + $mockObjectType = new ObjectType(ClassName::MOCK_OBJECT); + + foreach ($node->getProperties() as $property) { + if (count($property->props) !== 1) { + continue; + } + + // already use PHPUnit\Framework\MockObject\MockObject type + if ($this->isAlreadyTypedWithMockObject($property, $mockObjectType)) { + continue; + } + + $propertyName = (string) $this->getName($property); + $type = $this->assignToPropertyTypeInferer->inferPropertyInClassLike($property, $propertyName, $node); + + if (! $type instanceof Type) { + continue; + } + + $propertyType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PROPERTY); + if (! $propertyType instanceof Node) { + continue; + } + + if (! $this->isObjectType($propertyType, $mockObjectType)) { + continue; + } + + if (! $this->constructorAssignDetector->isPropertyAssigned($node, $propertyName)) { + if (! $propertyType instanceof NullableType) { + continue; + } + + $property->props[0]->default = $this->nodeFactory->createNull(); + } + + $property->type = $propertyType; + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_PROPERTIES; + } + + private function isAlreadyTypedWithMockObject(Property $property, ObjectType $mockObjectType): bool + { + if (! $property->type instanceof Node) { + return false; + } + + // complex type, used on purpose + if ($property->type instanceof IntersectionType) { + return true; + } + + return $this->isObjectType($property->type, $mockObjectType); + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector.php b/rules/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector.php new file mode 100644 index 00000000000..98d73554fec --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/TypedPropertyFromDocblockSetUpDefinedRector.php @@ -0,0 +1,185 @@ +doctrine = $this->container('doctrine.orm.entity_manager'); + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +class SomeClass extends TestCase +{ + private \Doctrine\ORM\EntityManagerInterface $doctrine; + + protected function setUp(): void + { + $this->doctrine = $this->container('doctrine.orm.entity_manager'); + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + // nothing useful here + $setUpClassMethod = $node->getMethod(MethodName::SET_UP); + if (! $setUpClassMethod instanceof ClassMethod) { + return null; + } + + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + // already known type + if ($property->type instanceof Node) { + continue; + } + + // some magic might be going on + if ($property->isStatic()) { + continue; + } + + if (! $property->isPrivate()) { + continue; + } + + // exactly one property + if (count($property->props) !== 1) { + continue; + } + + $propertyName = $property->props[0]->name->toString(); + if (! $this->constructorAssignDetector->isPropertyAssigned($node, $propertyName)) { + continue; + } + + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNode($property); + if (! $propertyPhpDocInfo instanceof PhpDocInfo) { + continue; + } + + $varType = $propertyPhpDocInfo->getVarType(); + if ($varType instanceof MixedType) { + continue; + } + + $nativePropertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $varType, + TypeKind::PROPERTY + ); + if (! $nativePropertyTypeNode instanceof Node) { + continue; + } + + $property->type = $nativePropertyTypeNode; + + // remove var tag + $this->removeVarTag($propertyPhpDocInfo, $property); + + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_PROPERTIES; + } + + private function removeVarTag(PhpDocInfo $propertyPhpDocInfo, Property $property): void + { + $varTagValueNode = $propertyPhpDocInfo->getVarTagValueNode(); + + if (! $varTagValueNode instanceof VarTagValueNode) { + return; + } + + if (! $this->deadVarTagValueNodeAnalyzer->isDead($varTagValueNode, $property)) { + return; + } + + $propertyPhpDocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); + } +} diff --git a/rules/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRector.php b/rules/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRector.php new file mode 100644 index 00000000000..16156ca4fea --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/TypedStaticPropertyInBehatContextRector.php @@ -0,0 +1,137 @@ +extends instanceof Name && $node->implements === []) { + return null; + } + + if (! $this->isObjectType($node, new ObjectType(BehatClassName::CONTEXT))) { + return null; + } + + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + if ($property->type instanceof Node) { + continue; + } + + if (! $property->isStatic()) { + continue; + } + + if ($this->hasNonNullDefault($property)) { + continue; + } + + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + $varType = $propertyPhpDocInfo->getVarType(); + if (! $varType instanceof ObjectType) { + continue; + } + + if ($varType instanceof ShortenedObjectType) { + $className = $varType->getFullyQualifiedName(); + } else { + $className = $varType->getClassName(); + } + + $property->type = new NullableType(new FullyQualified($className)); + + if (! $property->props[0]->default instanceof Node) { + $property->props[0]->default = $this->nodeFactory->createNull(); + } + + $this->varTagRemover->removeVarTagIfUseless($propertyPhpDocInfo, $property); + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + private function hasNonNullDefault(Property $property): bool + { + $soleProperty = $property->props[0]; + if (! $soleProperty->default instanceof Expr) { + return false; + } + + return ! $this->valueResolver->isNull($soleProperty->default); + } +} diff --git a/rules/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector.php b/rules/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector.php new file mode 100644 index 00000000000..6d93f496932 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Closure/AddClosureNeverReturnTypeRector.php @@ -0,0 +1,68 @@ +> + */ + public function getNodeTypes(): array + { + return [Closure::class]; + } + + /** + * @param Closure $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + return $this->addNeverReturnType->add($node, $scope); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NEVER_TYPE; + } +} diff --git a/rules/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector.php b/rules/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector.php deleted file mode 100644 index 92b9bc6cd44..00000000000 --- a/rules/TypeDeclaration/Rector/Closure/AddClosureReturnTypeRector.php +++ /dev/null @@ -1,101 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Closure::class]; - } - - /** - * @param Closure $node - */ - public function refactor(Node $node): ?Node - { - if ($node->returnType !== null) { - return null; - } - - $scope = $node->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - $inferedReturnType = $this->returnTypeInferer->inferFunctionLike($node); - - $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $inferedReturnType, - TypeKind::RETURN() - ); - if ($returnTypeNode === null) { - return null; - } - - $node->returnType = $returnTypeNode; - - return $node; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::SCALAR_TYPES; - } -} diff --git a/rules/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector.php b/rules/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector.php new file mode 100644 index 00000000000..31df8f9ba6f --- /dev/null +++ b/rules/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector.php @@ -0,0 +1,75 @@ +> + */ + public function getNodeTypes(): array + { + return [Closure::class]; + } + + /** + * @param Closure $node + */ + public function refactor(Node $node): ?Node + { + // already has return type → skip + if ($node->returnType instanceof Node) { + return null; + } + + if (! $this->silentVoidResolver->hasExclusiveVoid($node)) { + return null; + } + + $node->returnType = new Identifier('void'); + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::VOID_TYPE; + } +} diff --git a/rules/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector.php b/rules/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector.php new file mode 100644 index 00000000000..a872ed62472 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Closure/ClosureReturnTypeRector.php @@ -0,0 +1,87 @@ +> + */ + public function getNodeTypes(): array + { + return [Closure::class]; + } + + /** + * @param Closure $node + */ + public function refactor(Node $node): ?Node + { + // type is already set + if ($node->returnType instanceof Node) { + return null; + } + + $closureReturnType = $this->returnTypeInferer->inferFunctionLike($node); + + // handled by other rules + if ($closureReturnType instanceof NeverType) { + return null; + } + + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($closureReturnType, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + return null; + } + + $node->returnType = $returnTypeNode; + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } +} diff --git a/rules/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector.php b/rules/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector.php new file mode 100644 index 00000000000..94764c750e4 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector.php @@ -0,0 +1,123 @@ +> + */ + public function getNodeTypes(): array + { + return [Empty_::class, BooleanNot::class]; + } + + /** + * @param Empty_|BooleanNot $node + */ + public function refactor(Node $node): null|Instanceof_|BooleanNot + { + if ($node instanceof BooleanNot) { + if (! $node->expr instanceof Empty_) { + return null; + } + + $isNegated = true; + $empty = $node->expr; + } else { + $empty = $node; + $isNegated = false; + } + + if ($empty->expr instanceof ArrayDimFetch) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + + $exprType = $scope->getNativeType($empty->expr); + if (! $exprType instanceof UnionType) { + return null; + } + + $exprType = TypeCombinator::removeNull($exprType); + if (! $exprType instanceof ObjectType) { + return null; + } + + $objectType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($exprType, TypeKind::RETURN); + if (! $objectType instanceof Name) { + return null; + } + + $instanceof = new Instanceof_($empty->expr, $objectType); + + if ($isNegated) { + return $instanceof; + } + + return new BooleanNot($instanceof); + } +} diff --git a/rules/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector.php b/rules/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector.php new file mode 100644 index 00000000000..ab1175f522b --- /dev/null +++ b/rules/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector.php @@ -0,0 +1,179 @@ + $item > 1); +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +$items = [1, 2, 3]; +$result = array_filter($items, fn (int $item) => $item > 1); +CODE_SAMPLE + ), + + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + if (count($node->getArgs()) !== 2) { + return null; + } + + $hasChanged = false; + + foreach (NativeFuncCallPositions::ARRAY_AND_CALLBACK_POSITIONS as $functionName => $positions) { + if (! $this->isName($node, $functionName)) { + continue; + } + + $arrayPosition = $positions['array']; + $callbackPosition = $positions['callback']; + + $callbackArg = $node->getArg('callback', $callbackPosition); + if (! $callbackArg instanceof Arg) { + continue; + } + + $callbackArgExpr = $callbackArg->value; + if (! $callbackArgExpr instanceof ArrowFunction && ! $callbackArgExpr instanceof Closure) { + continue; + } + + if (count($callbackArgExpr->getParams()) !== 1) { + continue; + } + + $arrowFunction = $callbackArgExpr; + $arrowFunctionParam = $arrowFunction->getParams()[0]; + + // param is known already + if ($arrowFunctionParam->type instanceof Node) { + continue; + } + + $arrayArg = $node->getArg('array', $arrayPosition); + if (! $arrayArg instanceof Arg) { + continue; + } + + $passedExprType = $this->getType($arrayArg->value); + + $singlePassedExprType = $this->resolveArrayItemType($passedExprType); + if (! $singlePassedExprType instanceof Type) { + continue; + } + + if ($singlePassedExprType instanceof MixedType) { + continue; + } + + $paramType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $singlePassedExprType, + TypeKind::PARAM + ); + + if (! $paramType instanceof Node) { + continue; + } + + $hasChanged = true; + $arrowFunctionParam->type = $paramType; + } + + if ($hasChanged === false) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + private function resolveArrayItemType(Type $mainType): ?Type + { + if ($mainType instanceof ConstantArrayType || $mainType instanceof ArrayType) { + return $mainType->getItemType(); + } + + if ($mainType instanceof IntersectionType) { + foreach ($mainType->getTypes() as $subType) { + if ($subType instanceof AccessoryArrayListType) { + continue; + } + + if (! $subType->isArray()->yes()) { + continue; + } + + if (! $subType instanceof ArrayType) { + continue; + } + + return $subType->getItemType(); + } + } + + return null; + } +} diff --git a/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php b/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php new file mode 100644 index 00000000000..1fa3c45f010 --- /dev/null +++ b/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php @@ -0,0 +1,213 @@ + 'John']]; + +$result = array_map(fn ($item) => $item['name'], $array); +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +$array = [['name' => 'John']]; + +$result = array_map(fn (array $item) => $item['name'], $array); +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + if (count($node->getArgs()) !== 2) { + return null; + } + + $hasChanged = false; + + foreach (NativeFuncCallPositions::ARRAY_AND_CALLBACK_POSITIONS as $functionName => $positions) { + if (! $this->isName($node, $functionName)) { + continue; + } + + $callbackPosition = $positions['callback']; + + $closureExpr = $node->getArgs()[$callbackPosition]->value; + if (! $closureExpr instanceof ArrowFunction && ! $closureExpr instanceof Closure) { + continue; + } + + $arrayPosition = $positions['array']; + $closureItems = $node->getArgs()[$arrayPosition]->value; + + $isArrayVariableNames = $this->resolveIsArrayVariables($closureExpr); + $instanceofVariableNames = $this->resolveInstanceofVariables($closureExpr); + $skippedVariableNames = array_merge($isArrayVariableNames, $instanceofVariableNames); + + $dimFetchVariableNames = $this->resolveDimFetchVariableNames($closureExpr); + + foreach ($closureExpr->getParams() as $closureParam) { + if ($closureParam->type instanceof Node) { + // param is known already + continue; + } + + // skip is_array() checked variables + if ($this->isNames($closureParam->var, $skippedVariableNames)) { + continue; + } + + if (! $this->isNames($closureParam->var, $dimFetchVariableNames)) { + continue; + } + + $type = $this->getType($closureItems); + if ($type instanceof ArrayType && $type->getItemType()->isObject()->yes()) { + continue; + } + + $hasChanged = true; + $closureParam->type = new Identifier('array'); + } + } + + if ($hasChanged === false) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } + + /** + * @return string[] + */ + private function resolveDimFetchVariableNames(Closure|ArrowFunction $closureExpr): array + { + $closureNodes = $closureExpr instanceof ArrowFunction ? [$closureExpr->expr] : $closureExpr->stmts; + + /** @var ArrayDimFetch[] $arrayDimFetches */ + $arrayDimFetches = $this->betterNodeFinder->findInstancesOfScoped($closureNodes, ArrayDimFetch::class); + + $usedDimFetchVariableNames = []; + + foreach ($arrayDimFetches as $arrayDimFetch) { + if ($arrayDimFetch->var instanceof Variable) { + $type = $this->nodeTypeResolver->getNativeType($arrayDimFetch->var); + + if ($type->isString()->yes()) { + continue; + } + + $usedDimFetchVariableNames[] = (string) $this->getName($arrayDimFetch->var); + } + } + + return $usedDimFetchVariableNames; + } + + /** + * @return string[] + */ + private function resolveIsArrayVariables(Closure|ArrowFunction $closureExpr): array + { + $closureNodes = $closureExpr instanceof ArrowFunction ? [$closureExpr->expr] : $closureExpr->stmts; + + /** @var FuncCall[] $funcCalls */ + $funcCalls = $this->betterNodeFinder->findInstancesOfScoped($closureNodes, FuncCall::class); + + $variableNames = []; + + foreach ($funcCalls as $funcCall) { + if (! $this->isName($funcCall, 'is_array')) { + continue; + } + + $firstArgExpr = $funcCall->getArgs()[0] + ->value; + if (! $firstArgExpr instanceof Variable) { + continue; + } + + $variableNames[] = (string) $this->getName($firstArgExpr); + } + + return $variableNames; + } + + /** + * @return string[] + */ + private function resolveInstanceofVariables(Closure|ArrowFunction $closureExpr): array + { + $closureNodes = $closureExpr instanceof ArrowFunction ? [$closureExpr->expr] : $closureExpr->stmts; + + /** @var Instanceof_[] $instanceOfs */ + $instanceOfs = $this->betterNodeFinder->findInstancesOfScoped($closureNodes, Instanceof_::class); + + $variableNames = []; + + foreach ($instanceOfs as $instanceOf) { + if (! $instanceOf->expr instanceof Variable) { + continue; + } + + $variableNames[] = (string) $this->getName($instanceOf->expr); + } + + return $variableNames; + } +} diff --git a/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php b/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php new file mode 100644 index 00000000000..eb36169dd42 --- /dev/null +++ b/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php @@ -0,0 +1,216 @@ +isFirstClassCallable()) { + return null; + } + + if (! $this->isName($node, 'array_map')) { + return null; + } + + $funcReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + + if (! $funcReflection instanceof NativeFunctionReflection) { + return null; + } + + $args = $node->getArgs(); + + if (! isset($args[0]) || ! $args[0]->value instanceof Closure) { + return null; + } + + /** @var ArrayType[] $types */ + $types = array_filter(array_map(function (Arg|VariadicPlaceholder $arg): ?ArrayType { + if (! $arg instanceof Arg) { + return null; + } + + $type = $this->getType($arg->value); + + if ($type instanceof ArrayType) { + return $type; + } + + return null; + }, array_slice($node->args, 1))); + + $values = []; + $keys = []; + + foreach ($types as $type) { + $values[] = $type->getIterableValueType(); + $keys[] = $type->getIterableKeyType(); + } + + foreach ($values as $value) { + if ($value instanceof MixedType) { + $values = []; + break; + } elseif ($value instanceof UnionType) { + $values = [...$values, ...$value->getTypes()]; + } + } + + foreach ($keys as $key) { + if ($key instanceof MixedType) { + $keys = []; + break; + } elseif ($key instanceof UnionType) { + $keys = [...$keys, ...$key->getTypes()]; + } + } + + $filter = fn (Type $type): bool => ! $type instanceof UnionType; + + $valueType = $this->combineTypes(array_filter($values, $filter)); + $keyType = $this->combineTypes(array_filter($keys, $filter)); + + if (! $keyType instanceof Type && ! $valueType instanceof Type) { + return null; + } + + if ($this->updateClosureWithTypes($args[0]->value, $keyType, $valueType)) { + return $node; + } + + return null; + } + + private function updateClosureWithTypes(Closure $closure, ?Type $keyType, ?Type $valueType): bool + { + $changes = false; + $valueParam = $closure->params[0] ?? null; + $keyParam = $closure->params[1] ?? null; + + if ($valueParam instanceof Param && $valueType instanceof Type && $this->refactorParameter( + $closure->params[0], + $valueType + )) { + $changes = true; + } + + if ($keyParam instanceof Param && $keyType instanceof Type && $this->refactorParameter( + $closure->params[1], + $keyType + )) { + return true; + } + + return $changes; + } + + private function refactorParameter(Param $param, Type $type): bool + { + // already set → no change + if ($param->type instanceof Node) { + return false; + } + + $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM); + + if (! $paramTypeNode instanceof Node) { + return false; + } + + $param->type = $paramTypeNode; + + return true; + } + + /** + * @param Type[] $types + */ + private function combineTypes(array $types): ?Type + { + if ($types === []) { + return null; + } + + $types = array_reduce($types, function (array $types, Type $type): array { + foreach ($types as $previousType) { + if ($this->typeComparator->areTypesEqual($type, $previousType)) { + return $types; + } + } + + $types[] = $type; + return $types; + }, []); + + if (count($types) === 1) { + return $types[0]; + } + + return new UnionType(UnionTypeHelper::sortTypes($types)); + } +} diff --git a/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php b/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php new file mode 100644 index 00000000000..31c42d1aa32 --- /dev/null +++ b/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php @@ -0,0 +1,196 @@ +isFirstClassCallable()) { + return null; + } + + if (! $this->isName($node, 'array_reduce')) { + return null; + } + + $funcReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + + if (! $funcReflection instanceof NativeFunctionReflection) { + return null; + } + + $args = $node->getArgs(); + + if (! isset($args[1]) || ! $args[1]->value instanceof Closure) { + return null; + } + + $closureType = $this->getType($args[1]->value); + if (! $closureType instanceof ClosureType) { + return null; + } + + $carryType = $closureType->getReturnType(); + + if (isset($args[2])) { + $carryType = $this->combineTypes([$this->getType($args[2]->value), $carryType]); + } + + $type = $this->getType($args[0]->value); + $valueType = $type->getIterableValueType(); + + if ($this->updateClosureWithTypes($args[1]->value, $valueType, $carryType)) { + return $node; + } + + return null; + } + + private function updateClosureWithTypes(Closure $closure, ?Type $valueType, ?Type $carryType): bool + { + $changes = false; + $carryParam = $closure->params[0] ?? null; + $valueParam = $closure->params[1] ?? null; + + if ($valueParam instanceof Param && $valueType instanceof Type && $this->refactorParameter( + $valueParam, + $valueType + )) { + $changes = true; + } + + if ($carryParam instanceof Param && $carryType instanceof Type && $this->refactorParameter( + $carryParam, + $carryType + )) { + return true; + } + + return $changes; + } + + private function refactorParameter(Param $param, Type $type): bool + { + if ($type instanceof MixedType) { + return false; + } + + // already set → no change + if ($param->type instanceof Node) { + return false; + } + + $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM); + + if (! $paramTypeNode instanceof Node) { + return false; + } + + $param->type = $paramTypeNode; + + return true; + } + + /** + * @param Type[] $types + */ + private function combineTypes(array $types): ?Type + { + if ($types === []) { + return null; + } + + $types = array_reduce($types, function (array $types, Type $type): array { + foreach ($types as $previousType) { + if ($this->typeComparator->areTypesEqual($type, $previousType)) { + return $types; + } + } + + $types[] = $type; + return $types; + }, []); + + if (count($types) === 1) { + return $types[0]; + } + + foreach ($types as $key => $type) { + if ($type instanceof UnionType) { + foreach ($type->getTypes() as $unionedType) { + if ($unionedType instanceof IntersectionType) { + return null; + } + } + + $types = array_merge($types, $type->getTypes()); + unset($types[$key]); + } + } + + return new UnionType(UnionTypeHelper::sortTypes($types)); + } +} diff --git a/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector.php b/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector.php new file mode 100644 index 00000000000..6f54f3fe03d --- /dev/null +++ b/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector.php @@ -0,0 +1,268 @@ + $collection + */ + public function run(Collection $collection) + { + return $collection->map(function ($item, $key) { + return $item . $key; + }); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + /** + * @param Collection $collection + */ + public function run(Collection $collection) + { + return $collection->map(function (string $item, int $key) { + return $item . $key; + }); + } +} +CODE_SAMPLE + , + ), + ] + ); + } + + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + /** + * @param MethodCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + $varType = $this->getType($node->var); + + if (! $varType instanceof IntersectionType || ! $varType->isIterable()->yes()) { + return null; + } + + $className = $varType->getObjectClassNames()[0] ?? null; + + if ($className === null) { + return null; + } + + if (! $node->name instanceof Identifier) { + return null; + } + + $methodReflection = $this->methodReflectionResolver->resolveMethodReflection( + $className, + $node->name->name, + $node->getAttribute(AttributeKey::SCOPE), + ); + + if (! $methodReflection instanceof MethodReflection) { + return null; + } + + $parameters = $methodReflection->getVariants()[0] + ->getParameters(); + + if (! $this->methodSignatureUsesCallableWithIteratorTypes($className, $parameters)) { + return null; + } + + if (! $this->callUsesClosures($node->getArgs())) { + return null; + } + + $nameIndex = []; + foreach ($parameters as $index => $parameter) { + $nameIndex[$parameter->getName()] = $index; + } + + $valueType = $varType->getIterableValueType(); + $keyType = $varType->getIterableKeyType(); + + $changesMade = false; + + foreach ($node->getArgs() as $index => $arg) { + if (! $arg instanceof Arg) { + continue; + } + + if (! $arg->value instanceof Closure) { + continue; + } + + $parameter = (is_string($index) ? $parameters[$nameIndex[$index]] : $parameters[$index]); + + if ($this->updateClosureWithTypes($className, $parameter, $arg->value, $keyType, $valueType)) { + $changesMade = true; + } + } + + if ($changesMade) { + return $node; + } + + return null; + } + + private function updateClosureWithTypes( + string $className, + ParameterReflection $parameter, + Closure $closure, + Type $keyType, + Type $valueType + ): bool { + // get the ClosureType from the ParameterReflection + $callableType = $this->typeUnwrapper->unwrapFirstCallableTypeFromUnionType($parameter->getType()); + + if (! $callableType instanceof CallableType) { + return false; + } + + $changesMade = false; + + foreach ($callableType->getParameters() as $index => $parameterReflection) { + $closureParameter = $closure->getParams()[$index] ?? null; + + if (! $closureParameter instanceof Param) { + continue; + } + + if ( + $this->typeUnwrapper->isIterableTypeValue($className, $parameterReflection->getType()) + ) { + if ($this->refactorParameter($closureParameter, $valueType)) { + $changesMade = true; + } + } elseif ( + $this->typeUnwrapper->isIterableTypeKey($className, $parameterReflection->getType()) + ) { + if ($this->refactorParameter($closureParameter, $keyType)) { + $changesMade = true; + } + } + } + + return $changesMade; + } + + private function refactorParameter(Param $param, Type $type): bool + { + // already set → no change + if ($param->type instanceof Node) { + $currentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + if ($this->typeComparator->areTypesEqual($currentParamType, $type)) { + return false; + } + } + + $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM); + + if (! $paramTypeNode instanceof Node) { + return false; + } + + $param->type = $paramTypeNode; + + return true; + } + + /** + * @param class-string $className + * @param ParameterReflection[] $parameters + */ + private function methodSignatureUsesCallableWithIteratorTypes(string $className, array $parameters): bool + { + foreach ($parameters as $parameter) { + $callableType = $this->typeUnwrapper->unwrapFirstCallableTypeFromUnionType($parameter->getType()); + + if (! $callableType instanceof CallableType) { + continue; + } + + foreach ($callableType->getParameters() as $parameterReflection) { + if ( + $this->typeUnwrapper->isIterableTypeValue($className, $parameterReflection->getType()) || + $this->typeUnwrapper->isIterableTypeKey($className, $parameterReflection->getType()) + ) { + return true; + } + } + } + + return false; + } + + /** + * @param array $args + */ + private function callUsesClosures(array $args): bool + { + foreach ($args as $arg) { + if ($arg instanceof Arg && $arg->value instanceof Closure) { + return true; + } + } + + return false; + } +} diff --git a/rules/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector.php b/rules/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector.php new file mode 100644 index 00000000000..310c1e4bae3 --- /dev/null +++ b/rules/TypeDeclaration/Rector/FunctionLike/AddParamTypeSplFixedArrayRector.php @@ -0,0 +1,151 @@ + + */ + private const array SPL_FIXED_ARRAY_TO_SINGLE = [ + 'PhpCsFixer\Tokenizer\Tokens' => 'PhpCsFixer\Tokenizer\Token', + 'PhpCsFixer\Doctrine\Annotation\Tokens' => 'PhpCsFixer\Doctrine\Annotation\Token', + ]; + + public function __construct( + private readonly PhpDocTypeChanger $phpDocTypeChanger, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + ) { + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Function_::class, ClassMethod::class]; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Add exact fixed array type in known cases', + [ + new CodeSample( + <<<'CODE_SAMPLE' +use PhpCsFixer\Tokenizer\Tokens; + +class SomeClass +{ + public function run(Tokens $tokens) + { + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use PhpCsFixer\Tokenizer\Token; +use PhpCsFixer\Tokenizer\Tokens; + +class SomeClass +{ + /** + * @param Tokens + */ + public function run(Tokens $tokens) + { + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @param FunctionLike $node + */ + public function refactor(Node $node): ?Node + { + if ($node->getParams() === []) { + return null; + } + + $functionLikePhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $hasChanged = false; + + foreach ($node->getParams() as $param) { + if ($param->type === null) { + continue; + } + + $paramType = $this->nodeTypeResolver->getType($param->type); + if ($paramType->isSuperTypeOf(new ObjectType('SplFixedArray'))->no()) { + continue; + } + + if (! $paramType instanceof TypeWithClassName) { + continue; + } + + if ($paramType instanceof GenericObjectType) { + continue; + } + + $genericParamType = $this->resolveGenericType($paramType); + if (! $genericParamType instanceof Type) { + continue; + } + + $paramName = $this->getName($param); + $changedParamType = $this->phpDocTypeChanger->changeParamType( + $node, + $functionLikePhpDocInfo, + $genericParamType, + $param, + $paramName + ); + + if ($changedParamType) { + $hasChanged = true; + } + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function resolveGenericType(TypeWithClassName $typeWithClassName): GenericObjectType|null + { + foreach (self::SPL_FIXED_ARRAY_TO_SINGLE as $fixedArrayClass => $singleClass) { + if ($typeWithClassName->getClassName() === $fixedArrayClass) { + $genericObjectType = new ObjectType($singleClass); + return new GenericObjectType($typeWithClassName->getClassName(), [$genericObjectType]); + } + } + + return null; + } +} diff --git a/rules/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector.php b/rules/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector.php new file mode 100644 index 00000000000..556532a1774 --- /dev/null +++ b/rules/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector.php @@ -0,0 +1,114 @@ + + */ + public function provide(): Iterator + { + yield 1; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Function_::class, ClassMethod::class]; + } + + /** + * @param Function_|ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $scope = ScopeFetcher::fetch($node); + + $yieldNodes = $this->yieldNodeFinder->find($node); + if ($yieldNodes === []) { + return null; + } + + // skip already filled type + if ($node->returnType instanceof Node && $this->isNames( + $node->returnType, + ['Iterator', 'Generator', 'Traversable', 'iterable'] + )) { + return null; + } + + if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod( + $node, + $scope + )) { + return null; + } + + $yieldType = $this->yieldTypeResolver->resolveFromYieldNodes($yieldNodes, $node); + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($yieldType, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + return null; + } + + $node->returnType = $returnTypeNode; + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } +} diff --git a/rules/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector.php b/rules/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector.php deleted file mode 100644 index dac6288e09d..00000000000 --- a/rules/TypeDeclaration/Rector/FunctionLike/ParamTypeDeclarationRector.php +++ /dev/null @@ -1,193 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - // why not on Param node? because class like docblock is edited too for @param tags - return [Function_::class, ClassMethod::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change @param types to type declarations if not a BC-break', - [ - new CodeSample( - <<<'CODE_SAMPLE' -abstract class VendorParentClass -{ - /** - * @param int $number - */ - public function keep($number) - { - } -} - -final class ChildClass extends VendorParentClass -{ - /** - * @param int $number - */ - public function keep($number) - { - } - - /** - * @param int $number - */ - public function change($number) - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -abstract class VendorParentClass -{ - /** - * @param int $number - */ - public function keep($number) - { - } -} - -final class ChildClass extends VendorParentClass -{ - /** - * @param int $number - */ - public function keep($number) - { - } - - public function change(int $number) - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param ClassMethod|Function_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node->params === []) { - return null; - } - - foreach ($node->params as $param) { - $this->refactorParam($param, $node); - } - - return null; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::SCALAR_TYPES; - } - - private function refactorParam(Param $param, ClassMethod | Function_ $functionLike): void - { - if ($this->shouldSkipParam($param, $functionLike)) { - return; - } - - $inferedType = $this->paramTypeInferer->inferParam($param); - if ($inferedType instanceof MixedType) { - return; - } - - if ($inferedType instanceof NullType) { - return; - } - - if ($this->traitTypeAnalyzer->isTraitType($inferedType)) { - return; - } - - $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($inferedType, TypeKind::PARAM()); - if (! $paramTypeNode instanceof Node) { - return; - } - - $parentNode = $functionLike->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode instanceof Interface_ && $parentNode->extends !== []) { - return; - } - - $param->type = $paramTypeNode; - - $functionLikePhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - $this->paramTagRemover->removeParamTagsIfUseless($functionLikePhpDocInfo, $functionLike); - } - - private function shouldSkipParam(Param $param, ClassMethod | Function_ $functionLike): bool - { - if ($param->variadic) { - return true; - } - - if ($this->vendorLockResolver->isClassMethodParamLockedIn($functionLike)) { - return true; - } - - // no type → check it - if ($param->type === null) { - return false; - } - - // already set → skip - return ! $param->type->getAttribute(NewType::HAS_NEW_INHERITED_TYPE, false); - } -} diff --git a/rules/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector.php b/rules/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector.php deleted file mode 100644 index 872e9485329..00000000000 --- a/rules/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector.php +++ /dev/null @@ -1,236 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Function_::class, ClassMethod::class]; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change @return types and type from static analysis to type declarations if not a BC-break', - [ - new CodeSample( - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @return int - */ - public function getCount() - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - public function getCount(): int - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @param ClassMethod|Function_ $node - */ - public function refactor(Node $node): ?Node - { - if ($this->shouldSkipClassLike($node)) { - return null; - } - - if ($node instanceof ClassMethod && $this->shouldSkipClassMethod($node)) { - return null; - } - - $inferedReturnType = $this->returnTypeInferer->inferFunctionLikeWithExcludedInferers( - $node, - [ReturnTypeDeclarationReturnTypeInferer::class] - ); - - if ($inferedReturnType instanceof MixedType) { - return null; - } - - if ($this->returnTypeAlreadyAddedChecker->isSameOrBetterReturnTypeAlreadyAdded($node, $inferedReturnType)) { - return null; - } - - return $this->processType($node, $inferedReturnType); - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::SCALAR_TYPES; - } - - private function processType(ClassMethod | Function_ $node, Type $inferedType): ?Node - { - $inferredReturnNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $inferedType, - TypeKind::RETURN() - ); - - // nothing to change in PHP code - if (! $inferredReturnNode instanceof Node) { - return null; - } - - if ($this->shouldSkipInferredReturnNode($node)) { - return null; - } - - // should be previous overridden? - if ($node->returnType !== null && $this->shouldSkipExistingReturnType($node, $inferedType)) { - return null; - } - - /** @var Name|NullableType|PhpParserUnionType $inferredReturnNode */ - $this->addReturnType($node, $inferredReturnNode); - $this->nonInformativeReturnTagRemover->removeReturnTagIfNotUseful($node); - - return $node; - } - - private function shouldSkipClassMethod(ClassMethod $classMethod): bool - { - if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($classMethod)) { - return true; - } - - return $this->vendorLockResolver->isReturnChangeVendorLockedIn($classMethod); - } - - private function shouldSkipInferredReturnNode(ClassMethod | Function_ $functionLike): bool - { - // already overridden by previous populateChild() method run - if ($functionLike->returnType === null) { - return false; - } - - return (bool) $functionLike->returnType->getAttribute(AttributeKey::DO_NOT_CHANGE); - } - - private function shouldSkipExistingReturnType(ClassMethod | Function_ $functionLike, Type $inferedType): bool - { - if ($functionLike->returnType === null) { - return false; - } - - if ($functionLike instanceof ClassMethod && $this->vendorLockResolver->isReturnChangeVendorLockedIn( - $functionLike - )) { - return true; - } - - $currentType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($functionLike->returnType); - if ($this->objectTypeComparator->isCurrentObjectTypeSubType($currentType, $inferedType)) { - return true; - } - - return $this->isNullableTypeSubType($currentType, $inferedType); - } - - private function addReturnType( - ClassMethod | Function_ $functionLike, - Name | NullableType | PhpParserUnionType $inferredReturnNode - ): void { - if ($functionLike->returnType === null) { - $functionLike->returnType = $inferredReturnNode; - return; - } - - $isSubtype = $this->phpParserTypeAnalyzer->isCovariantSubtypeOf($inferredReturnNode, $functionLike->returnType); - if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::COVARIANT_RETURN) && $isSubtype) { - $functionLike->returnType = $inferredReturnNode; - return; - } - - if (! $isSubtype) { - // type override with correct one - $functionLike->returnType = $inferredReturnNode; - } - } - - private function isNullableTypeSubType(Type $currentType, Type $inferedType): bool - { - if (! $currentType instanceof UnionType) { - return false; - } - - if (! $inferedType instanceof UnionType) { - return false; - } - - return $inferedType->isSubTypeOf($currentType) - ->yes(); - } - - private function shouldSkipClassLike(FunctionLike $functionLike): bool - { - if (! $functionLike instanceof ClassMethod) { - return false; - } - - $classLike = $functionLike->getAttribute(AttributeKey::CLASS_NODE); - return ! $classLike instanceof Class_; - } -} diff --git a/rules/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector.php b/rules/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector.php new file mode 100644 index 00000000000..4b035421004 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Function_/AddFunctionVoidReturnTypeWhereNoReturnRector.php @@ -0,0 +1,75 @@ +> + */ + public function getNodeTypes(): array + { + return [Function_::class]; + } + + /** + * @param Function_ $node + */ + public function refactor(Node $node): ?Node + { + // already has return type → skip + if ($node->returnType instanceof Node) { + return null; + } + + if (! $this->silentVoidResolver->hasExclusiveVoid($node)) { + return null; + } + + $node->returnType = new Identifier('void'); + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::VOID_TYPE; + } +} diff --git a/rules/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector.php b/rules/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector.php deleted file mode 100644 index 95f0fb6b7ab..00000000000 --- a/rules/TypeDeclaration/Rector/MethodCall/FormerNullableArgumentToScalarTypedRector.php +++ /dev/null @@ -1,132 +0,0 @@ -setValue(null); - } - - public function setValue(string $value) - { - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - public function run() - { - $this->setValue(''); - } - - public function setValue(string $value) - { - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class, StaticCall::class]; - } - - /** - * @param MethodCall|StaticCall $node - */ - public function refactor(Node $node): ?Node - { - if ($node->args === []) { - return null; - } - - $methodParameterTypes = $this->callTypeAnalyzer->resolveMethodParameterTypes($node); - if ($methodParameterTypes === []) { - return null; - } - - foreach ($node->args as $key => $arg) { - if (! $this->valueResolver->isNull($arg->value)) { - continue; - } - - /** @var int $key */ - $this->refactorArg($arg, $methodParameterTypes, $key); - } - - return $node; - } - - /** - * @param Type[] $methodParameterTypes - */ - private function refactorArg(Arg $arg, array $methodParameterTypes, int $key): void - { - if (! isset($methodParameterTypes[$key])) { - return; - } - - $parameterType = $methodParameterTypes[$key]; - if ($parameterType instanceof StringType) { - $arg->value = new String_(''); - } - - if ($parameterType instanceof IntegerType) { - $arg->value = new LNumber(0); - } - - if ($parameterType instanceof FloatType) { - $arg->value = new DNumber(0); - } - - if ($parameterType instanceof BooleanType) { - $arg->value = $this->nodeFactory->createFalse(); - } - } -} diff --git a/rules/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector.php b/rules/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector.php deleted file mode 100644 index e56c93cce77..00000000000 --- a/rules/TypeDeclaration/Rector/Param/ParamTypeFromStrictTypedPropertyRector.php +++ /dev/null @@ -1,188 +0,0 @@ -age = $age; - } -} -CODE_SAMPLE - - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - private int $age; - - public function setAge(int $age) - { - $this->age = $age; - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Param::class]; - } - - /** - * @param Param $node - */ - public function refactor(Node $node): ?Node - { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof FunctionLike) { - return null; - } - - return $this->decorateParamWithType($parent, $node); - } - - public function decorateParamWithType(FunctionLike $functionLike, Param $param): ?Param - { - if ($param->type !== null) { - return null; - } - - $originalParamType = $this->resolveParamOriginalType($param); - - $paramName = $this->getName($param); - - /** @var Assign[] $assigns */ - $assigns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->getStmts(), Assign::class); - foreach ($assigns as $assign) { - if (! $this->nodeComparator->areNodesEqual($assign->expr, $param->var)) { - continue; - } - - if (! $assign->var instanceof PropertyFetch) { - continue; - } - - if ($this->hasTypeChangedBeforeAssign($assign, $paramName, $originalParamType)) { - return null; - } - - $singlePropertyTypeNode = $this->matchPropertySingleTypeNode($assign->var); - if (! $singlePropertyTypeNode instanceof Node) { - return null; - } - - $param->type = $singlePropertyTypeNode; - return $param; - } - - return null; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::TYPED_PROPERTIES; - } - - /** - * @return Node\Name|UnionType|NullableType|null - */ - private function matchPropertySingleTypeNode(PropertyFetch $propertyFetch): ?Node - { - $phpPropertyReflection = $this->reflectionResolver->resolvePropertyReflectionFromPropertyFetch($propertyFetch); - if (! $phpPropertyReflection instanceof PhpPropertyReflection) { - return null; - } - - $propertyType = $phpPropertyReflection->getNativeType(); - - if ($propertyType instanceof MixedType) { - return null; - } - - if ($propertyType instanceof \PHPStan\Type\UnionType) { - return null; - } - - if ($propertyType instanceof NullableType) { - return null; - } - - return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyType, TypeKind::PROPERTY()); - } - - private function hasTypeChangedBeforeAssign(Assign $assign, string $paramName, Type $originalType): bool - { - $scope = $assign->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - if (! $scope->hasVariableType($paramName)->yes()) { - return false; - } - - $currentParamType = $scope->getVariableType($paramName); - return ! $currentParamType->equals($originalType); - } - - private function resolveParamOriginalType(Param $param): Type - { - $scope = $param->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return new MixedType(); - } - - $paramName = $this->getName($param); - if (! $scope->hasVariableType($paramName)->yes()) { - return new MixedType(); - } - - return $scope->getVariableType($paramName); - } -} diff --git a/rules/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector.php b/rules/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector.php new file mode 100644 index 00000000000..2f87753194d --- /dev/null +++ b/rules/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector.php @@ -0,0 +1,127 @@ +> + */ + public function getNodeTypes(): array + { + return [Property::class]; + } + + /** + * @param Property $node + */ + public function refactor(Node $node): ?Node + { + // type is already known + if ($node->type !== null) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + foreach ($this->addPropertyTypeDeclarations as $addPropertyTypeDeclaration) { + if (! $this->isClassReflectionType($classReflection, $addPropertyTypeDeclaration->getClass())) { + continue; + } + + if (! $this->isName($node, $addPropertyTypeDeclaration->getPropertyName())) { + continue; + } + + $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $addPropertyTypeDeclaration->getType(), + TypeKind::PROPERTY + ); + if (! $typeNode instanceof Node) { + // invalid configuration + throw new ShouldNotHappenException(); + } + + $node->type = $typeNode; + return $node; + } + + return null; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsAOf($configuration, AddPropertyTypeDeclaration::class); + $this->addPropertyTypeDeclarations = $configuration; + } + + private function isClassReflectionType(ClassReflection $classReflection, string $type): bool + { + if ($classReflection->hasTraitUse($type)) { + return true; + } + + return $classReflection->is($type); + } +} diff --git a/rules/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector.php b/rules/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector.php deleted file mode 100644 index c177e03b949..00000000000 --- a/rules/TypeDeclaration/Rector/Property/CompleteVarDocTypePropertyRector.php +++ /dev/null @@ -1,92 +0,0 @@ -eventDispatcher = $eventDispatcher; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -final class SomeClass -{ - /** - * @var EventDispatcher - */ - private $eventDispatcher; - - public function __construct(EventDispatcher $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - } -} -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Property::class]; - } - - /** - * @param Property $node - */ - public function refactor(Node $node): ?Node - { - $propertyType = $this->propertyTypeInferer->inferProperty($node); - if ($propertyType instanceof MixedType) { - return null; - } - - if (! $node->isPrivate() && $propertyType instanceof NullType) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $propertyType); - - return $node; - } -} diff --git a/rules/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector.php b/rules/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector.php deleted file mode 100644 index 424fc1ccb63..00000000000 --- a/rules/TypeDeclaration/Rector/Property/PropertyTypeDeclarationRector.php +++ /dev/null @@ -1,106 +0,0 @@ -value = 123; - } -} -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -class SomeClass -{ - /** - * @var int - */ - private $value; - - public function run() - { - $this->value = 123; - } -} -CODE_SAMPLE - ), - ]); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [Property::class]; - } - - /** - * @param Property $node - */ - public function refactor(Node $node): ?Node - { - if (count($node->props) !== 1) { - return null; - } - - if ($node->type !== null) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - // is already set - foreach (['@var', '@phpstan-var', '@psalm-var'] as $tagName) { - $varType = $phpDocInfo->getVarType($tagName); - if (! $varType instanceof MixedType) { - return null; - } - } - - $type = $this->propertyTypeInferer->inferProperty($node); - if ($type instanceof MixedType) { - return null; - } - - if (! $node->isPrivate() && $type instanceof NullType) { - return null; - } - - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $type); - - return $node; - } -} diff --git a/rules/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector.php b/rules/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector.php new file mode 100644 index 00000000000..076f5a00b35 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector.php @@ -0,0 +1,236 @@ + $configuration + */ + public function configure(array $configuration): void + { + $this->inlinePublic = $configuration[self::INLINE_PUBLIC] ?? (bool) current($configuration); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Add typed property from assigned types', [ + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +final class SomeClass +{ + private $name; + + public function run() + { + $this->name = 'string'; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private string|null $name = null; + + public function run() + { + $this->name = 'string'; + } +} +CODE_SAMPLE + , + [ + self::INLINE_PUBLIC => false, + ] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_PROPERTIES; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + $classReflection = null; + + foreach ($node->getProperties() as $property) { + // non-private property can be anything with not inline public configured + if (! $property->isPrivate() && ! $this->inlinePublic) { + continue; + } + + if ($this->isDoctrineMappedProperty($property)) { + continue; + } + + if (! $classReflection instanceof ClassReflection) { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + } + + if (! $classReflection instanceof ClassReflection) { + return null; + } + + if (! $this->makePropertyTypedGuard->isLegal($property, $classReflection, $this->inlinePublic)) { + continue; + } + + $inferredType = $this->allAssignNodePropertyTypeInferer->inferProperty( + $property, + $classReflection, + $this->getFile() + ); + if (! $inferredType instanceof Type) { + continue; + } + + if ($inferredType instanceof MixedType) { + continue; + } + + $inferredType = $this->decorateTypeWithNullableIfDefaultPropertyNull($property, $inferredType); + $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($inferredType, TypeKind::PROPERTY); + if (! $typeNode instanceof Node) { + continue; + } + + $hasChanged = true; + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + if ($inferredType instanceof UnionType && ($typeNode instanceof NodeUnionType || $typeNode instanceof NullableType)) { + $this->propertyTypeDecorator->decoratePropertyUnionType( + $inferredType, + $typeNode, + $property, + $phpDocInfo, + false + ); + } else { + $property->type = $typeNode; + } + + if (! $property->type instanceof Node) { + continue; + } + + $this->varTagRemover->removeVarTagIfUseless($phpDocInfo, $property); + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function decorateTypeWithNullableIfDefaultPropertyNull(Property $property, Type $inferredType): Type + { + $defaultExpr = $property->props[0]->default; + if (! $defaultExpr instanceof Expr) { + return $inferredType; + } + + if (! $this->valueResolver->isNull($defaultExpr)) { + return $inferredType; + } + + if (TypeCombinator::containsNull($inferredType)) { + return $inferredType; + } + + return TypeCombinator::addNull($inferredType); + } + + /** + * Doctrine properties are handled in doctrine rules + */ + private function isDoctrineMappedProperty(Property $property): bool + { + $mappingClasses = array_merge( + CollectionMapping::TO_MANY_CLASSES, + CollectionMapping::TO_ONE_CLASSES, + [MappingClass::COLUMN] + ); + + return $this->attrinationFinder->hasByMany($property, $mappingClasses); + } +} diff --git a/rules/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector.php b/rules/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector.php index 69907776aec..4351c5a7337 100644 --- a/rules/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector.php +++ b/rules/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector.php @@ -5,14 +5,25 @@ namespace Rector\TypeDeclaration\Rector\Property; use PhpParser\Node; -use PhpParser\Node\Stmt\Property; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; +use PHPStan\Reflection\ClassReflection; use PHPStan\Type\MixedType; use PHPStan\Type\Type; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; -use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\ConstructorPropertyTypeInferer; +use Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; +use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector; +use Rector\TypeDeclaration\Guard\PropertyTypeOverrideGuard; +use Rector\TypeDeclaration\TypeAnalyzer\PropertyTypeDefaultValueAnalyzer; +use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\TrustedClassMethodPropertyTypeInferer; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,8 +34,16 @@ final class TypedPropertyFromStrictConstructorRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private ConstructorPropertyTypeInferer $constructorPropertyTypeInferer, - private VarTagRemover $varTagRemover + private readonly TrustedClassMethodPropertyTypeInferer $trustedClassMethodPropertyTypeInferer, + private readonly VarTagRemover $varTagRemover, + private readonly PhpDocTypeChanger $phpDocTypeChanger, + private readonly ConstructorAssignDetector $constructorAssignDetector, + private readonly PropertyTypeOverrideGuard $propertyTypeOverrideGuard, + private readonly ReflectionResolver $reflectionResolver, + private readonly DoctrineTypeAnalyzer $doctrineTypeAnalyzer, + private readonly PropertyTypeDefaultValueAnalyzer $propertyTypeDefaultValueAnalyzer, + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly StaticTypeMapper $staticTypeMapper ) { } @@ -65,42 +84,117 @@ public function __construct(string $name) */ public function getNodeTypes(): array { - return [Property::class]; + return [Class_::class]; } /** - * @param Property $node + * @param Class_ $node */ public function refactor(Node $node): ?Node { - if ($node->type !== null) { + $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { return null; } - $varType = $this->constructorPropertyTypeInferer->inferProperty($node); - if (! $varType instanceof Type) { - return null; - } - if ($varType instanceof MixedType) { + if (! $this->hasSomeUntypedProperties($node)) { return null; } - $propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($varType, TypeKind::PROPERTY()); - - if (! $propertyTypeNode instanceof Node) { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { return null; } - $node->type = $propertyTypeNode; + $hasChanged = false; + foreach ($node->getProperties() as $property) { + if (! $this->propertyTypeOverrideGuard->isLegal($property, $classReflection)) { + continue; + } + + $propertyType = $this->trustedClassMethodPropertyTypeInferer->inferProperty( + $node, + $property, + $constructClassMethod + ); + + if ($this->shouldSkipPropertyType($propertyType)) { + continue; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + // public property can be anything + if ($property->isPublic()) { + if (! $phpDocInfo->getVarType() instanceof MixedType) { + continue; + } + + $this->phpDocTypeChanger->changeVarType($property, $phpDocInfo, $propertyType); + $hasChanged = true; + continue; + } + + $propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $propertyType, + TypeKind::PROPERTY + ); + if (! $propertyTypeNode instanceof Node) { + continue; + } + + $propertyProperty = $property->props[0]; + + $propertyName = $this->getName($property); + if ($this->constructorAssignDetector->isPropertyAssigned($node, $propertyName)) { + $propertyProperty->default = null; + $hasChanged = true; + } + + if ($this->propertyTypeDefaultValueAnalyzer->doesConflictWithDefaultValue( + $propertyProperty, + $propertyType + )) { + continue; + } + + $property->type = $propertyTypeNode; + $this->varTagRemover->removeVarTagIfUseless($phpDocInfo, $property); + + $hasChanged = true; + } - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $this->varTagRemover->removeVarTagIfUseless($phpDocInfo, $node); + if ($hasChanged) { + return $node; + } - return $node; + return null; } public function provideMinPhpVersion(): int { return PhpVersionFeature::TYPED_PROPERTIES; } + + private function shouldSkipPropertyType(Type $propertyType): bool + { + if ($propertyType instanceof MixedType) { + return true; + } + + return $this->doctrineTypeAnalyzer->isInstanceOfCollectionType($propertyType); + } + + private function hasSomeUntypedProperties(Class_ $class): bool + { + foreach ($class->getProperties() as $property) { + if ($property->type instanceof Node) { + continue; + } + + return true; + } + + return false; + } } diff --git a/rules/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector.php b/rules/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector.php new file mode 100644 index 00000000000..74280309661 --- /dev/null +++ b/rules/TypeDeclaration/Rector/Property/TypedPropertyFromStrictSetUpRector.php @@ -0,0 +1,150 @@ +value = 1000; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +final class SomeClass extends TestCase +{ + private int $value; + + public function setUp() + { + $this->value = 1000; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $setUpClassMethod = $node->getMethod(MethodName::SET_UP); + if (! $setUpClassMethod instanceof ClassMethod) { + return null; + } + + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + // type is already set + if ($property->type !== null) { + continue; + } + + // is not private? we cannot be sure about other usage + if (! $property->isPrivate()) { + continue; + } + + $propertyType = $this->trustedClassMethodPropertyTypeInferer->inferProperty( + $node, + $property, + $setUpClassMethod + ); + + $propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $propertyType, + TypeKind::PROPERTY + ); + if (! $propertyTypeNode instanceof Node) { + continue; + } + + if ($propertyType instanceof ObjectType && $propertyType->getClassName() === ClassName::MOCK_OBJECT) { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + $varTag = $phpDocInfo->getVarTagValueNode(); + $varType = $phpDocInfo->getVarType(); + + if ($varTag instanceof VarTagValueNode && $varType instanceof ObjectType && $varType->getClassName() !== ClassName::MOCK_OBJECT) { + $varTag->type = new IntersectionTypeNode([ + new FullyQualifiedIdentifierTypeNode($propertyType->getClassName()), + new FullyQualifiedIdentifierTypeNode($varType->getClassName()), + ]); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); + } + } + + $property->type = $propertyTypeNode; + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::TYPED_PROPERTIES; + } +} diff --git a/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php b/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php new file mode 100644 index 00000000000..db8e89ffd3a --- /dev/null +++ b/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php @@ -0,0 +1,99 @@ +getFile()->hasShebang()) { + return null; + } + + // only add to namespaced files, as global namespace files are often included in other files + if (! $node->isNamespaced()) { + return null; + } + + // when first stmt is Declare_, verify if there is strict_types definition already, + // as multiple declare is allowed, with declare(strict_types=1) only allowed on very first stmt + if ($this->declareStrictTypeFinder->hasDeclareStrictTypes($node)) { + return null; + } + + $declaresStrictType = $this->nodeFactory->createDeclaresStrictType(); + $node->stmts = array_merge([$declaresStrictType, new Nop()], $node->stmts); + + return $node; + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [FileNode::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_70; + } +} diff --git a/rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php b/rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php new file mode 100644 index 00000000000..ff5a37f5401 --- /dev/null +++ b/rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php @@ -0,0 +1,77 @@ + 10, + ], + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return NodeGroup::STMTS_AWARE; + } + + /** + * @param StmtsAware $node + */ + public function refactor(Node $node): ?Node + { + throw new ShouldNotHappenException(sprintf( + '"%s" is deprecated as changes strict types randomly on each run. Use "%s" Rector on specific paths instead.', + self::class, + DeclareStrictTypesRector::class + )); + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + } +} diff --git a/rules/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector.php b/rules/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector.php new file mode 100644 index 00000000000..bb64e7469ef --- /dev/null +++ b/rules/TypeDeclaration/Rector/StmtsAwareInterface/SafeDeclareStrictTypesRector.php @@ -0,0 +1,89 @@ +declareStrictTypeFinder->hasDeclareStrictTypes($node)) { + return null; + } + + if (! $this->strictTypeSafetyChecker->isFileStrictTypeSafe($node)) { + return null; + } + + $declaresStrictType = $this->nodeFactory->createDeclaresStrictType(); + $node->stmts = array_merge([$declaresStrictType, new Nop()], $node->stmts); + + return $node; + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [FileNode::class]; + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_70; + } +} diff --git a/rules/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector.php b/rules/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector.php new file mode 100644 index 00000000000..0d7a4c69363 --- /dev/null +++ b/rules/TypeDeclaration/Rector/While_/WhileNullableToInstanceofRector.php @@ -0,0 +1,116 @@ +> + */ + public function getNodeTypes(): array + { + return [While_::class, Do_::class]; + } + + /** + * @param While_|Do_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->cond instanceof Assign) { + return null; + } + + if ($node->cond instanceof NotIdentical) { + return $this->refactorNotIdentical($node, $node->cond); + } + + $condNullableObjectType = $this->nullableTypeAnalyzer->resolveNullableObjectType($node->cond); + if (! $condNullableObjectType instanceof Type) { + return null; + } + + $node->cond = $this->createInstanceof($node->cond, $condNullableObjectType); + + return $node; + } + + private function createInstanceof(Expr $expr, ObjectType $objectType): Instanceof_ + { + $fullyQualified = new FullyQualified($objectType->getClassName()); + return new Instanceof_($expr, $fullyQualified); + } + + private function refactorNotIdentical(While_|Do_ $while, NotIdentical $notIdentical): While_|Do_|null + { + if (! $this->valueResolver->isNull($notIdentical->right)) { + return null; + } + + $condNullableObjectType = $this->nullableTypeAnalyzer->resolveNullableObjectType($notIdentical->left); + if (! $condNullableObjectType instanceof ObjectType) { + return null; + } + + $while->cond = $this->createInstanceof($notIdentical->left, $condNullableObjectType); + return $while; + } +} diff --git a/rules/TypeDeclaration/Sorter/TypeInfererSorter.php b/rules/TypeDeclaration/Sorter/TypeInfererSorter.php deleted file mode 100644 index 23779b3a44d..00000000000 --- a/rules/TypeDeclaration/Sorter/TypeInfererSorter.php +++ /dev/null @@ -1,44 +0,0 @@ -ensurePriorityIsUnique($sortedTypeInferers, $priorityAwareTypeInferer); - $sortedTypeInferers[$priorityAwareTypeInferer->getPriority()] = $priorityAwareTypeInferer; - } - - krsort($sortedTypeInferers); - - return $sortedTypeInferers; - } - - /** - * @param PriorityAwareTypeInfererInterface[] $sortedTypeInferers - */ - private function ensurePriorityIsUnique( - array $sortedTypeInferers, - PriorityAwareTypeInfererInterface $priorityAwareTypeInferer - ): void { - if (! isset($sortedTypeInferers[$priorityAwareTypeInferer->getPriority()])) { - return; - } - - $alreadySetPropertyTypeInferer = $sortedTypeInferers[$priorityAwareTypeInferer->getPriority()]; - throw new ConflictingPriorityException($priorityAwareTypeInferer, $alreadySetPropertyTypeInferer); - } -} diff --git a/rules/TypeDeclaration/TypeAlreadyAddedChecker/ReturnTypeAlreadyAddedChecker.php b/rules/TypeDeclaration/TypeAlreadyAddedChecker/ReturnTypeAlreadyAddedChecker.php deleted file mode 100644 index dff3356f297..00000000000 --- a/rules/TypeDeclaration/TypeAlreadyAddedChecker/ReturnTypeAlreadyAddedChecker.php +++ /dev/null @@ -1,157 +0,0 @@ -[] - */ - private const FOREACHABLE_TYPES = ['iterable', 'Iterator', 'Traversable', 'array']; - - public function __construct( - private NodeNameResolver $nodeNameResolver, - private StaticTypeMapper $staticTypeMapper, - private NodeComparator $nodeComparator - ) { - } - - public function isSameOrBetterReturnTypeAlreadyAdded(ClassMethod | Function_ $functionLike, Type $returnType): bool - { - $nodeReturnType = $functionLike->returnType; - - /** @param Identifier|Name|NullableType|PhpParserUnionType|null $returnTypeNode */ - if ($nodeReturnType === null) { - return false; - } - - $returnNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN()); - if ($this->nodeComparator->areNodesEqual($nodeReturnType, $returnNode)) { - return true; - } - - // is array <=> iterable <=> Iterator co-type? → skip - if ($this->isArrayIterableIteratorCoType($nodeReturnType, $returnType)) { - return true; - } - - if ($this->isUnionCoType($nodeReturnType, $returnType)) { - return true; - } - - // is class-string type? → skip - if ($returnType instanceof GenericObjectType && $returnType->getClassName() === 'class-string') { - return true; - } - - // prevent overriding self with itself - if (! $functionLike->returnType instanceof Name) { - return false; - } - - if ($functionLike->returnType->toLowerString() !== 'self') { - return false; - } - - // skip "self" by "static" override - if ($returnType instanceof ThisType) { - return true; - } - - $className = $functionLike->getAttribute(AttributeKey::CLASS_NAME); - - $nodeContent = $this->nodeComparator->printWithoutComments($returnNode); - $nodeContentWithoutPreslash = ltrim($nodeContent, '\\'); - - return $nodeContentWithoutPreslash === $className; - } - - private function isArrayIterableIteratorCoType( - Identifier | Name | NullableType | PhpParserUnionType $returnTypeNode, - Type $returnType - ): bool { - if (! $this->nodeNameResolver->isNames($returnTypeNode, self::FOREACHABLE_TYPES)) { - return false; - } - - return $this->isStaticTypeIterable($returnType); - } - - private function isUnionCoType( - Identifier | Name | NullableType | PhpParserUnionType $returnTypeNode, - Type $type - ): bool { - if (! $type instanceof UnionType) { - return false; - } - - // skip nullable type - $nullType = new NullType(); - if ($type->isSuperTypeOf($nullType)->yes()) { - return false; - } - - $classMethodReturnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($returnTypeNode); - return $type->isSuperTypeOf($classMethodReturnType) - ->yes(); - } - - private function isStaticTypeIterable(Type $type): bool - { - if ($this->isArrayIterableOrIteratorType($type)) { - return true; - } - - if ($type instanceof UnionType || $type instanceof IntersectionType) { - foreach ($type->getTypes() as $joinedType) { - if (! $this->isStaticTypeIterable($joinedType)) { - return false; - } - } - - return true; - } - - return false; - } - - private function isArrayIterableOrIteratorType(Type $type): bool - { - if ($type instanceof ArrayType) { - return true; - } - - if ($type instanceof IterableType) { - return true; - } - if (! $type instanceof ObjectType) { - return false; - } - return $type->getClassName() === Iterator::class; - } -} diff --git a/rules/TypeDeclaration/TypeAnalyzer/AdvancedArrayAnalyzer.php b/rules/TypeDeclaration/TypeAnalyzer/AdvancedArrayAnalyzer.php deleted file mode 100644 index a97e454a081..00000000000 --- a/rules/TypeDeclaration/TypeAnalyzer/AdvancedArrayAnalyzer.php +++ /dev/null @@ -1,99 +0,0 @@ -phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - - $arrayType = $this->typeNormalizer->convertConstantArrayTypeToArrayType($arrayType); - if (! $arrayType instanceof ArrayType) { - return false; - } - - $currentReturnType = $phpDocInfo->getReturnType(); - if (! $currentReturnType instanceof ArrayType) { - return false; - } - - if (! $currentReturnType->getItemType() instanceof ClassStringType) { - return false; - } - - return $arrayType->getItemType() instanceof StringType; - } - - public function isMixedOfSpecificOverride(ArrayType $arrayType, PhpDocInfo $phpDocInfo): bool - { - if (! $arrayType->getItemType() instanceof MixedType) { - return false; - } - - $currentReturnType = $phpDocInfo->getReturnType(); - return $currentReturnType instanceof ArrayType; - } - - public function isMoreSpecificArrayTypeOverride( - Type $newType, - ClassMethod $classMethod, - PhpDocInfo $phpDocInfo - ): bool { - if (! $newType instanceof ConstantArrayType) { - return false; - } - - if (! $newType->getItemType() instanceof NeverType) { - return false; - } - - $phpDocReturnType = $phpDocInfo->getReturnType(); - if (! $phpDocReturnType instanceof ArrayType) { - return false; - } - - return ! $phpDocReturnType->getItemType() instanceof VoidType; - } - - public function isNewAndCurrentTypeBothCallable(ArrayType $newArrayType, PhpDocInfo $phpDocInfo): bool - { - $currentReturnType = $phpDocInfo->getReturnType(); - if (! $currentReturnType instanceof ArrayType) { - return false; - } - - if (! $newArrayType->getItemType()->isCallable()->yes()) { - return false; - } - - return $currentReturnType->getItemType() - ->isCallable() - ->yes(); - } -} diff --git a/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php b/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php index ea639821015..16305eeb9be 100644 --- a/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php +++ b/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php @@ -4,93 +4,19 @@ namespace Rector\TypeDeclaration\TypeAnalyzer; -use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Type\ArrayType; -use PHPStan\Type\ClassStringType; -use PHPStan\Type\Constant\ConstantArrayType; -use PHPStan\Type\Constant\ConstantIntegerType; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\Generic\GenericClassStringType; -use PHPStan\Type\MixedType; -use PHPStan\Type\ObjectType; -use PHPStan\Type\StringType; -use PHPStan\Type\Type; -use PHPStan\Type\TypeTraverser; use PHPStan\Type\UnionType; -final class GenericClassStringTypeNormalizer +final readonly class GenericClassStringTypeNormalizer { - public function __construct( - private ReflectionProvider $reflectionProvider - ) { - } - - public function normalize(Type $type): ArrayType | UnionType | Type - { - $type = TypeTraverser::map($type, function (Type $type, $callback): Type { - if (! $type instanceof ConstantStringType) { - return $callback($type); - } - - $value = $type->getValue(); - - // skip string that look like classe - if ($value === 'error') { - return $callback($type); - } - - if (! $this->reflectionProvider->hasClass($value)) { - return $callback($type); - } - - return $this->resolveStringType($value); - }); - - if ($type instanceof UnionType) { - return $this->resolveClassStringInUnionType($type); - } - - return $type; - } - - private function resolveClassStringInUnionType(UnionType $type): UnionType | ArrayType + public function isAllGenericClassStringType(UnionType $unionType): bool { - $unionTypes = $type->getTypes(); - - foreach ($unionTypes as $unionType) { - if (! $unionType instanceof ArrayType) { - return $type; - } - - $keyType = $unionType->getKeyType(); - $itemType = $unionType->getItemType(); - - if ($itemType instanceof ConstantArrayType) { - $arrayType = new ArrayType(new MixedType(), new MixedType()); - return new ArrayType($keyType, $arrayType); - } - - if (! $keyType instanceof MixedType && ! $keyType instanceof ConstantIntegerType) { - return $type; - } - - if (! $itemType instanceof ClassStringType) { - return $type; + foreach ($unionType->getTypes() as $type) { + if (! $type instanceof GenericClassStringType) { + return false; } } - return new ArrayType(new MixedType(), new ClassStringType()); - } - - private function resolveStringType(string $value): GenericClassStringType | StringType - { - $classReflection = $this->reflectionProvider->getClass($value); - if ($classReflection->isBuiltIn()) { - return new GenericClassStringType(new ObjectType($value)); - } - if (str_contains($value, '\\')) { - return new GenericClassStringType(new ObjectType($value)); - } - return new StringType(); + return true; } } diff --git a/rules/TypeDeclaration/TypeAnalyzer/NullableTypeAnalyzer.php b/rules/TypeDeclaration/TypeAnalyzer/NullableTypeAnalyzer.php new file mode 100644 index 00000000000..5720329c936 --- /dev/null +++ b/rules/TypeDeclaration/TypeAnalyzer/NullableTypeAnalyzer.php @@ -0,0 +1,30 @@ +nodeTypeResolver->getNativeType($expr); + + $baseType = TypeCombinator::removeNull($exprType); + if (! $baseType instanceof ObjectType) { + return null; + } + + return $baseType; + } +} diff --git a/rules/TypeDeclaration/TypeAnalyzer/ObjectTypeComparator.php b/rules/TypeDeclaration/TypeAnalyzer/ObjectTypeComparator.php deleted file mode 100644 index 96f4b8842b3..00000000000 --- a/rules/TypeDeclaration/TypeAnalyzer/ObjectTypeComparator.php +++ /dev/null @@ -1,54 +0,0 @@ -isBothCallable($currentType, $newType)) { - return true; - } - - if (! $currentType instanceof ObjectType) { - return false; - } - - if (! $newType instanceof ObjectType) { - return false; - } - - return $newType->isSuperTypeOf($currentType) - ->yes(); - } - - private function isBothCallable(Type $currentType, Type $newType): bool - { - if ($currentType instanceof CallableType && $this->isClosure($newType)) { - return true; - } - - return $newType instanceof CallableType && $this->isClosure($currentType); - } - - private function isClosure(Type $type): bool - { - $closureObjectType = new ObjectType('Closure'); - return $closureObjectType->equals($type); - } -} diff --git a/rules/TypeDeclaration/TypeAnalyzer/ParameterTypeFromDataProviderResolver.php b/rules/TypeDeclaration/TypeAnalyzer/ParameterTypeFromDataProviderResolver.php new file mode 100644 index 00000000000..f24d7e72517 --- /dev/null +++ b/rules/TypeDeclaration/TypeAnalyzer/ParameterTypeFromDataProviderResolver.php @@ -0,0 +1,151 @@ +resolveParameterTypeFromDataProvider($parameterPosition, $dataProviderClassMethod); + } + + return TypeCombinator::union(...$paramTypes); + } + + private function resolveParameterTypeFromDataProvider( + int $parameterPosition, + ClassMethod $dataProviderClassMethod + ): Type { + $returns = $this->betterNodeFinder->findReturnsScoped($dataProviderClassMethod); + if ($returns !== []) { + return $this->resolveReturnStaticArrayTypeByParameterPosition($returns, $parameterPosition); + } + + /** @var Yield_[] $yields */ + $yields = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($dataProviderClassMethod, Yield_::class); + return $this->resolveYieldStaticArrayTypeByParameterPosition($yields, $parameterPosition); + } + + /** + * @param Return_[] $returns + */ + private function resolveReturnStaticArrayTypeByParameterPosition(array $returns, int $parameterPosition): Type + { + $firstReturnedExpr = $returns[0]->expr; + + if (! $firstReturnedExpr instanceof Array_) { + return new MixedType(); + } + + $paramOnPositionTypes = $this->resolveParamOnPositionTypes($firstReturnedExpr, $parameterPosition); + if ($paramOnPositionTypes === []) { + return new MixedType(); + } + + return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes); + } + + /** + * @param Yield_[] $yields + */ + private function resolveYieldStaticArrayTypeByParameterPosition(array $yields, int $parameterPosition): Type + { + $paramOnPositionTypes = []; + + foreach ($yields as $yield) { + if (! $yield->value instanceof Array_) { + continue; + } + + $type = $this->getTypeFromClassMethodYield($yield->value); + + if (! $type instanceof ConstantArrayType) { + return $type; + } + + foreach ($type->getValueTypes() as $position => $valueType) { + if ($position !== $parameterPosition) { + continue; + } + + $paramOnPositionTypes[] = $valueType; + } + } + + if ($paramOnPositionTypes === []) { + return new MixedType(); + } + + return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes); + } + + private function getTypeFromClassMethodYield(Array_ $classMethodYieldArray): MixedType | ConstantArrayType + { + $arrayType = $this->nodeTypeResolver->getType($classMethodYieldArray); + + // impossible to resolve + if (! $arrayType instanceof ConstantArrayType) { + return new MixedType(); + } + + return $arrayType; + } + + /** + * @return Type[] + */ + private function resolveParamOnPositionTypes(Array_ $array, int $parameterPosition): array + { + $paramOnPositionTypes = []; + + foreach ($array->items as $singleDataProvidedSet) { + if (! $singleDataProvidedSet instanceof ArrayItem || ! $singleDataProvidedSet->value instanceof Array_) { + return []; + } + + foreach ($singleDataProvidedSet->value->items as $position => $singleDataProvidedSetItem) { + if ($position !== $parameterPosition) { + continue; + } + + if (! $singleDataProvidedSetItem instanceof ArrayItem) { + continue; + } + + $paramOnPositionTypes[] = $this->nodeTypeResolver->getType($singleDataProvidedSetItem->value); + } + } + + return $paramOnPositionTypes; + } +} diff --git a/rules/TypeDeclaration/TypeAnalyzer/PropertyTypeDefaultValueAnalyzer.php b/rules/TypeDeclaration/TypeAnalyzer/PropertyTypeDefaultValueAnalyzer.php new file mode 100644 index 00000000000..12a7f3cae45 --- /dev/null +++ b/rules/TypeDeclaration/TypeAnalyzer/PropertyTypeDefaultValueAnalyzer.php @@ -0,0 +1,35 @@ +default instanceof Expr) { + return false; + } + + // the defaults can be in conflict + $defaultType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($propertyItem->default); + if ($defaultType->isArray()->yes() && $propertyType->isArray()->yes()) { + return false; + } + + // type is not matching, skip it + return ! $defaultType->isSuperTypeOf($propertyType) + ->yes(); + } +} diff --git a/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php b/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php new file mode 100644 index 00000000000..59774b41d07 --- /dev/null +++ b/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php @@ -0,0 +1,175 @@ + + */ + public function collectStrictReturnTypes(array $returns, Scope $scope): array + { + $containsStrictCall = false; + $returnedStrictTypeNodes = []; + + foreach ($returns as $return) { + if (! $return->expr instanceof Expr) { + return []; + } + + $returnedExpr = $return->expr; + + if ($returnedExpr instanceof MethodCall || $returnedExpr instanceof StaticCall || $returnedExpr instanceof FuncCall) { + $containsStrictCall = true; + $returnNode = $this->resolveMethodCallReturnNode($returnedExpr); + } elseif ($returnedExpr instanceof ClassConstFetch) { + $returnNode = $this->resolveConstFetchReturnNode($returnedExpr, $scope); + } elseif ($returnedExpr instanceof Array_ || $returnedExpr instanceof String_ || $returnedExpr instanceof Int_ || $returnedExpr instanceof Float_) { + $returnNode = $this->resolveLiteralReturnNode($returnedExpr, $scope); + } else { + return []; + } + + if (! $returnNode instanceof Node) { + return []; + } + + if ($returnNode instanceof Identifier && $returnNode->toString() === 'void') { + return []; + } + + $returnedStrictTypeNodes[] = $returnNode; + } + + if (! $containsStrictCall) { + return []; + } + + return $this->typeNodeUnwrapper->uniquateNodes($returnedStrictTypeNodes); + } + + public function resolveMethodCallReturnNode(MethodCall | StaticCall | FuncCall $call): ?Node + { + $returnType = $this->resolveMethodCallReturnType($call); + if (! $returnType instanceof Type) { + return null; + } + + return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN); + } + + public function resolveMethodCallReturnType(MethodCall | StaticCall | FuncCall $call): ?Type + { + $methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($call); + if ($methodReflection === null) { + return null; + } + + $scope = $call->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + $parametersAcceptorWithPhpDocs = ParametersAcceptorSelectorVariantsWrapper::select( + $methodReflection, + $call, + $scope + ); + + if ($methodReflection instanceof NativeFunctionReflection || $methodReflection instanceof NativeMethodReflection) { + $returnType = $parametersAcceptorWithPhpDocs->getReturnType(); + } elseif ($parametersAcceptorWithPhpDocs instanceof ExtendedParametersAcceptor) { + // native return type is needed, as docblock can be false + $returnType = $parametersAcceptorWithPhpDocs->getNativeReturnType(); + } else { + $returnType = $parametersAcceptorWithPhpDocs->getReturnType(); + } + + if ($returnType instanceof MixedType) { + if ($returnType->isExplicitMixed()) { + return $returnType; + } + + return null; + } + + return $this->normalizeStaticType($call, $returnType); + } + + private function normalizeStaticType(MethodCall | StaticCall | FuncCall $call, Type $type): Type + { + $reflectionClass = $this->reflectionResolver->resolveClassReflection($call); + $currentClassName = $reflectionClass instanceof ClassReflection + ? $reflectionClass->getName() + : null; + + return TypeTraverser::map($type, static function (Type $currentType, callable $traverseCallback) use ( + $currentClassName + ): Type { + if ($currentType instanceof StaticType && $currentClassName !== $currentType->getClassName()) { + return new FullyQualifiedObjectType($currentType->getClassName()); + } + + return $traverseCallback($currentType); + }); + } + + private function resolveLiteralReturnNode(Array_|Scalar $returnedExpr, Scope $scope): ?Node + { + $returnType = $scope->getType($returnedExpr); + return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN); + } + + private function resolveConstFetchReturnNode(ClassConstFetch $classConstFetch, Scope $scope): ?Node + { + $constType = $scope->getType($classConstFetch); + + if ($constType instanceof MixedType) { + return null; + } + + return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($constType, TypeKind::RETURN); + } +} diff --git a/rules/TypeDeclaration/TypeInferer/AssignToPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/AssignToPropertyTypeInferer.php index 58f83c67820..28a93d004d7 100644 --- a/rules/TypeDeclaration/TypeInferer/AssignToPropertyTypeInferer.php +++ b/rules/TypeDeclaration/TypeInferer/AssignToPropertyTypeInferer.php @@ -8,20 +8,33 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticPropertyFetch; +use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\ClassLike; +use PhpParser\Node\Stmt\Property; +use PhpParser\NodeVisitor; use PHPStan\Type\ArrayType; use PHPStan\Type\MixedType; use PHPStan\Type\NullType; use PHPStan\Type\Type; +use Rector\Enum\ClassName; +use Rector\NodeAnalyzer\ExprAnalyzer; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; +use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Node\Value\ValueResolver; use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector; use Rector\TypeDeclaration\AlreadyAssignDetector\NullTypeAssignDetector; use Rector\TypeDeclaration\AlreadyAssignDetector\PropertyDefaultAssignDetector; use Rector\TypeDeclaration\Matcher\PropertyAssignMatcher; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; -final class AssignToPropertyTypeInferer +/** + * @internal + */ +final readonly class AssignToPropertyTypeInferer { public function __construct( private ConstructorAssignDetector $constructorAssignDetector, @@ -30,48 +43,70 @@ public function __construct( private NullTypeAssignDetector $nullTypeAssignDetector, private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, private TypeFactory $typeFactory, - private NodeTypeResolver $nodeTypeResolver + private NodeTypeResolver $nodeTypeResolver, + private ExprAnalyzer $exprAnalyzer, + private ValueResolver $valueResolver, + private PropertyFetchAnalyzer $propertyFetchAnalyzer, + private PhpAttributeAnalyzer $phpAttributeAnalyzer ) { } - public function inferPropertyInClassLike(string $propertyName, ClassLike $classLike): ?Type + public function inferPropertyInClassLike(Property $property, string $propertyName, ClassLike $classLike): ?Type { - $assignedExprTypes = []; + if ($this->hasAssignDynamicPropertyValue($classLike, $propertyName)) { + return null; + } - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classLike->stmts, function (Node $node) use ( - $propertyName, - &$assignedExprTypes - ) { - if (! $node instanceof Assign) { - return null; - } + $assignedExprTypes = $this->getAssignedExprTypes($classLike, $property, $propertyName); - $expr = $this->propertyAssignMatcher->matchPropertyAssignExpr($node, $propertyName); - if (! $expr instanceof Expr) { - return null; - } + if ($this->shouldAddNullType($classLike, $propertyName, $assignedExprTypes)) { + $assignedExprTypes[] = new NullType(); + } - $exprStaticType = $this->resolveExprStaticTypeIncludingDimFetch($node); + return $this->resolveTypeWithVerifyDefaultValue($property, $assignedExprTypes); + } - $assignedExprTypes[] = $exprStaticType; + /** + * @param Type[] $assignedExprTypes + */ + private function resolveTypeWithVerifyDefaultValue(Property $property, array $assignedExprTypes): ?Type + { + $defaultPropertyValue = $property->props[0]->default; + if ($assignedExprTypes === []) { + // not typed, never assigned, but has default value, then pull type from default value + if (! $property->type instanceof Node && $defaultPropertyValue instanceof Expr) { + return $this->nodeTypeResolver->getType($defaultPropertyValue); + } return null; - }); - - if ($this->shouldAddNullType($classLike, $propertyName, $assignedExprTypes)) { - $assignedExprTypes[] = new NullType(); } - if ($assignedExprTypes === []) { + $inferredType = $this->typeFactory->createMixedPassedOrUnionType($assignedExprTypes); + if ($this->shouldSkipWithDifferentDefaultValueType($defaultPropertyValue, $inferredType)) { return null; } - return $this->typeFactory->createMixedPassedOrUnionType($assignedExprTypes); + return $inferredType; + } + + private function shouldSkipWithDifferentDefaultValueType(?Expr $expr, Type $inferredType): bool + { + if (! $expr instanceof Expr) { + return false; + } + + if ($this->valueResolver->isNull($expr)) { + return false; + } + + $defaultType = $this->nodeTypeResolver->getNativeType($expr); + return $inferredType->isSuperTypeOf($defaultType) + ->no(); } private function resolveExprStaticTypeIncludingDimFetch(Assign $assign): Type { - $exprStaticType = $this->nodeTypeResolver->getStaticType($assign->expr); + $exprStaticType = $this->nodeTypeResolver->getNativeType($assign->expr); if ($assign->var instanceof ArrayDimFetch) { return new ArrayType(new MixedType(), $exprStaticType); } @@ -96,6 +131,7 @@ private function shouldAddNullType(ClassLike $classLike, string $propertyName, a if ($isAssignedInConstructor) { return false; } + return ! $hasPropertyDefaultValue; } @@ -109,4 +145,76 @@ private function shouldAddNullType(ClassLike $classLike, string $propertyName, a return ! $hasPropertyDefaultValue; } + + private function hasAssignDynamicPropertyValue(ClassLike $classLike, string $propertyName): bool + { + $hasAssignDynamicPropertyValue = false; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classLike->stmts, function (Node $node) use ( + $propertyName, + &$hasAssignDynamicPropertyValue, + ): ?int { + if (! $node instanceof Assign) { + return null; + } + + $expr = $this->propertyAssignMatcher->matchPropertyAssignExpr($node, $propertyName); + if (! $expr instanceof Expr) { + if (! $this->propertyFetchAnalyzer->isLocalPropertyFetch($node->var)) { + return null; + } + + /** @var PropertyFetch|StaticPropertyFetch $assignVar */ + $assignVar = $node->var; + if (! $assignVar->name instanceof Identifier) { + $hasAssignDynamicPropertyValue = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + return null; + } + + return null; + }); + + return $hasAssignDynamicPropertyValue; + } + + /** + * @return array + */ + private function getAssignedExprTypes(ClassLike $classLike, Property $property, string $propertyName): array + { + $assignedExprTypes = []; + + $hasJmsType = $this->phpAttributeAnalyzer->hasPhpAttribute($property, ClassName::JMS_TYPE); + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classLike->stmts, function (Node $node) use ( + $propertyName, + &$assignedExprTypes, + $hasJmsType + ): ?int { + if (! $node instanceof Assign) { + return null; + } + + $expr = $this->propertyAssignMatcher->matchPropertyAssignExpr($node, $propertyName); + if (! $expr instanceof Expr) { + return null; + } + + if ($this->exprAnalyzer->isNonTypedFromParam($node->expr)) { + $assignedExprTypes = $hasJmsType + ? [] + : [new MixedType()]; + + return NodeVisitor::STOP_TRAVERSAL; + } + + $assignedExprTypes[] = $this->resolveExprStaticTypeIncludingDimFetch($node); + + return null; + }); + + return $assignedExprTypes; + } } diff --git a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ParamTypeInferer.php deleted file mode 100644 index bfaaccc3bce..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer.php +++ /dev/null @@ -1,37 +0,0 @@ -paramTypeInferers as $paramTypeInferer) { - $paramType = $paramTypeInferer->inferParam($param); - if ($paramType instanceof MixedType) { - continue; - } - - return $this->genericClassStringTypeNormalizer->normalize($paramType); - } - - return new MixedType(); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/FunctionLikeDocParamTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/FunctionLikeDocParamTypeInferer.php deleted file mode 100644 index 521d9fa3eea..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/FunctionLikeDocParamTypeInferer.php +++ /dev/null @@ -1,61 +0,0 @@ -resolveScopeNode($param); - if (! $functionLike instanceof FunctionLike) { - return new MixedType(); - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - - $paramTypesByName = $phpDocInfo->getParamTypesByName(); - if ($paramTypesByName === []) { - return new MixedType(); - } - - return $this->matchParamNodeFromDoc($paramTypesByName, $param); - } - - /** - * @return ClassMethod|Function_|null - */ - private function resolveScopeNode(Param $param): ?Node - { - return $this->parentFinder->findByTypes($param, [ClassMethod::class, Function_::class]); - } - - /** - * @param Type[] $paramWithTypes - */ - private function matchParamNodeFromDoc(array $paramWithTypes, Param $param): Type - { - $paramNodeName = '$' . $this->nodeNameResolver->getName($param->var); - return $paramWithTypes[$paramNodeName] ?? new MixedType(); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/GetterNodeParamTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/GetterNodeParamTypeInferer.php deleted file mode 100644 index 4c797c6aad6..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/GetterNodeParamTypeInferer.php +++ /dev/null @@ -1,93 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return new MixedType(); - } - - /** @var ClassMethod $classMethod */ - $classMethod = $param->getAttribute(AttributeKey::PARENT_NODE); - - /** @var string $paramName */ - $paramName = $this->nodeNameResolver->getName($param); - - $propertyNames = $this->propertyFetchAssignManipulator->getPropertyNamesOfAssignOfVariable( - $classMethod, - $paramName - ); - if ($propertyNames === []) { - return new MixedType(); - } - - $returnType = new MixedType(); - - // resolve property assigns - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classLike, function (Node $node) use ( - $propertyNames, - &$returnType - ): ?int { - if (! $node instanceof Return_) { - return null; - } - if ($node->expr === null) { - return null; - } - $isMatch = $this->propertyFetchAnalyzer->isLocalPropertyOfNames($node->expr, $propertyNames); - if (! $isMatch) { - return null; - } - - // what is return type? - $classMethod = $node->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - - $methodReturnType = $phpDocInfo->getReturnType(); - if ($methodReturnType instanceof MixedType) { - return null; - } - - $returnType = $methodReturnType; - - return NodeTraverser::STOP_TRAVERSAL; - }); - - return $returnType; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/KnownArrayParamTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/KnownArrayParamTypeInferer.php deleted file mode 100644 index c54d872043d..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/KnownArrayParamTypeInferer.php +++ /dev/null @@ -1,54 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return new MixedType(); - } - - $className = $this->nodeNameResolver->getName($classLike); - if (! $className) { - return new MixedType(); - } - - if (! $this->reflectionProvider->hasClass($className)) { - return new MixedType(); - } - - $classReflection = $this->reflectionProvider->getClass($className); - $paramName = $this->nodeNameResolver->getName($param); - - // @todo create map later - if ($paramName === 'configs' && $classReflection->isSubclassOf( - 'Symfony\Component\DependencyInjection\Extension\Extension' - )) { - return new ArrayType(new MixedType(), new StringType()); - } - - return new MixedType(); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/PHPUnitDataProviderParamTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/PHPUnitDataProviderParamTypeInferer.php deleted file mode 100644 index a72c76fea2a..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/PHPUnitDataProviderParamTypeInferer.php +++ /dev/null @@ -1,208 +0,0 @@ -\w+)(\(\))?#'; - - private NodeTypeResolver $nodeTypeResolver; - - public function __construct( - private BetterNodeFinder $betterNodeFinder, - private TypeFactory $typeFactory, - private PhpDocInfoFactory $phpDocInfoFactory - ) { - } - - // Prevents circular reference - - #[Required] - public function autowirePHPUnitDataProviderParamTypeInferer(NodeTypeResolver $nodeTypeResolver): void - { - $this->nodeTypeResolver = $nodeTypeResolver; - } - - public function inferParam(Param $param): Type - { - $dataProviderClassMethod = $this->resolveDataProviderClassMethod($param); - if (! $dataProviderClassMethod instanceof ClassMethod) { - return new MixedType(); - } - - $parameterPosition = $param->getAttribute(AttributeKey::PARAMETER_POSITION); - if ($parameterPosition === null) { - return new MixedType(); - } - - /** @var Return_[] $returns */ - $returns = $this->betterNodeFinder->findInstanceOf((array) $dataProviderClassMethod->stmts, Return_::class); - if ($returns !== []) { - return $this->resolveReturnStaticArrayTypeByParameterPosition($returns, $parameterPosition); - } - - /** @var Yield_[] $yields */ - $yields = $this->betterNodeFinder->findInstanceOf((array) $dataProviderClassMethod->stmts, Yield_::class); - return $this->resolveYieldStaticArrayTypeByParameterPosition($yields, $parameterPosition); - } - - private function resolveDataProviderClassMethod(Param $param): ?ClassMethod - { - $phpDocInfo = $this->getFunctionLikePhpDocInfo($param); - - $phpDocTagNode = $phpDocInfo->getByName('@dataProvider'); - if (! $phpDocTagNode instanceof PhpDocTagNode) { - return null; - } - - $classLike = $param->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return null; - } - - if (! $phpDocTagNode->value instanceof GenericTagValueNode) { - return null; - } - - $content = $phpDocTagNode->value->value; - $match = Strings::match($content, self::METHOD_NAME_REGEX); - if ($match === null) { - return null; - } - - $methodName = $match['method_name']; - return $classLike->getMethod($methodName); - } - - /** - * @param Return_[] $returns - */ - private function resolveReturnStaticArrayTypeByParameterPosition(array $returns, int $parameterPosition): Type - { - $firstReturnedExpr = $returns[0]->expr; - - if (! $firstReturnedExpr instanceof Array_) { - return new MixedType(); - } - - $paramOnPositionTypes = $this->resolveParamOnPositionTypes($firstReturnedExpr, $parameterPosition); - if ($paramOnPositionTypes === []) { - return new MixedType(); - } - - return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes); - } - - /** - * @param Yield_[] $yields - */ - private function resolveYieldStaticArrayTypeByParameterPosition(array $yields, int $parameterPosition): Type - { - $paramOnPositionTypes = []; - - foreach ($yields as $yield) { - if (! $yield->value instanceof Array_) { - continue; - } - - $type = $this->getTypeFromClassMethodYield($yield->value); - - if (! $type instanceof ConstantArrayType) { - return $type; - } - - foreach ($type->getValueTypes() as $position => $valueType) { - if ($position !== $parameterPosition) { - continue; - } - - $paramOnPositionTypes[] = $valueType; - } - } - - if ($paramOnPositionTypes === []) { - return new MixedType(); - } - - return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes); - } - - private function getTypeFromClassMethodYield(Array_ $classMethodYieldArrayNode): MixedType | ConstantArrayType - { - $arrayTypes = $this->nodeTypeResolver->resolve($classMethodYieldArrayNode); - - // impossible to resolve - if (! $arrayTypes instanceof ConstantArrayType) { - return new MixedType(); - } - - return $arrayTypes; - } - - private function getFunctionLikePhpDocInfo(Param $param): PhpDocInfo - { - $parent = $param->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof FunctionLike) { - throw new ShouldNotHappenException(); - } - - return $this->phpDocInfoFactory->createFromNodeOrEmpty($parent); - } - - /** - * @return Type[] - */ - private function resolveParamOnPositionTypes(Array_ $array, int $parameterPosition): array - { - $paramOnPositionTypes = []; - - foreach ($array->items as $singleDataProvidedSet) { - if (! $singleDataProvidedSet instanceof ArrayItem || ! $singleDataProvidedSet->value instanceof Array_) { - throw new ShouldNotHappenException(); - } - - foreach ($singleDataProvidedSet->value->items as $position => $singleDataProvidedSetItem) { - if ($position !== $parameterPosition) { - continue; - } - if (! $singleDataProvidedSetItem instanceof ArrayItem) { - continue; - } - - $paramOnPositionTypes[] = $this->nodeTypeResolver->resolve($singleDataProvidedSetItem->value); - } - } - - return $paramOnPositionTypes; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/PropertyNodeParamTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/PropertyNodeParamTypeInferer.php deleted file mode 100644 index 5b5f172b0a3..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/PropertyNodeParamTypeInferer.php +++ /dev/null @@ -1,70 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return new MixedType(); - } - - $paramName = $this->nodeNameResolver->getName($param); - - /** @var ClassMethod $classMethod */ - $classMethod = $param->getAttribute(AttributeKey::PARENT_NODE); - - $propertyStaticTypes = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod, function (Node $node) use ( - $paramName, - &$propertyStaticTypes - ) { - if (! $node instanceof Assign) { - return null; - } - - if (! $this->propertyFetchAnalyzer->isVariableAssignToThisPropertyFetch($node, $paramName)) { - return null; - } - - $staticType = $this->nodeTypeResolver->getStaticType($node->var); - - if ($staticType !== null) { - $propertyStaticTypes[] = $staticType; - } - - return null; - }); - - return $this->typeFactory->createMixedPassedOrUnionType($propertyStaticTypes); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/SplFixedArrayParamTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/SplFixedArrayParamTypeInferer.php deleted file mode 100644 index 6d705c2e724..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ParamTypeInferer/SplFixedArrayParamTypeInferer.php +++ /dev/null @@ -1,31 +0,0 @@ -type === null) { - return new MixedType(); - } - - $paramType = $this->nodeTypeResolver->resolve($param->type); - return $this->splArrayFixedTypeNarrower->narrow($paramType); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer.php deleted file mode 100644 index 79a8418b4c3..00000000000 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer.php +++ /dev/null @@ -1,126 +0,0 @@ -propertyTypeInferers = $typeInfererSorter->sort($propertyTypeInferers); - } - - public function inferProperty(Property $property): Type - { - $resolvedTypes = []; - - foreach ($this->propertyTypeInferers as $propertyTypeInferer) { - $type = $propertyTypeInferer->inferProperty($property); - if (! $type instanceof Type) { - continue; - } - - if ($type instanceof VoidType) { - continue; - } - - $resolvedTypes[] = $type; - } - - $resolvedTypes = $this->typeFactory->uniquateTypes($resolvedTypes); - - // if nothing is clear from variable use, we use @var doc as fallback - if ($resolvedTypes !== []) { - $resolvedType = $this->typeFactory->createMixedPassedOrUnionType($resolvedTypes); - } else { - // void type is not allowed in properties - $resolvedType = $this->varDocPropertyTypeInferer->inferProperty($property); - if ($resolvedType instanceof VoidType) { - return new MixedType(); - } - } - - // default value type must be added to each resolved type if set - $propertyDefaultValue = $property->props[0]->default; - if ($propertyDefaultValue instanceof Expr) { - $resolvedType = $this->unionTypeWithDefaultExpr($property, $resolvedType); - } - - return $this->genericClassStringTypeNormalizer->normalize($resolvedType); - } - - private function shouldUnionWithDefaultValue(Type $defaultValueType, Type $type): bool - { - if ($defaultValueType instanceof MixedType) { - return false; - } - - // skip empty array type (mixed[]) - if ($defaultValueType instanceof ArrayType && $defaultValueType->getItemType() instanceof NeverType && ! $type instanceof MixedType) { - return false; - } - - if ($type instanceof MixedType) { - return true; - } - - return ! $this->doctrineTypeAnalyzer->isDoctrineCollectionWithIterableUnionType($type); - } - - private function unionWithDefaultValueType(Type $defaultValueType, Type $resolvedType): Type - { - $types = []; - $types[] = $defaultValueType; - - if (! $resolvedType instanceof MixedType) { - $types[] = $resolvedType; - } - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } - - private function unionTypeWithDefaultExpr(Property $property, Type $resolvedType): Type - { - $defaultValueType = $this->defaultValuePropertyTypeInferer->inferProperty($property); - if (! $defaultValueType instanceof Type) { - return $resolvedType; - } - - if (! $this->shouldUnionWithDefaultValue($defaultValueType, $resolvedType)) { - return $resolvedType; - } - - return $this->unionWithDefaultValueType($defaultValueType, $resolvedType); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/AllAssignNodePropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/AllAssignNodePropertyTypeInferer.php index a5770c1f974..70abab2fb43 100644 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/AllAssignNodePropertyTypeInferer.php +++ b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/AllAssignNodePropertyTypeInferer.php @@ -4,36 +4,48 @@ namespace Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer; +use PhpParser\Node; +use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Property; +use PHPStan\Reflection\ClassReflection; use PHPStan\Type\Type; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\TypeDeclaration\Contract\TypeInferer\PropertyTypeInfererInterface; +use Rector\PhpParser\AstResolver; +use Rector\PhpParser\Node\BetterNodeFinder; use Rector\TypeDeclaration\TypeInferer\AssignToPropertyTypeInferer; +use Rector\ValueObject\Application\File; -final class AllAssignNodePropertyTypeInferer implements PropertyTypeInfererInterface +final readonly class AllAssignNodePropertyTypeInferer { public function __construct( private AssignToPropertyTypeInferer $assignToPropertyTypeInferer, - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, + private AstResolver $astResolver, + private BetterNodeFinder $betterNodeFinder ) { } - public function inferProperty(Property $property): ?Type + public function inferProperty(Property $property, ClassReflection $classReflection, File $file): ?Type { - $classLike = $property->getAttribute(AttributeKey::CLASS_NODE); - if ($classLike === null) { - // anonymous class possibly? + if ($classReflection->getFileName() === $file->getFilePath()) { + $className = $classReflection->getName(); + $classLike = $this->betterNodeFinder->findFirst( + $file->getNewStmts(), + fn (Node $node): bool => $node instanceof ClassLike && $this->nodeNameResolver->isName( + $node, + $className + ) + ); + } else { + $classLike = $this->astResolver->resolveClassFromClassReflection($classReflection); + } + + if (! $classLike instanceof ClassLike) { return null; } $propertyName = $this->nodeNameResolver->getName($property); - return $this->assignToPropertyTypeInferer->inferPropertyInClassLike($propertyName, $classLike); - } - - public function getPriority(): int - { - return 1500; + return $this->assignToPropertyTypeInferer->inferPropertyInClassLike($property, $propertyName, $classLike); } } diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/ConstructorPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/ConstructorPropertyTypeInferer.php deleted file mode 100644 index 0ef560e2fb9..00000000000 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/ConstructorPropertyTypeInferer.php +++ /dev/null @@ -1,204 +0,0 @@ -betterNodeFinder->findParentType($property, ClassLike::class); - if (! $classLike instanceof ClassLike) { - return null; - } - - $classMethod = $classLike->getMethod(MethodName::CONSTRUCT); - if (! $classMethod instanceof ClassMethod) { - return null; - } - - $propertyName = $this->nodeNameResolver->getName($property); - - $param = $this->classMethodPropertyFetchManipulator->resolveParamForPropertyFetch($classMethod, $propertyName); - if (! $param instanceof Param) { - return null; - } - - // A. infer from type declaration of parameter - if ($param->type !== null) { - return $this->resolveFromParamType($param, $classMethod, $propertyName); - } - - return null; - } - - public function getPriority(): int - { - return 800; - } - - private function resolveFromParamType(Param $param, ClassMethod $classMethod, string $propertyName): Type - { - $type = $this->resolveParamTypeToPHPStanType($param); - if ($type instanceof MixedType) { - return new MixedType(); - } - - $types = []; - - // it's an array - annotation → make type more precise, if possible - if ($type instanceof ArrayType || $param->variadic) { - $types[] = $this->getResolveParamStaticTypeAsPHPStanType($classMethod, $propertyName); - } else { - $types[] = $type; - } - - if ($this->isParamNullable($param)) { - $types[] = new NullType(); - } - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } - - private function resolveParamTypeToPHPStanType(Param $param): Type - { - if ($param->type === null) { - return new MixedType(); - } - - if ($this->paramAnalyzer->isNullable($param)) { - /** @var NullableType $type */ - $type = $param->type; - - $types = []; - $types[] = new NullType(); - $types[] = $this->staticTypeMapper->mapPhpParserNodePHPStanType($type->type); - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } - - // special case for alias - if ($param->type instanceof FullyQualified) { - $type = $this->resolveFullyQualifiedOrAliasedObjectType($param); - if ($type !== null) { - return $type; - } - } - - return $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - } - - private function getResolveParamStaticTypeAsPHPStanType(ClassMethod $classMethod, string $propertyName): Type - { - $paramStaticType = new ArrayType(new MixedType(), new MixedType()); - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $classMethod->stmts, - function (Node $node) use ($propertyName, &$paramStaticType): ?int { - if (! $node instanceof Variable) { - return null; - } - - if (! $this->nodeNameResolver->isName($node, $propertyName)) { - return null; - } - - $paramStaticType = $this->nodeTypeResolver->getStaticType($node); - - return NodeTraverser::STOP_TRAVERSAL; - } - ); - - return $paramStaticType; - } - - private function isParamNullable(Param $param): bool - { - if ($this->paramAnalyzer->isNullable($param)) { - return true; - } - - if ($param->default !== null) { - $defaultValueStaticType = $this->nodeTypeResolver->getStaticType($param->default); - if ($defaultValueStaticType instanceof NullType) { - return true; - } - } - - return false; - } - - private function resolveFullyQualifiedOrAliasedObjectType(Param $param): ?Type - { - if ($param->type === null) { - return null; - } - - $fullyQualifiedName = $this->nodeNameResolver->getName($param->type); - if (! $fullyQualifiedName) { - return null; - } - - $originalName = $param->type->getAttribute(AttributeKey::ORIGINAL_NAME); - if (! $originalName instanceof Name) { - return null; - } - - // if the FQN has different ending than the original, it was aliased and we need to return the alias - if (! \str_ends_with($fullyQualifiedName, '\\' . $originalName->toString())) { - $className = $originalName->toString(); - - if ($this->reflectionProvider->hasClass($className)) { - return new FullyQualifiedObjectType($className); - } - - // @note: $fullyQualifiedName is a guess, needs real life test - return new AliasedObjectType($originalName->toString(), $fullyQualifiedName); - } - - return null; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DefaultValuePropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DefaultValuePropertyTypeInferer.php deleted file mode 100644 index 534f070efa5..00000000000 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DefaultValuePropertyTypeInferer.php +++ /dev/null @@ -1,36 +0,0 @@ -props[0]; - if ($propertyProperty->default === null) { - return new MixedType(); - } - - return $this->nodeTypeResolver->getStaticType($propertyProperty->default); - } - - public function getPriority(): int - { - return 100; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DoctrineColumnPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DoctrineColumnPropertyTypeInferer.php deleted file mode 100644 index 5dc48aefb28..00000000000 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DoctrineColumnPropertyTypeInferer.php +++ /dev/null @@ -1,113 +0,0 @@ -doctrineTypeToScalarType = [ - 'tinyint' => new BooleanType(), - // integers - 'smallint' => new IntegerType(), - 'mediumint' => new IntegerType(), - 'int' => new IntegerType(), - 'integer' => new IntegerType(), - 'bigint' => new IntegerType(), - 'numeric' => new IntegerType(), - // floats - 'decimal' => new FloatType(), - 'float' => new FloatType(), - 'double' => new FloatType(), - 'real' => new FloatType(), - // strings - 'tinytext' => new StringType(), - 'mediumtext' => new StringType(), - 'longtext' => new StringType(), - 'text' => new StringType(), - 'varchar' => new StringType(), - 'string' => new StringType(), - 'char' => new StringType(), - 'longblob' => new StringType(), - 'blob' => new StringType(), - 'mediumblob' => new StringType(), - 'tinyblob' => new StringType(), - 'binary' => new StringType(), - 'varbinary' => new StringType(), - 'set' => new StringType(), - // date time objects - 'date' => new ObjectType(self::DATE_TIME_INTERFACE), - 'datetime' => new ObjectType(self::DATE_TIME_INTERFACE), - 'timestamp' => new ObjectType(self::DATE_TIME_INTERFACE), - 'time' => new ObjectType(self::DATE_TIME_INTERFACE), - 'year' => new ObjectType(self::DATE_TIME_INTERFACE), - ]; - } - - public function inferProperty(Property $property): ?Type - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - - $doctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass('Doctrine\ORM\Mapping\Column'); - if (! $doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { - return null; - } - - $type = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('type'); - if ($type === null) { - return new MixedType(); - } - - $scalarType = $this->doctrineTypeToScalarType[$type] ?? null; - if (! $scalarType instanceof Type) { - return new MixedType(); - } - - $types = [$scalarType]; - - $isNullable = $doctrineAnnotationTagValueNode->getValue('nullable'); - - // is nullable? - if ($isNullable instanceof ConstExprTrueNode) { - $types[] = new NullType(); - } - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } - - public function getPriority(): int - { - return 2000; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DoctrineRelationPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DoctrineRelationPropertyTypeInferer.php deleted file mode 100644 index 8f67af7ae8a..00000000000 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/DoctrineRelationPropertyTypeInferer.php +++ /dev/null @@ -1,127 +0,0 @@ -phpDocInfoFactory->createFromNodeOrEmpty($property); - - $toManyRelationTagValueNode = $phpDocInfo->getByAnnotationClasses([ - 'Doctrine\ORM\Mapping\OneToMany', - 'Doctrine\ORM\Mapping\ManyToMany', - ]); - if ($toManyRelationTagValueNode !== null) { - return $this->processToManyRelation($property, $toManyRelationTagValueNode); - } - - $toOneRelationTagValueNode = $phpDocInfo->getByAnnotationClasses([ - 'Doctrine\ORM\Mapping\ManyToOne', - 'Doctrine\ORM\Mapping\OneToOne', - ]); - - if ($toOneRelationTagValueNode !== null) { - $joinDoctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass( - 'Doctrine\ORM\Mapping\JoinColumn' - ); - - return $this->processToOneRelation( - $property, - $toOneRelationTagValueNode, - $joinDoctrineAnnotationTagValueNode - ); - } - - return null; - } - - public function getPriority(): int - { - return 2100; - } - - private function processToManyRelation( - Property $property, - DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode - ): Type { - $types = []; - - $targetEntity = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('targetEntity'); - if ($targetEntity) { - $entityFullyQualifiedClass = $this->shortClassExpander->resolveFqnTargetEntity($targetEntity, $property); - $types[] = new ArrayType(new MixedType(), new FullyQualifiedObjectType($entityFullyQualifiedClass)); - } - - $types[] = new FullyQualifiedObjectType(self::COLLECTION_TYPE); - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } - - private function processToOneRelation( - Property $property, - DoctrineAnnotationTagValueNode $toOneDoctrineAnnotationTagValueNode, - ?DoctrineAnnotationTagValueNode $joinDoctrineAnnotationTagValueNode - ): Type { - $targetEntity = $toOneDoctrineAnnotationTagValueNode->getValueWithoutQuotes('targetEntity'); - if ($targetEntity === null) { - return new MixedType(); - } - - if (\str_ends_with($targetEntity, '::class')) { - $targetEntity = Strings::before($targetEntity, '::class'); - } - - // resolve to FQN - $tagFullyQualifiedName = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($targetEntity, $property); - - $types = []; - $types[] = new FullyQualifiedObjectType($tagFullyQualifiedName); - - if ($this->shouldAddNullType($joinDoctrineAnnotationTagValueNode)) { - $types[] = new NullType(); - } - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } - - private function shouldAddNullType(?DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode): bool - { - if ($doctrineAnnotationTagValueNode === null) { - return true; - } - - $isNullableValue = $doctrineAnnotationTagValueNode->getValue('nullable'); - return $isNullableValue instanceof ConstExprTrueNode; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/GetterPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/GetterPropertyTypeInferer.php deleted file mode 100644 index 5f3828bdf67..00000000000 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/GetterPropertyTypeInferer.php +++ /dev/null @@ -1,82 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - // anonymous class - return null; - } - - /** @var string $propertyName */ - $propertyName = $this->nodeNameResolver->getName($property); - - foreach ($classLike->getMethods() as $classMethod) { - if (! $this->classMethodAndPropertyAnalyzer->hasClassMethodOnlyStatementReturnOfPropertyFetch( - $classMethod, - $propertyName - )) { - continue; - } - - $returnType = $this->inferClassMethodReturnType($classMethod); - - if (! $returnType instanceof MixedType) { - return $returnType; - } - } - - return null; - } - - public function getPriority(): int - { - return 1700; - } - - private function inferClassMethodReturnType(ClassMethod $classMethod): Type - { - $returnTypeDeclarationType = $this->functionLikeReturnTypeResolver->resolveFunctionLikeReturnTypeToPHPStanType( - $classMethod - ); - - if (! $returnTypeDeclarationType instanceof MixedType) { - return $returnTypeDeclarationType; - } - - $inferedType = $this->returnedNodesReturnTypeInferer->inferFunctionLike($classMethod); - if (! $inferedType instanceof MixedType) { - return $inferedType; - } - - return $this->returnTagReturnTypeInferer->inferFunctionLike($classMethod); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/GetterTypeDeclarationPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/GetterTypeDeclarationPropertyTypeInferer.php index f30ad23bc62..90abc7d85e9 100644 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/GetterTypeDeclarationPropertyTypeInferer.php +++ b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/GetterTypeDeclarationPropertyTypeInferer.php @@ -5,43 +5,29 @@ namespace Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Property; -use PHPStan\Type\ArrayType; use PHPStan\Type\MixedType; use PHPStan\Type\Type; -use Rector\Core\PhpParser\Node\BetterNodeFinder; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\TypeDeclaration\Contract\TypeInferer\PropertyTypeInfererInterface; use Rector\TypeDeclaration\FunctionLikeReturnTypeResolver; use Rector\TypeDeclaration\NodeAnalyzer\ClassMethodAndPropertyAnalyzer; -final class GetterTypeDeclarationPropertyTypeInferer implements PropertyTypeInfererInterface +final readonly class GetterTypeDeclarationPropertyTypeInferer { public function __construct( private FunctionLikeReturnTypeResolver $functionLikeReturnTypeResolver, private ClassMethodAndPropertyAnalyzer $classMethodAndPropertyAnalyzer, - private NodeNameResolver $nodeNameResolver, - private BetterNodeFinder $betterNodeFinder + private NodeNameResolver $nodeNameResolver ) { } - public function inferProperty(Property $property): ?Type + public function inferProperty(Property $property, Class_ $class): ?Type { - $classLike = $this->betterNodeFinder->findParentType($property, ClassLike::class); - if (! $classLike instanceof Class_) { - // anonymous class - return null; - } - /** @var string $propertyName */ $propertyName = $this->nodeNameResolver->getName($property); - foreach ($classLike->getMethods() as $classMethod) { - if (! $this->classMethodAndPropertyAnalyzer->hasClassMethodOnlyStatementReturnOfPropertyFetch( - $classMethod, - $propertyName - )) { + foreach ($class->getMethods() as $classMethod) { + if (! $this->classMethodAndPropertyAnalyzer->hasPropertyFetchReturn($classMethod, $propertyName)) { continue; } @@ -49,7 +35,7 @@ public function inferProperty(Property $property): ?Type $classMethod ); // let PhpDoc solve that later for more precise type - if ($returnType instanceof ArrayType) { + if ($returnType->isArray()->yes()) { return new MixedType(); } @@ -60,9 +46,4 @@ public function inferProperty(Property $property): ?Type return null; } - - public function getPriority(): int - { - return 630; - } } diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/SetterTypeDeclarationPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/SetterTypeDeclarationPropertyTypeInferer.php new file mode 100644 index 00000000000..bc07a55b5d4 --- /dev/null +++ b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/SetterTypeDeclarationPropertyTypeInferer.php @@ -0,0 +1,53 @@ +nodeNameResolver->getName($property); + + foreach ($class->getMethods() as $classMethod) { + if (! $this->classMethodAndPropertyAnalyzer->hasOnlyPropertyAssign($classMethod, $propertyName)) { + continue; + } + + $paramTypeNode = $classMethod->params[0]->type ?? null; + if (! $paramTypeNode instanceof Node) { + return null; + } + + $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($paramTypeNode); + + // let PhpDoc solve that later for more precise type + if ($paramType->isArray()->yes()) { + return new MixedType(); + } + + if (! $paramType instanceof MixedType) { + return $paramType; + } + } + + return null; + } +} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/SingleMethodAssignedNodePropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/SingleMethodAssignedNodePropertyTypeInferer.php deleted file mode 100644 index b7b53f019e3..00000000000 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/SingleMethodAssignedNodePropertyTypeInferer.php +++ /dev/null @@ -1,82 +0,0 @@ -betterNodeFinder->findParentType($property, ClassLike::class); - if (! $classLike instanceof Class_) { - return null; - } - - $classMethod = $classLike->getMethod(MethodName::CONSTRUCT); - if (! $classMethod instanceof ClassMethod) { - return null; - } - - $propertyName = $this->nodeNameResolver->getName($property); - $assignedNode = $this->resolveAssignedNodeToProperty($classMethod, $propertyName); - if (! $assignedNode instanceof Expr) { - return null; - } - - return $this->nodeTypeResolver->getStaticType($assignedNode); - } - - public function getPriority(): int - { - return 750; - } - - private function resolveAssignedNodeToProperty(ClassMethod $classMethod, string $propertyName): ?Expr - { - $assignedNode = null; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $classMethod->stmts, - function (Node $node) use ($propertyName, &$assignedNode): ?int { - if (! $node instanceof Assign) { - return null; - } - - if (! $this->nodeNameResolver->isName($node->var, $propertyName)) { - return null; - } - - $assignedNode = $node->expr; - - return NodeTraverser::STOP_TRAVERSAL; - } - ); - - return $assignedNode; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/TrustedClassMethodPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/TrustedClassMethodPropertyTypeInferer.php new file mode 100644 index 00000000000..717b947a5b0 --- /dev/null +++ b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/TrustedClassMethodPropertyTypeInferer.php @@ -0,0 +1,245 @@ +nodeNameResolver->getName($property); + + // 1. direct property = param assign + $param = $this->classMethodPropertyFetchManipulator->findParamAssignToPropertyName($classMethod, $propertyName); + if ($param instanceof Param) { + return $this->resolveTypeFromParam($param, $classMethod, $propertyName, $property, $class); + } + + // 2. different assign + /** @var Expr[] $assignedExprs */ + $assignedExprs = $this->classMethodPropertyFetchManipulator->findAssignsToPropertyName( + $classMethod, + $propertyName + ); + + $resolvedTypes = []; + foreach ($assignedExprs as $assignedExpr) { + $resolvedTypes[] = $this->nodeTypeResolver->getNativeType($assignedExpr); + } + + if ($resolvedTypes === []) { + return new MixedType(); + } + + $resolvedType = count($resolvedTypes) === 1 + ? $resolvedTypes[0] + : TypeCombinator::union(...$resolvedTypes); + + return $this->resolveType($property, $propertyName, $class, $resolvedType); + } + + private function resolveType( + Property $property, + string $propertyName, + Class_ $class, + Type $resolvedType + ): Type { + $exactType = $this->assignToPropertyTypeInferer->inferPropertyInClassLike($property, $propertyName, $class); + + if (! $exactType instanceof UnionType) { + return $resolvedType; + } + + if ($this->typeComparator->areTypesEqual($resolvedType, $exactType)) { + return $resolvedType; + } + + return new MixedType(); + } + + private function resolveFromParamType(Param $param, ClassMethod $classMethod, string $propertyName): Type + { + $type = $this->resolveParamTypeToPHPStanType($param); + if ($type instanceof MixedType) { + return new MixedType(); + } + + $types = []; + + // it's an array - annotation → make type more precise, if possible + if ($type->isArray()->yes() || $param->variadic) { + $types[] = $this->getResolveParamStaticTypeAsPHPStanType($classMethod, $propertyName); + } else { + $types[] = $type; + } + + if ($this->isParamNullable($param)) { + $types[] = new NullType(); + } + + return $this->typeFactory->createMixedPassedOrUnionType($types); + } + + private function resolveParamTypeToPHPStanType(Param $param): Type + { + if (! $param->type instanceof Node) { + return new MixedType(); + } + + if ($this->paramAnalyzer->isNullable($param)) { + /** @var NullableType $type */ + $type = $param->type; + + $types = []; + $types[] = new NullType(); + $types[] = $this->staticTypeMapper->mapPhpParserNodePHPStanType($type->type); + + return $this->typeFactory->createMixedPassedOrUnionType($types); + } + + // special case for alias + if ($param->type instanceof FullyQualified) { + $type = $this->resolveFullyQualifiedOrAliasedObjectType($param); + if ($type instanceof Type) { + return $type; + } + } + + return $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + } + + private function getResolveParamStaticTypeAsPHPStanType(ClassMethod $classMethod, string $propertyName): Type + { + $paramStaticType = new ArrayType(new MixedType(), new MixedType()); + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + (array) $classMethod->stmts, + function (Node $node) use ($propertyName, &$paramStaticType): ?int { + if (! $node instanceof Variable) { + return null; + } + + if (! $this->nodeNameResolver->isName($node, $propertyName)) { + return null; + } + + $paramStaticType = $this->nodeTypeResolver->getType($node); + + return NodeVisitor::STOP_TRAVERSAL; + } + ); + + return $paramStaticType; + } + + private function isParamNullable(Param $param): bool + { + if ($this->paramAnalyzer->isNullable($param)) { + return true; + } + + if ($param->default instanceof Expr) { + $defaultValueStaticType = $this->nodeTypeResolver->getType($param->default); + if ($defaultValueStaticType->isNull()->yes()) { + return true; + } + } + + return false; + } + + private function resolveFullyQualifiedOrAliasedObjectType(Param $param): ?Type + { + if (! $param->type instanceof Node) { + return null; + } + + $fullyQualifiedName = $this->nodeNameResolver->getName($param->type); + if (! is_string($fullyQualifiedName)) { + return null; + } + + $originalName = $param->type->getAttribute(AttributeKey::ORIGINAL_NAME); + if (! $originalName instanceof Name) { + return null; + } + + // if the FQN has different ending than the original, it was aliased and we need to return the alias + if (! \str_ends_with($fullyQualifiedName, '\\' . $originalName->toString())) { + $className = $originalName->toString(); + + if ($this->reflectionProvider->hasClass($className)) { + return new FullyQualifiedObjectType($className); + } + + // @note: $fullyQualifiedName is a guess, needs real life test + return new AliasedObjectType($originalName->toString(), $fullyQualifiedName); + } + + return null; + } + + private function resolveTypeFromParam( + Param $param, + ClassMethod $classMethod, + string $propertyName, + Property $property, + Class_ $class + ): Type { + if (! $param->type instanceof Node) { + return new MixedType(); + } + + $resolvedType = $this->resolveFromParamType($param, $classMethod, $propertyName); + return $this->resolveType($property, $propertyName, $class, $resolvedType); + } +} diff --git a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/VarDocPropertyTypeInferer.php b/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/VarDocPropertyTypeInferer.php deleted file mode 100644 index acaa86214b6..00000000000 --- a/rules/TypeDeclaration/TypeInferer/PropertyTypeInferer/VarDocPropertyTypeInferer.php +++ /dev/null @@ -1,23 +0,0 @@ -phpDocInfoFactory->createFromNodeOrEmpty($property); - return $phpDocInfo->getVarType(); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer.php index 3001552109f..1d516d9970d 100644 --- a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer.php +++ b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer.php @@ -4,186 +4,30 @@ namespace Rector\TypeDeclaration\TypeInferer; -use PhpParser\Node; -use PhpParser\Node\FunctionLike; -use PhpParser\Node\Name\FullyQualified; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\UnionType as PhpParserUnionType; +use PhpParser\Node\Stmt\Function_; use PHPStan\Type\MixedType; -use PHPStan\Type\ThisType; use PHPStan\Type\Type; -use PHPStan\Type\UnionType; -use Rector\Core\Configuration\Option; -use Rector\Core\Php\PhpVersionProvider; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; -use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface; -use Rector\TypeDeclaration\Sorter\TypeInfererSorter; -use Rector\TypeDeclaration\TypeAnalyzer\GenericClassStringTypeNormalizer; -use Rector\TypeDeclaration\TypeNormalizer; -use Symplify\PackageBuilder\Parameter\ParameterProvider; +use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnedNodesReturnTypeInfererTypeInferer; -final class ReturnTypeInferer +/** + * @internal + */ +final readonly class ReturnTypeInferer { - /** - * @var ReturnTypeInfererInterface[] - */ - private array $returnTypeInferers = []; - - /** - * @param ReturnTypeInfererInterface[] $returnTypeInferers - */ public function __construct( - array $returnTypeInferers, - private TypeNormalizer $typeNormalizer, - TypeInfererSorter $typeInfererSorter, - private GenericClassStringTypeNormalizer $genericClassStringTypeNormalizer, - private PhpVersionProvider $phpVersionProvider, - private ParameterProvider $parameterProvider + private ReturnedNodesReturnTypeInfererTypeInferer $returnedNodesReturnTypeInfererTypeInferer ) { - $this->returnTypeInferers = $typeInfererSorter->sort($returnTypeInferers); - } - - public function inferFunctionLike(FunctionLike $functionLike): Type - { - return $this->inferFunctionLikeWithExcludedInferers($functionLike, []); } - /** - * @param array> $excludedInferers - */ - public function inferFunctionLikeWithExcludedInferers(FunctionLike $functionLike, array $excludedInferers): Type + public function inferFunctionLike(ClassMethod|Function_|Closure $functionLike): Type { - $isSupportedStaticReturnType = $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersionFeature::STATIC_RETURN_TYPE - ); - - $isAutoImport = $this->parameterProvider->provideBoolParameter(Option::AUTO_IMPORT_NAMES); - $isAutoImportFullyQuafiedReturn = $this->isAutoImportWithFullyQualifiedReturn($isAutoImport, $functionLike); - if ($isAutoImportFullyQuafiedReturn) { + $originalType = $this->returnedNodesReturnTypeInfererTypeInferer->inferFunctionLike($functionLike); + if ($originalType instanceof MixedType) { return new MixedType(); } - foreach ($this->returnTypeInferers as $returnTypeInferer) { - if ($this->shouldSkipExcludedTypeInferer($returnTypeInferer, $excludedInferers)) { - continue; - } - - $originalType = $returnTypeInferer->inferFunctionLike($functionLike); - if ($originalType instanceof MixedType) { - continue; - } - - $type = $this->typeNormalizer->normalizeArrayTypeAndArrayNever($originalType); - - // in case of void, check return type of children methods - if ($type instanceof MixedType) { - continue; - } - - $type = $this->verifyStaticType($type, $isSupportedStaticReturnType); - if (! $type instanceof Type) { - continue; - } - - // normalize ConstStringType to ClassStringType - return $this->genericClassStringTypeNormalizer->normalize($type); - } - - return new MixedType(); - } - - public function verifyStaticType(Type $type, bool $isSupportedStaticReturnType): ?Type - { - if ($this->isStaticType($type)) { - if (! $isSupportedStaticReturnType) { - return null; - } - - /** @var FullyQualifiedObjectType $type */ - return new ThisType($type->getClassName()); - } - - if (! $type instanceof UnionType) { - return $type; - } - - $returnTypes = $type->getTypes(); - $types = []; - $hasStatic = false; - foreach ($returnTypes as $returnType) { - if ($this->isStaticType($returnType)) { - /** @var FullyQualifiedObjectType $returnType */ - $types[] = new ThisType($returnType->getClassName()); - $hasStatic = true; - continue; - } - - $types[] = $returnType; - } - - if (! $hasStatic) { - return $type; - } - - if (! $isSupportedStaticReturnType) { - return null; - } - - return new UnionType($types); - } - - private function isAutoImportWithFullyQualifiedReturn(bool $isAutoImport, FunctionLike $functionLike): bool - { - if (! $isAutoImport) { - return false; - } - - if (! $functionLike instanceof ClassMethod) { - return false; - } - - if ($this->isNamespacedFullyQualified($functionLike->returnType)) { - return true; - } - - if (! $functionLike->returnType instanceof PhpParserUnionType) { - return false; - } - - $types = $functionLike->returnType->types; - foreach ($types as $type) { - if ($this->isNamespacedFullyQualified($type)) { - return true; - } - } - - return false; - } - - private function isNamespacedFullyQualified(?Node $node): bool - { - return $node instanceof FullyQualified && str_contains($node->toString(), '\\'); - } - - private function isStaticType(Type $type): bool - { - return $type instanceof FullyQualifiedObjectType && $type->getClassName() === 'static'; - } - - /** - * @param array> $excludedInferers - */ - private function shouldSkipExcludedTypeInferer( - ReturnTypeInfererInterface $returnTypeInferer, - array $excludedInferers - ): bool { - foreach ($excludedInferers as $excludedInferer) { - if (is_a($returnTypeInferer, $excludedInferer)) { - return true; - } - } - - return false; + return $originalType; } } diff --git a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnTagReturnTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnTagReturnTypeInferer.php deleted file mode 100644 index 3253a19f4f3..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnTagReturnTypeInferer.php +++ /dev/null @@ -1,35 +0,0 @@ -phpDocInfoFactory->createFromNodeOrEmpty($functionLike); - return $phpDocInfo->getReturnType(); - } - - public function getPriority(): int - { - return 400; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnTypeDeclarationReturnTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnTypeDeclarationReturnTypeInferer.php deleted file mode 100644 index 87dba2ac177..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnTypeDeclarationReturnTypeInferer.php +++ /dev/null @@ -1,40 +0,0 @@ -getReturnType() === null) { - return new MixedType(); - } - - // resolve later with more precise type, e.g. Type[] - if ($this->nodeNameResolver->isNames($functionLike->getReturnType(), ['array', 'iterable'])) { - return new MixedType(); - } - - return $this->functionLikeReturnTypeResolver->resolveFunctionLikeReturnTypeToPHPStanType($functionLike); - } - - public function getPriority(): int - { - return 2000; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnedNodesReturnTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnedNodesReturnTypeInferer.php deleted file mode 100644 index af13e1897e4..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnedNodesReturnTypeInferer.php +++ /dev/null @@ -1,196 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if ($classLike === null) { - return new MixedType(); - } - - if ($functionLike instanceof ClassMethod && $classLike instanceof Interface_) { - return new MixedType(); - } - - $types = []; - - $localReturnNodes = $this->collectReturns($functionLike); - if ($localReturnNodes === []) { - return $this->resolveNoLocalReturnNodes($classLike, $functionLike); - } - - foreach ($localReturnNodes as $localReturnNode) { - $returnedExprType = $this->nodeTypeResolver->getStaticType($localReturnNode); - $returnedExprType = $this->correctWithNestedType($returnedExprType, $localReturnNode, $functionLike); - - $types[] = $this->splArrayFixedTypeNarrower->narrow($returnedExprType); - } - - if ($this->silentVoidResolver->hasSilentVoid($functionLike)) { - $types[] = new VoidType(); - } - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } - - public function getPriority(): int - { - return 1000; - } - - /** - * @return Return_[] - */ - private function collectReturns(FunctionLike $functionLike): array - { - $returns = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $functionLike->getStmts(), function ( - Node $node - ) use (&$returns): ?int { - // skip Return_ nodes in nested functions or switch statements - if ($node instanceof FunctionLike) { - return NodeTraverser::DONT_TRAVERSE_CHILDREN; - } - - if (! $node instanceof Return_) { - return null; - } - - $returns[] = $node; - - return null; - }); - - return $returns; - } - - private function resolveNoLocalReturnNodes(ClassLike $classLike, FunctionLike $functionLike): VoidType | MixedType - { - // void type - if (! $this->isAbstractMethod($classLike, $functionLike)) { - return new VoidType(); - } - - return new MixedType(); - } - - private function isAbstractMethod(ClassLike $classLike, FunctionLike $functionLike): bool - { - if ($functionLike instanceof ClassMethod && $functionLike->isAbstract()) { - return true; - } - - if (! $classLike instanceof Class_) { - return false; - } - return $classLike->isAbstract(); - } - - private function inferFromReturnedMethodCall(Return_ $return, FunctionLike $originalFunctionLike): Type - { - if (! $return->expr instanceof MethodCall) { - return new MixedType(); - } - - $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromMethodCall($return->expr); - if (! $methodReflection instanceof MethodReflection) { - return new MixedType(); - } - - return $this->resolveClassMethod($methodReflection, $originalFunctionLike); - } - - private function isArrayTypeMixed(Type $type): bool - { - if (! $type instanceof ArrayType) { - return false; - } - - if (! $type->getItemType() instanceof MixedType) { - return false; - } - - return $type->getKeyType() instanceof MixedType; - } - - private function correctWithNestedType(Type $resolvedType, Return_ $return, FunctionLike $functionLike): Type - { - if ($resolvedType instanceof MixedType || $this->isArrayTypeMixed($resolvedType)) { - $correctedType = $this->inferFromReturnedMethodCall($return, $functionLike); - - // override only if has some extra value - if (! $correctedType instanceof MixedType && ! $correctedType instanceof VoidType) { - return $correctedType; - } - } - - return $resolvedType; - } - - private function resolveClassMethod(MethodReflection $methodReflection, FunctionLike $originalFunctionLike): Type - { - $classMethod = $this->reflectionAstResolver->resolveClassMethodFromMethodReflection($methodReflection); - if (! $classMethod instanceof ClassMethod) { - return new MixedType(); - } - - $classMethodCacheKey = $this->betterStandardPrinter->print($classMethod); - $functionLikeCacheKey = $this->betterStandardPrinter->print($originalFunctionLike); - - if ($classMethodCacheKey === $functionLikeCacheKey) { - return new MixedType(); - } - - return $this->inferFunctionLike($classMethod); - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnedNodesReturnTypeInfererTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnedNodesReturnTypeInfererTypeInferer.php new file mode 100644 index 00000000000..2e6de1c5cf1 --- /dev/null +++ b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/ReturnedNodesReturnTypeInfererTypeInferer.php @@ -0,0 +1,78 @@ +reflectionResolver->resolveClassReflection($functionLike); + if ($functionLike instanceof ClassMethod && (! $classReflection instanceof ClassReflection || $classReflection->isInterface())) { + return new MixedType(); + } + + $types = []; + + // empty returns can have yield, use MixedType() instead + $localReturnNodes = $this->betterNodeFinder->findReturnsScoped($functionLike); + if ($localReturnNodes === []) { + return new MixedType(); + } + + $hasVoid = false; + foreach ($localReturnNodes as $localReturnNode) { + if (! $localReturnNode->expr instanceof Expr) { + $hasVoid = true; + $types[] = new VoidType(); + + continue; + } + + $returnedExprType = $this->nodeTypeResolver->getNativeType($localReturnNode->expr); + $types[] = $this->splArrayFixedTypeNarrower->narrow($returnedExprType); + } + + if (! $hasVoid && $this->silentVoidResolver->hasSilentVoid($functionLike)) { + $types[] = new VoidType(); + } + + $returnType = $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($types); + + // only void? + if ($returnType->isVoid()->yes()) { + return new MixedType(); + } + + return $returnType; + } +} diff --git a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/SetterNodeReturnTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/SetterNodeReturnTypeInferer.php deleted file mode 100644 index 1b935bcc293..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/SetterNodeReturnTypeInferer.php +++ /dev/null @@ -1,54 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof ClassLike) { - return new MixedType(); - } - - $returnedPropertyNames = $this->functionLikeManipulator->getReturnedLocalPropertyNames($functionLike); - - $types = []; - foreach ($returnedPropertyNames as $returnedPropertyName) { - $inferredPropertyType = $this->assignToPropertyTypeInferer->inferPropertyInClassLike( - $returnedPropertyName, - $classLike - ); - if (! $inferredPropertyType instanceof Type) { - continue; - } - $types[] = $inferredPropertyType; - } - - return $this->typeFactory->createMixedPassedOrUnionType($types); - } - - public function getPriority(): int - { - return 600; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/YieldNodesReturnTypeInferer.php b/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/YieldNodesReturnTypeInferer.php deleted file mode 100644 index 58ceef44ffa..00000000000 --- a/rules/TypeDeclaration/TypeInferer/ReturnTypeInferer/YieldNodesReturnTypeInferer.php +++ /dev/null @@ -1,106 +0,0 @@ -findCurrentScopeYieldNodes($functionLike); - - if ($yieldNodes === []) { - return new MixedType(); - } - - $types = []; - foreach ($yieldNodes as $yieldNode) { - $value = $this->resolveYieldValue($yieldNode); - if (! $value instanceof Expr) { - continue; - } - - $resolvedType = $this->nodeTypeResolver->getStaticType($value); - if ($resolvedType instanceof MixedType) { - continue; - } - $types[] = $resolvedType; - } - - if ($types === []) { - return new FullyQualifiedObjectType('Iterator'); - } - - $types = $this->typeFactory->createMixedPassedOrUnionType($types); - return new FullyQualifiedGenericObjectType('Iterator', [$types]); - } - - public function getPriority(): int - { - return 1200; - } - - /** - * @return Yield_[]|YieldFrom[] - */ - private function findCurrentScopeYieldNodes(FunctionLike $functionLike): array - { - $yieldNodes = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $functionLike->getStmts(), function ( - Node $node - ) use (&$yieldNodes): ?int { - // skip nested scope - if ($node instanceof FunctionLike) { - return NodeTraverser::DONT_TRAVERSE_CHILDREN; - } - - if (! $node instanceof Yield_ && ! $node instanceof YieldFrom) { - return null; - } - - $yieldNodes[] = $node; - return null; - }); - - return $yieldNodes; - } - - private function resolveYieldValue(Yield_ | YieldFrom $yieldExpr): ?Expr - { - if ($yieldExpr instanceof Yield_) { - return $yieldExpr->value; - } - - return $yieldExpr->expr; - } -} diff --git a/rules/TypeDeclaration/TypeInferer/SilentVoidResolver.php b/rules/TypeDeclaration/TypeInferer/SilentVoidResolver.php index 790203c54f8..ca18286b132 100644 --- a/rules/TypeDeclaration/TypeInferer/SilentVoidResolver.php +++ b/rules/TypeDeclaration/TypeInferer/SilentVoidResolver.php @@ -4,114 +4,197 @@ namespace Rector\TypeDeclaration\TypeInferer; +use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\Exit_; +use PhpParser\Node\Expr\Throw_; use PhpParser\Node\Expr\Yield_; +use PhpParser\Node\Expr\YieldFrom; +use PhpParser\Node\FunctionLike; use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Break_; +use PhpParser\Node\Stmt\Case_; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Continue_; +use PhpParser\Node\Stmt\Do_; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Finally_; use PhpParser\Node\Stmt\Function_; -use PhpParser\Node\Stmt\Interface_; +use PhpParser\Node\Stmt\Goto_; +use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\Stmt\Switch_; -use PhpParser\Node\Stmt\Throw_; -use PhpParser\Node\Stmt\Trait_; use PhpParser\Node\Stmt\TryCatch; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\NodeTypeResolver\Node\AttributeKey; - -final class SilentVoidResolver +use PhpParser\Node\Stmt\While_; +use PhpParser\NodeVisitor; +use PHPStan\Reflection\ClassReflection; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\Reflection\ReflectionResolver; +use Rector\TypeDeclaration\NodeAnalyzer\NeverFuncCallAnalyzer; + +final readonly class SilentVoidResolver { public function __construct( private BetterNodeFinder $betterNodeFinder, + private ReflectionResolver $reflectionResolver, + private NeverFuncCallAnalyzer $neverFuncCallAnalyzer, + private ValueResolver $valueResolver, + private SimpleCallableNodeTraverser $simpleCallableNodeTraverser ) { } public function hasExclusiveVoid(ClassMethod | Closure | Function_ $functionLike): bool { - $classLike = $functionLike->getAttribute(AttributeKey::CLASS_NODE); - if ($classLike instanceof Interface_) { + $classReflection = $this->reflectionResolver->resolveClassReflection($functionLike); + if ($classReflection instanceof ClassReflection && $classReflection->isInterface()) { return false; } - if ($classLike instanceof Trait_) { - return false; - } + return ! (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped( + $functionLike, + function (Node $subNode): bool { + if ($subNode instanceof Yield_ || $subNode instanceof YieldFrom) { + return true; + } - if ($this->hasNeverType($functionLike)) { - return false; - } + return $subNode instanceof Return_ && $subNode->expr instanceof Expr; + } + ); + } - if ($this->betterNodeFinder->hasInstancesOf((array) $functionLike->stmts, [Yield_::class])) { + public function hasSilentVoid(FunctionLike $functionLike, bool $withNativeNeverType = true): bool + { + if ($functionLike instanceof ArrowFunction) { return false; } - /** @var Return_[] $returns */ - $returns = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Return_::class); - foreach ($returns as $return) { - if ($return->expr !== null) { - return false; + $stmts = (array) $functionLike->getStmts(); + return ! $this->hasStmtsAlwaysReturnOrExit($stmts, $withNativeNeverType); + } + + /** + * @param Stmt[]|Expression[] $stmts + */ + private function hasStmtsAlwaysReturnOrExit(array $stmts, bool $withNativeNeverType): bool + { + foreach ($stmts as $stmt) { + if ($this->neverFuncCallAnalyzer->isWithNeverTypeExpr($stmt, $withNativeNeverType)) { + return true; } + + if ($this->isStopped($stmt)) { + return true; + } + + // has switch with always return + if ($stmt instanceof Switch_ && $this->isSwitchWithAlwaysReturnOrExit($stmt, $withNativeNeverType)) { + return true; + } + + if ($stmt instanceof TryCatch && $this->isTryCatchAlwaysReturnOrExit($stmt, $withNativeNeverType)) { + return true; + } + + if ($this->isIfReturn($stmt, $withNativeNeverType)) { + return true; + } + + if (! $this->isDoOrWhileWithAlwaysReturnOrExit($stmt, $withNativeNeverType)) { + continue; + } + + return true; } - return true; + return false; } - public function hasSilentVoid(ClassMethod | Closure | Function_ | ArrowFunction $functionLike): bool + private function isFoundLoopControl(Do_|While_ $node): bool { - if ($functionLike instanceof ArrowFunction) { + $isFoundLoopControl = false; + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $node->stmts, + static function (Node $subNode) use (&$isFoundLoopControl) { + if ($subNode instanceof Class_ || $subNode instanceof Function_ || $subNode instanceof Closure) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($subNode instanceof Break_ || $subNode instanceof Continue_ || $subNode instanceof Goto_) { + $isFoundLoopControl = true; + return NodeVisitor::STOP_TRAVERSAL; + } + } + ); + + return $isFoundLoopControl; + } + + private function isDoOrWhileWithAlwaysReturnOrExit(Stmt $stmt, bool $withNativeNeverType): bool + { + if (! $stmt instanceof Do_ && ! $stmt instanceof While_) { return false; } - if ($this->hasStmtsAlwaysReturn((array) $functionLike->stmts)) { + if ($this->valueResolver->isTrue($stmt->cond)) { + return ! $this->isFoundLoopControl($stmt); + } + + if (! $this->hasStmtsAlwaysReturnOrExit($stmt->stmts, $withNativeNeverType)) { return false; } - foreach ((array) $functionLike->stmts as $stmt) { - // has switch with always return - if ($stmt instanceof Switch_ && $this->isSwitchWithAlwaysReturn($stmt)) { - return false; - } + return $stmt instanceof Do_ && ! $this->isFoundLoopControl($stmt); + } - // is part of try/catch - if ($stmt instanceof TryCatch && $this->isTryCatchAlwaysReturn($stmt)) { - return false; - } + private function isIfReturn(Stmt|Expr $stmt, bool $withNativeNeverType): bool + { + if (! $stmt instanceof If_) { + return false; + } - if ($stmt instanceof Throw_) { + foreach ($stmt->elseifs as $elseIf) { + if (! $this->hasStmtsAlwaysReturnOrExit($elseIf->stmts, $withNativeNeverType)) { return false; } } - return true; + if (! $stmt->else instanceof Else_) { + return false; + } + + if (! $this->hasStmtsAlwaysReturnOrExit($stmt->stmts, $withNativeNeverType)) { + return false; + } + + return $this->hasStmtsAlwaysReturnOrExit($stmt->else->stmts, $withNativeNeverType); } - /** - * @param Stmt[]|Expression[] $stmts - */ - private function hasStmtsAlwaysReturn(array $stmts): bool + private function isStopped(Stmt $stmt): bool { - foreach ($stmts as $stmt) { - if ($stmt instanceof Expression) { - $stmt = $stmt->expr; - } - - // is 1st level return - if ($stmt instanceof Return_) { - return true; - } + if ($stmt instanceof Expression) { + $stmt = $stmt->expr; } - return false; + return $stmt instanceof Throw_ + || $stmt instanceof Exit_ + || ($stmt instanceof Return_ && $stmt->expr instanceof Expr) + || $stmt instanceof Yield_ + || $stmt instanceof YieldFrom; } - private function isSwitchWithAlwaysReturn(Switch_ $switch): bool + private function isSwitchWithAlwaysReturnOrExit(Switch_ $switch, bool $withNativeNeverType): bool { $hasDefault = false; foreach ($switch->cases as $case) { - if ($case->cond === null) { - $hasDefault = true; + if (! $case->cond instanceof Expr) { + $hasDefault = $case->stmts !== []; break; } } @@ -120,45 +203,47 @@ private function isSwitchWithAlwaysReturn(Switch_ $switch): bool return false; } - $casesWithReturnCount = $this->resolveReturnCount($switch); + $casesWithReturnOrExitCount = $this->resolveReturnOrExitCount($switch, $withNativeNeverType); - // has same amount of returns as switches - return count($switch->cases) === $casesWithReturnCount; + $cases = array_filter($switch->cases, static fn (Case_ $case): bool => $case->stmts !== []); + + // has same amount of first return or exit nodes as switches + return count($cases) === $casesWithReturnOrExitCount; } - private function isTryCatchAlwaysReturn(TryCatch $tryCatch): bool + private function isTryCatchAlwaysReturnOrExit(TryCatch $tryCatch, bool $withNativeNeverType): bool { - if (! $this->hasStmtsAlwaysReturn($tryCatch->stmts)) { - return false; + $hasReturnOrExitInFinally = $tryCatch->finally instanceof Finally_ && $this->hasStmtsAlwaysReturnOrExit( + $tryCatch->finally->stmts, + $withNativeNeverType + ); + + if (! $this->hasStmtsAlwaysReturnOrExit($tryCatch->stmts, $withNativeNeverType)) { + return $hasReturnOrExitInFinally; } foreach ($tryCatch->catches as $catch) { - return $this->hasStmtsAlwaysReturn($catch->stmts); + if ($this->hasStmtsAlwaysReturnOrExit($catch->stmts, $withNativeNeverType)) { + continue; + } + + if ($hasReturnOrExitInFinally) { + continue; + } + + return false; } return true; } - /** - * @see https://phpstan.org/writing-php-code/phpdoc-types#bottom-type - */ - private function hasNeverType(ClassMethod | Closure | Function_ $functionLike): bool - { - return $this->betterNodeFinder->hasInstancesOf($functionLike, [Throw_::class]); - } - - private function resolveReturnCount(Switch_ $switch): int + private function resolveReturnOrExitCount(Switch_ $switch, bool $withNativeNeverType): int { $casesWithReturnCount = 0; foreach ($switch->cases as $case) { - foreach ($case->stmts as $caseStmt) { - if (! $caseStmt instanceof Return_) { - continue; - } - + if ($this->hasStmtsAlwaysReturnOrExit($case->stmts, $withNativeNeverType)) { ++$casesWithReturnCount; - break; } } diff --git a/rules/TypeDeclaration/TypeInferer/SplArrayFixedTypeNarrower.php b/rules/TypeDeclaration/TypeInferer/SplArrayFixedTypeNarrower.php index 0198148db88..b9a46e0654b 100644 --- a/rules/TypeDeclaration/TypeInferer/SplArrayFixedTypeNarrower.php +++ b/rules/TypeDeclaration/TypeInferer/SplArrayFixedTypeNarrower.php @@ -7,7 +7,7 @@ use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use PHPStan\Type\TypeWithClassName; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; final class SplArrayFixedTypeNarrower { @@ -17,7 +17,8 @@ public function narrow(Type $paramType): Type return $paramType; } - if (! $paramType instanceof TypeWithClassName) { + $className = ClassNameFromObjectTypeResolver::resolve($paramType); + if ($className === null) { return $paramType; } @@ -27,11 +28,11 @@ public function narrow(Type $paramType): Type $types = []; - if ($paramType->getClassName() === 'PhpCsFixer\Tokenizer\Tokens') { + if ($className === 'PhpCsFixer\Tokenizer\Tokens') { $types[] = new ObjectType('PhpCsFixer\Tokenizer\Token'); } - if ($paramType->getClassName() === 'PhpCsFixer\Doctrine\Annotation\Tokens') { + if ($className === 'PhpCsFixer\Doctrine\Annotation\Tokens') { $types[] = new ObjectType('PhpCsFixer\Doctrine\Annotation\Token'); } @@ -39,6 +40,6 @@ public function narrow(Type $paramType): Type return $paramType; } - return new GenericObjectType($paramType->getClassName(), $types); + return new GenericObjectType($className, $types); } } diff --git a/rules/TypeDeclaration/TypeNormalizer.php b/rules/TypeDeclaration/TypeNormalizer.php deleted file mode 100644 index d399a7da80d..00000000000 --- a/rules/TypeDeclaration/TypeNormalizer.php +++ /dev/null @@ -1,229 +0,0 @@ -getItemType() instanceof UnionType) { - /** @var UnionType $unionType */ - $unionType = $constantArrayType->getItemType(); - foreach ($unionType->getTypes() as $unionedType) { - if ($unionedType instanceof ConstantStringType) { - $stringType = new StringType(); - $nonConstantValueTypes[$stringType::class] = $stringType; - } elseif ($unionedType instanceof ObjectType) { - $nonConstantValueTypes[] = $unionedType; - } else { - return null; - } - } - } else { - return null; - } - - return $this->createArrayTypeFromNonConstantValueTypes($nonConstantValueTypes); - } - - /** - * Turn nested array union types to unique ones: - * e.g. int[]|string[][]|bool[][]|string[][] - * ↓ - * int[]|string[][]|bool[][] - */ - public function normalizeArrayOfUnionToUnionArray(Type $type, int $arrayNesting = 1): Type - { - if (! $type instanceof ArrayType) { - return $type; - } - - // first collection of types - if ($arrayNesting === 1) { - $this->collectedNestedArrayTypes = []; - } - - if ($type->getItemType() instanceof ArrayType) { - ++$arrayNesting; - $this->normalizeArrayOfUnionToUnionArray($type->getItemType(), $arrayNesting); - } elseif ($type->getItemType() instanceof UnionType) { - $this->collectNestedArrayTypeFromUnionType($type->getItemType(), $arrayNesting); - } else { - $this->collectedNestedArrayTypes[] = new NestedArrayType( - $type->getItemType(), - $arrayNesting, - $type->getKeyType() - ); - } - - return $this->createUnionedTypesFromArrayTypes($this->collectedNestedArrayTypes); - } - - /** - * From "string[]|mixed[]" based on empty array to to "string[]" - */ - public function normalizeArrayTypeAndArrayNever(Type $type): Type - { - return TypeTraverser::map($type, function (Type $traversedType, callable $traverserCallable): Type { - if ($this->isConstantArrayNever($traversedType)) { - // not sure why, but with direct new node everything gets nulled to MixedType - $this->privatesAccessor->setPrivateProperty($traversedType, 'keyType', new MixedType()); - $this->privatesAccessor->setPrivateProperty($traversedType, 'itemType', new MixedType()); - - return $traversedType; - } - - if ($traversedType instanceof UnionType) { - $traversedTypeTypes = $traversedType->getTypes(); - $countTraversedTypes = count($traversedTypeTypes); - - if ($this->isUnionMixedArrayNeverType($countTraversedTypes, $traversedTypeTypes)) { - return new MixedType(); - } - - $collectedTypes = $this->getCollectedTypes($traversedTypeTypes); - $countCollectedTypes = count($collectedTypes); - - // re-create new union types - if ($countTraversedTypes !== $countCollectedTypes && $countTraversedTypes > 2) { - return $this->typeFactory->createMixedPassedOrUnionType($collectedTypes); - } - } - - if ($traversedType instanceof NeverType) { - return new MixedType(); - } - - return $traverserCallable($traversedType, $traverserCallable); - }); - } - - private function isConstantArrayNever(Type $type): bool - { - return $type instanceof ConstantArrayType && $type->getKeyType() instanceof NeverType && $type->getItemType() instanceof NeverType; - } - - /** - * @param Type[] $traversedTypeTypes - * @return Type[] - */ - private function getCollectedTypes(array $traversedTypeTypes): array - { - $collectedTypes = []; - foreach ($traversedTypeTypes as $traversedTypeType) { - // basically an empty array - not useful at all - if ($this->isArrayNeverType($traversedTypeType)) { - continue; - } - - $collectedTypes[] = $traversedTypeType; - } - - return $collectedTypes; - } - - /** - * @param Type[] $traversedTypeTypes - */ - private function isUnionMixedArrayNeverType(int $countTraversedTypes, array $traversedTypeTypes): bool - { - return $countTraversedTypes === 2 && ($this->isArrayNeverType( - $traversedTypeTypes[0] - ) || $this->isArrayNeverType($traversedTypeTypes[1])); - } - - /** - * @param array $nonConstantValueTypes - */ - private function createArrayTypeFromNonConstantValueTypes(array $nonConstantValueTypes): ArrayType - { - $nonConstantValueTypes = array_values($nonConstantValueTypes); - if (count($nonConstantValueTypes) > 1) { - $nonConstantValueType = $this->unionTypeFactory->createUnionObjectType($nonConstantValueTypes); - } else { - $nonConstantValueType = $nonConstantValueTypes[0]; - } - - return new ArrayType(new MixedType(), $nonConstantValueType); - } - - private function collectNestedArrayTypeFromUnionType(UnionType $unionType, int $arrayNesting): void - { - foreach ($unionType->getTypes() as $unionedType) { - if ($unionedType instanceof ArrayType) { - ++$arrayNesting; - $this->normalizeArrayOfUnionToUnionArray($unionedType, $arrayNesting); - } else { - $this->collectedNestedArrayTypes[] = new NestedArrayType($unionedType, $arrayNesting); - } - } - } - - /** - * @param NestedArrayType[] $collectedNestedArrayTypes - */ - private function createUnionedTypesFromArrayTypes(array $collectedNestedArrayTypes): UnionType | ArrayType - { - $unionedTypes = []; - foreach ($collectedNestedArrayTypes as $collectedNestedArrayType) { - $arrayType = $collectedNestedArrayType->getType(); - for ($i = 0; $i < $collectedNestedArrayType->getArrayNestingLevel(); ++$i) { - $arrayType = new ArrayType($collectedNestedArrayType->getKeyType(), $arrayType); - } - - /** @var ArrayType $arrayType */ - $unionedTypes[] = $arrayType; - } - - if (count($unionedTypes) > 1) { - return $this->unionTypeFactory->createUnionObjectType($unionedTypes); - } - - return $unionedTypes[0]; - } - - private function isArrayNeverType(Type $type): bool - { - if (! $type instanceof ArrayType) { - return false; - } - - return $type->getKeyType() instanceof NeverType && $type->getItemType() instanceof NeverType; - } -} diff --git a/rules/TypeDeclaration/ValueObject/AddParamTypeDeclaration.php b/rules/TypeDeclaration/ValueObject/AddParamTypeDeclaration.php index 46b2eb602f8..bdfd17c41d4 100644 --- a/rules/TypeDeclaration/ValueObject/AddParamTypeDeclaration.php +++ b/rules/TypeDeclaration/ValueObject/AddParamTypeDeclaration.php @@ -6,15 +6,20 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use Rector\Validation\RectorAssert; -final class AddParamTypeDeclaration +final readonly class AddParamTypeDeclaration { + /** + * @param int<0, max> $position + */ public function __construct( private string $className, private string $methodName, private int $position, private Type $paramType ) { + RectorAssert::className($className); } public function getObjectType(): ObjectType diff --git a/rules/TypeDeclaration/ValueObject/AddPropertyTypeDeclaration.php b/rules/TypeDeclaration/ValueObject/AddPropertyTypeDeclaration.php new file mode 100644 index 00000000000..538f1ea9050 --- /dev/null +++ b/rules/TypeDeclaration/ValueObject/AddPropertyTypeDeclaration.php @@ -0,0 +1,34 @@ +class; + } + + public function getPropertyName(): string + { + return $this->propertyName; + } + + public function getType(): Type + { + return $this->type; + } +} diff --git a/rules/TypeDeclaration/ValueObject/AddReturnTypeDeclaration.php b/rules/TypeDeclaration/ValueObject/AddReturnTypeDeclaration.php index 6bfeeda6b1f..67ee85f58ca 100644 --- a/rules/TypeDeclaration/ValueObject/AddReturnTypeDeclaration.php +++ b/rules/TypeDeclaration/ValueObject/AddReturnTypeDeclaration.php @@ -6,14 +6,19 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use Rector\Validation\RectorAssert; -final class AddReturnTypeDeclaration +/** + * @api + */ +final readonly class AddReturnTypeDeclaration { public function __construct( private string $class, private string $method, private Type $returnType ) { + RectorAssert::className($class); } public function getClass(): string diff --git a/rules/TypeDeclaration/ValueObject/AssignToVariable.php b/rules/TypeDeclaration/ValueObject/AssignToVariable.php new file mode 100644 index 00000000000..5090db4918b --- /dev/null +++ b/rules/TypeDeclaration/ValueObject/AssignToVariable.php @@ -0,0 +1,26 @@ +variableName; + } + + public function getAssignedExpr(): Expr + { + return $this->assignedExpr; + } +} diff --git a/rules/TypeDeclaration/ValueObject/DataProviderNodes.php b/rules/TypeDeclaration/ValueObject/DataProviderNodes.php new file mode 100644 index 00000000000..bb1ed23a9f1 --- /dev/null +++ b/rules/TypeDeclaration/ValueObject/DataProviderNodes.php @@ -0,0 +1,90 @@ +\w+)(\(\))?#'; + + /** + * @param Attribute[] $attributes + * @param PhpDocTagNode[] $phpDocTagNodes + */ + public function __construct( + private Class_ $class, + private array $attributes, + private array $phpDocTagNodes, + ) { + Assert::allIsInstanceOf($attributes, Attribute::class); + Assert::allIsInstanceOf($phpDocTagNodes, PhpDocTagNode::class); + } + + /** + * @return ClassMethod[] + */ + public function getClassMethods(): array + { + $classMethods = []; + + foreach ($this->phpDocTagNodes as $phpDocTagNode) { + if ($phpDocTagNode->value instanceof GenericTagValueNode) { + $methodName = $this->matchMethodName($phpDocTagNode->value->value); + if (! is_string($methodName)) { + continue; + } + + $classMethod = $this->class->getMethod($methodName); + if (! $classMethod instanceof ClassMethod) { + continue; + } + + $classMethods[] = $classMethod; + } + } + + foreach ($this->attributes as $attribute) { + $value = $attribute->args[0]->value; + if (! $value instanceof String_) { + continue; + } + + $methodName = $this->matchMethodName($value->value); + if (! is_string($methodName)) { + continue; + } + + $classMethod = $this->class->getMethod($methodName); + if (! $classMethod instanceof ClassMethod) { + continue; + } + + $classMethods[] = $classMethod; + } + + return $classMethods; + } + + private function matchMethodName(string $content): ?string + { + $match = Strings::match($content, self::METHOD_NAME_REGEX); + if ($match === null) { + return null; + } + + return $match['method_name']; + } +} diff --git a/rules/TypeDeclaration/ValueObject/NestedArrayType.php b/rules/TypeDeclaration/ValueObject/NestedArrayType.php deleted file mode 100644 index 5ebabd06f09..00000000000 --- a/rules/TypeDeclaration/ValueObject/NestedArrayType.php +++ /dev/null @@ -1,33 +0,0 @@ -type; - } - - public function getArrayNestingLevel(): int - { - return $this->arrayNestingLevel; - } - - public function getKeyType(): Type - { - return $this->keyType ?: new MixedType(); - } -} diff --git a/rules/TypeDeclaration/ValueObject/NewType.php b/rules/TypeDeclaration/ValueObject/NewType.php deleted file mode 100644 index f46b7ca34ed..00000000000 --- a/rules/TypeDeclaration/ValueObject/NewType.php +++ /dev/null @@ -1,13 +0,0 @@ -getMethod(MethodName::CONSTRUCT); + if (! $constructorClassMethod instanceof ClassMethod) { + return null; + } + + if ($constructorClassMethod->stmts === null) { + return null; + } + + $assigns = $this->betterNodeFinder->findInstancesOfScoped($constructorClassMethod->stmts, Assign::class); + foreach ($assigns as $assign) { + if (! $assign->var instanceof PropertyFetch) { + continue; + } + + $propertyFetch = $assign->var; + if (! $this->nodeNameResolver-> isName($propertyFetch->var, 'this')) { + continue; + } + + if (! $this->nodeNameResolver-> isName($propertyFetch->name, $propertyName)) { + continue; + } + + return $this->nodeTypeResolver->getType($assign->expr); + } + + return null; + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php b/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php new file mode 100644 index 00000000000..1f626242910 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php @@ -0,0 +1,148 @@ +isBareMixedType($type)) { + // no value + return false; + } + + $typeNode = $this->createTypeNode($type); + + // no value iterable type + if ($typeNode instanceof IdentifierTypeNode) { + return false; + } + + $this->phpDocTypeChanger->changeParamTypeNode($functionLike, $phpDocInfo, $param, $parameterName, $typeNode); + + return true; + } + + public function decorateGenericIterableReturnType( + Type|TypeNode $typeOrTypeNode, + PhpDocInfo $classMethodPhpDocInfo, + FunctionLike $functionLike + ): bool { + if ($typeOrTypeNode instanceof TypeNode) { + $type = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeOrTypeNode, $functionLike); + } else { + $type = $typeOrTypeNode; + } + + if ($this->isBareMixedType($type)) { + // no value + return false; + } + + if ($typeOrTypeNode instanceof TypeNode) { + $typeNode = $typeOrTypeNode; + } else { + $typeNode = $this->createTypeNode($typeOrTypeNode); + } + + // no value iterable type + if ($typeNode instanceof IdentifierTypeNode) { + return false; + } + + $this->phpDocTypeChanger->changeReturnTypeNode($functionLike, $classMethodPhpDocInfo, $typeNode); + + return true; + } + + public function decorateGenericIterableVarType(Type $type, PhpDocInfo $phpDocInfo, Property $property): bool + { + $typeNode = $this->createTypeNode($type); + + if ($this->isBareMixedType($type)) { + // no value + return false; + } + + // no value iterable type + if ($typeNode instanceof IdentifierTypeNode) { + return false; + } + + $this->phpDocTypeChanger->changeVarTypeNode($property, $phpDocInfo, $typeNode); + + return true; + } + + private function createTypeNode(Type $type): TypeNode + { + $generalizedType = $this->typeNormalizer->generalizeConstantTypes($type); + + // turn into rather generic short return typeOrTypeNode, to keep it open to extension later and readable to human + $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($generalizedType); + if ($typeNode instanceof IdentifierTypeNode && $typeNode->name === 'mixed') { + return new ArrayTypeNode($typeNode); + } + + return $typeNode; + } + + private function isBareMixedType(Type $type): bool + { + if ($type instanceof MixedType) { + return true; + } + + $normalizedResolvedParameterType = $this->typeNormalizer->generalizeConstantTypes($type); + + // most likely mixed, skip + return $this->isArrayMixed($normalizedResolvedParameterType); + } + + private function isArrayMixed(Type $type): bool + { + if (! $type instanceof ArrayType) { + return false; + } + + if ($type->getItemType() instanceof NeverType) { + return true; + } + + if (! $type->getItemType() instanceof MixedType) { + return false; + } + + return $type->getKeyType() instanceof IntegerType; + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeFinder/ArrayDimFetchFinder.php b/rules/TypeDeclarationDocblocks/NodeFinder/ArrayDimFetchFinder.php new file mode 100644 index 00000000000..a4047bed05b --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeFinder/ArrayDimFetchFinder.php @@ -0,0 +1,124 @@ +betterNodeFinder->findInstancesOfScoped((array) $classMethod->stmts, Assign::class); + + $exprs = []; + foreach ($assigns as $assign) { + if (! $assign->var instanceof ArrayDimFetch) { + continue; + } + + $arrayDimFetch = $assign->var; + if (! $arrayDimFetch->var instanceof Variable) { + continue; + } + + if (! $this->nodeNameResolver->isName($arrayDimFetch->var, $variableName)) { + continue; + } + + $exprs[] = $assign->expr; + } + + return $exprs; + } + + /** + * Look for bare assigns, $this->someProperty[] = ... + * @return Expr[] + */ + public function findDimFetchAssignToPropertyName(Class_ $class, string $variableName): array + { + $assigns = $this->betterNodeFinder->findInstancesOfScoped($class->getMethods(), Assign::class); + + $exprs = []; + foreach ($assigns as $assign) { + if (! $assign->var instanceof ArrayDimFetch) { + continue; + } + + $arrayDimFetch = $assign->var; + if ($arrayDimFetch->dim instanceof Expr) { + continue; + } + + if (! $arrayDimFetch->var instanceof PropertyFetch) { + continue; + } + + $propertyFetch = $arrayDimFetch->var; + + if (! $this->nodeNameResolver->isName($propertyFetch->var, 'this')) { + continue; + } + + if (! $this->nodeNameResolver->isName($propertyFetch->name, $variableName)) { + continue; + } + + $exprs[] = $assign->expr; + } + + return $exprs; + } + + /** + * @return ArrayDimFetch[] + */ + public function findByVariableName(Node $node, string $variableName): array + { + $dimFetches = $this->betterNodeFinder->findInstancesOfScoped([$node], ArrayDimFetch::class); + + return array_filter($dimFetches, function (ArrayDimFetch $arrayDimFetch) use ($variableName): bool { + if (! $arrayDimFetch->var instanceof Variable) { + return false; + } + + return $this->nodeNameResolver->isName($arrayDimFetch->var, $variableName); + }); + } + + /** + * @return ArrayDimFetch[] + */ + public function findByDimName(ClassMethod $classMethod, string $dimName): array + { + $dimFetches = $this->betterNodeFinder->findInstancesOfScoped([$classMethod], ArrayDimFetch::class); + + return array_filter($dimFetches, function (ArrayDimFetch $arrayDimFetch) use ($dimName): bool { + if (! $arrayDimFetch->dim instanceof Variable) { + return false; + } + + return $this->nodeNameResolver->isName($arrayDimFetch->dim, $dimName); + }); + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeFinder/ArrayMapClosureExprFinder.php b/rules/TypeDeclarationDocblocks/NodeFinder/ArrayMapClosureExprFinder.php new file mode 100644 index 00000000000..031f2a137f2 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeFinder/ArrayMapClosureExprFinder.php @@ -0,0 +1,66 @@ + + */ + public function findByVariableName(ClassMethod|Function_ $functionLike, string $variableName): array + { + if ($functionLike->stmts === null) { + return []; + } + + /** @var FuncCall[] $funcCalls */ + $funcCalls = $this->betterNodeFinder->findInstancesOfScoped($functionLike->stmts, FuncCall::class); + + $arrayMapClosures = []; + + foreach ($funcCalls as $funcCall) { + if ($funcCall->isFirstClassCallable()) { + continue; + } + + if (! $this->nodeNameResolver->isName($funcCall, 'array_map')) { + continue; + } + + $secondArg = $funcCall->getArgs()[1]; + if (! $secondArg->value instanceof Variable) { + continue; + } + + if (! $this->nodeNameResolver->isName($secondArg->value, $variableName)) { + continue; + } + + $firstArg = $funcCall->getArgs()[0]; + if (! $firstArg->value instanceof Closure && ! $firstArg->value instanceof ArrowFunction) { + continue; + } + + $arrayMapClosures[] = $firstArg->value; + } + + return $arrayMapClosures; + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeFinder/DataProviderMethodsFinder.php b/rules/TypeDeclarationDocblocks/NodeFinder/DataProviderMethodsFinder.php new file mode 100644 index 00000000000..bad7624d35e --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeFinder/DataProviderMethodsFinder.php @@ -0,0 +1,75 @@ +getMethods() as $classMethod) { + $currentDataProviderNodes = $this->findDataProviderNodes($class, $classMethod); + $dataProviderClassMethods = array_merge( + $dataProviderClassMethods, + $currentDataProviderNodes->getClassMethods() + ); + } + + return $dataProviderClassMethods; + } + + public function findDataProviderNodes(Class_ $class, ClassMethod $classMethod): DataProviderNodes + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); + if ($phpDocInfo instanceof PhpDocInfo) { + $phpdocNodes = $phpDocInfo->getTagsByName('@dataProvider'); + } else { + $phpdocNodes = []; + } + + $attributes = $this->findDataProviderAttributes($classMethod); + + return new DataProviderNodes($class, $attributes, $phpdocNodes); + } + + /** + * @return array + */ + private function findDataProviderAttributes(ClassMethod $classMethod): array + { + $dataProviders = []; + + foreach ($classMethod->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attribute) { + if (! $this->nodeNameResolver->isName($attribute->name, TestClassName::DATA_PROVIDER)) { + continue; + } + + $dataProviders[] = $attribute; + } + } + + return $dataProviders; + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeFinder/GetterClassMethodPropertyFinder.php b/rules/TypeDeclarationDocblocks/NodeFinder/GetterClassMethodPropertyFinder.php new file mode 100644 index 00000000000..c1ddfefe86a --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeFinder/GetterClassMethodPropertyFinder.php @@ -0,0 +1,74 @@ +stmts === null || count($classMethod->stmts) !== 1) { + return null; + } + + $onlyStmt = $classMethod->stmts[0]; + if (! $onlyStmt instanceof Return_) { + return null; + } + + if (! $onlyStmt->expr instanceof PropertyFetch) { + return null; + } + + $propertyFetch = $onlyStmt->expr; + if (! $this->nodeNameResolver->isName($propertyFetch->var, 'this')) { + return null; + } + + $propertyName = $this->nodeNameResolver->getName($propertyFetch->name); + if (! is_string($propertyName)) { + return null; + } + + $property = $class->getProperty($propertyName); + if ($property instanceof Property) { + return $property; + } + + // try also promoted property in constructor + $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { + return null; + } + + foreach ($constructClassMethod->getParams() as $param) { + if (! $param->isPromoted()) { + continue; + } + + if (! $this->nodeNameResolver->isName($param, $propertyName)) { + continue; + } + + return $param; + } + + return null; + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeFinder/PropertyGetterFinder.php b/rules/TypeDeclarationDocblocks/NodeFinder/PropertyGetterFinder.php new file mode 100644 index 00000000000..c8c55a57d43 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeFinder/PropertyGetterFinder.php @@ -0,0 +1,69 @@ +nodeNameResolver->getName($property); + foreach ($class->getMethods() as $classMethod) { + if ($classMethod->isMagic()) { + continue; + } + + if ($classMethod->isAbstract()) { + continue; + } + + if ($classMethod->stmts === null) { + continue; + } + + if (count($classMethod->stmts) !== 1) { + continue; + } + + $onlyStmt = $classMethod->stmts[0]; + if (! $onlyStmt instanceof Return_) { + continue; + } + + if (! $onlyStmt->expr instanceof PropertyFetch) { + continue; + } + + $propertyFetch = $onlyStmt->expr; + if (! $propertyFetch->var instanceof Variable) { + continue; + } + + if (! $this->nodeNameResolver->isName($propertyFetch->var, 'this')) { + continue; + } + + if (! $this->nodeNameResolver->isName($propertyFetch->name, $propertyName)) { + continue; + } + + return $classMethod; + } + + return null; + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeFinder/ReturnNodeFinder.php b/rules/TypeDeclarationDocblocks/NodeFinder/ReturnNodeFinder.php new file mode 100644 index 00000000000..47440606d86 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeFinder/ReturnNodeFinder.php @@ -0,0 +1,34 @@ +betterNodeFinder->findReturnsScoped($functionLike); + if (! $this->returnAnalyzer->hasOnlyReturnWithExpr($functionLike, $returnsScoped)) { + return null; + } + + if (count($returnsScoped) !== 1) { + return null; + } + + return $returnsScoped[0]; + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeFinder/YieldNodeFinder.php b/rules/TypeDeclarationDocblocks/NodeFinder/YieldNodeFinder.php new file mode 100644 index 00000000000..1d0a611c97d --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeFinder/YieldNodeFinder.php @@ -0,0 +1,59 @@ +simpleCallableNodeTraverser->traverseNodesWithCallable((array) $functionLike->getStmts(), static function ( + Node $node + ) use (&$yieldNodes): ?int { + // skip anonymous class and inner function + if ($node instanceof Class_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + // skip nested scope + if ($node instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($node instanceof Stmt && ! $node instanceof Expression) { + $yieldNodes = []; + return NodeVisitor::STOP_TRAVERSAL; + } + + if (! $node instanceof Yield_ && ! $node instanceof YieldFrom) { + return null; + } + + $yieldNodes[] = $node; + return null; + }); + + return $yieldNodes; + } +} diff --git a/rules/TypeDeclarationDocblocks/PrivateMethodFlagger.php b/rules/TypeDeclarationDocblocks/PrivateMethodFlagger.php new file mode 100644 index 00000000000..403d8ff9bc9 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/PrivateMethodFlagger.php @@ -0,0 +1,28 @@ +isPrivate()) { + return true; + } + + if ($classMethod->isFinal() && ! $class->extends instanceof Name && $class->implements === []) { + return true; + } + + $isClassFinal = $class->isFinal() || FeatureFlags::treatClassesAsFinal($class); + + return $isClassFinal && ! $class->extends instanceof Name && $class->implements === [] && $classMethod->isProtected(); + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector.php new file mode 100644 index 00000000000..9b6cf452a9e --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockBasedOnArrayMapRector.php @@ -0,0 +1,150 @@ + trim($name), $names); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + /** + * @param string[] $names + */ + public function run(array $names): void + { + $names = array_map(fn(string $name) => trim($name), $names); + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->getParams() === []) { + return null; + } + + $hasChanged = false; + $functionPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + foreach ($node->params as $param) { + // handle only arrays + if (! $this->isArrayParam($param)) { + continue; + } + + $paramName = $this->getName($param); + + $arrayMapClosures = $this->arrayMapClosureExprFinder->findByVariableName($node, $paramName); + if ($arrayMapClosures === []) { + continue; + } + + foreach ($arrayMapClosures as $arrayMapClosure) { + $params = $arrayMapClosure->getParams(); + if ($params === []) { + continue; + } + + $firstParam = $params[0]; + $paramTypeNode = $firstParam->type; + if ($paramTypeNode === null) { + continue; + } + + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag( + $functionPhpDocInfo->getParamTagValueByName($paramName) + )) { + continue; + } + + $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($paramTypeNode); + $arrayParamType = new ArrayType(new MixedType(), $paramType); + + if ($this->nodeDocblockTypeDecorator->decorateGenericIterableParamType( + $arrayParamType, + $functionPhpDocInfo, + $node, + $param, + $paramName + )) { + $hasChanged = true; + } + } + + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + private function isArrayParam(Param $param): bool + { + if (! $param->type instanceof Identifier) { + return false; + } + + return $this->isName($param->type, 'array'); + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector.php new file mode 100644 index 00000000000..70de92dddc4 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromAssignsParamToParamReferenceRector.php @@ -0,0 +1,139 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + if ($node->getParams() === []) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + foreach ($node->getParams() as $param) { + if (! $param->byRef) { + continue; + } + + if (! $param->type instanceof Identifier) { + continue; + } + + if (! $this->isName($param->type, 'array')) { + continue; + } + + $paramName = $this->getName($param); + $paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName); + + // already defined, lets skip it + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($paramTagValueNode)) { + continue; + } + + $exprs = $this->arrayDimFetchFinder->findDimFetchAssignToVariableName($node, $paramName); + + // to kick off with one + if (count($exprs) !== 1) { + continue; + } + + $assignedExprType = $this->getType($exprs[0]); + $iterableType = new ArrayType(new MixedType(), $assignedExprType); + $hasParamTypeChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableParamType( + $iterableType, + $phpDocInfo, + $node, + $param, + $paramName + ); + + if (! $hasParamTypeChanged) { + continue; + } + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector.php new file mode 100644 index 00000000000..93fd0236517 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector.php @@ -0,0 +1,165 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->getParams() === []) { + continue; + } + + if (! $this->testsNodeAnalyzer->isTestClassMethod($classMethod)) { + continue; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + + $dataProviderNodes = $this->dataProviderMethodsFinder->findDataProviderNodes($node, $classMethod); + if ($dataProviderNodes->getClassMethods() === []) { + continue; + } + + foreach ($classMethod->getParams() as $paramPosition => $param) { + // we are interested only in array params + if (! $param->type instanceof Node) { + continue; + } + + if (! $this->isNames($param->type, ['array', 'iterable'])) { + continue; + } + + $paramName = $this->getName($param); + $paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName); + + // already defined, lets skip it + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($paramTagValueNode)) { + continue; + } + + $parameterType = $this->parameterTypeFromDataProviderResolver->resolve( + $paramPosition, + $dataProviderNodes->getClassMethods() + ); + + $hasParamTypeChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableParamType( + $parameterType, + $phpDocInfo, + $classMethod, + $param, + $paramName + ); + + if (! $hasParamTypeChanged) { + continue; + } + + $hasChanged = true; + } + + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector.php new file mode 100644 index 00000000000..950d50778e6 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector.php @@ -0,0 +1,215 @@ + $data + */ + public function process(array $data): void + { + $item = $data['key']; + + $anotherItem = $data['another_key']; + } +} +CODE_SAMPLE + ), + + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->getParams() === []) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + $hasChanged = false; + + foreach ($node->getParams() as $param) { + if (! $param->type instanceof Node) { + continue; + } + + if (! $this->isName($param->type, 'array')) { + continue; + } + + $paramName = $this->getName($param); + $paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName); + + // already defined, lets skip it + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($paramTagValueNode)) { + continue; + } + + $dimFetches = $this->arrayDimFetchFinder->findByVariableName($node, $paramName); + if ($dimFetches === []) { + continue; + } + + // skip for now, not to create more error on incompatible parent @param doc override + if ($node instanceof ClassMethod && $this->parentClassMethodTypeOverrideGuard->hasParentClassMethod( + $node + )) { + continue; + } + + $keyTypes = []; + foreach ($dimFetches as $dimFetch) { + // nothing to resolve here + if (! $dimFetch->dim instanceof Expr) { + continue; + } + + $keyTypes[] = $this->getType($dimFetch->dim); + } + + // most likely not being read + if ($keyTypes === []) { + continue; + } + + if ($this->isOnlyStringType($keyTypes)) { + $this->createParamTagValueNode($phpDocInfo, $paramName, 'string', $paramTagValueNode); + $hasChanged = true; + continue; + } + + if ($this->isOnlyIntegerType($keyTypes)) { + $this->createParamTagValueNode($phpDocInfo, $paramName, 'int', $paramTagValueNode); + $hasChanged = true; + continue; + } + + } + + if ($hasChanged === false) { + return null; + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; + } + + /** + * @param Type[] $keyTypes + */ + private function isOnlyStringType(array $keyTypes): bool + { + foreach ($keyTypes as $keyType) { + if ($keyType->isString()->yes()) { + continue; + } + + return false; + } + + return true; + } + + /** + * @param Type[] $keyTypes + */ + private function isOnlyIntegerType(array $keyTypes): bool + { + foreach ($keyTypes as $keyType) { + if ($keyType->isInteger()->yes()) { + continue; + } + + return false; + } + + return true; + } + + private function createParamTagValueNode( + PhpDocInfo $phpDocInfo, + string $paramName, + string $keyTypeValue, + ?ParamTagValueNode $paramTagValueNode + ): void { + $arrayGenericTypeNode = new GenericTypeNode(new IdentifierTypeNode('array'), [ + new IdentifierTypeNode($keyTypeValue), + new IdentifierTypeNode('mixed'), + ]); + + if ($paramTagValueNode instanceof ParamTagValueNode) { + $paramTagValueNode->type = $arrayGenericTypeNode; + } else { + $paramTagValueNode = new ParamTagValueNode($arrayGenericTypeNode, false, '$' . $paramName, '', false); + $phpDocInfo->addTagValueNode($paramTagValueNode); + } + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector.php new file mode 100644 index 00000000000..5809dc3f431 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForArrayDimAssignedObjectRector.php @@ -0,0 +1,234 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $returnType = $phpDocInfo->getReturnType(); + + if ($returnType instanceof ArrayType && ! $returnType->getItemType() instanceof MixedType) { + return null; + } + + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($phpDocInfo->getReturnTagValue())) { + return null; + } + + // definitely not an array return + if ($node->returnType instanceof Node && ! $this->isName($node->returnType, 'array')) { + return null; + } + + $onlyReturnWithExpr = $this->returnNodeFinder->findOnlyReturnWithExpr($node); + if (! $onlyReturnWithExpr instanceof Return_ || ! $onlyReturnWithExpr->expr instanceof Variable) { + return null; + } + + // is expr only used to array dim assign? + + $returnedType = $this->getType($onlyReturnWithExpr->expr); + + $returnedVariableName = $this->getName($onlyReturnWithExpr->expr); + if (! is_string($returnedVariableName)) { + return null; + } + + if ($this->isVariableExclusivelyArrayDimAssigned($node, $returnedVariableName) === false) { + return null; + } + + $arrayObjectType = $this->matchArrayObjectType($returnedType); + if (! $arrayObjectType instanceof ObjectType) { + return null; + } + + $objectTypeArrayType = new ArrayType(new MixedType(), $arrayObjectType); + if (! $this->nodeDocblockTypeDecorator->decorateGenericIterableReturnType( + $objectTypeArrayType, + $phpDocInfo, + $node + )) { + return null; + } + + return $node; + } + + private function matchArrayObjectType(Type $type): ?Type + { + if (! $type instanceof ArrayType) { + return null; + } + + if (! $type->getItemType() instanceof ObjectType) { + return null; + } + + return $type->getItemType(); + } + + private function isVariableExclusivelyArrayDimAssigned( + ClassMethod|Function_ $functionLike, + string $variableName + ): bool { + $isVariableExclusivelyArrayDimAssigned = true; + + $this->traverseNodesWithCallable((array) $functionLike->stmts, function ($node) use ( + $variableName, + &$isVariableExclusivelyArrayDimAssigned + ): ?int { + if ($node instanceof Assign) { + if ($node->var instanceof ArrayDimFetch) { + $arrayDimFetch = $node->var; + + if (! $arrayDimFetch->var instanceof Variable) { + $isVariableExclusivelyArrayDimAssigned = false; + return null; + } + + if ($this->isName($arrayDimFetch->var, $variableName)) { + if ($arrayDimFetch->dim instanceof Expr) { + $isVariableExclusivelyArrayDimAssigned = false; + } + + $assignedType = $this->getType($node->expr); + if (! $assignedType instanceof ObjectType) { + $isVariableExclusivelyArrayDimAssigned = false; + } + + if ($assignedType instanceof NonExistingObjectType) { + $isVariableExclusivelyArrayDimAssigned = false; + } + + // ignore lower value + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + } + + if ($node->var instanceof Variable && $this->isName( + $node->var, + $variableName + ) && $node->expr instanceof Array_) { + if ($node->expr->items === []) { + // ignore empty array assignment + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + $isVariableExclusivelyArrayDimAssigned = false; + } + } + + if ($node instanceof Return_ && $node->expr instanceof Variable) { + if ($this->isName($node->expr, $variableName)) { + // ignore lower value + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + $isVariableExclusivelyArrayDimAssigned = false; + } + + if ($node instanceof Variable && $this->isName($node, $variableName)) { + $isVariableExclusivelyArrayDimAssigned = false; + } + + return null; + }); + + return $isVariableExclusivelyArrayDimAssigned; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector.php new file mode 100644 index 00000000000..a2b9441b9d3 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForCommonObjectDenominatorRector.php @@ -0,0 +1,236 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($phpDocInfo->getReturnTagValue())) { + return null; + } + + // definitely not an array return + if ($node->returnType instanceof Node && ! $this->isName($node->returnType, 'array')) { + return null; + } + + $onlyReturnWithExpr = $this->returnNodeFinder->findOnlyReturnWithExpr($node); + if (! $onlyReturnWithExpr instanceof Return_ || ! $onlyReturnWithExpr->expr instanceof Expr) { + return null; + } + + $returnedType = $this->getType($onlyReturnWithExpr->expr); + if (! $returnedType instanceof ConstantArrayType) { + return null; + } + + $referencedClasses = []; + foreach ($returnedType->getValueTypes() as $valueType) { + // each item must refer some classes + if ($valueType->getReferencedClasses() === []) { + return null; + } + + /** + * not an object, can be nested array, or string class as Foo::class + */ + if (! $valueType->isObject()->yes()) { + return null; + } + + $referencedClasses = array_merge($referencedClasses, $valueType->getReferencedClasses()); + } + + // nothing to find here + if ($referencedClasses === []) { + return null; + } + + $uniqueReferencedClasses = array_unique($referencedClasses, SORT_REGULAR); + + if (count($uniqueReferencedClasses) === 1) { + $firstSharedType = $uniqueReferencedClasses[0]; + } else { + + $parentClassesAndInterfaces = []; + + foreach ($referencedClasses as $referencedClass) { + $parentClassesAndInterfaces[] = $this->resolveParentClassesAndInterfaces($referencedClass); + } + + $firstSharedTypes = array_intersect(...$parentClassesAndInterfaces); + $firstSharedType = $firstSharedTypes[0] ?? null; + } + + if ($firstSharedType === null) { + return null; + } + + $objectTypeArrayType = new ArrayType( + $this->resolveKeyType($returnedType), + new FullyQualifiedObjectType($firstSharedType) + ); + + $hasChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableReturnType( + $objectTypeArrayType, + $phpDocInfo, + $node + ); + if (! $hasChanged) { + return null; + } + + return $node; + } + + /** + * @return UnionType|IntegerType|StringType|MixedType + */ + private function resolveKeyType(ConstantArrayType $constantArrayType): Type + { + $keyType = $constantArrayType->getKeyType(); + + if ($keyType instanceof UnionType) { + $types = []; + foreach ($keyType->getTypes() as $type) { + if ($type->isString()->yes()) { + $types[] = new StringType(); + } elseif ($type->isInteger()->yes()) { + $types[] = new IntegerType(); + } else { + return new MixedType(); + } + } + + $uniqueKeyTypes = array_unique($types, SORT_REGULAR); + if (count($uniqueKeyTypes) === 1) { + return $uniqueKeyTypes[0]; + } + + return new UnionType($uniqueKeyTypes); + } + + return new MixedType(); + } + + /** + * @return string[] + */ + private function resolveParentClassesAndInterfaces(string $className): array + { + $referenceClassReflection = $this->reflectionProvider->getClass($className); + + $currentParentClassesAndInterfaces = $referenceClassReflection->getParentClassesNames(); + + foreach ($referenceClassReflection->getInterfaces() as $classReflection) { + $currentParentClassesAndInterfaces[] = $classReflection->getName(); + } + + return $currentParentClassesAndInterfaces; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php new file mode 100644 index 00000000000..3698f2bb697 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php @@ -0,0 +1,220 @@ + + */ + public function toArray() + { + $items = []; + + if (mt_rand(0, 1)) { + $items['key'] = 'value'; + } + + if (mt_rand(0, 1)) { + $items['another_key'] = 'another_value'; + } + + return $items; + } +} +CODE_SAMPLE + ), + + ] + ); + } + + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?ClassMethod + { + if ($node->stmts === null) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($phpDocInfo->getReturnTagValue())) { + return null; + } + + $soleReturn = $this->returnNodeFinder->findOnlyReturnWithExpr($node); + if (! $soleReturn instanceof Return_) { + return null; + } + + // only variable + if (! $soleReturn->expr instanceof Variable) { + return null; + } + + // @todo check type here + $returnedExprType = $this->getType($soleReturn->expr); + + if (! $this->isConstantArrayType($returnedExprType)) { + return null; + } + + // find stmts with $item = []; + $returnedVariableName = $this->getName($soleReturn->expr); + if (! is_string($returnedVariableName)) { + return null; + } + + if (! $this->isVariableInstantiated($node, $returnedVariableName)) { + return null; + } + + if ($returnedExprType->getReferencedClasses() !== []) { + // better handled by shared-interface/class rule, to avoid turning objects to mixed + return null; + } + + // conditional assign + $genericUnionedTypeNodes = []; + + if ($returnedExprType instanceof UnionType) { + foreach ($returnedExprType->getTypes() as $unionedType) { + if ($unionedType instanceof ConstantArrayType) { + // skip empty array + if ($unionedType->getKeyTypes() === [] && $unionedType->getValueTypes() === []) { + continue; + } + + $genericUnionedTypeNode = $this->constantArrayTypeGeneralizer->generalize($unionedType); + $genericUnionedTypeNodes[] = $genericUnionedTypeNode; + } + } + } else { + /** @var ConstantArrayType $returnedExprType */ + $genericTypeNode = $this->constantArrayTypeGeneralizer->generalize($returnedExprType); + $this->phpDocTypeChanger->changeReturnTypeNode($node, $phpDocInfo, $genericTypeNode); + + return $node; + } + + // @todo handle multiple type nodes + $this->phpDocTypeChanger->changeReturnTypeNode($node, $phpDocInfo, $genericUnionedTypeNodes[0]); + + return $node; + } + + private function isVariableInstantiated(ClassMethod $classMethod, string $returnedVariableName): bool + { + foreach ((array) $classMethod->stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof Assign) { + continue; + } + + $assign = $stmt->expr; + if (! $assign->var instanceof Variable) { + continue; + } + + if (! $this->isName($assign->var, $returnedVariableName)) { + continue; + } + + // must be array assignment + if (! $assign->expr instanceof Array_) { + continue; + } + + return true; + } + + return false; + } + + private function isConstantArrayType(Type $returnedExprType): bool + { + if ($returnedExprType instanceof UnionType) { + foreach ($returnedExprType->getTypes() as $unionedType) { + if (! $unionedType instanceof ConstantArrayType) { + return false; + } + } + + return true; + } + + return $returnedExprType instanceof ConstantArrayType; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector.php new file mode 100644 index 00000000000..2351f65b957 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForJsonArrayRector.php @@ -0,0 +1,170 @@ + + */ + public function provide(string $contents): array + { + return json_decode($contents, true); + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + // definitely not an array return + if ($node->returnType instanceof Node && ! $this->isName($node->returnType, 'array')) { + return null; + } + + $onlyReturnWithExpr = $this->returnNodeFinder->findOnlyReturnWithExpr($node); + if (! $onlyReturnWithExpr instanceof Return_) { + return null; + } + + $returnedExpr = $onlyReturnWithExpr->expr; + if (! $returnedExpr instanceof Expr) { + return null; + } + + if (! $this->isJsonDecodeToArray($returnedExpr)) { + return null; + } + + $classMethodDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($classMethodDocInfo->getReturnTagValue())) { + return null; + } + + $hasChanged = $this->phpDocTypeChanger->changeReturnType( + $node, + $phpDocInfo, + new ArrayType(new StringType(), new MixedType()) + ); + + if (! $hasChanged) { + return null; + } + + return $node; + } + + private function isJsonDecodeToArray(Expr $expr): bool + { + if ($expr instanceof FuncCall) { + if (! $this->isName($expr, 'json_decode')) { + return false; + } + + if ($expr->isFirstClassCallable()) { + return false; + } + + $arg = $expr->getArg('associative', 1); + if (! $arg instanceof Arg) { + return false; + } + + return $this->valueResolver->isTrue($arg->value); + } + + if ($expr instanceof StaticCall) { + if (! $this->isName($expr->class, NetteClassName::JSON)) { + return false; + } + + if (! $this->isName($expr->name, 'decode')) { + return false; + } + + if ($expr->isFirstClassCallable()) { + return false; + } + + $arg = $expr->getArg('forceArrays', 1); + if (! $arg instanceof Arg) { + return false; + } + + return $this->valueResolver->isTrue($arg->value); + } + + return false; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector.php new file mode 100644 index 00000000000..2040a39a407 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector.php @@ -0,0 +1,161 @@ +repository->findAll(); + } +} + +final class Repository +{ + /** + * @return SomeEntity[] + */ + public function findAll(): array + { + // ... + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeController +{ + /** + * @return SomeEntity[] + */ + public function getAll(): array + { + return $this->repository->findAll(); + } +} + +final class Repository +{ + /** + * @return SomeEntity[] + */ + public function findAll(): array + { + // ... + } +} +CODE_SAMPLE + ), + + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($phpDocInfo->getReturnTagValue())) { + return null; + } + + // definitely not an array return + if (! $node->returnType instanceof Node || ! $this->isName($node->returnType, 'array')) { + return null; + } + + $onlyReturnWithExpr = $this->returnNodeFinder->findOnlyReturnWithExpr($node); + if (! $onlyReturnWithExpr instanceof Return_ || (! $onlyReturnWithExpr->expr instanceof MethodCall && ! $onlyReturnWithExpr->expr instanceof StaticCall)) { + return null; + } + + $returnedMethodCall = $onlyReturnWithExpr->expr; + + // skip doctrine connection calls, as to generic and not helpful + $callerType = $this->getType( + $returnedMethodCall instanceof MethodCall ? $returnedMethodCall->var : $returnedMethodCall->class + ); + if ($callerType instanceof ObjectType && $callerType->isInstanceOf(DoctrineClass::CONNECTION)->yes()) { + return null; + } + + $calledClassMethod = $this->astResolver->resolveClassMethodFromCall($returnedMethodCall); + if (! $calledClassMethod instanceof ClassMethod) { + return null; + } + + if (! $calledClassMethod->returnType instanceof Identifier) { + return null; + } + + if (! $this->isName($calledClassMethod->returnType, 'array')) { + return null; + } + + $calledClassMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($calledClassMethod); + + $calledReturnTagValue = $calledClassMethodPhpDocInfo->getReturnTagValue(); + if (! $calledReturnTagValue instanceof ReturnTagValueNode) { + return null; + } + + if (! $this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($calledReturnTagValue)) { + return null; + } + + $this->phpDocTypeChanger->changeReturnType($node, $phpDocInfo, $calledClassMethodPhpDocInfo->getReturnType()); + + return $node; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector.php new file mode 100644 index 00000000000..fc2a1dd214e --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector.php @@ -0,0 +1,140 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Add @return array docblock to a getter method based on @var of the property', [ + new CodeSample( + <<<'CODE_SAMPLE' +class SomeClass +{ + /** + * @var int[] + */ + private array $items; + + public function getItems(): array + { + return $this->items; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + /** + * @var int[] + */ + private array $items; + + /** + * @return int[] + */ + public function getItems(): array + { + return $this->items; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isAnonymous()) { + return null; + } + + $hasChanged = false; + foreach ($node->getMethods() as $classMethod) { + if (! $classMethod->returnType instanceof Node) { + continue; + } + + if (! $this->isName($classMethod->returnType, 'array')) { + continue; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($phpDocInfo->getReturnTagValue())) { + continue; + } + + $propertyOrParam = $this->getterClassMethodPropertyFinder->find($classMethod, $node); + if (! $propertyOrParam instanceof Node) { + continue; + } + + $propertyDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($propertyOrParam); + + $varTagValueNode = $propertyDocInfo->getVarTagValueNode(); + + if (! $varTagValueNode instanceof VarTagValueNode) { + continue; + } + + // is type useful? + if (! $varTagValueNode->type instanceof GenericTypeNode && ! $varTagValueNode->type instanceof ArrayTypeNode) { + continue; + } + + if (! $this->nodeDocblockTypeDecorator->decorateGenericIterableReturnType( + $varTagValueNode->type, + $phpDocInfo, + $classMethod + )) { + continue; + } + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector.php new file mode 100644 index 00000000000..978acccb496 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector.php @@ -0,0 +1,143 @@ + 'now', + ]; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + /** + * @return array + */ + public function getItems(): array + { + return [ + 'hey' => 'now', + ]; + } +} +CODE_SAMPLE + ), + + ] + ); + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->stmts === null) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($phpDocInfo->getReturnTagValue())) { + return null; + } + + $soleReturn = $this->returnNodeFinder->findOnlyReturnWithExpr($node); + if (! $soleReturn instanceof Return_) { + return null; + } + + if (! $soleReturn->expr instanceof Array_) { + return null; + } + + if ($this->shouldSkipReturnMixedAndEmptyArray($phpDocInfo, $soleReturn->expr)) { + return null; + } + + // resolve simple type + $returnedType = $this->getType($soleReturn->expr); + if (! $returnedType instanceof ConstantArrayType) { + return null; + } + + // better handled by shared-interface/class rule, to avoid turning objects to mixed + if ($returnedType->getReferencedClasses() !== [] && count($returnedType->getReferencedClasses()) === count( + $returnedType->getValueTypes() + )) { + return null; + } + + $genericTypeNode = $this->constantArrayTypeGeneralizer->generalize($returnedType); + $this->phpDocTypeChanger->changeReturnTypeNode($node, $phpDocInfo, $genericTypeNode); + + return $node; + } + + private function shouldSkipReturnMixedAndEmptyArray(PhpDocInfo $phpDocInfo, Array_ $array): bool + { + if ($array->items !== []) { + return false; + } + + $returnTagValueNode = $phpDocInfo->getReturnTagValue(); + if (! $returnTagValueNode instanceof ReturnTagValueNode) { + return false; + } + + // better than array{} + return $returnTagValueNode->type instanceof ArrayTypeNode; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector.php new file mode 100644 index 00000000000..a9b357e74fa --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnArrayDocblockFromDataProviderParamRector.php @@ -0,0 +1,170 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if (! $classMethod->isPublic()) { + continue; + } + + if (! $this->testsNodeAnalyzer->isTestClassMethod($classMethod)) { + continue; + } + + // sole param required + if (count($classMethod->getParams()) !== 1) { + continue; + } + + $dataProviderNodes = $this->dataProviderMethodsFinder->findDataProviderNodes($node, $classMethod); + + $paramTypesByPosition = []; + foreach ($classMethod->getParams() as $position => $param) { + if (! $param->type instanceof Node) { + continue; + } + + $paramTypesByPosition[$position] = $param->type; + } + + if ($paramTypesByPosition === []) { + continue; + } + + foreach ($dataProviderNodes->getClassMethods() as $dataProviderClassMethod) { + if (! $dataProviderClassMethod->returnType instanceof Identifier) { + continue; + } + + if (! $this->isName($dataProviderClassMethod->returnType, 'array')) { + continue; + } + + // already set return tag + $dataProviderPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($dataProviderClassMethod); + if ($dataProviderPhpDocInfo->getReturnTagValue() instanceof ReturnTagValueNode) { + continue; + } + + $paramTypeNode = $paramTypesByPosition[0]; + $returnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($paramTypeNode); + $arrayReturnType = new ArrayType(new MixedType(), $returnType); + + $arrayReturnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( + $arrayReturnType, + ); + + $returnTagValueNode = new ReturnTagValueNode($arrayReturnTypeNode, ''); + + $dataProviderPhpDocInfo->addTagValueNode($returnTagValueNode); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($dataProviderClassMethod); + + $hasChanged = true; + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php new file mode 100644 index 00000000000..007b9c971ef --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php @@ -0,0 +1,179 @@ +> + */ + public function provideItems() + { + return [ + [['item1', 'item2']], + [['item3', 'item4']], + ]; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $hasChanged = false; + + $dataProviderClassMethods = $this->dataProviderMethodsFinder->findDataProviderNodesInClass($node); + if ($dataProviderClassMethods === []) { + return null; + } + + foreach ($dataProviderClassMethods as $dataProviderClassMethod) { + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($dataProviderClassMethod); + + $returnTagValueNode = $classMethodPhpDocInfo->getReturnTagValue(); + + // already set + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($returnTagValueNode)) { + continue; + } + + $soleReturn = $this->returnNodeFinder->findOnlyReturnWithExpr($dataProviderClassMethod); + + // unable to resolve type + if ($soleReturn instanceof Return_) { + if (! $soleReturn->expr instanceof Expr) { + continue; + } + + $soleReturnType = $this->getType($soleReturn->expr); + + $hasClassMethodChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableReturnType( + $soleReturnType, + $classMethodPhpDocInfo, + $dataProviderClassMethod + ); + + if (! $hasClassMethodChanged) { + continue; + } + + $hasChanged = true; + continue; + } + + $yields = $this->yieldNodeFinder->find($dataProviderClassMethod); + if ($yields !== []) { + $yieldType = $this->yieldTypeResolver->resolveFromYieldNodes($yields, $dataProviderClassMethod); + + if ($yieldType instanceof FullyQualifiedGenericObjectType && $yieldType->getClassName() === 'Generator' && ! $dataProviderClassMethod->returnType instanceof Node) { + // most likely, a static iterator is used in data test fixtures + $yieldType = new FullyQualifiedGenericObjectType('Iterator', $yieldType->getTypes()); + } + + $hasClassMethodChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableReturnType( + $yieldType, + $classMethodPhpDocInfo, + $dataProviderClassMethod + ); + + if (! $hasClassMethodChanged) { + continue; + } + + $hasChanged = true; + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector.php new file mode 100644 index 00000000000..0c742963f3c --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector.php @@ -0,0 +1,154 @@ +items[] = [ + 'name' => 'John', + ]; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + /** + * @var array> + */ + private array $items = []; + + public function run() + { + $this->items[] = [ + 'name' => 'John', + ]; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + if (! $this->isPropertyTypeArray($property)) { + continue; + } + + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($propertyPhpDocInfo->getVarTagValueNode())) { + continue; + } + + $propertyName = $this->getName($property); + + $assignedExprs = $this->arrayDimFetchFinder->findDimFetchAssignToPropertyName($node, $propertyName); + + $assignedExprTypes = []; + foreach ($assignedExprs as $assignedExpr) { + $assignedExprTypes[] = $this->getType($assignedExpr); + } + + // nothing to add + if ($assignedExprTypes === []) { + continue; + } + + $uniqueGeneralizedUnionTypes = $this->typeFactory->uniquateTypes($assignedExprTypes); + + if (count($uniqueGeneralizedUnionTypes) > 1) { + $generalizedUnionedTypes = new UnionType($uniqueGeneralizedUnionTypes); + } else { + $generalizedUnionedTypes = $uniqueGeneralizedUnionTypes[0]; + } + + $arrayReturnType = new ArrayType(new MixedType(), $generalizedUnionedTypes); + + $hasPropertyChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableVarType( + $arrayReturnType, + $propertyPhpDocInfo, + $property + ); + + if ($hasPropertyChanged === false) { + continue; + } + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + private function isPropertyTypeArray(Property $property): bool + { + if (! $property->type instanceof Identifier) { + return false; + } + + return $this->isName($property->type, 'array'); + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php new file mode 100644 index 00000000000..5757210532b --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php @@ -0,0 +1,155 @@ +run(['item1', 'item2']); + } + + private function run(array $items) + { + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function go() + { + $this->run(['item1', 'item2']); + } + + /** + * @param string[] $items + */ + private function run(array $items) + { + } +} +CODE_SAMPLE + ), + + ]); + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->getParams() === []) { + continue; + } + + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + + $methodCalls = $this->localMethodCallFinder->match($node, $classMethod); + $classMethodParameterTypes = $this->callTypesResolver->resolveTypesFromCalls($methodCalls); + + foreach ($classMethod->getParams() as $parameterPosition => $param) { + if (! $this->hasParamArrayType($param)) { + continue; + } + + $parameterName = $this->getName($param); + $parameterTagValueNode = $classMethodPhpDocInfo->getParamTagValueByName($parameterName); + + // already known, skip + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($parameterTagValueNode)) { + continue; + } + + if ($parameterTagValueNode instanceof ParamTagValueNode + && $classMethod->isPublic() && + $this->usefulArrayTagNodeAnalyzer->isMixedArray($parameterTagValueNode->type)) { + // on public method, skip if there is mixed[], as caller can be anything + continue; + } + + $resolvedParameterType = $classMethodParameterTypes[$parameterPosition] ?? $classMethodParameterTypes[$parameterName] ?? null; + + if (! $resolvedParameterType instanceof Type) { + continue; + } + + // in case of array type declaration, null cannot be passed or is already casted + $resolvedParameterType = TypeCombinator::removeNull($resolvedParameterType); + + $hasClassMethodChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableParamType( + $resolvedParameterType, + $classMethodPhpDocInfo, + $classMethod, + $param, + $parameterName + ); + + if ($hasClassMethodChanged) { + $hasChanged = true; + } + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + private function hasParamArrayType(Param $param): bool + { + if (! $param->type instanceof Node) { + return false; + } + + return $this->isName($param->type, 'array'); + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector.php new file mode 100644 index 00000000000..686fed4904b --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromGetterReturnRector.php @@ -0,0 +1,136 @@ +items; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + /** + * @var int[] + */ + private array $items; + + /** + * @return int[] + */ + public function getItems(): array + { + return $this->items; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + // property type must be known + if (! $property->type instanceof Identifier) { + continue; + } + + if (! $this->isName($property->type, 'array')) { + continue; + } + + if (count($property->props) > 1) { + continue; + } + + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + // type is already known, skip it + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($propertyPhpDocInfo->getVarTagValueNode())) { + continue; + } + + $propertyGetterMethod = $this->propertyGetterFinder->find($property, $node); + if (! $propertyGetterMethod instanceof ClassMethod) { + continue; + } + + $classMethodDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($propertyGetterMethod); + $returnTagValueNode = $classMethodDocInfo->getReturnTagValue(); + if (! $returnTagValueNode instanceof ReturnTagValueNode) { + continue; + } + + $isPropertyChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableVarType( + $classMethodDocInfo->getReturnType(), + $propertyPhpDocInfo, + $property + ); + + if (! $isPropertyChanged) { + continue; + } + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php new file mode 100644 index 00000000000..c1c617d8825 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php @@ -0,0 +1,108 @@ +getProperties() as $property) { + if (! $property->type instanceof Identifier) { + continue; + } + + if (! $this->isName($property->type, 'array')) { + continue; + } + + if (count($property->props) > 1) { + continue; + } + + $soleProperty = $property->props[0]; + if (! $soleProperty->default instanceof Array_) { + continue; + } + + $propertyDefaultType = $this->getType($soleProperty->default); + + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + // type is already known + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($propertyPhpDocInfo->getVarTagValueNode())) { + continue; + } + + if ($this->nodeDocblockTypeDecorator->decorateGenericIterableVarType( + $propertyDefaultType, + $propertyPhpDocInfo, + $property + )) { + $hasChanged = true; + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector.php new file mode 100644 index 00000000000..a4037008953 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarFromParamDocblockInConstructorRector.php @@ -0,0 +1,139 @@ +items = $items; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + /** + * @var string[] + */ + private array $items; + + /** + * @param string[] $items + */ + public function __construct(array $items) + { + $this->items = $items; + } +} +CODE_SAMPLE + ), + + ]); + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $constructorClassMethod = $node->getMethod(MethodName::CONSTRUCT); + if (! $constructorClassMethod instanceof ClassMethod) { + return null; + } + + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + if (! $this->isArrayTypedProperty($property)) { + continue; + } + + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + // @var tag already given + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($propertyPhpDocInfo->getVarTagValueNode())) { + continue; + } + + $propertyName = $this->getName($property); + + $assignedType = $this->constructorAssignedTypeResolver->resolve($node, $propertyName); + if (! $assignedType instanceof ArrayType) { + continue; + } + + $hasPropertyChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableVarType( + $assignedType, + $propertyPhpDocInfo, + $property + ); + + if (! $hasPropertyChanged) { + continue; + } + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + private function isArrayTypedProperty(Property $property): bool + { + if (! $property->type instanceof Node) { + return false; + } + + return $this->isName($property->type, 'array'); + } +} diff --git a/rules/TypeDeclarationDocblocks/TagNodeAnalyzer/UsefulArrayTagNodeAnalyzer.php b/rules/TypeDeclarationDocblocks/TagNodeAnalyzer/UsefulArrayTagNodeAnalyzer.php new file mode 100644 index 00000000000..645c741ec80 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/TagNodeAnalyzer/UsefulArrayTagNodeAnalyzer.php @@ -0,0 +1,34 @@ +type; + if (! $type instanceof IdentifierTypeNode) { + return ! $this->isMixedArray($type); + } + + return ! in_array($type->name, ['array', 'mixed', 'iterable'], true); + } + + public function isMixedArray(TypeNode $typeNode): bool + { + return $typeNode instanceof SpacingAwareArrayTypeNode && $typeNode->type instanceof IdentifierTypeNode && $typeNode->type->name === 'mixed'; + } +} diff --git a/rules/TypeDeclarationDocblocks/TypeResolver/ConstantArrayTypeGeneralizer.php b/rules/TypeDeclarationDocblocks/TypeResolver/ConstantArrayTypeGeneralizer.php new file mode 100644 index 00000000000..3ca83ab2218 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/TypeResolver/ConstantArrayTypeGeneralizer.php @@ -0,0 +1,85 @@ +currentNesting = 0; + } else { + ++$this->currentNesting; + } + + $genericKeyType = $this->typeNormalizer->generalizeConstantTypes($constantArrayType->getKeyType()); + + $itemType = $constantArrayType->getItemType(); + + if ($itemType instanceof NeverType) { + return ArrayShapeNode::createSealed([]); + } + + if ($itemType instanceof ConstantArrayType) { + if ($this->currentNesting >= self::MAX_NESTING) { + $genericItemType = new MixedType(); + } else { + $genericItemType = $this->generalize($itemType, false); + } + } else { + $genericItemType = $this->typeNormalizer->generalizeConstantTypes($itemType); + } + + // correction + if ($genericItemType instanceof NeverType) { + $genericItemType = new MixedType(); + } + + return $this->createArrayGenericTypeNode($genericKeyType, $genericItemType); + } + + private function createArrayGenericTypeNode( + Type $keyType, + Type|GenericTypeNode|ArrayShapeNode $itemType + ): GenericTypeNode { + $keyDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($keyType); + + if ($itemType instanceof Type) { + $itemDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($itemType); + } else { + $itemDocTypeNode = $itemType; + } + + return new GenericTypeNode(new IdentifierTypeNode('array'), [$keyDocTypeNode, $itemDocTypeNode]); + } +} diff --git a/rules/TypeDeclarationDocblocks/TypeResolver/YieldTypeResolver.php b/rules/TypeDeclarationDocblocks/TypeResolver/YieldTypeResolver.php new file mode 100644 index 00000000000..e3bbc4b68b0 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/TypeResolver/YieldTypeResolver.php @@ -0,0 +1,100 @@ + $yieldNodes + */ + public function resolveFromYieldNodes( + array $yieldNodes, + ClassMethod|Function_ $functionLike + ): FullyQualifiedObjectType|FullyQualifiedGenericObjectType { + $yieldedTypes = $this->resolveYieldedTypes($yieldNodes); + + $className = $this->resolveClassName($functionLike); + + if ($yieldedTypes === []) { + return new FullyQualifiedObjectType($className); + } + + $yieldedTypes = $this->typeFactory->createMixedPassedOrUnionType($yieldedTypes); + return new FullyQualifiedGenericObjectType($className, [$yieldedTypes]); + } + + private function resolveYieldValue(Yield_ | YieldFrom $yield): ?Expr + { + if ($yield instanceof Yield_) { + return $yield->value; + } + + return $yield->expr; + } + + /** + * @param array $yieldNodes + * @return Type[] + */ + private function resolveYieldedTypes(array $yieldNodes): array + { + $yieldedTypes = []; + + foreach ($yieldNodes as $yieldNode) { + $value = $this->resolveYieldValue($yieldNode); + if (! $value instanceof Expr) { + // one of the yields is empty + return []; + } + + $resolvedType = $this->nodeTypeResolver->getType($value); + if ($resolvedType instanceof MixedType) { + continue; + } + + $yieldedTypes[] = $resolvedType; + } + + return $yieldedTypes; + } + + private function resolveClassName(Function_|ClassMethod|Closure $functionLike): string + { + $returnTypeNode = $functionLike->getReturnType(); + + if ($returnTypeNode instanceof Identifier && $returnTypeNode->name === 'iterable') { + return 'Iterator'; + } + + if ($returnTypeNode instanceof Name && ! $this->nodeNameResolver->isName($returnTypeNode, 'Generator')) { + return $this->nodeNameResolver->getName($returnTypeNode); + } + + return 'Generator'; + } +} diff --git a/rules/Unambiguous/NodeAnalyzer/FluentMethodCallsCollector.php b/rules/Unambiguous/NodeAnalyzer/FluentMethodCallsCollector.php new file mode 100644 index 00000000000..94387aedd78 --- /dev/null +++ b/rules/Unambiguous/NodeAnalyzer/FluentMethodCallsCollector.php @@ -0,0 +1,60 @@ +var instanceof MethodCall) { + return []; + } + + /** @var MethodCall[] $methodCalls */ + $methodCalls = []; + + $currentMethodCall = $firstMethodCall; + $classNameObjectType = null; + while ($currentMethodCall instanceof MethodCall) { + if ($currentMethodCall->isFirstClassCallable()) { + return []; + } + + // must be exactly one argument + if (count($currentMethodCall->getArgs()) !== 1) { + return []; + } + + $objectType = $this->nodeTypeResolver->getType($currentMethodCall->var); + if (! $objectType instanceof ObjectType) { + return []; + } + + if ($classNameObjectType === null) { + $classNameObjectType = $objectType->getClassName(); + } elseif ($classNameObjectType !== $objectType->getClassName()) { + return []; + } + + $methodCalls[] = $currentMethodCall; + $currentMethodCall = $currentMethodCall->var; + } + + return $methodCalls; + } +} diff --git a/rules/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector.php b/rules/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector.php new file mode 100644 index 00000000000..4b8c8654979 --- /dev/null +++ b/rules/Unambiguous/Rector/Class_/RemoveReturnThisFromSetterClassMethodRector.php @@ -0,0 +1,121 @@ +name = $name; + return $this; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + private $name; + + public function setName(string $name): void + { + $this->name = $name; + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->isMagic()) { + continue; + } + + // skip void return type + if ($classMethod->returnType instanceof Identifier && $this->isName($classMethod->returnType, 'void')) { + continue; + } + + if (count($classMethod->params) !== 1) { + continue; + } + + $soleParam = $classMethod->params[0]; + + // magic spread + if ($soleParam->variadic) { + continue; + } + + $paramName = $this->getName($soleParam->var); + if (! is_string($paramName)) { + continue; + } + + if (! $this->classMethodAndPropertyAnalyzer->hasPropertyAssignWithReturnThis($classMethod)) { + continue; + } + + // remove 2nd stmts, that is "return $this;" + unset($classMethod->stmts[1]); + $classMethod->returnType = new Identifier('void'); + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/rules/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector.php b/rules/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector.php new file mode 100644 index 00000000000..4b94cd32c40 --- /dev/null +++ b/rules/Unambiguous/Rector/Expression/FluentSettersToStandaloneCallMethodRector.php @@ -0,0 +1,179 @@ +setName('John') + ->setAge(30); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $someFluentClass = new SomeFluentClass(); + $someFluentClass->setName('John'); + $someFluentClass->setAge(30); + + return $someFluentClass; + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Expression::class, Return_::class]; + } + + /** + * @param Expression|Return_ $node + */ + public function refactor(Node $node): ?array + { + if (! $node->expr instanceof MethodCall) { + return null; + } + + $methodCalls = $this->fluentMethodCallsCollector->resolve($node->expr); + + // at least 2 method calls + if (count($methodCalls) < 1) { + return null; + } + + $lastMethodCall = end($methodCalls); + $rootExpr = $lastMethodCall->var; + + if (! $rootExpr instanceof New_) { + return null; + } + + if ($this->shouldSkipForVendorOrInternal($node->expr)) { + return null; + } + + $variableName = $this->resolveVariableName($rootExpr); + $someVariable = new Variable($variableName); + $firstAssign = new Assign($someVariable, $rootExpr); + + return $this->createStmts($firstAssign, $methodCalls, $someVariable, $node); + } + + private function resolveVariableName(Expr $expr): string + { + if (! $expr instanceof New_) { + return 'someVariable'; + } + + if ($expr->class instanceof Name) { + return $this->propertyNaming->fqnToVariableName($expr->class); + } + + return 'someVariable'; + } + + private function shouldSkipForVendorOrInternal(MethodCall $firstMethodCall): bool + { + $callerType = $this->getType($firstMethodCall); + if ($callerType instanceof ObjectType) { + $classReflection = $callerType->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + $fileName = $classReflection->getFileName(); + if ($fileName === null || str_contains($fileName, 'vendor')) { + return true; + } + } + + return false; + } + + /** + * @param MethodCall[] $methodCalls + * @return Stmt[] + */ + private function createStmts( + Assign $firstAssign, + array $methodCalls, + Variable $someVariable, + Expression|Return_ $firstStmt + ): array { + $stmts = [new Expression($firstAssign)]; + + // revert to normal order + $methodCalls = array_reverse($methodCalls); + + foreach ($methodCalls as $methodCall) { + $methodCall->var = $someVariable; + // inlines indent and removes () around first expr + $methodCall->setAttribute(AttributeKey::ORIGINAL_NODE, null); + $stmts[] = new Expression($methodCall); + } + + if ($firstStmt instanceof Return_) { + $firstStmt->expr = $someVariable; + $stmts[] = $firstStmt; + } + + $firstStmt->expr = $someVariable; + + return $stmts; + } +} diff --git a/rules/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector.php b/rules/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector.php index 2d5559a891e..f4e46f7b005 100644 --- a/rules/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector.php +++ b/rules/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector.php @@ -5,10 +5,12 @@ namespace Rector\Visibility\Rector\ClassConst; use PhpParser\Node; -use PhpParser\Node\Stmt\ClassConst; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\Visibility; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\Interface_; +use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\Visibility; use Rector\Visibility\ValueObject\ChangeConstantVisibility; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,20 +21,20 @@ */ final class ChangeConstantVisibilityRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const CLASS_CONSTANT_VISIBILITY_CHANGES = 'class_constant_visibility_changes'; - /** * @var ChangeConstantVisibility[] */ private array $classConstantVisibilityChanges = []; + public function __construct( + private readonly VisibilityManipulator $visibilityManipulator, + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Change visibility of constant from parent class.', + 'Change visibility of constant from parent class', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' @@ -46,7 +48,7 @@ class MyClass extends FrameworkClass public const SOME_CONSTANT = 1; } CODE_SAMPLE - , + , <<<'CODE_SAMPLE' class FrameworkClass { @@ -58,12 +60,8 @@ class MyClass extends FrameworkClass protected const SOME_CONSTANT = 1; } CODE_SAMPLE - , - [ - self::CLASS_CONSTANT_VISIBILITY_CHANGES => [ - new ChangeConstantVisibility('ParentObject', 'SOME_CONSTANT', Visibility::PROTECTED), - ], - ] + , + [new ChangeConstantVisibility('ParentObject', 'SOME_CONSTANT', Visibility::PROTECTED)] ), ] ); @@ -74,35 +72,48 @@ class MyClass extends FrameworkClass */ public function getNodeTypes(): array { - return [ClassConst::class]; + return [Class_::class, Interface_::class]; } /** - * @param ClassConst $node + * @param Class_|Interface_ $node */ public function refactor(Node $node): ?Node { + $hasChanged = false; + foreach ($this->classConstantVisibilityChanges as $classConstantVisibilityChange) { if (! $this->isObjectType($node, $classConstantVisibilityChange->getObjectType())) { continue; } - if (! $this->isName($node, $classConstantVisibilityChange->getConstant())) { - continue; - } + foreach ($node->getConstants() as $classConst) { + if (! $this->isName($classConst, $classConstantVisibilityChange->getConstant())) { + continue; + } - $this->visibilityManipulator->changeNodeVisibility($node, $classConstantVisibilityChange->getVisibility()); + $this->visibilityManipulator->changeNodeVisibility( + $classConst, + $classConstantVisibilityChange->getVisibility() + ); + $hasChanged = true; + } + } + if ($hasChanged) { return $node; } return null; } + /** + * @param mixed[] $configuration + */ public function configure(array $configuration): void { - $classConstantVisibilityChanges = $configuration[self::CLASS_CONSTANT_VISIBILITY_CHANGES] ?? []; - Assert::allIsInstanceOf($classConstantVisibilityChanges, ChangeConstantVisibility::class); - $this->classConstantVisibilityChanges = $classConstantVisibilityChanges; + Assert::allIsAOf($configuration, ChangeConstantVisibility::class); + + $this->classConstantVisibilityChanges = $configuration; } } diff --git a/rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php b/rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php index 69ab7adfed1..99bdc6c9183 100644 --- a/rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php +++ b/rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php @@ -6,10 +6,12 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\Contract\Rector\ConfigurableRectorInterface; -use Rector\Core\Rector\AbstractRector; -use Rector\Core\ValueObject\Visibility; +use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver; +use Rector\PHPStan\ScopeFetcher; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; +use Rector\Rector\AbstractRector; +use Rector\ValueObject\Visibility; use Rector\Visibility\ValueObject\ChangeMethodVisibility; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,25 +22,21 @@ */ final class ChangeMethodVisibilityRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @var string - */ - public const METHOD_VISIBILITIES = 'method_visibilities'; - /** * @var ChangeMethodVisibility[] */ private array $methodVisibilities = []; public function __construct( - private ParentClassScopeResolver $parentClassScopeResolver + private readonly ParentClassScopeResolver $parentClassScopeResolver, + private readonly VisibilityManipulator $visibilityManipulator, ) { } public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Change visibility of method from parent class.', + 'Change visibility of method from parent class', [ new ConfiguredCodeSample( <<<'CODE_SAMPLE' @@ -56,7 +54,7 @@ public function someMethod() } } CODE_SAMPLE - , + , <<<'CODE_SAMPLE' class FrameworkClass { @@ -73,11 +71,7 @@ protected function someMethod() } CODE_SAMPLE , - [ - self::METHOD_VISIBILITIES => [ - new ChangeMethodVisibility('FrameworkClass', 'someMethod', Visibility::PROTECTED), - ], - ] + [new ChangeMethodVisibility('FrameworkClass', 'someMethod', Visibility::PROTECTED)] ), ] ); @@ -96,7 +90,12 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $parentClassName = $this->parentClassScopeResolver->resolveParentClassName($node); + if ($this->methodVisibilities === []) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + $parentClassName = $this->parentClassScopeResolver->resolveParentClassName($scope); if ($parentClassName === null) { return null; } @@ -115,17 +114,16 @@ public function refactor(Node $node): ?Node return $node; } - return $node; + return null; } /** - * @param array $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void { - $methodVisibilities = $configuration[self::METHOD_VISIBILITIES] ?? []; - Assert::allIsInstanceOf($methodVisibilities, ChangeMethodVisibility::class); + Assert::allIsAOf($configuration, ChangeMethodVisibility::class); - $this->methodVisibilities = $methodVisibilities; + $this->methodVisibilities = $configuration; } } diff --git a/rules/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector.php b/rules/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector.php new file mode 100644 index 00000000000..ffe9008a3ba --- /dev/null +++ b/rules/Visibility/Rector/ClassMethod/ExplicitPublicClassMethodRector.php @@ -0,0 +1,67 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + return $this->visibilityManipulator->publicize($node); + } +} diff --git a/rules/Visibility/ValueObject/ChangeConstantVisibility.php b/rules/Visibility/ValueObject/ChangeConstantVisibility.php index 1f02e49ccf7..e503f509158 100644 --- a/rules/Visibility/ValueObject/ChangeConstantVisibility.php +++ b/rules/Visibility/ValueObject/ChangeConstantVisibility.php @@ -5,14 +5,16 @@ namespace Rector\Visibility\ValueObject; use PHPStan\Type\ObjectType; +use Rector\Validation\RectorAssert; -final class ChangeConstantVisibility +final readonly class ChangeConstantVisibility { public function __construct( private string $class, private string $constant, private int $visibility ) { + RectorAssert::className($class); } public function getObjectType(): ObjectType diff --git a/rules/Visibility/ValueObject/ChangeMethodVisibility.php b/rules/Visibility/ValueObject/ChangeMethodVisibility.php index 85f0ef17285..d7118af05b5 100644 --- a/rules/Visibility/ValueObject/ChangeMethodVisibility.php +++ b/rules/Visibility/ValueObject/ChangeMethodVisibility.php @@ -4,13 +4,16 @@ namespace Rector\Visibility\ValueObject; -final class ChangeMethodVisibility +use Rector\Validation\RectorAssert; + +final readonly class ChangeMethodVisibility { public function __construct( private string $class, private string $method, private int $visibility ) { + RectorAssert::className($class); } public function getClass(): string diff --git a/scoper.php b/scoper.php index bca204af0a2..e9af1de3cfe 100644 --- a/scoper.php +++ b/scoper.php @@ -2,90 +2,96 @@ declare(strict_types=1); +use Isolated\Symfony\Component\Finder\Finder; use Nette\Utils\DateTime; use Nette\Utils\Strings; -use Rector\Compiler\PhpScoper\StaticEasyPrefixer; -use Rector\Compiler\Unprefixer; -use Rector\Compiler\ValueObject\ScoperOption; -use Rector\Core\Application\VersionResolver; +use Rector\Application\VersionResolver; +use Rector\Utils\Compiler\Unprefixer; require_once __DIR__ . '/vendor/autoload.php'; -// [BEWARE] this path is relative to the root and location of this file -$filePathsToRemoveNamespace = [ - // @see https://github.com/rectorphp/rector/issues/2852#issuecomment-586315588 - 'vendor/symfony/deprecation-contracts/function.php', - // it would make polyfill function work only with namespace = brokes - 'vendor/symfony/polyfill-ctype/bootstrap.php', - 'vendor/symfony/polyfill-ctype/bootstrap80.php', - 'vendor/symfony/polyfill-intl-normalizer/bootstrap.php', - 'vendor/symfony/polyfill-intl-normalizer/bootstrap80.php', - 'vendor/symfony/polyfill-intl-grapheme/bootstrap.php', - 'vendor/symfony/polyfill-intl-grapheme/bootstrap80.php', - 'vendor/symfony/polyfill-mbstring/bootstrap.php', - 'vendor/symfony/polyfill-mbstring/bootstrap80.php', - 'vendor/symfony/polyfill-php80/bootstrap.php', - 'vendor/symfony/polyfill-php74/bootstrap.php', - 'vendor/symfony/polyfill-php73/bootstrap.php', - 'vendor/symfony/polyfill-php72/bootstrap.php', - 'vendor/symfony/polyfill-uuid/bootstrap.php', -]; + // remove phpstan, because it is already prefixed in its own scope $dateTime = DateTime::from('now'); -$timestamp = $dateTime->format('Ymd'); -/** - * @var array - */ -const UNPREFIX_CLASSES_BY_FILE = [ - // make UT=1 in tests work - 'packages/Testing/PHPUnit/AbstractRectorTestCase.php' => [ - 'PHPUnit\Framework\ExpectationFailedException', +$timestamp = $dateTime->format('Ym'); + +// @see https://github.com/humbug/php-scoper/blob/master/docs/further-reading.md +$polyfillFinder = Finder::create() + ->files() + ->in(__DIR__ . '/vendor/symfony/polyfill-*') + ->name('bootstrap*.php'); + +$excludedFiles = array_map( + static fn (SplFileInfo $fileInfo): string => $fileInfo->getPathname(), + iterator_to_array($polyfillFinder->getIterator()), +); + +// see https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#configuration +return [ + 'prefix' => 'RectorPrefix' . $timestamp, + + // exclude + 'exclude-classes' => [ + 'PHPUnit\Framework\Constraint\IsEqual', 'PHPUnit\Framework\TestCase', + 'PHPUnit\Runner\Version', + 'PHPUnit\Framework\ExpectationFailedException', ], - - // unprefixed ComposerJson as part of public API in ComposerRectorInterface - 'rules/Composer/Contract/Rector/ComposerRectorInterface.php' => [ - 'Symplify\ComposerJsonManipulator\ValueObject\ComposerJson', + 'exclude-namespaces' => [ + '#^Rector#', + '#^PhpParser#', + '#^PHPStan#', + '#^Symplify\\\\RuleDocGenerator#', + '#^Symfony\\\\Polyfill#', ], - 'packages/Testing/PHPUnit/AbstractTestCase.php' => ['PHPUnit\Framework\TestCase'], -]; -// see https://github.com/humbug/php-scoper -return [ - ScoperOption::PREFIX => 'RectorPrefix' . $timestamp, - ScoperOption::WHITELIST => StaticEasyPrefixer::getExcludedNamespacesAndClasses(), - ScoperOption::PATCHERS => [ - // [BEWARE] $filePath is absolute! - - // fixes https://github.com/rectorphp/rector-prefixed/runs/2143717534 - function (string $filePath, string $prefix, string $content) use ($filePathsToRemoveNamespace): string { - // @see https://regex101.com/r/0jaVB1/1 - $prefixedNamespacePattern = '#^namespace (.*?);$#m'; - - foreach ($filePathsToRemoveNamespace as $filePathToRemoveNamespace) { - if (\str_ends_with($filePath, $filePathToRemoveNamespace)) { - return Strings::replace($content, $prefixedNamespacePattern, ''); - } - } - return $content; - }, + 'exclude-files' => $excludedFiles, + + // expose + 'expose-classes' => ['Normalizer'], + 'expose-functions' => [ + 'u', + 'b', + 's', + 'trigger_deprecation', + 'dump_with_depth', + 'dn', + 'dump_node', + 'print_node', + ], + 'expose-constants' => ['__RECTOR_RUNNING__', '#^SYMFONY\_[\p{L}_]+$#'], + + 'patchers' => [ + // fix short import bug, @see https://github.com/rectorphp/rector-scoper-017/blob/23f3256a6f5a18483d6eb4659d69ba117501e2e3/vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php#L6 + static fn (string $filePath, string $prefix, string $content): string => str_replace( + sprintf('use %s\PhpParser;', $prefix), + 'use PhpParser;', + $content + ), - function (string $filePath, string $prefix, string $content): string { - foreach (UNPREFIX_CLASSES_BY_FILE as $endFilePath => $unprefixClasses) { - if (! \str_ends_with($filePath, $endFilePath)) { - continue; - } + static fn (string $filePath, string $prefix, string $content): string => + // comment out + str_replace('\\' . $prefix . '\trigger_deprecation(', '// \trigger_deprecation(', $content), - foreach ($unprefixClasses as $unprefixClass) { - $doubleQuotedClass = preg_quote('\\' . $unprefixClass); - $content = Strings::replace($content, '#' . $prefix . $doubleQuotedClass . '#', $unprefixClass); - } + // make external rules easier to write without enforcing getRuleDefinition() + // as they are not designed for open-sourcing + // remove implements is the safest way to avoid error on conflict with real dependency of symplify/rule-doc-generator + static function (string $filePath, string $prefix, string $content): string { + if (! \str_ends_with($filePath, 'src/Contract/Rector/RectorInterface.php')) { + return $content; } - return $content; - }, + // remove DocumentedRuleInterface implements + $content = str_replace( + 'interface RectorInterface extends NodeVisitor, DocumentedRuleInterface', + 'interface RectorInterface extends NodeVisitor', + $content + ); + // remove use import itself, to make contract clean + return str_replace('use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface;', '', $content); + }, - function (string $filePath, string $prefix, string $content): string { + static function (string $filePath, string $prefix, string $content): string { if (! \str_ends_with($filePath, 'src/Application/VersionResolver.php')) { return $content; } @@ -101,64 +107,8 @@ function (string $filePath, string $prefix, string $content): string { ); }, - // unprefixed SmartFileInfo - fn (string $filePath, string $prefix, string $content): string => Strings::replace( - $content, - '#' . $prefix . '\\\\Symplify\\\\SmartFileSystem\\\\SmartFileInfo#', - 'Symplify\SmartFileSystem\SmartFileInfo' - ), - - // unprefixed Statement - fn (string $filePath, string $prefix, string $content): string => Strings::replace( - $content, - '#' . $prefix . '\\\\Helmich\\\\TypoScriptParser\\\\Parser\\\\AST\\\\Statement#', - 'Helmich\TypoScriptParser\Parser\AST\Statement' - ), - - // unprefixed Traverser - fn (string $filePath, string $prefix, string $content): string => Strings::replace( - $content, - '#' . $prefix . '\\\\Helmich\\\\TypoScriptParser\\\\Parser\\\\Traverser\\\\Traverser#', - 'Helmich\TypoScriptParser\Parser\Traverser\Traverser' - ), - - // unprefixed PHPUnit IsEqual - fn (string $filePath, string $prefix, string $content): string => Strings::replace( - $content, - '#' . $prefix . '\\\\PHPUnit\\\\Framework\\\\Constraint\\\\IsEqual#', - 'PHPUnit\Framework\Constraint\IsEqual' - ), - - // unprefixed ContainerConfigurator - function (string $filePath, string $prefix, string $content): string { - // keep vendor prefixed the prefixed file loading; not part of public API - // except @see https://github.com/symfony/symfony/commit/460b46f7302ec7319b8334a43809523363bfef39#diff-1cd56b329433fc34d950d6eeab9600752aa84a76cbe0693d3fab57fed0f547d3R110 - if (str_contains($filePath, 'vendor/symfony') && ! str_ends_with( - $filePath, - 'vendor/symfony/dependency-injection/Loader/PhpFileLoader.php' - )) { - return $content; - } - - return Strings::replace( - $content, - '#' . $prefix . '\\\\Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\Configurator\\\\ContainerConfigurator#', - 'Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator' - ); - }, - - // get version for prefixed version - function (string $filePath, string $prefix, string $content): string { - if (! \str_ends_with($filePath, 'src/Configuration/Configuration.php')) { - return $content; - } - - // @see https://regex101.com/r/gLefQk/1 - return Strings::replace($content, '#\(\'rector\/rector-src\'\)#', "('rector/rector')"); - }, - // un-prefix composer plugin - function (string $filePath, string $prefix, string $content): string { + static function (string $filePath, string $prefix, string $content): string { if (! \str_ends_with($filePath, 'vendor/rector/extension-installer/src/Plugin.php')) { return $content; } @@ -167,46 +117,10 @@ function (string $filePath, string $prefix, string $content): string { return Strings::replace($content, '#' . $prefix . '\\\\Composer\\\\#', 'Composer\\'); }, - // fixes https://github.com/rectorphp/rector/issues/6007 - function (string $filePath, string $prefix, string $content): string { - if (! \str_contains($filePath, 'vendor/')) { - return $content; - } - - // @see https://regex101.com/r/lBV8IO/2 - $fqcnReservedPattern = sprintf('#(\\\\)?%s\\\\(parent|self|static)#m', $prefix); - $matches = Strings::matchAll($content, $fqcnReservedPattern); - - if ($matches === []) { - return $content; - } - - foreach ($matches as $match) { - $content = str_replace($match[0], $match[2], $content); - } - - return $content; - }, - - // fixes https://github.com/rectorphp/rector/issues/6010 + test case prefix - function (string $filePath, string $prefix, string $content): string { - // @see https://regex101.com/r/bA1nQa/1 - if (! Strings::match($filePath, '#vendor/symfony/polyfill-php\d{2}/Resources/stubs#')) { - return $content; - } - - // @see https://regex101.com/r/x5Ukrx/1 - $namespace = sprintf('#namespace %s;#m', $prefix); - return Strings::replace($content, $namespace); - }, - // unprefix string classes, as they're string on purpose - they have to be checked in original form, not prefixed - function (string $filePath, string $prefix, string $content): string { + static function (string $filePath, string $prefix, string $content): string { // skip vendor, expect rector packages - if (\str_contains($filePath, 'vendor/') && ! \str_contains($filePath, 'vendor/rector') && ! \str_contains( - $filePath, - 'vendor/ssch/typo3-rector' - )) { + if (\str_contains($filePath, 'vendor/') && ! \str_contains($filePath, 'vendor/rector')) { return $content; } @@ -218,24 +132,13 @@ function (string $filePath, string $prefix, string $content): string { return Unprefixer::unprefixQuoted($content, $prefix); }, - // scoper missed PSR-4 autodiscovery in Symfony - function (string $filePath, string $prefix, string $content): string { - // scoper missed PSR-4 autodiscovery in Symfony - if (! \str_ends_with($filePath, 'config.php') && ! \str_ends_with($filePath, 'services.php')) { - return $content; - } - - // skip "Rector\\" namespace - if (\str_contains($content, '$services->load(\'Rector')) { - return $content; - } - - // skip "Ssch\\" namespace - if (\str_contains($content, '$services->load(\'Ssch')) { + // unprefix regex content on doctrine inflector + static function (string $filePath, string $prefix, string $content): string { + if (! \str_contains($filePath, 'vendor/doctrine/inflector')) { return $content; } - return Strings::replace($content, '#services\->load\(\'#', "services->load('" . $prefix . '\\'); + return str_replace("'" . $prefix . '\\', "'\\", $content); }, ], ]; diff --git a/scripts/add-phpstan-self-replace.php b/scripts/add-phpstan-self-replace.php new file mode 100644 index 00000000000..b6770821156 --- /dev/null +++ b/scripts/add-phpstan-self-replace.php @@ -0,0 +1,24 @@ +resolvePackageVersion('phpstan/phpstan'); + +$composerJson = Json::decode($composerJsonFileContents, forceArrays: true); +$composerJson['replace']['phpstan/phpstan'] = $phpstanVersion; + +$modifiedComposerJsonFileContents = Json::encode($composerJson, pretty: true); +FileSystem::write(__DIR__ . '/../composer.json', $modifiedComposerJsonFileContents, null); + +echo 'Done!' . PHP_EOL; diff --git a/scripts/avoid-short-node-names-in-fixtures.php b/scripts/avoid-short-node-names-in-fixtures.php new file mode 100644 index 00000000000..3c488da66b4 --- /dev/null +++ b/scripts/avoid-short-node-names-in-fixtures.php @@ -0,0 +1,68 @@ +files() + ->name('*.php') + ->in(__DIR__ . '/../vendor/nikic/php-parser/lib/PhpParser/Node'); + +/** @var SplFileInfo[] $nodeFileInfos */ +$nodeFileInfos = iterator_to_array($nodeFileFinder->getIterator()); + +$shortNodeClassNames = []; +foreach ($nodeFileInfos as $nodeFileInfo) { + $shortNodeClassNames[] = $nodeFileInfo->getBasename('.php'); +} + +$symfonyStyle = new SymfonyStyle(new ArrayInput([]), new ConsoleOutput()); + +$hasErrors = false; + +foreach ($fixtureFiles as $fixtureFile) { + $shortClassNameMatch = Strings::match( + $fixtureFile->getContents(), + '/\b(?:class|interface)\s+(?[A-Z]\w*)/' + ); + if ($shortClassNameMatch === null) { + continue; + } + + $fixtureClassName = $shortClassNameMatch['name']; + if (! in_array($fixtureClassName, $shortNodeClassNames, true)) { + continue; + } + + $symfonyStyle->writeln(sprintf( + 'Fixture class name "%s" conflicts with native short node name.%sChange it to different to avoid IDE incorrect autocomplete:%s%s%s', + $fixtureClassName, + PHP_EOL, + PHP_EOL, + $fixtureFile->getRealPath(), + PHP_EOL + )); + + $hasErrors = true; +} + +if ($hasErrors) { + exit(1); +} + +exit(0); diff --git a/scripts/check-before-after-same-fixtures.php b/scripts/check-before-after-same-fixtures.php new file mode 100644 index 00000000000..30118cb4045 --- /dev/null +++ b/scripts/check-before-after-same-fixtures.php @@ -0,0 +1,67 @@ +symfonyStyle = new SymfonyStyle(new ArgvInput(), new ConsoleOutput()); + } + + /** + * @param string[] $testDirectories + * @return Command::SUCCESS|Command::FAILURE + */ + public function run(array $testDirectories): int + { + $fixtureFiles = FixtureFinder::find($testDirectories); + + $invalidFixturePaths = []; + foreach ($fixtureFiles as $fixtureFile) { + if (! $this->hasFileSameBeforeAndAfterPart($fixtureFile)) { + continue; + } + + $invalidFixturePaths[] = substr($fixtureFile->getRealPath(), strlen(getcwd()) + 1); + } + + if ($invalidFixturePaths === []) { + $this->symfonyStyle->success(sprintf('All %d fixtures are valid', count($fixtureFiles))); + return Command::SUCCESS; + } + + $this->symfonyStyle->error( + 'The following fixtures have the same before and after content. Remove the part after "-----" to fix them' + ); + $this->symfonyStyle->listing($invalidFixturePaths); + + return Command::FAILURE; + } + + private function hasFileSameBeforeAndAfterPart(SplFileInfo $fixtureFile): bool + { + $parts = Strings::split($fixtureFile->getContents(), '#^\s*-----\s*$#m'); + if (count($parts) !== 2) { + return false; + } + + return trim($parts[0]) === trim($parts[1]); + } +} + +$sameBeforeAfterFixtureDetector = new SameBeforeAfterFixtureDetector(); +exit($sameBeforeAfterFixtureDetector->run([__DIR__ . '/../tests', __DIR__ . '/../rules-tests'])); diff --git a/scripts/list-unused-rules.php b/scripts/list-unused-rules.php new file mode 100644 index 00000000000..179a2c9edb4 --- /dev/null +++ b/scripts/list-unused-rules.php @@ -0,0 +1,76 @@ +find([ + __DIR__ . '/../rules', + __DIR__ . '/../vendor/rector/rector-doctrine', + __DIR__ . '/../vendor/rector/rector-phpunit', + __DIR__ . '/../vendor/rector/rector-symfony', + __DIR__ . '/../vendor/rector/rector-downgrade-php', +]); + +$symfonyStyle = new SymfonyStyle(new ArrayInput([]), new ConsoleOutput()); +$symfonyStyle->writeln(sprintf('Found Rector %d rules', count($rectorClasses))); + +$rectorSeFinder = new RectorSetFilesFinder(); + +$rectorSetFiles = $rectorSeFinder->find([ + __DIR__ . '/../config/set', + __DIR__ . '/../vendor/rector/rector-symfony/config/sets', + __DIR__ . '/../vendor/rector/rector-doctrine/config/sets', + __DIR__ . '/../vendor/rector/rector-phpunit/config/sets', + __DIR__ . '/../vendor/rector/rector-downgrade-php/config/set', +]); + +$symfonyStyle->writeln(sprintf('Found %d sets', count($rectorSetFiles))); + +$usedRectorClassResolver = new UsedRectorClassResolver(); +$usedRectorRules = $usedRectorClassResolver->resolve($rectorSetFiles); + +$symfonyStyle->newLine(); +$symfonyStyle->writeln(sprintf('Found %d used Rector rules in sets', count($usedRectorRules))); + +$unusedRectorRules = array_diff($rectorClasses, $usedRectorRules); + +$symfonyStyle->newLine(); +$symfonyStyle->listing($unusedRectorRules); + +$symfonyStyle->writeln( + sprintf('Found %d Rector rules not in any set', count($unusedRectorRules)) +); +$symfonyStyle->newLine(); + +final class UsedRectorClassResolver +{ + /** + * @param string[] $rectorSetFiles + * @return string[] + */ + public function resolve(array $rectorSetFiles): array + { + $setRectorsResolver = new SetRectorsResolver(); + $rulesConfiguration = $setRectorsResolver->resolveFromFilePathsIncludingConfiguration($rectorSetFiles); + + $usedRectorRules = []; + foreach ($rulesConfiguration as $ruleConfiguration) { + $usedRectorRules[] = is_string($ruleConfiguration) ? $ruleConfiguration : array_keys($ruleConfiguration)[0]; + } + + sort($usedRectorRules); + + return array_unique($usedRectorRules); + } +} diff --git a/scripts/no-php-file-in-fixtures.php b/scripts/no-php-file-in-fixtures.php new file mode 100644 index 00000000000..d363b557ff7 --- /dev/null +++ b/scripts/no-php-file-in-fixtures.php @@ -0,0 +1,109 @@ +symfonyStyle = new SymfonyStyle(new ArgvInput(), new ConsoleOutput()); + } + + /** + * @param string[] $testDirectories + * @return Command::SUCCESS|Command::FAILURE + */ + public function run(array $testDirectories): int + { + $phpFiles = $this->findPhpFiles($testDirectories); + + $allFixtureFiles = $this->findFixtureFiles($testDirectories); + + $relativePhpFiles = []; + foreach ($phpFiles as $phpFile) { + $relativeFilePath = substr($phpFile->getRealPath(), strlen(getcwd()) + 1); + + // should skip? + if (in_array($relativeFilePath, self::EXCLUDED_FILES, true)) { + continue; + } + + $relativePhpFiles[] = $relativeFilePath; + } + + if ($relativePhpFiles === []) { + $this->symfonyStyle->success(sprintf('All %d fixtures are valid', count($allFixtureFiles))); + return Command::SUCCESS; + } + + $this->symfonyStyle->error( + 'The following "*.php* files were found in /Fixtures directory, but only "*.php.inc" files are picked up and allowed. Rename their suffix or remove them' + ); + $this->symfonyStyle->listing($relativePhpFiles); + + return Command::FAILURE; + } + + /** + * @param string[] $directories + * @return SplFileInfo[] + */ + private function findPhpFiles(array $directories): array + { + Assert::allDirectory($directories); + + $finder = (new Finder()) + ->files() + ->in($directories) + ->path('/Fixture') + ->path('/Fixture*') + ->notPath('Source') + ->name('*.php') + ->sortByName(); + + return iterator_to_array($finder->getIterator()); + } + + /** + * @param string[] $directories + * @return SplFileInfo[] + */ + private function findFixtureFiles(array $directories): array + { + Assert::allDirectory($directories); + + $finder = (new Finder()) + ->files() + ->in($directories) + ->path('Fixture') + ->path('Fixture*') + ->notPath('Source') + ->sortByName(); + + return iterator_to_array($finder->getIterator()); + } +} + +$noPhpFileInFixturesDetector = new NoPhpFileInFixturesDetector(); + +exit($noPhpFileInFixturesDetector->run([__DIR__ . '/../rules-tests'])); diff --git a/scripts/src/Finder/FixtureFinder.php b/scripts/src/Finder/FixtureFinder.php new file mode 100644 index 00000000000..e21a611408e --- /dev/null +++ b/scripts/src/Finder/FixtureFinder.php @@ -0,0 +1,29 @@ +files() + ->in($directories) + ->name('*.php.inc') + ->sortByName(); + + return iterator_to_array($finder->getIterator()); + } +} diff --git a/scripts/src/Finder/RectorClassFinder.php b/scripts/src/Finder/RectorClassFinder.php new file mode 100644 index 00000000000..dbc1b39c57a --- /dev/null +++ b/scripts/src/Finder/RectorClassFinder.php @@ -0,0 +1,47 @@ +acceptFiles = ['*Rector.php']; + $robotLoader->addDirectory(...$dirs); + + $robotLoader->setTempDirectory(sys_get_temp_dir() . '/rector-rules'); + $robotLoader->refresh(); + + /** @var array $rectorClasses */ + $rectorClasses = array_keys($robotLoader->getIndexedClasses()); + + $usableRectorClasses = []; + + // remove deprecated and abstract classes + foreach ($rectorClasses as $rectorClass) { + $rectorClassReflection = new ReflectionClass($rectorClass); + if ($rectorClassReflection->isAbstract()) { + continue; + } + + if ($rectorClassReflection->implementsInterface(DeprecatedInterface::class)) { + continue; + } + + $usableRectorClasses[] = $rectorClass; + } + + return $usableRectorClasses; + } +} diff --git a/scripts/src/Finder/RectorSetFilesFinder.php b/scripts/src/Finder/RectorSetFilesFinder.php new file mode 100644 index 00000000000..de972608545 --- /dev/null +++ b/scripts/src/Finder/RectorSetFilesFinder.php @@ -0,0 +1,37 @@ +in($configDirs) + ->files() + ->name('*.php'); + + /** @var SplFileInfo[] $setFileInfos */ + $setFileInfos = iterator_to_array($finder->getIterator()); + + $setFiles = []; + foreach ($setFileInfos as $setFileInfo) { + $setFiles[] = $setFileInfo->getRealPath(); + } + + return $setFiles; + } +} diff --git a/scripts/test-fixture-stats.php b/scripts/test-fixture-stats.php new file mode 100644 index 00000000000..aad49cd7079 --- /dev/null +++ b/scripts/test-fixture-stats.php @@ -0,0 +1,43 @@ +in(__DIR__ . '/../rules-tests') + ->directories() + ->name('#Rector$#') + ->getIterator(); + +$ruleToFixtureCount = []; + +foreach ($finder as $rectorTestDirectory) { + if ($rectorTestDirectory->getBasename() === 'Rector') { + continue; + } + + $fixtureCount = Finder::create() + ->files() + ->name('*.php.inc') + ->in($rectorTestDirectory->getPathname()) + ->count(); + + // very few fixture files, not relevant + if ($fixtureCount <= 15) { + continue; + } + + $ruleToFixtureCount[$rectorTestDirectory->getBasename()] = $fixtureCount; +} + +asort($ruleToFixtureCount); + +foreach ($ruleToFixtureCount as $rule => $fixtureCount) { + echo ' * ' . $rule . ': '; + echo $fixtureCount . PHP_EOL; +} diff --git a/scripts/unique-rector-short-class-name.php b/scripts/unique-rector-short-class-name.php new file mode 100644 index 00000000000..ee0001601e5 --- /dev/null +++ b/scripts/unique-rector-short-class-name.php @@ -0,0 +1,69 @@ +find([ + __DIR__ . '/../rules', + __DIR__ . '/../vendor/rector/rector-doctrine', + __DIR__ . '/../vendor/rector/rector-phpunit', + __DIR__ . '/../vendor/rector/rector-symfony', + __DIR__ . '/../vendor/rector/rector-downgrade-php', +]); + +/** + * @param string[] $classNames + * @return string[] + */ +function getShortClassNames(array $classNames): array +{ + $shortClassNames = []; + foreach ($classNames as $className) { + $shortClassNames[] = substr($className, strrpos($className, '\\') + 1); + } + + return $shortClassNames; +} + +/** + * @param string[] $shortClassNames + * @return string[] + */ +function filterDuplicatedValues(array $shortClassNames): array +{ + $classNamesToCounts = array_count_values($shortClassNames); + $duplicatedShortClassNames = []; + + foreach ($classNamesToCounts as $className => $count) { + if ($count === 1) { + // unique, skip + continue; + } + + $duplicatedShortClassNames[] = $className; + } + + return $duplicatedShortClassNames; +} + +$shortClassNames = getShortClassNames($rectorClassNames); +$duplicatedShortClassNames = filterDuplicatedValues($shortClassNames); + +if ($duplicatedShortClassNames === []) { + echo "All Rector class names are unique!\n"; + exit(ExitCode::SUCCESS); +} + +echo "The following Rector class names are duplicated:\n"; +foreach ($duplicatedShortClassNames as $duplicatedShortClassName) { + echo sprintf("- %s\n", $duplicatedShortClassName); +} + +exit(ExitCode::FAILURE); diff --git a/scripts/validate-phpstan-version.php b/scripts/validate-phpstan-version.php new file mode 100644 index 00000000000..577dac124c2 --- /dev/null +++ b/scripts/validate-phpstan-version.php @@ -0,0 +1,45 @@ +resolve(__DIR__ . '/../composer.json', 'phpstan/phpstan'); +$downgradedPHPStanVersion = $packageVersionResolver->resolve( + __DIR__ . '/../build/target-repository/composer.json', + 'phpstan/phpstan' +); + +if ($localPHPStanVersion === $downgradedPHPStanVersion) { + echo '[OK] PHPStan version in local and downgraded composer.json are equal, good job!' . PHP_EOL; + exit(Command::SUCCESS); +} + +echo sprintf( + '[ERROR] PHPStan version in local composer.json is "%s", in downgraded "%s".%sMake sure they are equal first.', + $localPHPStanVersion, + $downgradedPHPStanVersion, + PHP_EOL +) . PHP_EOL; +exit(Command::FAILURE); + +final class PackageVersionResolver +{ + public function resolve(string $composerFilePath, string $packageName): string + { + $composerJson = JsonFileSystem::readFilePath($composerFilePath); + $packageVersion = $composerJson['require'][$packageName] ?? null; + + if ($packageVersion === null) { + throw new ShouldNotHappenException(); + } + + return $packageVersion; + } +} diff --git a/src/Application/ApplicationFileProcessor.php b/src/Application/ApplicationFileProcessor.php index 65a1f6549fa..932593d032f 100644 --- a/src/Application/ApplicationFileProcessor.php +++ b/src/Application/ApplicationFileProcessor.php @@ -2,95 +2,269 @@ declare(strict_types=1); -namespace Rector\Core\Application; - -use Rector\Core\Application\FileDecorator\FileDiffFileDecorator; -use Rector\Core\Application\FileSystem\RemovedAndAddedFilesProcessor; -use Rector\Core\Contract\Processor\FileProcessorInterface; -use Rector\Core\ValueObject\Application\File; -use Rector\Core\ValueObject\Configuration; -use Rector\FileFormatter\FileFormatter; +namespace Rector\Application; + +use Nette\Utils\FileSystem as UtilsFileSystem; +use PHPStan\Parser\ParserErrorsException; +use Rector\Application\Provider\CurrentFileProvider; +use Rector\Caching\Detector\ChangedFilesDetector; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\FileSystem\FilesFinder; +use Rector\Parallel\Application\ParallelFileProcessor; +use Rector\PhpParser\Parser\ParserErrors; +use Rector\Reporting\MissConfigurationReporter; +use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment; +use Rector\Util\ArrayParametersMerger; +use Rector\ValueObject\Application\File; +use Rector\ValueObject\Configuration; +use Rector\ValueObject\Error\SystemError; +use Rector\ValueObject\FileProcessResult; +use Rector\ValueObject\ProcessResult; +use Rector\ValueObject\Reporting\FileDiff; +use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symplify\SmartFileSystem\SmartFileSystem; +use Symplify\EasyParallel\CpuCoreCountProvider; +use Symplify\EasyParallel\Exception\ParallelShouldNotHappenException; +use Symplify\EasyParallel\ScheduleFactory; +use Throwable; final class ApplicationFileProcessor { + private const string ARGV = 'argv'; + /** - * @param FileProcessorInterface[] $fileProcessors + * @var SystemError[] */ + private array $systemErrors = []; + public function __construct( - private SmartFileSystem $smartFileSystem, - private FileDiffFileDecorator $fileDiffFileDecorator, - private FileFormatter $fileFormatter, - private RemovedAndAddedFilesProcessor $removedAndAddedFilesProcessor, - private SymfonyStyle $symfonyStyle, - private array $fileProcessors = [] + private readonly SymfonyStyle $symfonyStyle, + private readonly FilesFinder $filesFinder, + private readonly ParallelFileProcessor $parallelFileProcessor, + private readonly ScheduleFactory $scheduleFactory, + private readonly CpuCoreCountProvider $cpuCoreCountProvider, + private readonly ChangedFilesDetector $changedFilesDetector, + private readonly CurrentFileProvider $currentFileProvider, + private readonly FileProcessor $fileProcessor, + private readonly ArrayParametersMerger $arrayParametersMerger, + private readonly MissConfigurationReporter $missConfigurationReporter, ) { } - /** - * @param File[] $files - */ - public function run(array $files, Configuration $configuration): void + public function run(Configuration $configuration, InputInterface $input): ProcessResult { - $this->processFiles($files, $configuration); - $this->fileFormatter->format($files); + $filePaths = $this->filesFinder->findFilesInPaths($configuration->getPaths(), $configuration); - $this->fileDiffFileDecorator->decorate($files); - $this->printFiles($files, $configuration); - } + // no files found + if ($filePaths === []) { + return new ProcessResult([], [], 0); + } - /** - * @param File[] $files - */ - private function processFiles(array $files, Configuration $configuration): void - { + $this->missConfigurationReporter->reportVendorInPaths($filePaths); + $this->missConfigurationReporter->reportStartWithShortOpenTag(); + + $this->configureCustomErrorHandler(); + + /** + * Mimic @see https://github.com/phpstan/phpstan-src/blob/ab154e1da54d42fec751e17a1199b3e07591e85e/src/Command/AnalyseApplication.php#L188C23-L244 + */ if ($configuration->shouldShowProgressBar()) { - $fileCount = count($files); + $fileCount = count($filePaths); $this->symfonyStyle->progressStart($fileCount); + $this->symfonyStyle->progressAdvance(0); + + $postFileCallback = function (int $stepCount): void { + $this->symfonyStyle->progressAdvance($stepCount); + // running in parallel here → nothing else to do + }; + } else { + $postFileCallback = static function (int $stepCount): void { + }; } - foreach ($files as $file) { - foreach ($this->fileProcessors as $fileProcessor) { - if (! $fileProcessor->supports($file, $configuration)) { - continue; - } + if ($configuration->isDebug()) { + $preFileCallback = function (string $filePath): void { + $this->symfonyStyle->writeln('[file] ' . $filePath); + }; + } else { + $preFileCallback = null; + } + + if ($configuration->isParallel()) { + $processResult = $this->runParallel($filePaths, $input, $postFileCallback); + } else { + $processResult = $this->processFiles($filePaths, $configuration, $preFileCallback, $postFileCallback); + } + + $processResult->addSystemErrors($this->systemErrors); - $fileProcessor->process($file, $configuration); + $this->restoreErrorHandler(); + + return $processResult; + } + + /** + * @param string[] $filePaths + * @param callable(string $file): void|null $preFileCallback + * @param callable(int $fileCount): void|null $postFileCallback + */ + public function processFiles( + array $filePaths, + Configuration $configuration, + ?callable $preFileCallback = null, + ?callable $postFileCallback = null + ): ProcessResult { + /** @var SystemError[] $systemErrors */ + $systemErrors = []; + + /** @var FileDiff[] $fileDiffs */ + $fileDiffs = []; + + $totalChanged = 0; + foreach ($filePaths as $filePath) { + if ($preFileCallback !== null) { + $preFileCallback($filePath); } - // progress bar +1 - if ($configuration->shouldShowProgressBar()) { - $this->symfonyStyle->progressAdvance(); + $file = new File($filePath, UtilsFileSystem::read($filePath)); + + try { + $fileProcessResult = $this->processFile($file, $configuration); + + $systemErrors = $this->arrayParametersMerger->merge( + $systemErrors, + $fileProcessResult->getSystemErrors() + ); + + $currentFileDiff = $fileProcessResult->getFileDiff(); + if ($currentFileDiff instanceof FileDiff) { + $fileDiffs[] = $currentFileDiff; + } + + // progress bar on parallel handled on runParallel() + if (is_callable($postFileCallback)) { + $postFileCallback(1); + } + + if ($fileProcessResult->hasChanged()) { + ++$totalChanged; + } + } catch (Throwable $throwable) { + $this->changedFilesDetector->invalidateFile($filePath); + + if (StaticPHPUnitEnvironment::isPHPUnitRun()) { + throw $throwable; + } + + $systemErrors[] = $this->resolveSystemError($throwable, $filePath); } } - $this->removedAndAddedFilesProcessor->run($configuration); + return new ProcessResult($systemErrors, $fileDiffs, $totalChanged); + } + + private function processFile(File $file, Configuration $configuration): FileProcessResult + { + $this->currentFileProvider->setFile($file); + + $fileProcessResult = $this->fileProcessor->processFile($file, $configuration); + + if ($fileProcessResult->getSystemErrors() !== []) { + $this->changedFilesDetector->invalidateFile($file->getFilePath()); + } elseif (! $configuration->isDryRun() || ! $fileProcessResult->getFileDiff() instanceof FileDiff) { + $this->changedFilesDetector->cacheFile($file->getFilePath()); + } + + return $fileProcessResult; + } + + private function resolveSystemError(Throwable $throwable, string $filePath): SystemError + { + $errorMessage = sprintf('System error: "%s"', $throwable->getMessage()) . PHP_EOL; + + if ($this->symfonyStyle->isDebug()) { + $errorMessage .= PHP_EOL . 'Stack trace:' . PHP_EOL . $throwable->getTraceAsString(); + } else { + $errorMessage .= 'Run Rector with "--debug" option and post the report here: https://github.com/rectorphp/rector/issues/new'; + } + + if ($throwable instanceof ParserErrorsException) { + $throwable = new ParserErrors($throwable); + } + + return new SystemError($errorMessage, $filePath, $throwable->getLine()); } /** - * @param File[] $files + * Inspired by @see https://github.com/phpstan/phpstan-src/blob/89af4e7db257750cdee5d4259ad312941b6b25e8/src/Analyser/Analyser.php#L134 */ - private function printFiles(array $files, Configuration $configuration): void + private function configureCustomErrorHandler(): void { - if ($configuration->isDryRun()) { - return; - } + $errorHandlerCallback = function (int $code, string $message, string $file, int $line): bool { + if ((error_reporting() & $code) === 0) { + // silence @ operator + return true; + } - foreach ($files as $file) { - if (! $file->hasChanged()) { - continue; + // not relevant for us + if (in_array($code, [E_DEPRECATED, E_WARNING], true)) { + return true; } - $this->printFile($file); + $this->systemErrors[] = new SystemError($message, $file, $line); + + return true; + }; + + set_error_handler($errorHandlerCallback); + } + + private function restoreErrorHandler(): void + { + restore_error_handler(); + } + + /** + * @param string[] $filePaths + * @param callable(int $stepCount): void $postFileCallback + */ + private function runParallel( + array $filePaths, + InputInterface $input, + callable $postFileCallback, + ): ProcessResult { + $schedule = $this->scheduleFactory->create( + $this->cpuCoreCountProvider->provide(), + SimpleParameterProvider::provideIntParameter(Option::PARALLEL_JOB_SIZE), + SimpleParameterProvider::provideIntParameter(Option::PARALLEL_MAX_NUMBER_OF_PROCESSES), + $filePaths + ); + + $mainScript = $this->resolveCalledRectorBinary(); + if ($mainScript === null) { + throw new ParallelShouldNotHappenException('[parallel] Main script was not found'); } + + // mimics see https://github.com/phpstan/phpstan-src/commit/9124c66dcc55a222e21b1717ba5f60771f7dda92#diff-387b8f04e0db7a06678eb52ce0c0d0aff73e0d7d8fc5df834d0a5fbec198e5daR139 + return $this->parallelFileProcessor->process($schedule, $mainScript, $postFileCallback, $input); } - private function printFile(File $file): void + /** + * Path to called "rector" binary file, e.g. "vendor/bin/rector" returns "vendor/bin/rector" This is needed to re-call the + * rector binary in sub-process in the same location. + */ + private function resolveCalledRectorBinary(): ?string { - $smartFileInfo = $file->getSmartFileInfo(); + if (! isset($_SERVER[self::ARGV][0])) { + return null; + } + + $potentialRectorBinaryPath = $_SERVER[self::ARGV][0]; + if (! file_exists($potentialRectorBinaryPath)) { + return null; + } - $this->smartFileSystem->dumpFile($smartFileInfo->getPathname(), $file->getFileContent()); - $this->smartFileSystem->chmod($smartFileInfo->getRealPath(), $smartFileInfo->getPerms()); + return $potentialRectorBinaryPath; } } diff --git a/src/Application/ChangedNodeScopeRefresher.php b/src/Application/ChangedNodeScopeRefresher.php new file mode 100644 index 00000000000..2fff52d0abb --- /dev/null +++ b/src/Application/ChangedNodeScopeRefresher.php @@ -0,0 +1,171 @@ +scopeAnalyzer->isRefreshable($node)) { + return; + } + + if (! $mutatingScope instanceof MutatingScope) { + $errorMessage = sprintf('Node "%s" with is missing scope required for scope refresh', $node::class); + + throw new ShouldNotHappenException($errorMessage); + } + + /** + * The reindex is needed to: + * - be used by PHPStan processNodes() that relies on indexed arrays start from 0 + * - use traverser to avoid issues when multiples rules apply, and higher node remove deep node, + * which the next rule use deep node, for example: + * - first rule: - Class_ → ClassMethod → remove stmt with index 0 + * - second rule: - ClassMethod → here fetch the index 0 that no longer exists + */ + SimpleCallableNodeTraverser::traverse( + $node, + fn (Node $subNode): ?Node => NodeAttributeReIndexer::reIndexNodeAttributes($subNode) + ); + + $stmts = $this->resolveStmts($node); + $this->phpStanNodeScopeResolver->processNodes($stmts, $filePath, $mutatingScope); + } + + /** + * @return Stmt[] + */ + private function resolveStmts(Node $node): array + { + if ($node instanceof Stmt) { + return [$node]; + } + + if ($node instanceof Expr) { + return [new Expression($node)]; + } + + // moved from Expr/Stmt to directly under Node on PHPParser 5 + if ($node instanceof ArrayItem) { + return [new Expression(new Array_([$node]))]; + } + + if ($node instanceof ClosureUse) { + $closure = new Closure(); + $closure->uses[] = $node; + return [new Expression($closure)]; + } + + if ($node instanceof DeclareItem) { + return [new Declare_([$node])]; + } + + if ($node instanceof PropertyItem) { + return [new Property(Modifiers::PUBLIC, [$node])]; + } + + if ($node instanceof StaticVar) { + return [new Static_([$node])]; + } + + if ($node instanceof UseItem) { + return [new Use_([$node])]; + } + + if ($node instanceof Param) { + $closure = new Closure(); + $closure->params[] = $node; + return [new Expression($closure)]; + } + + if ($node instanceof AttributeGroup) { + $class = new Class_(null); + $class->attrGroups[] = $node; + + $this->setLineAttributesOnClass($class, $node); + + return [$class]; + } + + if ($node instanceof Attribute) { + $class = new Class_(null); + $class->attrGroups[] = new AttributeGroup([$node]); + + $this->setLineAttributesOnClass($class, $node); + + return [$class]; + } + + if ($node instanceof Arg) { + $class = new Class_(null, [], [ + 'startLine' => $node->getStartLine(), + 'endLine' => $node->getEndLine(), + ]); + $new = new New_($class, [$node]); + + return [new Expression($new)]; + } + + $errorMessage = sprintf('Complete parent node of "%s" be a stmt.', $node::class); + throw new ShouldNotHappenException($errorMessage); + } + + private function setLineAttributesOnClass(Class_ $class, Attribute|AttributeGroup $node): void + { + $this->simpleCallableNodeTraverser->traverseNodesWithCallable([$class], function (Node $subNode) use ( + $node + ): Node { + if ($subNode->getStartLine() >= 0 && $subNode->getEndLine() >= 0) { + return $subNode; + } + + $subNode->setAttribute('startLine', $node->getStartLine()); + $subNode->setAttribute('endLine', $node->getEndLine()); + + return $subNode; + }); + } +} diff --git a/src/Application/FileDecorator/FileDiffFileDecorator.php b/src/Application/FileDecorator/FileDiffFileDecorator.php deleted file mode 100644 index e3c1094bbfe..00000000000 --- a/src/Application/FileDecorator/FileDiffFileDecorator.php +++ /dev/null @@ -1,37 +0,0 @@ -hasChanged()) { - continue; - } - - $fileDiff = $this->fileDiffFactory->createFileDiff( - $file, - $file->getOriginalFileContent(), - $file->getFileContent() - ); - - $file->setFileDiff($fileDiff); - } - } -} diff --git a/src/Application/FileProcessor.php b/src/Application/FileProcessor.php index 6ce848a4303..fa9e863e4d4 100644 --- a/src/Application/FileProcessor.php +++ b/src/Application/FileProcessor.php @@ -2,46 +2,181 @@ declare(strict_types=1); -namespace Rector\Core\Application; +namespace Rector\Application; -use PhpParser\Lexer; -use Rector\ChangesReporting\Collector\AffectedFilesCollector; -use Rector\Core\PhpParser\NodeTraverser\RectorNodeTraverser; -use Rector\Core\PhpParser\Parser\Parser; -use Rector\Core\ValueObject\Application\File; +use Nette\Utils\FileSystem; +use PHPStan\AnalysedCodeException; +use PHPStan\Parser\ParserErrorsException; +use Rector\Caching\Detector\ChangedFilesDetector; +use Rector\ChangesReporting\ValueObjectFactory\ErrorFactory; +use Rector\ChangesReporting\ValueObjectFactory\FileDiffFactory; +use Rector\Exception\ShouldNotHappenException; +use Rector\FileSystem\FilePathHelper; use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator; +use Rector\PhpParser\Node\FileNode; +use Rector\PhpParser\NodeTraverser\RectorNodeTraverser; +use Rector\PhpParser\Parser\ParserErrors; +use Rector\PhpParser\Parser\RectorParser; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\PostRector\Application\PostFileProcessor; +use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment; +use Rector\ValueObject\Application\File; +use Rector\ValueObject\Configuration; +use Rector\ValueObject\Error\SystemError; +use Rector\ValueObject\FileProcessResult; +use Symfony\Component\Console\Style\SymfonyStyle; +use Throwable; -final class FileProcessor +final readonly class FileProcessor { public function __construct( - private AffectedFilesCollector $affectedFilesCollector, - private Lexer $lexer, + private BetterStandardPrinter $betterStandardPrinter, + private RectorNodeTraverser $rectorNodeTraverser, + private SymfonyStyle $symfonyStyle, + private FileDiffFactory $fileDiffFactory, + private ChangedFilesDetector $changedFilesDetector, + private ErrorFactory $errorFactory, + private FilePathHelper $filePathHelper, + private PostFileProcessor $postFileProcessor, + private RectorParser $rectorParser, private NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator, - private Parser $parser, - private RectorNodeTraverser $rectorNodeTraverser ) { } - public function parseFileInfoToLocalCache(File $file): void + public function processFile(File $file, Configuration $configuration): FileProcessResult { - // store tokens by absolute path, so we don't have to print them right now - $smartFileInfo = $file->getSmartFileInfo(); - $oldStmts = $this->parser->parseFileInfo($smartFileInfo); - $oldTokens = $this->lexer->getTokens(); + // 1. parse files to nodes + $parsingSystemError = $this->parseFileAndDecorateNodes($file); + if ($parsingSystemError instanceof SystemError) { + // we cannot process this file as the parsing and type resolving itself went wrong + return new FileProcessResult([$parsingSystemError], null, false); + } - // @todo may need tweak to refresh PHPStan types to avoid issue like in https://github.com/rectorphp/rector/issues/6561 - $newStmts = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($file, $oldStmts); - $file->hydrateStmtsAndTokens($newStmts, $oldStmts, $oldTokens); + $fileHasChanged = false; + $filePath = $file->getFilePath(); + + do { + $file->changeHasChanged(false); + + // 1. change nodes with Rector Rules + $newStmts = $this->rectorNodeTraverser->traverse($file->getNewStmts()); + + // 2. apply post rectors + $postNewStmts = $this->postFileProcessor->traverse($newStmts, $file); + + // 3. this is needed for new tokens added in "afterTraverse()" + $file->changeNewStmts($postNewStmts); + + // 4. print to file or string + // important to detect if file has changed + $this->printFile($file, $configuration, $filePath); + + // no change in current iteration, stop + if (! $file->hasChanged()) { + break; + } + + $fileHasChanged = true; + } while (true); + + // 5. add as cacheable if not changed at all + if (! $fileHasChanged) { + $this->changedFilesDetector->addCacheableFile($filePath); + } else { + // when changed, set final status changed to true + // to ensure it make sense to verify in next process when needed + $file->changeHasChanged(true); + } + + $rectorWithLineChanges = $file->getRectorWithLineChanges(); + if ($file->hasChanged() || $rectorWithLineChanges !== []) { + $currentFileDiff = $this->fileDiffFactory->createFileDiffWithLineChanges( + $configuration->shouldShowDiffs(), + $file, + $file->getOriginalFileContent(), + $file->getFileContent(), + $file->getRectorWithLineChanges() + ); + + $file->setFileDiff($currentFileDiff); + } + + return new FileProcessResult([], $file->getFileDiff(), $file->hasChanged()); + } + + private function parseFileAndDecorateNodes(File $file): ?SystemError + { + try { + try { + $this->parseFileNodes($file); + } catch (ParserErrorsException) { + $this->parseFileNodes($file, false); + } + } catch (ShouldNotHappenException $shouldNotHappenException) { + throw $shouldNotHappenException; + } catch (AnalysedCodeException $analysedCodeException) { + // inform about missing classes in tests + if (StaticPHPUnitEnvironment::isPHPUnitRun()) { + throw $analysedCodeException; + } + + return $this->errorFactory->createAutoloadError($analysedCodeException, $file->getFilePath()); + } catch (Throwable $throwable) { + if ($this->symfonyStyle->isVerbose() || StaticPHPUnitEnvironment::isPHPUnitRun()) { + throw $throwable; + } + + $relativeFilePath = $this->filePathHelper->relativePath($file->getFilePath()); + + if ($throwable instanceof ParserErrorsException) { + $throwable = new ParserErrors($throwable); + } + + return new SystemError($throwable->getMessage(), $relativeFilePath, $throwable->getLine()); + } + + return null; } - public function refactor(File $file): void + private function printFile(File $file, Configuration $configuration, string $filePath): void { - $newStmts = $this->rectorNodeTraverser->traverse($file->getNewStmts()); - $file->changeNewStmts($newStmts); + // only save to string first, no need to print to file when not needed + $newFileContent = $this->betterStandardPrinter->printFormatPreserving( + $file->getNewStmts(), + $file->getOldStmts(), + $file->getOldTokens() + ); + + // change file content early to make $file->hasChanged() based on new content + $file->changeFileContent($newFileContent); + if ($configuration->isDryRun()) { + return; + } - $this->affectedFilesCollector->removeFromList($file); - while ($otherTouchedFile = $this->affectedFilesCollector->getNext()) { - $this->refactor($otherTouchedFile); + if (! $file->hasChanged()) { + return; } + + FileSystem::write($filePath, $newFileContent, null); + } + + private function parseFileNodes(File $file, bool $forNewestSupportedVersion = true): void + { + // store tokens by original file content, so we don't have to print them right now + $stmtsAndTokens = $this->rectorParser->parseFileContentToStmtsAndTokens( + $file->getOriginalFileContent(), + $forNewestSupportedVersion + ); + + $oldStmts = $stmtsAndTokens->getStmts(); + + // wrap in FileNode to allow file-level rules + $oldStmts = [new FileNode($oldStmts)]; + + $oldTokens = $stmtsAndTokens->getTokens(); + + $newStmts = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($file->getFilePath(), $oldStmts); + + $file->hydrateStmtsAndTokens($newStmts, $oldStmts, $oldTokens); } } diff --git a/src/Application/FileProcessor/PhpFileProcessor.php b/src/Application/FileProcessor/PhpFileProcessor.php deleted file mode 100644 index 8db769d4fe5..00000000000 --- a/src/Application/FileProcessor/PhpFileProcessor.php +++ /dev/null @@ -1,171 +0,0 @@ -tryCatchWrapper($file, function (File $file): void { - $this->fileProcessor->parseFileInfoToLocalCache($file); - }, ApplicationPhase::PARSING()); - - // 2. change nodes with Rectors - $loopCounter = 0; - do { - ++$loopCounter; - - if ($loopCounter === 10) { // ensure no infinite loop - break; - } - - $file->changeHasChanged(false); - $this->refactorNodesWithRectors($file); - - // 3. apply post rectors - $this->tryCatchWrapper($file, function (File $file): void { - $newStmts = $this->postFileProcessor->traverse($file->getNewStmts()); - - // this is needed for new tokens added in "afterTraverse()" - $file->changeNewStmts($newStmts); - }, ApplicationPhase::POST_RECTORS()); - - // 4. print to file or string - $this->currentFileProvider->setFile($file); - - // cannot print file with errors, as print would break everything to original nodes - if ($file->hasErrors()) { - // cannot print file with errors, as print would b - $this->notifyPhase($file, ApplicationPhase::PRINT_SKIP()); - continue; - } - - // important to detect if file has changed - $this->tryCatchWrapper($file, function (File $file) use ($configuration): void { - $this->printFile($file, $configuration); - }, ApplicationPhase::PRINT()); - } while ($file->hasChanged()); - } - - public function supports(File $file, Configuration $configuration): bool - { - $smartFileInfo = $file->getSmartFileInfo(); - return $smartFileInfo->hasSuffixes($configuration->getFileExtensions()); - } - - /** - * @return string[] - */ - public function getSupportedFileExtensions(): array - { - return ['php']; - } - - private function refactorNodesWithRectors(File $file): void - { - $this->currentFileProvider->setFile($file); - - $this->tryCatchWrapper($file, function (File $file): void { - $this->fileProcessor->refactor($file); - }, ApplicationPhase::REFACTORING()); - } - - private function tryCatchWrapper(File $file, callable $callback, ApplicationPhase $applicationPhase): void - { - $this->currentFileProvider->setFile($file); - $this->notifyPhase($file, $applicationPhase); - - try { - if (in_array($file, $this->notParsedFiles, true)) { - // we cannot process this file - return; - } - - $callback($file); - } catch (ShouldNotHappenException $shouldNotHappenException) { - throw $shouldNotHappenException; - } catch (AnalysedCodeException $analysedCodeException) { - // inform about missing classes in tests - if (StaticPHPUnitEnvironment::isPHPUnitRun()) { - throw $analysedCodeException; - } - - $this->notParsedFiles[] = $file; - $error = $this->errorFactory->createAutoloadError($analysedCodeException, $file->getSmartFileInfo()); - $file->addRectorError($error); - } catch (Throwable $throwable) { - if ($this->symfonyStyle->isVerbose() || StaticPHPUnitEnvironment::isPHPUnitRun()) { - throw $throwable; - } - - $rectorError = new RectorError($throwable->getMessage(), $file->getSmartFileInfo(), $throwable->getLine()); - $file->addRectorError($rectorError); - } - } - - private function printFile(File $file, Configuration $configuration): void - { - $smartFileInfo = $file->getSmartFileInfo(); - if ($this->removedAndAddedFilesCollector->isFileRemoved($smartFileInfo)) { - // skip, because this file exists no more - return; - } - - $newContent = $configuration->isDryRun() - ? $this->formatPerservingPrinter->printParsedStmstAndTokensToString($file) - : $this->formatPerservingPrinter->printParsedStmstAndTokens($file); - - $file->changeFileContent($newContent); - $this->fileDiffFileDecorator->decorate([$file]); - } - - private function notifyPhase(File $file, ApplicationPhase $applicationPhase): void - { - if (! $this->symfonyStyle->isVerbose()) { - return; - } - - $smartFileInfo = $file->getSmartFileInfo(); - $relativeFilePath = $smartFileInfo->getRelativeFilePathFromDirectory(getcwd()); - $message = sprintf('[%s] %s', $applicationPhase, $relativeFilePath); - $this->symfonyStyle->writeln($message); - } -} diff --git a/src/Application/FileSystem/RemovedAndAddedFilesCollector.php b/src/Application/FileSystem/RemovedAndAddedFilesCollector.php deleted file mode 100644 index 7584a72eb55..00000000000 --- a/src/Application/FileSystem/RemovedAndAddedFilesCollector.php +++ /dev/null @@ -1,134 +0,0 @@ -removedFileInfos[] = $smartFileInfo; - } - - /** - * @return SmartFileInfo[] - */ - public function getRemovedFiles(): array - { - return $this->removedFileInfos; - } - - public function isFileRemoved(SmartFileInfo $smartFileInfo): bool - { - // early assign to variable for increase performance - // @see https://3v4l.org/FM3vY#focus=8.0.7 vs https://3v4l.org/JZW7b#focus=8.0.7 - $pathname = $smartFileInfo->getPathname(); - foreach ($this->removedFileInfos as $removedFileInfo) { - if ($removedFileInfo->getPathname() !== $pathname) { - continue; - } - - return true; - } - - foreach ($this->movedFiles as $movedFile) { - $file = $movedFile->getFile(); - $fileInfo = $file->getSmartFileInfo(); - if ($fileInfo->getPathname() !== $pathname) { - continue; - } - - return true; - } - - return false; - } - - public function addAddedFile(AddedFileInterface $addedFile): void - { - $this->addedFiles[] = $addedFile; - } - - /** - * @return AddedFileWithContent[] - */ - public function getAddedFilesWithContent(): array - { - return array_filter( - $this->addedFiles, - fn (AddedFileInterface $addedFile): bool => $addedFile instanceof AddedFileWithContent - ); - } - - /** - * @return AddedFileWithNodes[] - */ - public function getAddedFilesWithNodes(): array - { - return array_filter( - $this->addedFiles, - fn (AddedFileInterface $addedFile): bool => $addedFile instanceof AddedFileWithNodes - ); - } - - public function getAffectedFilesCount(): int - { - return count($this->addedFiles) + count($this->removedFileInfos); - } - - public function getAddedFileCount(): int - { - return count($this->addedFiles); - } - - public function getRemovedFilesCount(): int - { - return count($this->removedFileInfos); - } - - /** - * For testing - */ - public function reset(): void - { - $this->addedFiles = []; - $this->movedFiles = []; - $this->removedFileInfos = []; - } - - public function addMovedFile(File $file, string $newPathName): void - { - $this->movedFiles[] = new MovedFile($file, $newPathName); - } - - /** - * @return MovedFile[] - */ - public function getMovedFiles(): array - { - return $this->movedFiles; - } -} diff --git a/src/Application/FileSystem/RemovedAndAddedFilesProcessor.php b/src/Application/FileSystem/RemovedAndAddedFilesProcessor.php deleted file mode 100644 index 5b91a5ab0ac..00000000000 --- a/src/Application/FileSystem/RemovedAndAddedFilesProcessor.php +++ /dev/null @@ -1,110 +0,0 @@ -processAddedFilesWithContent($configuration); - $this->processAddedFilesWithNodes($configuration); - $this->processMovedFilesWithNodes($configuration); - - $this->processDeletedFiles($configuration); - } - - private function processDeletedFiles(Configuration $configuration): void - { - foreach ($this->removedAndAddedFilesCollector->getRemovedFiles() as $removedFile) { - $relativePath = $removedFile->getRelativeFilePathFromDirectory(getcwd()); - - if ($configuration->isDryRun()) { - $message = sprintf('File "%s" will be removed', $relativePath); - $this->symfonyStyle->warning($message); - } else { - $message = sprintf('File "%s" was removed', $relativePath); - $this->symfonyStyle->warning($message); - $this->smartFileSystem->remove($removedFile->getPathname()); - } - } - } - - private function processAddedFilesWithContent(Configuration $configuration): void - { - foreach ($this->removedAndAddedFilesCollector->getAddedFilesWithContent() as $addedFileWithContent) { - if ($configuration->isDryRun()) { - $message = sprintf('File "%s" will be added', $addedFileWithContent->getFilePath()); - $this->symfonyStyle->note($message); - } else { - $this->smartFileSystem->dumpFile( - $addedFileWithContent->getFilePath(), - $addedFileWithContent->getFileContent() - ); - $message = sprintf('File "%s" was added', $addedFileWithContent->getFilePath()); - $this->symfonyStyle->note($message); - } - } - } - - private function processAddedFilesWithNodes(Configuration $configuration): void - { - foreach ($this->removedAndAddedFilesCollector->getAddedFilesWithNodes() as $addedFileWithNode) { - $fileContent = $this->nodesWithFileDestinationPrinter->printNodesWithFileDestination( - $addedFileWithNode - ); - - if ($configuration->isDryRun()) { - $message = sprintf('File "%s" will be added', $addedFileWithNode->getFilePath()); - $this->symfonyStyle->note($message); - } else { - $this->smartFileSystem->dumpFile($addedFileWithNode->getFilePath(), $fileContent); - $message = sprintf('File "%s" was added', $addedFileWithNode->getFilePath()); - $this->symfonyStyle->note($message); - } - } - } - - private function processMovedFilesWithNodes(Configuration $configuration): void - { - foreach ($this->removedAndAddedFilesCollector->getMovedFiles() as $movedFile) { - $fileContent = $this->nodesWithFileDestinationPrinter->printNodesWithFileDestination($movedFile); - - if ($configuration->isDryRun()) { - $message = sprintf( - 'File "%s" will be moved to "%s"', - $movedFile->getFilePath(), - $movedFile->getNewFilePath() - ); - $this->symfonyStyle->note($message); - } else { - $this->smartFileSystem->dumpFile($movedFile->getNewFilePath(), $fileContent); - $this->smartFileSystem->remove($movedFile->getFilePath()); - - $message = sprintf( - 'File "%s" was moved to "%s"', - $movedFile->getFilePath(), - $movedFile->getNewFilePath() - ); - $this->symfonyStyle->note($message); - } - } - } -} diff --git a/src/Application/NodeAttributeReIndexer.php b/src/Application/NodeAttributeReIndexer.php new file mode 100644 index 00000000000..66eb86a3f2b --- /dev/null +++ b/src/Application/NodeAttributeReIndexer.php @@ -0,0 +1,81 @@ +elseifs = array_values($node->elseifs); + return $node; + } + + if ($node instanceof TryCatch) { + $node->catches = array_values($node->catches); + return $node; + } + + if ($node instanceof FunctionLike) { + /** @var ClassMethod|Function_|Closure $node */ + $node->params = array_values($node->params); + + if ($node instanceof Closure) { + $node->uses = array_values($node->uses); + } + + return $node; + } + + if ($node instanceof CallLike) { + /** @var FuncCall|MethodCall|New_|NullsafeMethodCall|StaticCall $node */ + $node->args = array_values($node->args); + return $node; + } + + if ($node instanceof Switch_) { + $node->cases = array_values($node->cases); + return $node; + } + + return null; + } + + private static function reIndexStmtsKeys(Node $node): ?Node + { + if (! NodeGroup::isStmtAwareNode($node) && ! $node instanceof ClassLike) { + return null; + } + + Assert::propertyExists($node, 'stmts'); + if ($node->stmts === null) { + return null; + } + + $node->stmts = array_values($node->stmts); + + return $node; + } +} diff --git a/src/Application/Provider/CurrentFileProvider.php b/src/Application/Provider/CurrentFileProvider.php new file mode 100644 index 00000000000..bb3f563bb41 --- /dev/null +++ b/src/Application/Provider/CurrentFileProvider.php @@ -0,0 +1,25 @@ +file = $file; + } + + public function getFile(): ?File + { + return $this->file; + } +} diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index f46cb72c50e..dffbe0bb260 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -2,51 +2,72 @@ declare(strict_types=1); -namespace Rector\Core\Application; +namespace Rector\Application; use DateTime; -use Rector\Core\Exception\VersionException; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Process\Process; +use Rector\Exception\VersionException; /** + * @api + * * Inspired by https://github.com/composer/composer/blob/master/src/Composer/Composer.php * See https://github.com/composer/composer/blob/6587715d0f8cae0cd39073b3bc5f018d0e6b84fe/src/Composer/Compiler.php#L208 + * + * @see \Rector\Tests\Application\VersionResolverTest */ final class VersionResolver { /** - * @var string + * @api */ - public const PACKAGE_VERSION = '@package_version@'; + public const string PACKAGE_VERSION = '@package_version@'; /** - * @var string + * @api */ - public const RELEASE_DATE = '@release_date@'; + public const string RELEASE_DATE = '@release_date@'; + + private const int SUCCESS_CODE = 0; public static function resolvePackageVersion(): string { - $process = new Process(['git', 'log', '--pretty="%H"', '-n1', 'HEAD'], __DIR__); - if ($process->run() !== Command::SUCCESS) { + // resolve current tag + exec('git tag --points-at', $tagExecOutput, $tagExecResultCode); + + if ($tagExecResultCode !== self::SUCCESS_CODE) { throw new VersionException( - 'You must ensure to run compile from composer git repository clone and that git binary is available.' + 'Ensure to run compile from composer git repository clone and that git binary is available.' + ); + } + + if ($tagExecOutput !== []) { + $tag = $tagExecOutput[0]; + if ($tag !== '') { + return $tag; + } + } + + exec('git log --pretty="%H" -n1 HEAD', $commitHashExecOutput, $commitHashResultCode); + + if ($commitHashResultCode !== 0) { + throw new VersionException( + 'Ensure to run compile from composer git repository clone and that git binary is available.' ); } - $version = trim($process->getOutput()); + $version = trim($commitHashExecOutput[0]); return trim($version, '"'); } public static function resolverReleaseDateTime(): DateTime { - $process = new Process(['git', 'log', '-n1', '--pretty=%ci', 'HEAD'], __DIR__); - if ($process->run() !== Command::SUCCESS) { + exec('git log -n1 --pretty=%ci HEAD', $output, $resultCode); + if ($resultCode !== self::SUCCESS_CODE) { throw new VersionException( 'You must ensure to run compile from composer git repository clone and that git binary is available.' ); } - return new DateTime(trim($process->getOutput())); + return new DateTime(trim($output[0])); } } diff --git a/src/Autoloading/AdditionalAutoloader.php b/src/Autoloading/AdditionalAutoloader.php index ab773772974..699b9073497 100644 --- a/src/Autoloading/AdditionalAutoloader.php +++ b/src/Autoloading/AdditionalAutoloader.php @@ -2,22 +2,20 @@ declare(strict_types=1); -namespace Rector\Core\Autoloading; +namespace Rector\Autoloading; -use Rector\Core\Configuration\Option; -use Rector\Core\StaticReflection\DynamicSourceLocatorDecorator; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\StaticReflection\DynamicSourceLocatorDecorator; use Symfony\Component\Console\Input\InputInterface; -use Symplify\PackageBuilder\Parameter\ParameterProvider; -use Symplify\SmartFileSystem\FileSystemGuard; +use Webmozart\Assert\Assert; /** * Should it pass autoload files/directories to PHPStan analyzer? */ -final class AdditionalAutoloader +final readonly class AdditionalAutoloader { public function __construct( - private FileSystemGuard $fileSystemGuard, - private ParameterProvider $parameterProvider, private DynamicSourceLocatorDecorator $dynamicSourceLocatorDecorator ) { } @@ -34,13 +32,17 @@ public function autoloadInput(InputInterface $input): void return; } - $this->fileSystemGuard->ensureFileExists($autoloadFile, 'Extra autoload'); + Assert::fileExists($autoloadFile, sprintf('Extra autoload file %s was not found', $autoloadFile)); + require_once $autoloadFile; } public function autoloadPaths(): void { - $autoloadPaths = $this->parameterProvider->provideArrayParameter(Option::AUTOLOAD_PATHS); - $this->dynamicSourceLocatorDecorator->addPaths($autoloadPaths); + $autoloadPaths = SimpleParameterProvider::provideArrayParameter(Option::AUTOLOAD_PATHS); + $autoloadPaths = $this->dynamicSourceLocatorDecorator->addPaths($autoloadPaths); + + // set values of Option::AUTOLOAD_PATHS with transformed paths + SimpleParameterProvider::setParameter(Option::AUTOLOAD_PATHS, $autoloadPaths); } } diff --git a/src/Autoloading/BootstrapFilesIncluder.php b/src/Autoloading/BootstrapFilesIncluder.php index e1ac67a5bec..edcae9247a6 100644 --- a/src/Autoloading/BootstrapFilesIncluder.php +++ b/src/Autoloading/BootstrapFilesIncluder.php @@ -2,28 +2,28 @@ declare(strict_types=1); -namespace Rector\Core\Autoloading; - -use Rector\Core\Configuration\Option; -use Rector\Core\Exception\ShouldNotHappenException; -use Symplify\PackageBuilder\Parameter\ParameterProvider; -use Throwable; +namespace Rector\Autoloading; + +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\Exception\ShouldNotHappenException; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use SplFileInfo; use Webmozart\Assert\Assert; +/** + * @see \Rector\Tests\Autoloading\BootstrapFilesIncluderTest + */ final class BootstrapFilesIncluder { - public function __construct( - private ParameterProvider $parameterProvider - ) { - } - /** * Inspired by * @see https://github.com/phpstan/phpstan-src/commit/aad1bf888ab7b5808898ee5fe2228bb8bb4e4cf1 */ public function includeBootstrapFiles(): void { - $bootstrapFiles = $this->parameterProvider->provideArrayParameter(Option::BOOTSTRAP_FILES); + $bootstrapFiles = SimpleParameterProvider::provideArrayParameter(Option::BOOTSTRAP_FILES); Assert::allString($bootstrapFiles); @@ -33,20 +33,26 @@ public function includeBootstrapFiles(): void throw new ShouldNotHappenException(sprintf('Bootstrap file "%s" does not exist.', $bootstrapFile)); } - try { - require_once $bootstrapFile; - } catch (Throwable $throwable) { - $errorMessage = sprintf( - '"%s" thrown in "%s" on line %d while loading bootstrap file %s: %s', - $throwable::class, - $throwable->getFile(), - $throwable->getLine(), - $bootstrapFile, - $throwable->getMessage() - ); - - throw new ShouldNotHappenException($errorMessage, $throwable->getCode(), $throwable); - } + require $bootstrapFile; + } + + $this->requireRectorStubs(); + } + + private function requireRectorStubs(): void + { + $stubsRectorDirectory = realpath(__DIR__ . '/../../stubs-rector'); + if ($stubsRectorDirectory === false) { + return; + } + + $dir = new RecursiveDirectoryIterator($stubsRectorDirectory, RecursiveDirectoryIterator::SKIP_DOTS); + + $stubs = new RecursiveIteratorIterator($dir); + + foreach ($stubs as $stub) { + /** @var SplFileInfo $stub */ + require_once $stub->getRealPath(); } } } diff --git a/packages/BetterPhpDocParser/Annotation/AnnotationNaming.php b/src/BetterPhpDocParser/Annotation/AnnotationNaming.php similarity index 100% rename from packages/BetterPhpDocParser/Annotation/AnnotationNaming.php rename to src/BetterPhpDocParser/Annotation/AnnotationNaming.php diff --git a/packages/BetterPhpDocParser/Attributes/AttributeMirrorer.php b/src/BetterPhpDocParser/Attributes/AttributeMirrorer.php similarity index 94% rename from packages/BetterPhpDocParser/Attributes/AttributeMirrorer.php rename to src/BetterPhpDocParser/Attributes/AttributeMirrorer.php index 0fd22287bde..60323cdb868 100644 --- a/packages/BetterPhpDocParser/Attributes/AttributeMirrorer.php +++ b/src/BetterPhpDocParser/Attributes/AttributeMirrorer.php @@ -12,7 +12,7 @@ final class AttributeMirrorer /** * @var string[] */ - private const ATTRIBUTES_TO_MIRROR = [ + private const array ATTRIBUTES_TO_MIRROR = [ PhpDocAttributeKey::PARENT, PhpDocAttributeKey::START_AND_END, PhpDocAttributeKey::ORIG_NODE, diff --git a/src/BetterPhpDocParser/Comment/CommentsMerger.php b/src/BetterPhpDocParser/Comment/CommentsMerger.php new file mode 100644 index 00000000000..ea053957846 --- /dev/null +++ b/src/BetterPhpDocParser/Comment/CommentsMerger.php @@ -0,0 +1,70 @@ +nodeComparator->areSameNode($newNode, $oldNode)) { + return; + } + + $oldPhpDocInfo = $oldNode->getAttribute(AttributeKey::PHP_DOC_INFO); + $newPhpDocInfo = $newNode->getAttribute(AttributeKey::PHP_DOC_INFO); + + if ($newPhpDocInfo instanceof PhpDocInfo) { + if (! $oldPhpDocInfo instanceof PhpDocInfo) { + return; + } + + if ((string) $oldPhpDocInfo->getPhpDocNode() !== (string) $newPhpDocInfo->getPhpDocNode()) { + return; + } + } + + $newNode->setAttribute(AttributeKey::PHP_DOC_INFO, $oldPhpDocInfo); + if (! $newNode instanceof Nop) { + $newNode->setAttribute(AttributeKey::COMMENTS, $oldNode->getAttribute(AttributeKey::COMMENTS)); + } + } + + /** + * @param Node[] $mergedNodes + */ + public function keepComments(Node $newNode, array $mergedNodes): void + { + $comments = $newNode->getComments(); + + foreach ($mergedNodes as $mergedNode) { + $comments = array_merge($comments, $mergedNode->getComments()); + } + + if ($comments === []) { + return; + } + + $newNode->setAttribute(AttributeKey::COMMENTS, $comments); + + // remove so comments "win" + $newNode->setAttribute(AttributeKey::PHP_DOC_INFO, null); + } +} diff --git a/src/BetterPhpDocParser/Contract/BasePhpDocNodeVisitorInterface.php b/src/BetterPhpDocParser/Contract/BasePhpDocNodeVisitorInterface.php new file mode 100644 index 00000000000..3f11b38e534 --- /dev/null +++ b/src/BetterPhpDocParser/Contract/BasePhpDocNodeVisitorInterface.php @@ -0,0 +1,11 @@ +betterTokenIterator === null) { + if (! $this->betterTokenIterator instanceof BetterTokenIterator) { throw new ShouldNotHappenException(); } diff --git a/src/BetterPhpDocParser/Guard/NewPhpDocFromPHPStanTypeGuard.php b/src/BetterPhpDocParser/Guard/NewPhpDocFromPHPStanTypeGuard.php new file mode 100644 index 00000000000..2c7efa4d820 --- /dev/null +++ b/src/BetterPhpDocParser/Guard/NewPhpDocFromPHPStanTypeGuard.php @@ -0,0 +1,32 @@ +isLegalUnionType($type); + } + + return true; + } + + private function isLegalUnionType(UnionType $type): bool + { + foreach ($type->getTypes() as $unionType) { + if ($unionType instanceof MixedType) { + return false; + } + } + + return true; + } +} diff --git a/src/BetterPhpDocParser/PhpDoc/ArrayItemNode.php b/src/BetterPhpDocParser/PhpDoc/ArrayItemNode.php new file mode 100644 index 00000000000..1e5111779f6 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDoc/ArrayItemNode.php @@ -0,0 +1,41 @@ +key !== null && ! is_int($this->key)) { + $value .= $this->key . '='; + } + + if (is_array($this->value)) { + foreach ($this->value as $singleValue) { + $value .= $singleValue; + } + } elseif ($this->value instanceof DoctrineAnnotationTagValueNode) { + $value .= '@' . ltrim((string) $this->value->identifierTypeNode, '@') . $this->value; + } else { + $value .= $this->value; + } + + return $value; + } +} diff --git a/packages/BetterPhpDocParser/PhpDoc/DoctrineAnnotationTagValueNode.php b/src/BetterPhpDocParser/PhpDoc/DoctrineAnnotationTagValueNode.php similarity index 80% rename from packages/BetterPhpDocParser/PhpDoc/DoctrineAnnotationTagValueNode.php rename to src/BetterPhpDocParser/PhpDoc/DoctrineAnnotationTagValueNode.php index b4772d9cadc..6897661f991 100644 --- a/packages/BetterPhpDocParser/PhpDoc/DoctrineAnnotationTagValueNode.php +++ b/src/BetterPhpDocParser/PhpDoc/DoctrineAnnotationTagValueNode.php @@ -12,7 +12,7 @@ final class DoctrineAnnotationTagValueNode extends AbstractValuesAwareNode implements Stringable { /** - * @param array $values + * @param ArrayItemNode[] $values */ public function __construct( public IdentifierTypeNode $identifierTypeNode, @@ -45,21 +45,7 @@ public function __toString(): string } $itemContents = $this->printValuesContent($this->values); - return sprintf('(%s)', $itemContents); - } - - /** - * @param string[] $classNames - */ - public function hasClassNames(array $classNames): bool - { - foreach ($classNames as $className) { - if ($this->hasClassName($className)) { - return true; - } - } - - return false; + return \sprintf('(%s)', $itemContents); } public function hasClassName(string $className): bool @@ -69,7 +55,7 @@ public function hasClassName(string $className): bool return true; } - // the name is not fully qualified in the original name, look for resolvd class attirubte + // the name is not fully qualified in the original name, look for resolved class attribute $resolvedClass = $this->identifierTypeNode->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS); return $resolvedClass === $className; } diff --git a/packages/BetterPhpDocParser/PhpDoc/SpacelessPhpDocTagNode.php b/src/BetterPhpDocParser/PhpDoc/SpacelessPhpDocTagNode.php similarity index 93% rename from packages/BetterPhpDocParser/PhpDoc/SpacelessPhpDocTagNode.php rename to src/BetterPhpDocParser/PhpDoc/SpacelessPhpDocTagNode.php index 70647241499..d7928efd494 100644 --- a/packages/BetterPhpDocParser/PhpDoc/SpacelessPhpDocTagNode.php +++ b/src/BetterPhpDocParser/PhpDoc/SpacelessPhpDocTagNode.php @@ -4,6 +4,7 @@ namespace Rector\BetterPhpDocParser\PhpDoc; +use Override; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use Stringable; @@ -13,6 +14,7 @@ */ final class SpacelessPhpDocTagNode extends PhpDocTagNode implements Stringable { + #[Override] public function __toString(): string { return $this->name . $this->value; diff --git a/src/BetterPhpDocParser/PhpDoc/StringNode.php b/src/BetterPhpDocParser/PhpDoc/StringNode.php new file mode 100644 index 00000000000..ae3509e9db8 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDoc/StringNode.php @@ -0,0 +1,35 @@ +value = str_replace('""', '"', $this->value); + + if (str_contains($this->value, "'") && ! str_contains($this->value, "\n")) { + $kind = String_::KIND_DOUBLE_QUOTED; + } else { + $kind = String_::KIND_SINGLE_QUOTED; + } + + $this->setAttribute(AttributeKey::KIND, $kind); + } + + public function __toString(): string + { + return '"' . str_replace('"', '""', $this->value) . '"'; + } +} diff --git a/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php new file mode 100644 index 00000000000..a49e14f8841 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php @@ -0,0 +1,565 @@ +, string> + */ + private const array TAGS_TYPES_TO_NAMES = [ + ReturnTagValueNode::class => '@return', + ParamTagValueNode::class => '@param', + VarTagValueNode::class => '@var', + MethodTagValueNode::class => '@method', + PropertyTagValueNode::class => '@property', + ExtendsTagValueNode::class => '@extends', + ImplementsTagValueNode::class => '@implements', + ]; + + private bool $isSingleLine = false; + + private readonly PhpDocNode $originalPhpDocNode; + + public function __construct( + private readonly PhpDocNode $phpDocNode, + private readonly BetterTokenIterator $betterTokenIterator, + private readonly StaticTypeMapper $staticTypeMapper, + private readonly \PhpParser\Node $node, + private readonly AnnotationNaming $annotationNaming, + private readonly PhpDocNodeByTypeFinder $phpDocNodeByTypeFinder + ) { + $this->originalPhpDocNode = clone $phpDocNode; + + if (! $betterTokenIterator->containsTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + $this->isSingleLine = true; + } + } + + /** + * @api + */ + public function addPhpDocTagNode(PhpDocChildNode $phpDocChildNode): void + { + $this->phpDocNode->children[] = $phpDocChildNode; + // to give node more space + $this->makeMultiLined(); + } + + public function getPhpDocNode(): PhpDocNode + { + return $this->phpDocNode; + } + + public function getOriginalPhpDocNode(): PhpDocNode + { + return $this->originalPhpDocNode; + } + + /** + * @return list + */ + public function getTokens(): array + { + return $this->betterTokenIterator->getTokens(); + } + + public function getTokenCount(): int + { + return $this->betterTokenIterator->count(); + } + + public function getVarTagValueNode(string $tagName = '@var'): ?VarTagValueNode + { + return $this->phpDocNode->getVarTagValues($tagName)[0] ?? null; + } + + /** + * @return array + */ + public function getTagsByName(string $name): array + { + // for simple tag names only + if (str_contains($name, '\\')) { + return []; + } + + $tags = $this->phpDocNode->getTags(); + $name = $this->annotationNaming->normalizeName($name); + + $tags = array_filter($tags, static fn (PhpDocTagNode $phpDocTagNode): bool => $phpDocTagNode->name === $name); + + return array_values($tags); + } + + public function getParamType(string $name): Type + { + $paramTagValueNodes = $this->getParamTagValueByName($name); + return $this->getTypeOrMixed($paramTagValueNodes); + } + + /** + * @return ParamTagValueNode[] + */ + public function getParamTagValueNodes(): array + { + return $this->phpDocNode->getParamTagValues(); + } + + public function getVarType(string $tagName = '@var'): Type + { + return $this->getTypeOrMixed($this->getVarTagValueNode($tagName)); + } + + public function getReturnType(): Type + { + return $this->getTypeOrMixed($this->getReturnTagValue()); + } + + /** + * @param class-string $type + */ + public function hasByType(string $type): bool + { + return $this->phpDocNodeByTypeFinder->findByType($this->phpDocNode, $type) !== []; + } + + /** + * @param array> $types + */ + public function hasByTypes(array $types): bool + { + foreach ($types as $type) { + if ($this->hasByType($type)) { + return true; + } + } + + return false; + } + + /** + * @param string[] $names + */ + public function hasByNames(array $names): bool + { + foreach ($names as $name) { + if ($this->hasByName($name)) { + return true; + } + } + + return false; + } + + public function hasByName(string $name): bool + { + return (bool) $this->getTagsByName($name); + } + + /** + * @api + */ + public function getByName(string $name): ?Node + { + return $this->getTagsByName($name)[0] ?? null; + } + + /** + * @param string[] $classes + */ + public function getByAnnotationClasses(array $classes): ?DoctrineAnnotationTagValueNode + { + $doctrineAnnotationTagValueNodes = $this->phpDocNodeByTypeFinder->findDoctrineAnnotationsByClasses( + $this->phpDocNode, + $classes + ); + + return $doctrineAnnotationTagValueNodes[0] ?? null; + } + + /** + * @api doctrine/symfony + */ + public function getByAnnotationClass(string $class): ?DoctrineAnnotationTagValueNode + { + $doctrineAnnotationTagValueNodes = $this->phpDocNodeByTypeFinder->findDoctrineAnnotationsByClass( + $this->phpDocNode, + $class + ); + return $doctrineAnnotationTagValueNodes[0] ?? null; + } + + public function hasByAnnotationClass(string $class): bool + { + return $this->findByAnnotationClass($class) !== []; + } + + /** + * @param string[] $annotationsClasses + */ + public function hasByAnnotationClasses(array $annotationsClasses): bool + { + return $this->getByAnnotationClasses($annotationsClasses) instanceof DoctrineAnnotationTagValueNode; + } + + public function findOneByAnnotationClass(string $desiredClass): ?DoctrineAnnotationTagValueNode + { + $foundTagValueNodes = $this->findByAnnotationClass($desiredClass); + return $foundTagValueNodes[0] ?? null; + } + + /** + * @template T of \PHPStan\PhpDocParser\Ast\Node + * @param class-string $typeToRemove + */ + public function removeByType(string $typeToRemove, ?string $name = null): bool + { + $hasChanged = false; + + if ($name === '') { + $name = null; + } + + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', static function (Node $node) use ( + $typeToRemove, + &$hasChanged, + $name + ): ?int { + if ($node instanceof PhpDocTagNode && $node->value instanceof $typeToRemove) { + // keep special annotation for tools + if (str_starts_with($node->name, '@psalm-')) { + return null; + } + + if (str_starts_with($node->name, '@phpstan-')) { + return null; + } + + if ($name !== null && $node->value instanceof VarTagValueNode && $node->value->variableName !== '$' . ltrim( + $name, + '$' + )) { + return PhpDocNodeTraverser::DONT_TRAVERSE_CHILDREN; + } + + $hasChanged = true; + return PhpDocNodeTraverser::NODE_REMOVE; + } + + if (! $node instanceof $typeToRemove) { + return null; + } + + $hasChanged = true; + return PhpDocNodeTraverser::NODE_REMOVE; + }); + + return $hasChanged; + } + + public function removeByName(string $tagName): bool + { + $tagName = '@' . ltrim($tagName, '@'); + $hasChanged = false; + + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', static function (Node $node) use ( + $tagName, + &$hasChanged + ): ?int { + if ($node instanceof PhpDocTagNode && $node->name === $tagName) { + $hasChanged = true; + return PhpDocNodeTraverser::NODE_REMOVE; + } + + return null; + }); + + return $hasChanged; + } + + public function addTagValueNode(PhpDocTagValueNode $phpDocTagValueNode): void + { + if ($phpDocTagValueNode instanceof DoctrineAnnotationTagValueNode) { + if ($phpDocTagValueNode->identifierTypeNode instanceof ShortenedIdentifierTypeNode) { + $name = '@' . $phpDocTagValueNode->identifierTypeNode; + } else { + $name = '@\\' . $phpDocTagValueNode->identifierTypeNode; + } + + $spacelessPhpDocTagNode = new SpacelessPhpDocTagNode($name, $phpDocTagValueNode); + $this->addPhpDocTagNode($spacelessPhpDocTagNode); + return; + } + + $name = $this->resolveNameForPhpDocTagValueNode($phpDocTagValueNode); + if (! is_string($name)) { + throw new ShouldNotHappenException(sprintf( + 'Name could not be resolved for "%s" tag value node. Complete it to %s::TAGS_TYPES_TO_NAMES constant', + $phpDocTagValueNode::class, + self::class, + )); + } + + $phpDocTagNode = new PhpDocTagNode($name, $phpDocTagValueNode); + $this->addPhpDocTagNode($phpDocTagNode); + } + + public function isNewNode(): bool + { + if ($this->phpDocNode->children === []) { + return false; + } + + return $this->betterTokenIterator->count() === 0; + } + + public function isSingleLine(): bool + { + return $this->isSingleLine; + } + + public function hasInvalidTag(string $name): bool + { + // fallback for invalid tag value node + foreach ($this->phpDocNode->children as $phpDocChildNode) { + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if ($phpDocChildNode->name !== $name) { + continue; + } + + if (! $phpDocChildNode->value instanceof InvalidTagValueNode) { + continue; + } + + return true; + } + + return false; + } + + public function getReturnTagValue(): ?ReturnTagValueNode + { + $returnTagValueNodes = $this->phpDocNode->getReturnTagValues(); + return $returnTagValueNodes[0] ?? null; + } + + public function getParamTagValueByName(string $name): ?ParamTagValueNode + { + $desiredParamNameWithDollar = '$' . ltrim($name, '$'); + + foreach ($this->getParamTagValueNodes() as $paramTagValueNode) { + if ($paramTagValueNode->parameterName !== $desiredParamNameWithDollar) { + continue; + } + + return $paramTagValueNode; + } + + return null; + } + + /** + * @return string[] + */ + public function getTemplateNames(): array + { + $templateNames = []; + foreach ($this->phpDocNode->getTemplateTagValues() as $templateTagValueNode) { + $templateNames[] = $templateTagValueNode->name; + } + + return $templateNames; + } + + public function makeMultiLined(): void + { + $this->isSingleLine = false; + } + + public function getNode(): \PhpParser\Node + { + return $this->node; + } + + /** + * @return string[] + */ + public function getAnnotationClassNames(): array + { + /** @var IdentifierTypeNode[] $identifierTypeNodes */ + $identifierTypeNodes = $this->phpDocNodeByTypeFinder->findByType($this->phpDocNode, IdentifierTypeNode::class); + + $resolvedClasses = []; + + foreach ($identifierTypeNodes as $identifierTypeNode) { + $resolvedClasses[] = ltrim($identifierTypeNode->name, '@'); + } + + return $resolvedClasses; + } + + /** + * @return string[] + */ + public function getGenericTagClassNames(): array + { + /** @var GenericTagValueNode[] $genericTagValueNodes */ + $genericTagValueNodes = $this->phpDocNodeByTypeFinder->findByType( + $this->phpDocNode, + GenericTagValueNode::class + ); + + $resolvedClasses = []; + + foreach ($genericTagValueNodes as $genericTagValueNode) { + if ($genericTagValueNode->value === '') { + continue; + } + + // add default original value + $resolvedClasses[] = $genericTagValueNode->value; + if (! str_contains($genericTagValueNode->value, '::')) { + continue; + } + + // add resolved class name if any + $resolvedClass = $genericTagValueNode->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS); + if ($resolvedClass === null) { + $resolvedClasses[] = $genericTagValueNode->value; + continue; + } + + $resolvedClasses[] = $resolvedClass; + } + + return $resolvedClasses; + } + + /** + * @return string[] + */ + public function getConstFetchNodeClassNames(): array + { + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + + $classNames = []; + + $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', static function (Node $node) use ( + &$classNames, + ): ?ConstTypeNode { + if (! $node instanceof ConstTypeNode) { + return null; + } + + if (! $node->constExpr instanceof ConstFetchNode) { + return null; + } + + $classNames[] = $node->constExpr->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS); + return $node; + }); + + return $classNames; + } + + /** + * @return string[] + */ + public function getArrayItemNodeClassNames(): array + { + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + + $classNames = []; + + $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', static function (Node $node) use ( + &$classNames, + ): ?ArrayItemNode { + if (! $node instanceof ArrayItemNode) { + return null; + } + + $resolvedClass = $node->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS); + if ($resolvedClass === null) { + return null; + } + + $classNames[] = $resolvedClass; + return $node; + }); + + return $classNames; + } + + /** + * @param class-string $desiredClass + * @return DoctrineAnnotationTagValueNode[] + */ + public function findByAnnotationClass(string $desiredClass): array + { + return $this->phpDocNodeByTypeFinder->findDoctrineAnnotationsByClass($this->phpDocNode, $desiredClass); + } + + private function resolveNameForPhpDocTagValueNode(PhpDocTagValueNode $phpDocTagValueNode): ?string + { + foreach (self::TAGS_TYPES_TO_NAMES as $tagValueNodeType => $name) { + /** @var class-string $tagValueNodeType */ + if ($phpDocTagValueNode instanceof $tagValueNodeType) { + return $name; + } + } + + return null; + } + + private function getTypeOrMixed(?PhpDocTagValueNode $phpDocTagValueNode): MixedType | Type + { + if (! $phpDocTagValueNode instanceof PhpDocTagValueNode) { + return new MixedType(); + } + + return $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($phpDocTagValueNode, $this->node); + } +} diff --git a/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfoFactory.php b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfoFactory.php new file mode 100644 index 00000000000..9a3375021f6 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfoFactory.php @@ -0,0 +1,137 @@ + + */ + private array $phpDocInfosByObjectId = []; + + public function __construct( + private readonly PhpDocNodeMapper $phpDocNodeMapper, + private readonly Lexer $lexer, + private readonly BetterPhpDocParser $betterPhpDocParser, + private readonly StaticTypeMapper $staticTypeMapper, + private readonly AnnotationNaming $annotationNaming, + private readonly PhpDocNodeByTypeFinder $phpDocNodeByTypeFinder + ) { + } + + public function createFromNodeOrEmpty(Node $node): PhpDocInfo + { + // already added + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo instanceof PhpDocInfo) { + return $phpDocInfo; + } + + $phpDocInfo = $this->createFromNode($node); + if ($phpDocInfo instanceof PhpDocInfo) { + return $phpDocInfo; + } + + return $this->createEmpty($node); + } + + public function createFromNode(Node $node): ?PhpDocInfo + { + $objectId = spl_object_id($node); + + if (isset($this->phpDocInfosByObjectId[$objectId])) { + return $this->phpDocInfosByObjectId[$objectId]; + } + + $docComment = $node->getDocComment(); + if (! $docComment instanceof Doc) { + if ($node->getComments() === []) { + return null; + } + + // create empty node + $tokenIterator = new BetterTokenIterator([]); + $phpDocNode = new PhpDocNode([]); + } else { + $tokens = $this->lexer->tokenize($docComment->getText()); + $tokenIterator = new BetterTokenIterator($tokens); + + $phpDocNode = $this->betterPhpDocParser->parseWithNode($tokenIterator, $node); + $this->setPositionOfLastToken($phpDocNode); + } + + $phpDocInfo = $this->createFromPhpDocNode($phpDocNode, $tokenIterator, $node); + $this->phpDocInfosByObjectId[$objectId] = $phpDocInfo; + + return $phpDocInfo; + } + + /** + * @api downgrade + */ + public function createEmpty(Node $node): PhpDocInfo + { + $phpDocNode = new PhpDocNode([]); + $phpDocInfo = $this->createFromPhpDocNode($phpDocNode, new BetterTokenIterator([]), $node); + + // multiline by default + $phpDocInfo->makeMultiLined(); + + return $phpDocInfo; + } + + /** + * Needed for printing + */ + private function setPositionOfLastToken(PhpDocNode $phpDocNode): void + { + if ($phpDocNode->children === []) { + return; + } + + $phpDocChildNodes = $phpDocNode->children; + $phpDocChildNode = array_pop($phpDocChildNodes); + $startAndEnd = $phpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); + + if ($startAndEnd instanceof StartAndEnd) { + $phpDocNode->setAttribute(PhpDocAttributeKey::LAST_PHP_DOC_TOKEN_POSITION, $startAndEnd->getEnd()); + } + } + + private function createFromPhpDocNode( + PhpDocNode $phpDocNode, + BetterTokenIterator $betterTokenIterator, + Node $node + ): PhpDocInfo { + $this->phpDocNodeMapper->transform($phpDocNode, $betterTokenIterator); + + $phpDocInfo = new PhpDocInfo( + $phpDocNode, + $betterTokenIterator, + $this->staticTypeMapper, + $node, + $this->annotationNaming, + $this->phpDocNodeByTypeFinder + ); + + $node->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); + + return $phpDocInfo; + } +} diff --git a/src/BetterPhpDocParser/PhpDocInfo/TokenIteratorFactory.php b/src/BetterPhpDocParser/PhpDocInfo/TokenIteratorFactory.php new file mode 100644 index 00000000000..06f969eaffd --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocInfo/TokenIteratorFactory.php @@ -0,0 +1,36 @@ +lexer->tokenize($content); + return new BetterTokenIterator($tokens); + } + + public function createFromTokenIterator(TokenIterator $tokenIterator): BetterTokenIterator + { + if ($tokenIterator instanceof BetterTokenIterator) { + return $tokenIterator; + } + + // keep original tokens and index position + $tokens = $tokenIterator->getTokens(); + $currentIndex = $tokenIterator->currentTokenIndex(); + + return new BetterTokenIterator($tokens, $currentIndex); + } +} diff --git a/src/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php b/src/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php new file mode 100644 index 00000000000..88fc559a1aa --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php @@ -0,0 +1,212 @@ +processAssertChoiceTagValueNode($oldToNewClasses, $phpDocInfo, $hasChanged); + $this->processDoctrineRelationTagValueNode($node, $oldToNewClasses, $phpDocInfo, $hasChanged); + $this->processSerializerTypeTagValueNode($oldToNewClasses, $phpDocInfo, $hasChanged); + + return $hasChanged; + } + + /** + * @param array $oldToNewClasses + */ + private function processAssertChoiceTagValueNode( + array $oldToNewClasses, + PhpDocInfo $phpDocInfo, + bool &$hasChanged + ): void { + $assertChoiceDoctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass( + 'Symfony\Component\Validator\Constraints\Choice' + ); + if (! $assertChoiceDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { + return; + } + + $callbackArrayItemNode = $assertChoiceDoctrineAnnotationTagValueNode->getValue('callback'); + if (! $callbackArrayItemNode instanceof ArrayItemNode) { + return; + } + + $callbackClass = $callbackArrayItemNode->value; + + // array is needed for callable + if (! $callbackClass instanceof CurlyListNode) { + return; + } + + $callableCallbackArrayItems = $callbackClass->getValues(); + $classNameArrayItemNode = $callableCallbackArrayItems[0]; + $classNameStringNode = $classNameArrayItemNode->value; + + if (! $classNameStringNode instanceof StringNode) { + return; + } + + foreach ($oldToNewClasses as $oldClass => $newClass) { + if ($classNameStringNode->value !== $oldClass) { + continue; + } + + $this->renamedNameCollector->add($oldClass); + $classNameStringNode->value = $newClass; + + // trigger reprint + $classNameArrayItemNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); + $hasChanged = true; + break; + } + } + + /** + * @param array $oldToNewClasses + */ + private function processDoctrineRelationTagValueNode( + Node $node, + array $oldToNewClasses, + PhpDocInfo $phpDocInfo, + bool &$hasChanged + ): void { + $doctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClasses([ + 'Doctrine\ORM\Mapping\OneToMany', + 'Doctrine\ORM\Mapping\ManyToMany', + 'Doctrine\ORM\Mapping\Embedded', + ]); + + if (! $doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { + return; + } + + $this->processDoctrineToMany($doctrineAnnotationTagValueNode, $node, $oldToNewClasses, $hasChanged); + } + + /** + * @param array $oldToNewClasses + */ + private function processSerializerTypeTagValueNode( + array $oldToNewClasses, + PhpDocInfo $phpDocInfo, + bool &$hasChanged + ): void { + $doctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass(ClassName::JMS_TYPE); + if (! $doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { + return; + } + + $classNameArrayItemNode = $doctrineAnnotationTagValueNode->getSilentValue(); + + foreach ($oldToNewClasses as $oldClass => $newClass) { + if ($classNameArrayItemNode instanceof ArrayItemNode && $classNameArrayItemNode->value instanceof StringNode) { + $classNameStringNode = $classNameArrayItemNode->value; + + if ($classNameStringNode->value === $oldClass) { + $classNameStringNode->value = $newClass; + continue; + } + + $this->renamedNameCollector->add($oldClass); + $classNameStringNode->value = Strings::replace( + $classNameStringNode->value, + '#\b' . preg_quote($oldClass, '#') . '\b#', + $newClass + ); + + $classNameArrayItemNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); + + $hasChanged = true; + } + + $currentTypeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type'); + if (! $currentTypeArrayItemNode instanceof ArrayItemNode) { + continue; + } + + $currentTypeStringNode = $currentTypeArrayItemNode->value; + if (! $currentTypeStringNode instanceof StringNode) { + continue; + } + + if ($currentTypeStringNode->value === $oldClass) { + $currentTypeStringNode->value = $newClass; + + $hasChanged = true; + } + } + } + + /** + * @param array $oldToNewClasses + */ + private function processDoctrineToMany( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, + Node $node, + array $oldToNewClasses, + bool &$hasChanged + ): void { + $classKey = $doctrineAnnotationTagValueNode->hasClassName( + 'Doctrine\ORM\Mapping\Embedded' + ) ? 'class' : 'targetEntity'; + + $targetEntityArrayItemNode = $doctrineAnnotationTagValueNode->getValue($classKey); + if (! $targetEntityArrayItemNode instanceof ArrayItemNode) { + return; + } + + $targetEntityStringNode = $targetEntityArrayItemNode->value; + if (! $targetEntityStringNode instanceof StringNode) { + return; + } + + $targetEntityClass = $targetEntityStringNode->value; + + // resolve to FQN + $tagFullyQualifiedName = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($targetEntityClass, $node); + + foreach ($oldToNewClasses as $oldClass => $newClass) { + if ($tagFullyQualifiedName !== $oldClass) { + continue; + } + + $this->renamedNameCollector->add($oldClass); + $targetEntityStringNode->value = $newClass; + $targetEntityArrayItemNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); + + $hasChanged = true; + } + } +} diff --git a/src/BetterPhpDocParser/PhpDocManipulator/PhpDocTagRemover.php b/src/BetterPhpDocParser/PhpDocManipulator/PhpDocTagRemover.php new file mode 100644 index 00000000000..d808b41ca95 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocManipulator/PhpDocTagRemover.php @@ -0,0 +1,71 @@ +getPhpDocNode(); + + foreach ($phpDocNode->children as $key => $phpDocChildNode) { + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if ($this->areAnnotationNamesEqual($name, $phpDocChildNode->name)) { + unset($phpDocNode->children[$key]); + $hasChanged = true; + } + + if ($phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode && $phpDocChildNode->value->hasClassName( + $name + )) { + unset($phpDocNode->children[$key]); + $hasChanged = true; + } + } + + return $hasChanged; + } + + public function removeTagValueFromNode(PhpDocInfo $phpDocInfo, Node $desiredNode): bool + { + $phpDocNode = $phpDocInfo->getPhpDocNode(); + $hasChanged = false; + + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + $phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', static function (Node $node) use ( + $desiredNode, + &$hasChanged + ): ?int { + if ($node instanceof PhpDocTagNode && $node->value === $desiredNode) { + $hasChanged = true; + return PhpDocNodeTraverser::NODE_REMOVE; + } + + if ($node !== $desiredNode) { + return null; + } + + $hasChanged = true; + return PhpDocNodeTraverser::NODE_REMOVE; + }); + + return $hasChanged; + } + + private function areAnnotationNamesEqual(string $firstAnnotationName, string $secondAnnotationName): bool + { + return trim($firstAnnotationName, '@') === trim($secondAnnotationName, '@'); + } +} diff --git a/src/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php b/src/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php new file mode 100644 index 00000000000..f268cdeb472 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php @@ -0,0 +1,260 @@ +> + */ + private const array ALLOWED_TYPES = [ + GenericTypeNode::class, + SpacingAwareArrayTypeNode::class, + SpacingAwareCallableTypeNode::class, + ArrayShapeNode::class, + ]; + + /** + * @var string[] + */ + private const array ALLOWED_IDENTIFIER_TYPENODE_TYPES = ['class-string']; + + public function __construct( + private StaticTypeMapper $staticTypeMapper, + private TypeComparator $typeComparator, + private ParamPhpDocNodeFactory $paramPhpDocNodeFactory, + private NewPhpDocFromPHPStanTypeGuard $newPhpDocFromPHPStanTypeGuard, + private DocBlockUpdater $docBlockUpdater + ) { + } + + public function changeVarType(Stmt $stmt, PhpDocInfo $phpDocInfo, Type $newType): bool + { + // better skip, could crash hard + if ($phpDocInfo->hasInvalidTag('@var')) { + return false; + } + + // make sure the tags are not identical, e.g imported class vs FQN class + if ($this->typeComparator->areTypesEqual($phpDocInfo->getVarType(), $newType)) { + return false; + } + + // prevent existing type override by mixed + if (! $phpDocInfo->getVarType() instanceof MixedType && $newType instanceof ConstantArrayType && $newType->getIterableValueType() instanceof NeverType) { + return false; + } + + if (! $this->newPhpDocFromPHPStanTypeGuard->isLegal($newType)) { + return false; + } + + // override existing type + $newPHPStanPhpDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType); + + $currentVarTagValueNode = $phpDocInfo->getVarTagValueNode(); + if ($currentVarTagValueNode instanceof VarTagValueNode) { + // only change type + $currentVarTagValueNode->type = $newPHPStanPhpDocTypeNode; + } else { + // add completely new one + $varTagValueNode = new VarTagValueNode($newPHPStanPhpDocTypeNode, '', ''); + + $phpDocInfo->addTagValueNode($varTagValueNode); + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); + return true; + } + + public function changeReturnTypeNode( + FunctionLike $functionLike, + PhpDocInfo $phpDocInfo, + TypeNode $newTypeNode + ): void { + $existingReturnTagValueNode = $phpDocInfo->getReturnTagValue(); + if ($existingReturnTagValueNode instanceof ReturnTagValueNode) { + // enforce reprint of copied type node + $newTypeNode->setAttribute('orig_node', null); + + $existingReturnTagValueNode->type = $newTypeNode; + } else { + $returnTagValueNode = new ReturnTagValueNode($newTypeNode, ''); + $phpDocInfo->addTagValueNode($returnTagValueNode); + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($functionLike); + } + + public function changeParamTypeNode( + FunctionLike $functionLike, + PhpDocInfo $phpDocInfo, + Param $param, + string $paramName, + TypeNode $newTypeNode + ): void { + $existingParamTagValueNode = $phpDocInfo->getParamTagValueByName($paramName); + if ($existingParamTagValueNode instanceof ParamTagValueNode) { + $existingParamTagValueNode->type = $newTypeNode; + } else { + $paramTagValueNode = $this->paramPhpDocNodeFactory->create($newTypeNode, $param); + $phpDocInfo->addTagValueNode($paramTagValueNode); + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($functionLike); + } + + public function changeReturnType(FunctionLike $functionLike, PhpDocInfo $phpDocInfo, Type $newType): bool + { + // better not touch this, can crash + if ($phpDocInfo->hasInvalidTag('@return')) { + return false; + } + + // make sure the tags are not identical, e.g imported class vs FQN class + if ($this->typeComparator->areTypesEqual($phpDocInfo->getReturnType(), $newType)) { + return false; + } + + if (! $this->newPhpDocFromPHPStanTypeGuard->isLegal($newType)) { + return false; + } + + // override existing type + $newPHPStanPhpDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType); + + $currentReturnTagValueNode = $phpDocInfo->getReturnTagValue(); + + if ($currentReturnTagValueNode instanceof ReturnTagValueNode) { + // only change type + $currentReturnTagValueNode->type = $newPHPStanPhpDocTypeNode; + } else { + // add completely new one + $returnTagValueNode = new ReturnTagValueNode($newPHPStanPhpDocTypeNode, ''); + $phpDocInfo->addTagValueNode($returnTagValueNode); + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($functionLike); + + return true; + } + + public function changeParamType( + FunctionLike $functionLike, + PhpDocInfo $phpDocInfo, + Type $newType, + Param $param, + string $paramName + ): bool { + // better skip, could crash hard + if ($phpDocInfo->hasInvalidTag('@param')) { + return false; + } + + if (! $this->newPhpDocFromPHPStanTypeGuard->isLegal($newType)) { + return false; + } + + $phpDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType); + $paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName); + + // override existing type + if ($paramTagValueNode instanceof ParamTagValueNode) { + // already set + $currentType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( + $paramTagValueNode->type, + $param + ); + + // avoid overriding better type + if ($this->typeComparator->isSubtype($currentType, $newType)) { + return false; + } + + if ($this->typeComparator->areTypesEqual($currentType, $newType)) { + return false; + } + + $paramTagValueNode->type = $phpDocTypeNode; + } else { + $paramTagValueNode = $this->paramPhpDocNodeFactory->create($phpDocTypeNode, $param); + $phpDocInfo->addTagValueNode($paramTagValueNode); + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($functionLike); + + return true; + } + + public function isAllowed(TypeNode $typeNode): bool + { + if ($typeNode instanceof BracketsAwareUnionTypeNode || $typeNode instanceof BracketsAwareIntersectionTypeNode) { + foreach ($typeNode->types as $type) { + if ($this->isAllowed($type)) { + return true; + } + } + } + + if ($typeNode instanceof ConstTypeNode && $typeNode->constExpr instanceof ConstFetchNode) { + return true; + } + + if (in_array($typeNode::class, self::ALLOWED_TYPES, true)) { + return true; + } + + if (! $typeNode instanceof IdentifierTypeNode) { + return false; + } + + return in_array((string) $typeNode, self::ALLOWED_IDENTIFIER_TYPENODE_TYPES, true); + } + + /** + * @api downgrade + */ + public function changeVarTypeNode(Stmt $stmt, PhpDocInfo $phpDocInfo, TypeNode $typeNode): void + { + $existingVarTagValueNode = $phpDocInfo->getVarTagValueNode(); + if ($existingVarTagValueNode instanceof VarTagValueNode) { + $existingVarTagValueNode->type = $typeNode; + } else { + // add completely new one + $varTagValueNode = new VarTagValueNode($typeNode, '', ''); + $phpDocInfo->addTagValueNode($varTagValueNode); + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); + } +} diff --git a/packages/BetterPhpDocParser/PhpDocNodeFinder/PhpDocNodeByTypeFinder.php b/src/BetterPhpDocParser/PhpDocNodeFinder/PhpDocNodeByTypeFinder.php similarity index 82% rename from packages/BetterPhpDocParser/PhpDocNodeFinder/PhpDocNodeByTypeFinder.php rename to src/BetterPhpDocParser/PhpDocNodeFinder/PhpDocNodeByTypeFinder.php index f154cd18784..4a9931f0243 100644 --- a/packages/BetterPhpDocParser/PhpDocNodeFinder/PhpDocNodeByTypeFinder.php +++ b/src/BetterPhpDocParser/PhpDocNodeFinder/PhpDocNodeByTypeFinder.php @@ -4,16 +4,15 @@ namespace Rector\BetterPhpDocParser\PhpDocNodeFinder; +use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; -use Symplify\SimplePhpDocParser\PhpDocNodeTraverser; +use Rector\PhpDocParser\PhpDocParser\PhpDocNodeTraverser; -/** - * @template TNode as \PHPStan\PhpDocParser\Ast\Node - */ final class PhpDocNodeByTypeFinder { /** + * @template TNode as \PHPStan\PhpDocParser\Ast\Node * @param class-string $desiredType * @return array */ @@ -23,11 +22,11 @@ public function findByType(PhpDocNode $phpDocNode, string $desiredType): array $foundNodes = []; - $phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', function ($node) use ( + $phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', static function (Node $node) use ( &$foundNodes, $desiredType - ) { - if (! is_a($node, $desiredType, true)) { + ): Node { + if (! $node instanceof $desiredType) { return $node; } @@ -40,7 +39,7 @@ public function findByType(PhpDocNode $phpDocNode, string $desiredType): array } /** - * @param class-string[] $classes + * @param string[] $classes * @return DoctrineAnnotationTagValueNode[] */ public function findDoctrineAnnotationsByClasses(PhpDocNode $phpDocNode, array $classes): array @@ -49,7 +48,7 @@ public function findDoctrineAnnotationsByClasses(PhpDocNode $phpDocNode, array $ foreach ($classes as $class) { $justFoundTagValueNodes = $this->findDoctrineAnnotationsByClass($phpDocNode, $class); - $doctrineAnnotationTagValueNodes = array_merge($doctrineAnnotationTagValueNodes, $justFoundTagValueNodes); + $doctrineAnnotationTagValueNodes = [...$doctrineAnnotationTagValueNodes, ...$justFoundTagValueNodes]; } return $doctrineAnnotationTagValueNodes; @@ -65,7 +64,6 @@ public function findDoctrineAnnotationsByClass(PhpDocNode $phpDocNode, string $d /** @var DoctrineAnnotationTagValueNode[] $doctrineTagValueNodes */ $doctrineTagValueNodes = $this->findByType($phpDocNode, DoctrineAnnotationTagValueNode::class); - foreach ($doctrineTagValueNodes as $doctrineTagValueNode) { if ($doctrineTagValueNode->hasClassName($desiredClass)) { $desiredDoctrineTagValueNodes[] = $doctrineTagValueNode; diff --git a/src/BetterPhpDocParser/PhpDocNodeMapper.php b/src/BetterPhpDocParser/PhpDocNodeMapper.php new file mode 100644 index 00000000000..4c7cf3dd623 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocNodeMapper.php @@ -0,0 +1,48 @@ +phpDocNodeTraverser = new PhpDocNodeTraverser(); + $this->phpDocNodeTraverser->addPhpDocNodeVisitor($parentConnectingPhpDocNodeVisitor); + $this->phpDocNodeTraverser->addPhpDocNodeVisitor($cloningPhpDocNodeVisitor); + + foreach ($this->phpDocNodeVisitors as $phpDocNodeVisitor) { + $this->phpDocNodeTraverser->addPhpDocNodeVisitor($phpDocNodeVisitor); + } + } + + public function transform(PhpDocNode $phpDocNode, BetterTokenIterator $betterTokenIterator): void + { + $this->currentTokenIteratorProvider->setBetterTokenIterator($betterTokenIterator); + $this->phpDocNodeTraverser->traverse($phpDocNode); + } +} diff --git a/packages/BetterPhpDocParser/PhpDocNodeVisitor/ArrayTypePhpDocNodeVisitor.php b/src/BetterPhpDocParser/PhpDocNodeVisitor/ArrayTypePhpDocNodeVisitor.php similarity index 87% rename from packages/BetterPhpDocParser/PhpDocNodeVisitor/ArrayTypePhpDocNodeVisitor.php rename to src/BetterPhpDocParser/PhpDocNodeVisitor/ArrayTypePhpDocNodeVisitor.php index 81c0bcd9022..86d01a30edd 100644 --- a/packages/BetterPhpDocParser/PhpDocNodeVisitor/ArrayTypePhpDocNodeVisitor.php +++ b/src/BetterPhpDocParser/PhpDocNodeVisitor/ArrayTypePhpDocNodeVisitor.php @@ -9,12 +9,12 @@ use Rector\BetterPhpDocParser\Attributes\AttributeMirrorer; use Rector\BetterPhpDocParser\Contract\BasePhpDocNodeVisitorInterface; use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareArrayTypeNode; -use Symplify\SimplePhpDocParser\PhpDocNodeVisitor\AbstractPhpDocNodeVisitor; +use Rector\PhpDocParser\PhpDocParser\PhpDocNodeVisitor\AbstractPhpDocNodeVisitor; final class ArrayTypePhpDocNodeVisitor extends AbstractPhpDocNodeVisitor implements BasePhpDocNodeVisitorInterface { public function __construct( - private AttributeMirrorer $attributeMirrorer + private readonly AttributeMirrorer $attributeMirrorer ) { } diff --git a/packages/BetterPhpDocParser/PhpDocNodeVisitor/CallableTypePhpDocNodeVisitor.php b/src/BetterPhpDocParser/PhpDocNodeVisitor/CallableTypePhpDocNodeVisitor.php similarity index 84% rename from packages/BetterPhpDocParser/PhpDocNodeVisitor/CallableTypePhpDocNodeVisitor.php rename to src/BetterPhpDocParser/PhpDocNodeVisitor/CallableTypePhpDocNodeVisitor.php index 450bdb1beb7..cc06ea37372 100644 --- a/packages/BetterPhpDocParser/PhpDocNodeVisitor/CallableTypePhpDocNodeVisitor.php +++ b/src/BetterPhpDocParser/PhpDocNodeVisitor/CallableTypePhpDocNodeVisitor.php @@ -9,12 +9,12 @@ use Rector\BetterPhpDocParser\Attributes\AttributeMirrorer; use Rector\BetterPhpDocParser\Contract\BasePhpDocNodeVisitorInterface; use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareCallableTypeNode; -use Symplify\SimplePhpDocParser\PhpDocNodeVisitor\AbstractPhpDocNodeVisitor; +use Rector\PhpDocParser\PhpDocParser\PhpDocNodeVisitor\AbstractPhpDocNodeVisitor; final class CallableTypePhpDocNodeVisitor extends AbstractPhpDocNodeVisitor implements BasePhpDocNodeVisitorInterface { public function __construct( - private AttributeMirrorer $attributeMirrorer + private readonly AttributeMirrorer $attributeMirrorer ) { } @@ -31,7 +31,8 @@ public function enterNode(Node $node): ?Node $spacingAwareCallableTypeNode = new SpacingAwareCallableTypeNode( $node->identifier, $node->parameters, - $node->returnType + $node->returnType, + [] ); $this->attributeMirrorer->mirror($node, $spacingAwareCallableTypeNode); diff --git a/packages/BetterPhpDocParser/PhpDocNodeVisitor/ChangedPhpDocNodeVisitor.php b/src/BetterPhpDocParser/PhpDocNodeVisitor/ChangedPhpDocNodeVisitor.php similarity index 89% rename from packages/BetterPhpDocParser/PhpDocNodeVisitor/ChangedPhpDocNodeVisitor.php rename to src/BetterPhpDocParser/PhpDocNodeVisitor/ChangedPhpDocNodeVisitor.php index f9c62c679ee..f0c1bd016bd 100644 --- a/packages/BetterPhpDocParser/PhpDocNodeVisitor/ChangedPhpDocNodeVisitor.php +++ b/src/BetterPhpDocParser/PhpDocNodeVisitor/ChangedPhpDocNodeVisitor.php @@ -6,7 +6,7 @@ use PHPStan\PhpDocParser\Ast\Node; use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey; -use Symplify\SimplePhpDocParser\PhpDocNodeVisitor\AbstractPhpDocNodeVisitor; +use Rector\PhpDocParser\PhpDocParser\PhpDocNodeVisitor\AbstractPhpDocNodeVisitor; final class ChangedPhpDocNodeVisitor extends AbstractPhpDocNodeVisitor { diff --git a/packages/BetterPhpDocParser/PhpDocNodeVisitor/IntersectionTypeNodePhpDocNodeVisitor.php b/src/BetterPhpDocParser/PhpDocNodeVisitor/IntersectionTypeNodePhpDocNodeVisitor.php similarity index 88% rename from packages/BetterPhpDocParser/PhpDocNodeVisitor/IntersectionTypeNodePhpDocNodeVisitor.php rename to src/BetterPhpDocParser/PhpDocNodeVisitor/IntersectionTypeNodePhpDocNodeVisitor.php index a8261d817b0..f6947cf0164 100644 --- a/packages/BetterPhpDocParser/PhpDocNodeVisitor/IntersectionTypeNodePhpDocNodeVisitor.php +++ b/src/BetterPhpDocParser/PhpDocNodeVisitor/IntersectionTypeNodePhpDocNodeVisitor.php @@ -9,12 +9,12 @@ use Rector\BetterPhpDocParser\Attributes\AttributeMirrorer; use Rector\BetterPhpDocParser\Contract\BasePhpDocNodeVisitorInterface; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareIntersectionTypeNode; -use Symplify\SimplePhpDocParser\PhpDocNodeVisitor\AbstractPhpDocNodeVisitor; +use Rector\PhpDocParser\PhpDocParser\PhpDocNodeVisitor\AbstractPhpDocNodeVisitor; final class IntersectionTypeNodePhpDocNodeVisitor extends AbstractPhpDocNodeVisitor implements BasePhpDocNodeVisitorInterface { public function __construct( - private AttributeMirrorer $attributeMirrorer + private readonly AttributeMirrorer $attributeMirrorer ) { } diff --git a/src/BetterPhpDocParser/PhpDocNodeVisitor/TemplatePhpDocNodeVisitor.php b/src/BetterPhpDocParser/PhpDocNodeVisitor/TemplatePhpDocNodeVisitor.php new file mode 100644 index 00000000000..366948ad7af --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocNodeVisitor/TemplatePhpDocNodeVisitor.php @@ -0,0 +1,79 @@ +currentTokenIteratorProvider->provide(); + + $startIndex = $node->getAttribute(Attribute::START_INDEX); + $endIndex = $node->getAttribute(Attribute::END_INDEX); + if ($startIndex === null || $endIndex === null) { + throw new ShouldNotHappenException(); + } + + $prepositions = $this->resolvePreposition($betterTokenIterator, $startIndex, $endIndex); + $spacingAwareTemplateTagValueNode = new SpacingAwareTemplateTagValueNode( + $node->name, + $node->bound, + $node->description, + $prepositions + ); + + $this->attributeMirrorer->mirror($node, $spacingAwareTemplateTagValueNode); + + return $spacingAwareTemplateTagValueNode; + } + + private function resolvePreposition( + BetterTokenIterator $betterTokenIterator, + int $startIndex, + int $endIndex + ): string { + $partialTokens = $betterTokenIterator->partialTokens($startIndex, $endIndex); + + foreach ($partialTokens as $partialToken) { + if ($partialToken[1] !== Lexer::TOKEN_IDENTIFIER) { + continue; + } + + if (! in_array($partialToken[0], ['as', 'of'], true)) { + continue; + } + + return $partialToken[0]; + } + + return 'of'; + } +} diff --git a/src/BetterPhpDocParser/PhpDocNodeVisitor/UnionTypeNodePhpDocNodeVisitor.php b/src/BetterPhpDocParser/PhpDocNodeVisitor/UnionTypeNodePhpDocNodeVisitor.php new file mode 100644 index 00000000000..a8cf43ee537 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocNodeVisitor/UnionTypeNodePhpDocNodeVisitor.php @@ -0,0 +1,85 @@ +resolveStartAndEnd($node); + if (! $startAndEnd instanceof StartAndEnd) { + $firstKey = array_key_first($node->types); + $lastKey = array_key_last($node->types); + + $startAndEnd = new StartAndEnd( + $node->types[$firstKey]->getAttribute('startIndex'), + $node->types[$lastKey]->getAttribute('endIndex') + ); + } + + $betterTokenProvider = $this->currentTokenIteratorProvider->provide(); + + $isWrappedInCurlyBrackets = $this->isWrappedInCurlyBrackets($betterTokenProvider, $startAndEnd); + $bracketsAwareUnionTypeNode = new BracketsAwareUnionTypeNode($node->types, $isWrappedInCurlyBrackets); + + $this->attributeMirrorer->mirror($node, $bracketsAwareUnionTypeNode); + + return $bracketsAwareUnionTypeNode; + } + + private function isWrappedInCurlyBrackets(BetterTokenIterator $betterTokenProvider, StartAndEnd $startAndEnd): bool + { + $previousPosition = $startAndEnd->getStart() - 1; + + if ($betterTokenProvider->isTokenTypeOnPosition(Lexer::TOKEN_OPEN_PARENTHESES, $previousPosition)) { + return true; + } + + // there is no + 1, as end is right at the next token + return $betterTokenProvider->isTokenTypeOnPosition(Lexer::TOKEN_CLOSE_PARENTHESES, $startAndEnd->getEnd()); + } + + private function resolveStartAndEnd(UnionTypeNode $unionTypeNode): ?StartAndEnd + { + $starAndEnd = $unionTypeNode->getAttribute(PhpDocAttributeKey::START_AND_END); + if ($starAndEnd instanceof StartAndEnd) { + return $starAndEnd; + } + + // unwrap with parent array type... + $parentNode = $unionTypeNode->getAttribute(PhpDocAttributeKey::PARENT); + if (! $parentNode instanceof Node) { + return null; + } + + return $parentNode->getAttribute(PhpDocAttributeKey::START_AND_END); + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/ArrayItemClassNameDecorator.php b/src/BetterPhpDocParser/PhpDocParser/ArrayItemClassNameDecorator.php new file mode 100644 index 00000000000..66ad52c98ff --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/ArrayItemClassNameDecorator.php @@ -0,0 +1,63 @@ +__toString(), '::')) { + return; + } + + $this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', function (Node $node) use ( + $phpNode + ): Node|null { + if (! $node instanceof ArrayItemNode) { + return null; + } + + if (! is_string($node->value)) { + return null; + } + + $splitScopeResolution = explode('::', $node->value); + if (count($splitScopeResolution) !== 2) { + return null; + } + + $className = $this->resolveFullyQualifiedClass($splitScopeResolution[0], $phpNode); + $node->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $className); + + return $node; + }); + } + + private function resolveFullyQualifiedClass(string $className, PhpNode $phpNode): string + { + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($phpNode); + return $nameScope->resolveStringName($className); + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php b/src/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php new file mode 100644 index 00000000000..fe9b99f2084 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php @@ -0,0 +1,189 @@ +\r\n|\n)#"; + + /** + * @see https://regex101.com/r/JOKSmr/5 + */ + private const string MULTI_NEW_LINES_REGEX = '#(?\r\n|\n){2,}#'; + + /** + * @param PhpDocNodeDecoratorInterface[] $phpDocNodeDecorators + */ + public function __construct( + ParserConfig $parserConfig, + TypeParser $typeParser, + ConstExprParser $constExprParser, + private readonly TokenIteratorFactory $tokenIteratorFactory, + private readonly array $phpDocNodeDecorators, + private readonly PrivatesAccessor $privatesAccessor, + ) { + parent::__construct( + // ParserConfig + $parserConfig, + // TypeParser + $typeParser, + // ConstExprParser + $constExprParser, + ); + } + + public function parseWithNode(BetterTokenIterator $betterTokenIterator, Node $node): PhpDocNode + { + $betterTokenIterator->consumeTokenType(Lexer::TOKEN_OPEN_PHPDOC); + $betterTokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + $children = []; + if (! $betterTokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + $children[] = $this->parseChildAndStoreItsPositions($betterTokenIterator); + + while ($betterTokenIterator->tryConsumeTokenType( + Lexer::TOKEN_PHPDOC_EOL + ) && ! $betterTokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + $children[] = $this->parseChildAndStoreItsPositions($betterTokenIterator); + } + } + + // might be in the middle of annotations + $betterTokenIterator->tryConsumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC); + + $phpDocNode = new PhpDocNode($children); + foreach ($this->phpDocNodeDecorators as $phpDocNodeDecorator) { + $phpDocNodeDecorator->decorate($phpDocNode, $node); + } + + return $phpDocNode; + } + + #[Override] + public function parseTag(TokenIterator $tokenIterator): PhpDocTagNode + { + // replace generic nodes with DoctrineAnnotations + if (! $tokenIterator instanceof BetterTokenIterator) { + throw new ShouldNotHappenException(); + } + + $tag = $this->resolveTag($tokenIterator); + $phpDocTagValueNode = $this->parseTagValue($tokenIterator, $tag); + + return new PhpDocTagNode($tag, $phpDocTagValueNode); + } + + /** + * @param BetterTokenIterator $tokenIterator + */ + #[Override] + public function parseTagValue(TokenIterator $tokenIterator, string $tag): PhpDocTagValueNode + { + $isPrecededByHorizontalWhitespace = $tokenIterator->isPrecededByHorizontalWhitespace(); + + $startPosition = $tokenIterator->currentPosition(); + $phpDocTagValueNode = parent::parseTagValue($tokenIterator, $tag); + + $endPosition = $tokenIterator->currentPosition(); + + if ($isPrecededByHorizontalWhitespace && property_exists($phpDocTagValueNode, 'description')) { + $phpDocTagValueNode->description = Strings::replace( + (string) $phpDocTagValueNode->description, + self::NEW_LINE_REGEX, + static fn (array $match): string => $match['new_line'] . ' * ' + ); + } + + $startAndEnd = new StartAndEnd($startPosition, $endPosition); + $phpDocTagValueNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + + if ($phpDocTagValueNode instanceof GenericTagValueNode) { + $phpDocTagValueNode->value = Strings::replace( + $phpDocTagValueNode->value, + self::MULTI_NEW_LINES_REGEX, + static fn (array $match): string => (string) $match['new_line'] + ); + } + + return $phpDocTagValueNode; + } + + /** + * @return PhpDocTextNode|PhpDocTagNode + */ + private function parseChildAndStoreItsPositions(TokenIterator $tokenIterator): PhpDocChildNode + { + $betterTokenIterator = $this->tokenIteratorFactory->createFromTokenIterator($tokenIterator); + + $startPosition = $betterTokenIterator->currentPosition(); + + try { + /** @var PhpDocTextNode|PhpDocTagNode $phpDocNode */ + $phpDocNode = $this->privatesAccessor->callPrivateMethod($this, 'parseChild', [$betterTokenIterator]); + } catch (ParserException) { + $phpDocNode = new PhpDocTextNode(''); + } + + $endPosition = $betterTokenIterator->currentPosition(); + + $startAndEnd = new StartAndEnd($startPosition, $endPosition); + $phpDocNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + + return $phpDocNode; + } + + private function resolveTag(BetterTokenIterator $tokenIterator): string + { + $tag = $tokenIterator->currentTokenValue(); + $tokenIterator->next(); + + // there is a space → stop + if ($tokenIterator->isPrecededByHorizontalWhitespace()) { + return $tag; + } + + // is not e.g "@var " + // join tags like "@ORM\Column" etc. + if (! $tokenIterator->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { + return $tag; + } + + // @todo use joinUntil("(")? + $tag .= $tokenIterator->currentTokenValue(); + $tokenIterator->next(); + + return $tag; + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher.php b/src/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher.php new file mode 100644 index 00000000000..ffa903192fd --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher.php @@ -0,0 +1,119 @@ + + */ + private array $fullyQualifiedNameByHash = []; + + public function __construct( + private readonly UseImportNameMatcher $useImportNameMatcher, + private readonly UseImportsResolver $useImportsResolver, + private readonly ReflectionProvider $reflectionProvider + ) { + } + + /** + * @return non-empty-string + */ + public function resolveTagFullyQualifiedName(string $tag, Node $node): string + { + $uniqueId = $tag . spl_object_id($node); + if (isset($this->fullyQualifiedNameByHash[$uniqueId])) { + return $this->fullyQualifiedNameByHash[$uniqueId]; + } + + $tag = ltrim($tag, '@'); + + $uses = $this->useImportsResolver->resolve(); + $fullyQualifiedClass = $this->resolveFullyQualifiedClass($uses, $node, $tag); + + if ($fullyQualifiedClass === null) { + $fullyQualifiedClass = $tag; + } + + $this->fullyQualifiedNameByHash[$uniqueId] = $fullyQualifiedClass; + + return $fullyQualifiedClass; + } + + /** + * @param array $uses + * @return non-empty-string|null + */ + private function resolveFullyQualifiedClass(array $uses, Node $node, string $tag): ?string + { + $scope = $node->getAttribute(AttributeKey::SCOPE); + + if ($scope instanceof Scope) { + $namespace = $scope->getNamespace(); + if ($namespace !== null) { + $namespacedTag = $namespace . '\\' . $tag; + if ($this->reflectionProvider->hasClass($namespacedTag)) { + return $namespacedTag; + } + + if (! str_contains($tag, '\\')) { + return $this->resolveAsAliased($uses, $tag); + } + + if ($this->isPreslashedExistingClass($tag)) { + // Global or absolute Class + return $tag; + } + } + } + + return $this->useImportNameMatcher->matchNameWithUses($tag, $uses); + } + + /** + * @param array $uses + * @return non-empty-string|null + */ + private function resolveAsAliased(array $uses, string $tag): ?string + { + foreach ($uses as $use) { + $prefix = $this->useImportsResolver->resolvePrefix($use); + + foreach ($use->uses as $useUse) { + if (! $useUse->alias instanceof Identifier) { + continue; + } + + if ($useUse->alias->toString() === $tag) { + return $prefix . $useUse->name->toString(); + } + } + } + + return $this->useImportNameMatcher->matchNameWithUses($tag, $uses); + } + + private function isPreslashedExistingClass(string $tag): bool + { + if (! str_starts_with($tag, '\\')) { + return false; + } + + return $this->reflectionProvider->hasClass($tag); + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/ConstExprClassNameDecorator.php b/src/BetterPhpDocParser/PhpDocParser/ConstExprClassNameDecorator.php new file mode 100644 index 00000000000..d3a67f243b6 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/ConstExprClassNameDecorator.php @@ -0,0 +1,54 @@ +__toString(), '::')) { + return; + } + + $this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', function (Node $node) use ( + $phpNode + ): Node|null { + if (! $node instanceof ConstFetchNode) { + return null; + } + + $className = $this->resolveFullyQualifiedClass($node, $phpNode); + $node->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $className); + + return $node; + }); + } + + private function resolveFullyQualifiedClass(ConstFetchNode $constFetchNode, PhpNode $phpNode): string + { + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($phpNode); + return $nameScope->resolveStringName($constFetchNode->className); + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/DoctrineAnnotationDecorator.php b/src/BetterPhpDocParser/PhpDocParser/DoctrineAnnotationDecorator.php new file mode 100644 index 00000000000..505c86f9165 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/DoctrineAnnotationDecorator.php @@ -0,0 +1,583 @@ +.*?)(?\(.*?\)|,|\r?\n|$)#'; + + /** + * Special short annotations, that are resolved as FQN by Doctrine annotation parser + * @var string[] + */ + private const array ALLOWED_SHORT_ANNOTATIONS = ['Target']; + + /** + * @see https://regex101.com/r/xWaLOz/1 + */ + private const string NESTED_ANNOTATION_END_REGEX = '#(\s+)?\}\)(\s+)?#'; + + /** + * @see https://regex101.com/r/8rWY4r/1 + */ + private const string NEWLINE_ANNOTATION_FQCN_REGEX = '#\r?\n@\\\\#'; + + /** + * @see https://regex101.com/r/3zXEh7/1 + */ + private const string STAR_COMMENT_REGEX = '#^\s*\*#ms'; + + public function __construct( + private ClassAnnotationMatcher $classAnnotationMatcher, + private StaticDoctrineAnnotationParser $staticDoctrineAnnotationParser, + private TokenIteratorFactory $tokenIteratorFactory, + private AttributeMirrorer $attributeMirrorer, + private ObjectTypeSpecifier $objectTypeSpecifier + ) { + } + + public function decorate(PhpDocNode $phpDocNode, Node $phpNode): void + { + // merge split doctrine nested tags + $this->mergeNestedDoctrineAnnotations($phpDocNode); + $this->transformGenericTagValueNodesToDoctrineAnnotationTagValueNodes($phpDocNode, $phpNode); + } + + /** + * Join token iterator with all the following nodes if nested + */ + private function mergeNestedDoctrineAnnotations(PhpDocNode $phpDocNode): void + { + $removedKeys = []; + + foreach ($phpDocNode->children as $key => $phpDocChildNode) { + if (in_array($key, $removedKeys, true)) { + continue; + } + + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if (! $phpDocChildNode->value instanceof GenericTagValueNode) { + continue; + } + + $genericTagValueNode = $phpDocChildNode->value; + + while (isset($phpDocNode->children[$key])) { + ++$key; + + // no more next nodes + if (! isset($phpDocNode->children[$key])) { + break; + } + + $nextPhpDocChildNode = $phpDocNode->children[$key]; + + if ($nextPhpDocChildNode instanceof PhpDocTextNode && StringUtils::isMatch( + $nextPhpDocChildNode->text, + self::NESTED_ANNOTATION_END_REGEX + )) { + // @todo how to detect previously opened brackets? + // probably local property with holding count of opened brackets + $composedContent = $genericTagValueNode->value . PHP_EOL . $nextPhpDocChildNode->text; + $genericTagValueNode->value = $composedContent; + + $startAndEnd = $this->combineStartAndEnd($phpDocChildNode, $nextPhpDocChildNode); + $phpDocChildNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + + $removedKeys[] = $key; + $removedKeys[] = $key + 1; + continue; + } + + if (! $nextPhpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if (! $nextPhpDocChildNode->value instanceof GenericTagValueNode) { + continue; + } + + if ($this->isClosedContent($genericTagValueNode->value)) { + break; + } + + $composedContent = $genericTagValueNode->value . PHP_EOL . $nextPhpDocChildNode->name . $nextPhpDocChildNode->value->value; + + // cleanup the next from closing + $genericTagValueNode->value = $composedContent; + + $startAndEnd = $this->combineStartAndEnd($phpDocChildNode, $nextPhpDocChildNode); + $phpDocChildNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + + $currentChildValueNode = $phpDocNode->children[$key]; + if (! $currentChildValueNode instanceof PhpDocTagNode) { + continue; + } + + $currentGenericTagValueNode = $currentChildValueNode->value; + if (! $currentGenericTagValueNode instanceof GenericTagValueNode) { + continue; + } + + $removedKeys[] = $key; + } + } + + foreach (array_keys($phpDocNode->children) as $key) { + if (! in_array($key, $removedKeys, true)) { + continue; + } + + unset($phpDocNode->children[$key]); + } + } + + private function processTextSpacelessInTextNode( + PhpDocNode $phpDocNode, + PhpDocTextNode $phpDocTextNode, + Node $currentPhpNode, + int $key + ): void { + $spacelessPhpDocTagNodes = $this->resolveFqnAnnotationSpacelessPhpDocTagNode( + $phpDocTextNode, + $currentPhpNode + ); + + if ($spacelessPhpDocTagNodes === []) { + return; + } + + $texts = Strings::split($phpDocTextNode->text, self::NEWLINE_ANNOTATION_FQCN_REGEX); + $otherText = $texts[0]; + + if (! str_starts_with((string) $otherText, '@\\') && trim((string) $otherText) !== '') { + $phpDocNode->children[$key] = new PhpDocTextNode($otherText); + array_splice($phpDocNode->children, $key + 1, 0, $spacelessPhpDocTagNodes); + + return; + } + + unset($phpDocNode->children[$key]); + array_splice($phpDocNode->children, $key, 0, $spacelessPhpDocTagNodes); + } + + private function transformGenericTagValueNodesToDoctrineAnnotationTagValueNodes( + PhpDocNode $phpDocNode, + Node $currentPhpNode + ): void { + foreach ($phpDocNode->children as $key => $phpDocChildNode) { + // the @\FQN use case + if ($phpDocChildNode instanceof PhpDocTextNode) { + $this->processTextSpacelessInTextNode($phpDocNode, $phpDocChildNode, $currentPhpNode, $key); + continue; + } + + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + // single quoted got invalid tag, keep process + if ($phpDocChildNode->value instanceof InvalidTagValueNode) { + $name = ltrim($phpDocChildNode->name, '@'); + + $values = $phpDocChildNode->value->value; + $this->processDoctrine($currentPhpNode, $name, $phpDocChildNode, $phpDocNode, $key, $values); + } + + // needs stable correct detection of full class name + if ($phpDocChildNode->value instanceof DoctrineTagValueNode) { + $name = ltrim($phpDocChildNode->name, '@'); + + $values = implode(', ', $phpDocChildNode->value->annotation->arguments); + $this->processDoctrine($currentPhpNode, $name, $phpDocChildNode, $phpDocNode, $key, $values); + + continue; + } + + if (! $phpDocChildNode->value instanceof GenericTagValueNode) { + $this->processDescriptionAsSpacelessPhpDoctagNode( + $phpDocNode, + $phpDocChildNode, + $currentPhpNode, + $key + ); + continue; + } + + // known doc tag to annotation class + $fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName( + $phpDocChildNode->name, + $currentPhpNode + ); + + // not an annotations class + if (! \str_contains($fullyQualifiedAnnotationClass, '\\') && ! in_array( + $fullyQualifiedAnnotationClass, + self::ALLOWED_SHORT_ANNOTATIONS, + true + )) { + continue; + } + + while (isset($phpDocNode->children[$key]) && $phpDocNode->children[$key] !== $phpDocChildNode) { + ++$key; + } + + $phpDocTextNode = new PhpDocTextNode($phpDocChildNode->value->value); + $startAndEnd = $phpDocChildNode->value->getAttribute(PhpDocAttributeKey::START_AND_END); + + if (! $startAndEnd instanceof StartAndEnd) { + $spacelessPhpDocTagNode = $this->createSpacelessPhpDocTagNode( + $phpDocChildNode->name, + $phpDocChildNode->value, + $fullyQualifiedAnnotationClass, + $currentPhpNode + ); + + $this->attributeMirrorer->mirror($phpDocChildNode, $spacelessPhpDocTagNode); + $phpDocNode->children[$key] = $spacelessPhpDocTagNode; + + continue; + } + + $phpDocTextNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + $spacelessPhpDocTagNodes = $this->resolveFqnAnnotationSpacelessPhpDocTagNode( + $phpDocTextNode, + $currentPhpNode + ); + + if ($spacelessPhpDocTagNodes === []) { + $spacelessPhpDocTagNode = $this->createSpacelessPhpDocTagNode( + $phpDocChildNode->name, + $phpDocChildNode->value, + $fullyQualifiedAnnotationClass, + $currentPhpNode + ); + + $this->attributeMirrorer->mirror($phpDocChildNode, $spacelessPhpDocTagNode); + $phpDocNode->children[$key] = $spacelessPhpDocTagNode; + + continue; + } + + Assert::isAOf($phpDocNode->children[$key], PhpDocTagNode::class); + + $texts = Strings::split($phpDocChildNode->value->value, self::NEWLINE_ANNOTATION_FQCN_REGEX); + $phpDocNode->children[$key]->value = new GenericTagValueNode($texts[0]); + $phpDocNode->children[$key]->value->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + + $spacelessPhpDocTagNode = $this->createSpacelessPhpDocTagNode( + $phpDocNode->children[$key]->name, + $phpDocNode->children[$key]->value, + $fullyQualifiedAnnotationClass, + $currentPhpNode + ); + + $this->attributeMirrorer->mirror($phpDocNode->children[$key], $spacelessPhpDocTagNode); + $phpDocNode->children[$key] = $spacelessPhpDocTagNode; + + // require to reprint the generic + $phpDocNode->children[$key]->value->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); + + array_splice($phpDocNode->children, $key + 1, 0, $spacelessPhpDocTagNodes); + } + } + + private function processDoctrine( + Node $currentPhpNode, + string $name, + PhpDocTagNode $phpDocTagNode, + PhpDocNode $phpDocNode, + mixed $key, + string $values + ): void { + $type = $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType( + $currentPhpNode, + new ObjectType($name), + $currentPhpNode->getAttribute(AttributeKey::SCOPE) + ); + + $fullyQualifiedAnnotationClass = null; + + if ($type instanceof ShortenedObjectType || $type instanceof AliasedObjectType) { + $fullyQualifiedAnnotationClass = $type->getFullyQualifiedName(); + } elseif ($type instanceof ObjectType) { + $fullyQualifiedAnnotationClass = $type->getClassName(); + } + + if ($fullyQualifiedAnnotationClass === null) { + return; + } + + if ($values !== '') { + $values = Strings::replace($values, self::STAR_COMMENT_REGEX); + + if ($phpDocTagNode->value instanceof DoctrineTagValueNode) { + $values = '(' . $values . ')'; + if ($phpDocTagNode->value->description !== '') { + $values .= $phpDocTagNode->value->description; + } + } + } + + $genericTagValueNode = new GenericTagValueNode($values); + $startAndEnd = $phpDocTagNode->getAttribute(PhpDocAttributeKey::START_AND_END); + $genericTagValueNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + + $spacelessPhpDocTagNode = $this->createSpacelessPhpDocTagNode( + '@' . $name, + $genericTagValueNode, + $fullyQualifiedAnnotationClass, + $currentPhpNode + ); + + $this->attributeMirrorer->mirror($phpDocTagNode, $spacelessPhpDocTagNode); + $phpDocNode->children[$key] = $spacelessPhpDocTagNode; + } + + private function processDescriptionAsSpacelessPhpDoctagNode( + PhpDocNode $phpDocNode, + PhpDocTagNode $phpDocTagNode, + Node $currentPhpNode, + int $key + ): void { + if (! property_exists($phpDocTagNode->value, 'description')) { + return; + } + + $description = (string) $phpDocTagNode->value->description; + if (! str_contains($description, "\n")) { + return; + } + + $phpDocTextNode = new PhpDocTextNode($description); + $startAndEnd = $phpDocTagNode->value->getAttribute(PhpDocAttributeKey::START_AND_END); + if (! $startAndEnd instanceof StartAndEnd) { + return; + } + + $phpDocTextNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + $spacelessPhpDocTagNodes = $this->resolveFqnAnnotationSpacelessPhpDocTagNode( + $phpDocTextNode, + $currentPhpNode + ); + + if ($spacelessPhpDocTagNodes === []) { + return; + } + + while (isset($phpDocNode->children[$key]) && $phpDocNode->children[$key] !== $phpDocTagNode) { + ++$key; + } + + unset($phpDocNode->children[$key]); + + $classNode = new PhpDocTagNode($phpDocTagNode->name, $phpDocTagNode->value); + $description = Strings::replace($description, self::LONG_ANNOTATION_REGEX, ''); + $description = substr($description, 0, -7); + + $phpDocTagNode->value->description = $description; + $phpDocNode->children[$key] = $classNode; + + array_splice($phpDocNode->children, $key + 1, 0, $spacelessPhpDocTagNodes); + } + + /** + * This is closed block, e.g. {( ... )}, + * false on: {( ... ) + */ + private function isClosedContent(string $composedContent): bool + { + $composedTokenIterator = $this->tokenIteratorFactory->create($composedContent); + $tokenCount = $composedTokenIterator->count(); + + $openBracketCount = 0; + $closeBracketCount = 0; + if ($composedContent === '') { + return true; + } + + do { + if ($composedTokenIterator->isCurrentTokenType( + Lexer::TOKEN_OPEN_CURLY_BRACKET, + Lexer::TOKEN_OPEN_PARENTHESES + ) || \str_contains($composedTokenIterator->currentTokenValue(), '{') + || \str_contains($composedTokenIterator->currentTokenValue(), '(') + ) { + ++$openBracketCount; + } + + if ( + $composedTokenIterator->isCurrentTokenType( + Lexer::TOKEN_CLOSE_CURLY_BRACKET, + Lexer::TOKEN_CLOSE_PARENTHESES + // sometimes it gets mixed int ") + ) || \str_contains($composedTokenIterator->currentTokenValue(), '}') + || \str_contains($composedTokenIterator->currentTokenValue(), ')')) { + ++$closeBracketCount; + } + + $composedTokenIterator->next(); + } while ($composedTokenIterator->currentPosition() < ($tokenCount - 1)); + + return $openBracketCount === $closeBracketCount; + } + + private function createSpacelessPhpDocTagNode( + string $tagName, + GenericTagValueNode $genericTagValueNode, + string $fullyQualifiedAnnotationClass, + Node $currentPhpNode + ): SpacelessPhpDocTagNode { + $formerStartEnd = $genericTagValueNode->getAttribute(PhpDocAttributeKey::START_AND_END); + + return $this->createDoctrineSpacelessPhpDocTagNode( + $genericTagValueNode->value, + $tagName, + $fullyQualifiedAnnotationClass, + $formerStartEnd, + $currentPhpNode + ); + } + + private function createDoctrineSpacelessPhpDocTagNode( + string $annotationContent, + string $tagName, + string $fullyQualifiedAnnotationClass, + StartAndEnd $startAndEnd, + Node $currentPhpNode + ): SpacelessPhpDocTagNode { + $nestedTokenIterator = $this->tokenIteratorFactory->create($annotationContent); + + // mimics doctrine behavior just in phpdoc-parser syntax :) + // https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L742 + + $values = $this->staticDoctrineAnnotationParser->resolveAnnotationMethodCall( + $nestedTokenIterator, + $currentPhpNode + ); + + $identifierTypeNode = new IdentifierTypeNode($tagName); + $identifierTypeNode->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $fullyQualifiedAnnotationClass); + + $doctrineAnnotationTagValueNode = new DoctrineAnnotationTagValueNode( + $identifierTypeNode, + $annotationContent, + $values, + SilentKeyMap::CLASS_NAMES_TO_SILENT_KEYS[$fullyQualifiedAnnotationClass] ?? null, + ); + + $doctrineAnnotationTagValueNode->setAttribute(PhpDocAttributeKey::START_AND_END, $startAndEnd); + + return new SpacelessPhpDocTagNode($tagName, $doctrineAnnotationTagValueNode); + } + + private function combineStartAndEnd( + \PHPStan\PhpDocParser\Ast\Node $startPhpDocChildNode, + PhpDocChildNode $endPhpDocChildNode + ): StartAndEnd { + /** @var StartAndEnd $currentStartAndEnd */ + $currentStartAndEnd = $startPhpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); + + /** @var StartAndEnd $nextStartAndEnd */ + $nextStartAndEnd = $endPhpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); + + return new StartAndEnd($currentStartAndEnd->getStart(), $nextStartAndEnd->getEnd()); + } + + /** + * @return SpacelessPhpDocTagNode[] + */ + private function resolveFqnAnnotationSpacelessPhpDocTagNode( + PhpDocTextNode $phpDocTextNode, + Node $currentPhpNode + ): array { + $matches = Strings::matchAll($phpDocTextNode->text, self::LONG_ANNOTATION_REGEX); + + $spacelessPhpDocTagNodes = []; + foreach ($matches as $match) { + $fullyQualifiedAnnotationClass = $match['class_name'] ?? null; + if ($fullyQualifiedAnnotationClass === null) { + continue; + } + + $nestedAnnotationOpen = explode('(', (string) $fullyQualifiedAnnotationClass); + $fullyQualifiedAnnotationClass = $nestedAnnotationOpen[0]; + + $tagName = '@\\' . $fullyQualifiedAnnotationClass; + + $formerStartEnd = $phpDocTextNode->getAttribute(PhpDocAttributeKey::START_AND_END); + + $annotationContent = $this->resolveAnnotationContent( + $match['annotation_content'] ?? '', + $nestedAnnotationOpen + ); + + $spacelessPhpDocTagNodes[] = $this->createDoctrineSpacelessPhpDocTagNode( + $annotationContent, + $tagName, + $fullyQualifiedAnnotationClass, + $formerStartEnd, + $currentPhpNode + ); + } + + return $spacelessPhpDocTagNodes; + } + + /** + * @param string[]|null[] $nestedAnnotationOpen + */ + private function resolveAnnotationContent(string $annotationContent, array $nestedAnnotationOpen): string + { + if (! isset($nestedAnnotationOpen[1])) { + return $annotationContent; + } + + $trimmedNestedAnnotationOpen = trim($nestedAnnotationOpen[1]); + if (str_ends_with($trimmedNestedAnnotationOpen, '{')) { + return $annotationContent; + } + + if ($trimmedNestedAnnotationOpen === '') { + return $annotationContent; + } + + return '("' . trim($trimmedNestedAnnotationOpen, '"\'') . '")'; + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/PhpDocTagGenericUsesDecorator.php b/src/BetterPhpDocParser/PhpDocParser/PhpDocTagGenericUsesDecorator.php new file mode 100644 index 00000000000..df31fe464a9 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/PhpDocTagGenericUsesDecorator.php @@ -0,0 +1,75 @@ +__toString(), '::')) { + return; + } + + $this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', function (Node $node) use ( + $phpNode + ): Node|null { + if (! $node instanceof PhpDocTagNode) { + return null; + } + + if (! $node->value instanceof GenericTagValueNode) { + return null; + } + + if (! in_array($node->name, ['@uses', '@used-by', '@see'], true)) { + return null; + } + + $reference = $node->value->value; + if (! str_contains($reference, '::')) { + return null; + } + + if ($node->value->hasAttribute(PhpDocAttributeKey::RESOLVED_CLASS)) { + return null; + } + + $classValue = explode('::', $reference)[0]; + $className = $this->resolveFullyQualifiedClass($classValue, $phpNode); + $node->value->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $className); + + return $node; + }); + } + + private function resolveFullyQualifiedClass(string $classValue, PhpNode $phpNode): string + { + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($phpNode); + return $nameScope->resolveStringName($classValue); + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php b/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php new file mode 100644 index 00000000000..79fbfe00660 --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php @@ -0,0 +1,137 @@ +isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + return []; + } + + $tokenIterator->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + + // empty () + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + return []; + } + + return $this->resolveAnnotationValues($tokenIterator, $currentPhpNode); + } + + /** + * @api tests + * @see https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1215-L1224 + * @return CurlyListNode|string|array|ConstExprNode|DoctrineAnnotationTagValueNode|StringNode + */ + public function resolveAnnotationValue( + BetterTokenIterator $tokenIterator, + Node $currentPhpNode + ): CurlyListNode | string | array | ConstExprNode | DoctrineAnnotationTagValueNode | StringNode { + // skips dummy tokens like newlines + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + // no assign + if (! $tokenIterator->isNextTokenType(Lexer::TOKEN_EQUAL)) { + // 1. plain value - mimics https://github.com/doctrine/annotations/blob/0cb0cd2950a5c6cdbf22adbe2bfd5fd1ea68588f/lib/Doctrine/Common/Annotations/DocParser.php#L1234-L1282 + return $this->parseValue($tokenIterator, $currentPhpNode); + } + + // 2. assign key = value - mimics FieldAssignment() https://github.com/doctrine/annotations/blob/0cb0cd2950a5c6cdbf22adbe2bfd5fd1ea68588f/lib/Doctrine/Common/Annotations/DocParser.php#L1291-L1303 + /** @var int $key */ + $key = $this->parseValue($tokenIterator, $currentPhpNode); + $tokenIterator->consumeTokenType(Lexer::TOKEN_EQUAL); + + // mimics https://github.com/doctrine/annotations/blob/1.13.x/lib/Doctrine/Common/Annotations/DocParser.php#L1236-L1238 + $value = $this->parseValue($tokenIterator, $currentPhpNode); + + return [ + // plain token value + $key => $value, + ]; + } + + /** + * @see https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1051-L1079 + * + * @return ArrayItemNode[] + */ + private function resolveAnnotationValues(BetterTokenIterator $tokenIterator, Node $currentPhpNode): array + { + $values = []; + $resolvedValue = $this->resolveAnnotationValue($tokenIterator, $currentPhpNode); + + if (is_array($resolvedValue)) { + $values = [...$values, ...$resolvedValue]; + } else { + $values[] = $resolvedValue; + } + + while ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_COMMA)) { + $tokenIterator->next(); + + // if is next item just closing brackets + if ($tokenIterator->isNextTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + continue; + } + + $nestedValues = $this->resolveAnnotationValue($tokenIterator, $currentPhpNode); + + if (is_array($nestedValues)) { + $values = [...$values, ...$nestedValues]; + } else { + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_END)) { + break; + } + + $values[] = $nestedValues; + } + } + + return $this->arrayParser->createArrayFromValues($values); + } + + /** + * @return CurlyListNode|string|array|ConstExprNode|DoctrineAnnotationTagValueNode|StringNode + */ + private function parseValue( + BetterTokenIterator $tokenIterator, + Node $currentPhpNode + ): CurlyListNode | string | array | ConstExprNode | DoctrineAnnotationTagValueNode | StringNode { + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET)) { + $items = $this->arrayParser->parseCurlyArray($tokenIterator, $currentPhpNode); + return new CurlyListNode($items); + } + + return $this->plainValueParser->parseValue($tokenIterator, $currentPhpNode); + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParser.php b/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParser.php new file mode 100644 index 00000000000..e9940c8b7dd --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/ArrayParser.php @@ -0,0 +1,230 @@ +isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + return []; + } + + $tokenIterator->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); + + // If the array is empty, stop parsing and return. + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + $tokenIterator->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); + return []; + } + + // first item + $values[] = $this->resolveArrayItem($tokenIterator, $currentPhpNode); + + // 2nd+ item + while ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_COMMA)) { + // optional trailing comma + $tokenIterator->consumeTokenType(Lexer::TOKEN_COMMA); + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + break; + } + + $values[] = $this->resolveArrayItem($tokenIterator, $currentPhpNode); + if ($tokenIterator->isNextTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + break; + } + + // skip newlines + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } + + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + // special case for nested doctrine annotations + if (! $tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); + } + + return $this->createArrayFromValues($values); + } + + /** + * @param mixed[] $values + * @return ArrayItemNode[] + */ + public function createArrayFromValues(array $values): array + { + $arrayItemNodes = []; + + $naturalKey = 0; + foreach ($values as $key => $value) { + if (is_array($value)) { + [$nestedKey, $nestedValue] = $value; + + if ($nestedKey instanceof ConstExprIntegerNode) { + $nestedKey = $nestedKey->value; + } + + // curly candidate? + $arrayItemNodes[] = $this->createArrayItemFromKeyAndValue($nestedKey, $nestedValue); + } else { + $arrayItemNodes[] = $this->createArrayItemFromKeyAndValue($key !== $naturalKey ? $key : null, $value); + } + + ++$naturalKey; + } + + return $arrayItemNodes; + } + + /** + * Mimics https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1354-L1385 + * @return array + */ + private function resolveArrayItem(BetterTokenIterator $tokenIterator, Node $currentPhpNode): array + { + // skip newlines + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + $key = null; + + // join "ClassName::CONSTANT_REFERENCE" to identifier + if ($tokenIterator->isNextTokenTypes([Lexer::TOKEN_DOUBLE_COLON])) { + $key = $tokenIterator->currentTokenValue(); + + // "::" + $tokenIterator->next(); + $key .= $tokenIterator->currentTokenValue(); + + $tokenIterator->consumeTokenType(Lexer::TOKEN_DOUBLE_COLON); + $key .= $tokenIterator->currentTokenValue(); + + $tokenIterator->next(); + } + + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET, Lexer::TOKEN_COMMA)) { + // it's a value, not a key + return [null, $key]; + } + + if ( + $tokenIterator->isCurrentTokenType(Lexer::TOKEN_EQUAL, Lexer::TOKEN_COLON) || + $tokenIterator->isNextTokenTypes([Lexer::TOKEN_EQUAL, Lexer::TOKEN_COLON]) + ) { + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_EQUAL); + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_COLON); + + if ($key === null) { + if ($tokenIterator->isNextTokenType(Lexer::TOKEN_IDENTIFIER)) { + $key = $this->plainValueParser->parseValue($tokenIterator, $currentPhpNode); + } else { + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_COMMA); + $key = $this->plainValueParser->parseValue($tokenIterator, $currentPhpNode); + } + } + + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_EQUAL); + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_COLON); + + return [$key, $this->plainValueParser->parseValue($tokenIterator, $currentPhpNode)]; + } + + return [$key, $this->plainValueParser->parseValue($tokenIterator, $currentPhpNode)]; + } + + /** + * @return String_::KIND_SINGLE_QUOTED|String_::KIND_DOUBLE_QUOTED|null + */ + private function resolveQuoteKind(mixed $val): ?int + { + if ($this->isQuotedWith($val, '"')) { + return String_::KIND_DOUBLE_QUOTED; + } + + if ($this->isQuotedWith($val, "'")) { + return String_::KIND_SINGLE_QUOTED; + } + + return null; + } + + private function createArrayItemFromKeyAndValue(mixed $rawKey, mixed $rawValue): ArrayItemNode + { + $valueQuoteKind = $this->resolveQuoteKind($rawValue); + if (is_string($rawValue) && $valueQuoteKind === String_::KIND_DOUBLE_QUOTED) { + // give raw value + $value = new StringNode(substr($rawValue, 1, strlen($rawValue) - 2)); + } elseif ($valueQuoteKind === null && is_string($rawValue)) { + $lowerRawValue = strtolower($rawValue); + $value = match ($lowerRawValue) { + 'null' => null, + 'true' => true, + 'false' => false, + default => $rawValue + }; + } else { + $value = $rawValue; + } + + $keyQuoteKind = $this->resolveQuoteKind($rawKey); + if (is_string($rawKey) && $keyQuoteKind === String_::KIND_DOUBLE_QUOTED) { + // give raw value + $key = new StringNode(substr($rawKey, 1, strlen($rawKey) - 2)); + } else { + $key = $rawKey; + } + + if (is_string($value) && $valueQuoteKind === String_::KIND_SINGLE_QUOTED) { + $value = trim($value, "'"); + } + + if ($key !== null) { + return new ArrayItemNode($value, $key); + } + + return new ArrayItemNode($value); + } + + private function isQuotedWith(mixed $value, string $quotes): bool + { + if (! is_string($value)) { + return false; + } + + if (! str_starts_with($value, $quotes)) { + return false; + } + + return str_ends_with($value, $quotes); + } +} diff --git a/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/PlainValueParser.php b/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/PlainValueParser.php new file mode 100644 index 00000000000..c6d7e9da51e --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/PlainValueParser.php @@ -0,0 +1,197 @@ +staticDoctrineAnnotationParser = $staticDoctrineAnnotationParser; + $this->arrayParser = $arrayParser; + } + + /** + * @return string|mixed[]|ConstExprNode|DoctrineAnnotationTagValueNode|StringNode + */ + public function parseValue( + BetterTokenIterator $tokenIterator, + Node $currentPhpNode + ): string | array | ConstExprNode | DoctrineAnnotationTagValueNode | StringNode { + $currentTokenValue = $tokenIterator->currentTokenValue(); + + // temporary hackaround multi-line doctrine annotations + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_END)) { + return $currentTokenValue; + } + + // consume the token + $isOpenCurlyArray = $tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); + if ($isOpenCurlyArray) { + return $this->arrayParser->parseCurlyArray($tokenIterator, $currentPhpNode); + } + + $tokenIterator->next(); + + // normalize value + $constExprNode = $this->matchConstantValue($currentTokenValue); + if ($constExprNode instanceof ConstExprNode) { + return $constExprNode; + } + + $currentTokenValue = $this->parseStringValue($tokenIterator, $currentTokenValue); + + // nested entity!, supported in attribute since PHP 8.1 + if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + return $this->parseNestedDoctrineAnnotationTagValueNode( + $currentTokenValue, + $tokenIterator, + $currentPhpNode + ); + } + + $start = $tokenIterator->currentPosition(); + + // from "quote to quote" + if ($currentTokenValue === '"') { + do { + $tokenIterator->next(); + } while (! str_contains($tokenIterator->currentTokenValue(), '"')); + } + + $end = $tokenIterator->currentPosition(); + if ($start + 1 < $end) { + return new StringNode($tokenIterator->printFromTo($start, $end)); + } + + return $currentTokenValue; + } + + private function parseStringValue(BetterTokenIterator $tokenIterator, string $currentTokenValue): string + { + if ( + // start with a quote but doesn't end with one + (str_starts_with($currentTokenValue, '"') && ! str_ends_with($currentTokenValue, '"')) + ) { + $currentTokenValue = $this->parseMultilineOrWhiteSpacedString($tokenIterator, $currentTokenValue); + } else { + while ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON) || + $tokenIterator->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER) + ) { + $currentTokenValue .= $tokenIterator->currentTokenValue(); + $tokenIterator->next(); + } + } + + return $currentTokenValue; + } + + private function parseMultilineOrWhiteSpacedString( + BetterTokenIterator $tokenIterator, + string $currentTokenValue + ): string { + while (str_starts_with($currentTokenValue, '"') && ! str_ends_with($currentTokenValue, '"')) { + if (! $tokenIterator->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + $currentTokenValue .= ' '; + } + + if (str_starts_with($currentTokenValue, '"') && str_contains( + $tokenIterator->currentTokenValue(), + '"' + ) && $currentTokenValue !== $tokenIterator->currentTokenValue()) { + //starts with '"' and current token contains '"', should be the end + $currentTokenValue .= substr( + $tokenIterator->currentTokenValue(), + 0, + ((int) strpos($tokenIterator->currentTokenValue(), '"') + 1) + ); + $tokenIterator->next(); + break; + } + + $currentTokenValue .= $tokenIterator->currentTokenValue(); + $tokenIterator->next(); + } + + if (str_starts_with($currentTokenValue, '"') && str_ends_with($currentTokenValue, '"')) { + return trim(str_replace('"', '', $currentTokenValue)); + } + + return $currentTokenValue; + } + + private function parseNestedDoctrineAnnotationTagValueNode( + string $currentTokenValue, + BetterTokenIterator $tokenIterator, + Node $currentPhpNode + ): DoctrineAnnotationTagValueNode { + // @todo + $annotationShortName = $currentTokenValue; + $values = $this->staticDoctrineAnnotationParser->resolveAnnotationMethodCall($tokenIterator, $currentPhpNode); + + $fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName( + $annotationShortName, + $currentPhpNode + ); + + // keep the last ")" + $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + if ($tokenIterator->currentTokenValue() === ')') { + $tokenIterator->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + } + + // keep original name to differentiate between short and FQN class + $identifierTypeNode = new IdentifierTypeNode($annotationShortName); + $identifierTypeNode->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $fullyQualifiedAnnotationClass); + + return new DoctrineAnnotationTagValueNode($identifierTypeNode, $annotationShortName, $values); + } + + private function matchConstantValue(string $currentTokenValue): ConstExprNode | null + { + if (strtolower($currentTokenValue) === 'false') { + return new ConstExprFalseNode(); + } + + if (strtolower($currentTokenValue) === 'true') { + return new ConstExprTrueNode(); + } + + if (! is_numeric($currentTokenValue)) { + return null; + } + + if ((string) (int) $currentTokenValue !== $currentTokenValue) { + return null; + } + + return new ConstExprIntegerNode($currentTokenValue); + } +} diff --git a/src/BetterPhpDocParser/Printer/DocBlockInliner.php b/src/BetterPhpDocParser/Printer/DocBlockInliner.php new file mode 100644 index 00000000000..9d456690c56 --- /dev/null +++ b/src/BetterPhpDocParser/Printer/DocBlockInliner.php @@ -0,0 +1,26 @@ +\r\n|\n)#"; + + private int $tokenCount = 0; + + private int $currentTokenPosition = 0; + + /** + * @var mixed[] + */ + private array $tokens = []; + + private ?PhpDocInfo $phpDocInfo = null; + + private readonly PhpDocNodeTraverser $changedPhpDocNodeTraverser; + + public function __construct( + private readonly EmptyPhpDocDetector $emptyPhpDocDetector, + private readonly DocBlockInliner $docBlockInliner, + private readonly RemoveNodesStartAndEndResolver $removeNodesStartAndEndResolver, + private readonly ChangedPhpDocNodeVisitor $changedPhpDocNodeVisitor, + ) { + $changedPhpDocNodeTraverser = new PhpDocNodeTraverser(); + $changedPhpDocNodeTraverser->addPhpDocNodeVisitor($this->changedPhpDocNodeVisitor); + + $this->changedPhpDocNodeTraverser = $changedPhpDocNodeTraverser; + } + + public function printNew(PhpDocInfo $phpDocInfo): string + { + $docContent = (string) $phpDocInfo->getPhpDocNode(); + if ($phpDocInfo->isSingleLine()) { + return $this->docBlockInliner->inline($docContent); + } + + if ($phpDocInfo->getNode() instanceof InlineHTML) { + return ''; + } + + return $docContent; + } + + /** + * As in php-parser + * + * ref: https://github.com/nikic/PHP-Parser/issues/487#issuecomment-375986259 + * - Tokens[node.startPos .. subnode1.startPos] + * - Print(subnode1) + * - Tokens[subnode1.endPos .. subnode2.startPos] + * - Print(subnode2) + * - Tokens[subnode2.endPos .. node.endPos] + */ + public function printFormatPreserving(PhpDocInfo $phpDocInfo): string + { + if ($phpDocInfo->getTokens() === []) { + // completely new one, just print string version of it + if ($phpDocInfo->getPhpDocNode()->children === []) { + return ''; + } + + if ($phpDocInfo->getNode() instanceof InlineHTML) { + return 'getPhpDocNode() . PHP_EOL . '?>'; + } + + return (string) $phpDocInfo->getPhpDocNode(); + } + + $phpDocNode = $phpDocInfo->getPhpDocNode(); + $this->tokens = $phpDocInfo->getTokens(); + + $this->tokenCount = $phpDocInfo->getTokenCount(); + $this->phpDocInfo = $phpDocInfo; + + $this->currentTokenPosition = 0; + + $phpDocString = $this->printPhpDocNode($phpDocNode); + + // hotfix of extra space with callable () + return Strings::replace($phpDocString, self::CALLABLE_REGEX, 'callable('); + } + + /** + * @return Comment[] + */ + public function printToComments(PhpDocInfo $phpDocInfo): array + { + $printedPhpDocContents = $this->printFormatPreserving($phpDocInfo); + return [new Comment($printedPhpDocContents)]; + } + + private function getCurrentPhpDocInfo(): PhpDocInfo + { + if (! $this->phpDocInfo instanceof PhpDocInfo) { + throw new ShouldNotHappenException(); + } + + return $this->phpDocInfo; + } + + private function printPhpDocNode(PhpDocNode $phpDocNode): string + { + // no nodes were, so empty doc + if ($this->emptyPhpDocDetector->isPhpDocNodeEmpty($phpDocNode)) { + return ''; + } + + $output = ''; + + // node output + $nodeCount = count($phpDocNode->children); + + foreach ($phpDocNode->children as $key => $phpDocChildNode) { + $output .= $this->printDocChildNode($phpDocChildNode, $key + 1, $nodeCount); + } + + $output = $this->printEnd($output); + + // fix missing start + if (! $this->hasDocblockStart($output) && $output !== '') { + $output = '/**' . $output; + } + + // fix missing end + if (str_starts_with($output, '/**') && ! StringUtils::isMatch($output, self::CLOSING_DOCBLOCK_REGEX)) { + $output .= ' */'; + } + + return Strings::replace( + $output, + self::NEW_LINE_WITH_SPACE_REGEX, + static fn (array $match): string => (string) $match['new_line'] + ); + } + + private function hasDocblockStart(string $output): bool + { + foreach (self::DOCBLOCK_STARTS as $docblockStart) { + if (str_starts_with($output, $docblockStart)) { + return true; + } + } + + return false; + } + + private function printDocChildNode( + PhpDocChildNode $phpDocChildNode, + int $key = 0, + int $nodeCount = 0 + ): string { + $output = ''; + + $shouldReprintChildNode = $this->shouldReprint($phpDocChildNode); + + if ($phpDocChildNode instanceof PhpDocTagNode && + ($shouldReprintChildNode && ( + $phpDocChildNode->value instanceof ParamTagValueNode || + $phpDocChildNode->value instanceof ThrowsTagValueNode || + $phpDocChildNode->value instanceof VarTagValueNode || + $phpDocChildNode->value instanceof ReturnTagValueNode || + $phpDocChildNode->value instanceof PropertyTagValueNode + ))) { + // the type has changed → reprint + $phpDocChildNodeStartEnd = $phpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); + + // bump the last position of token after just printed node + if ($phpDocChildNodeStartEnd instanceof StartAndEnd) { + $this->currentTokenPosition = $phpDocChildNodeStartEnd->getEnd(); + } + + return $this->standardPrintPhpDocChildNode($phpDocChildNode); + } + + /** @var StartAndEnd|null $startAndEnd */ + $startAndEnd = $phpDocChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); + + if ($startAndEnd instanceof StartAndEnd && ! $shouldReprintChildNode) { + $isLastToken = $nodeCount === $key; + + // correct previously changed node + $this->correctPreviouslyReprintedFirstNode($key, $startAndEnd); + + $output = $this->addTokensFromTo( + $output, + $this->currentTokenPosition, + $startAndEnd->getEnd(), + $isLastToken + ); + + $this->currentTokenPosition = $startAndEnd->getEnd(); + + return rtrim($output); + } + + if ($startAndEnd instanceof StartAndEnd) { + $this->currentTokenPosition = $startAndEnd->getEnd(); + } + + $standardPrintedPhpDocChildNode = $this->standardPrintPhpDocChildNode($phpDocChildNode); + return $output . $standardPrintedPhpDocChildNode; + } + + private function printEnd(string $output): string + { + $lastTokenPosition = $this->getCurrentPhpDocInfo() + ->getPhpDocNode() + ->getAttribute(PhpDocAttributeKey::LAST_PHP_DOC_TOKEN_POSITION); + + if ($lastTokenPosition === null) { + $lastTokenPosition = $this->currentTokenPosition; + } + + if ($lastTokenPosition === 0) { + return $output . "\n */"; + } + + return $this->addTokensFromTo($output, $lastTokenPosition, $this->tokenCount, true); + } + + private function addTokensFromTo(string $output, int $from, int $to, bool $shouldSkipEmptyLinesAbove): string + { + // skip removed nodes + $positionJumpSet = []; + + $removedStartAndEnds = $this->removeNodesStartAndEndResolver->resolve( + $this->getCurrentPhpDocInfo() + ->getOriginalPhpDocNode(), + $this->getCurrentPhpDocInfo() + ->getPhpDocNode(), + $this->tokens + ); + + foreach ($removedStartAndEnds as $removedStartAndEnd) { + $positionJumpSet[$removedStartAndEnd->getStart()] = $removedStartAndEnd->getEnd(); + } + + // include also space before, in case of inlined docs + if (isset($this->tokens[$from - 1]) && $this->tokens[$from - 1][1] === Lexer::TOKEN_HORIZONTAL_WS) { + --$from; + } + + // skip extra empty lines above if this is the last one + if ($shouldSkipEmptyLinesAbove && + \str_contains((string) $this->tokens[$from][0], "\n") && + \str_contains((string) $this->tokens[$from + 1][0], "\n") + ) { + ++$from; + } + + return $this->appendToOutput($output, $from, $to, $positionJumpSet); + } + + /** + * @param array $positionJumpSet + */ + private function appendToOutput(string $output, int $from, int $to, array $positionJumpSet): string + { + for ($i = $from; $i < $to; ++$i) { + while (isset($positionJumpSet[$i])) { + $i = $positionJumpSet[$i]; + } + + $output .= $this->tokens[$i][0] ?? ''; + } + + return $output; + } + + private function correctPreviouslyReprintedFirstNode(int $key, StartAndEnd $startAndEnd): void + { + if ($this->currentTokenPosition !== 0) { + return; + } + + if ($key === 1) { + return; + } + + $startTokenPosition = $startAndEnd->getStart(); + + $tokens = $this->getCurrentPhpDocInfo() + ->getTokens(); + if (! isset($tokens[$startTokenPosition - 1])) { + return; + } + + $previousToken = $tokens[$startTokenPosition - 1]; + if ($previousToken[1] === Lexer::TOKEN_PHPDOC_EOL) { + --$startTokenPosition; + } + + $this->currentTokenPosition = $startTokenPosition; + } + + private function shouldReprint(PhpDocChildNode $phpDocChildNode): bool + { + $this->changedPhpDocNodeTraverser->traverse($phpDocChildNode); + return $this->changedPhpDocNodeVisitor->hasChanged(); + } + + private function standardPrintPhpDocChildNode(PhpDocChildNode $phpDocChildNode): string + { + $printedNode = (string) $phpDocChildNode; + + if ($this->getCurrentPhpDocInfo()->isSingleLine()) { + return ' ' . $printedNode; + } + + return self::NEWLINE_WITH_ASTERISK . ($printedNode === '' ? '' : ' ' . $printedNode); + } +} diff --git a/packages/BetterPhpDocParser/Printer/RemoveNodesStartAndEndResolver.php b/src/BetterPhpDocParser/Printer/RemoveNodesStartAndEndResolver.php similarity index 84% rename from packages/BetterPhpDocParser/Printer/RemoveNodesStartAndEndResolver.php rename to src/BetterPhpDocParser/Printer/RemoveNodesStartAndEndResolver.php index 08061a46a77..bab55bc40b4 100644 --- a/packages/BetterPhpDocParser/Printer/RemoveNodesStartAndEndResolver.php +++ b/src/BetterPhpDocParser/Printer/RemoveNodesStartAndEndResolver.php @@ -13,7 +13,7 @@ final class RemoveNodesStartAndEndResolver { /** - * @param mixed[] $tokens + * @param array $tokens * @return StartAndEnd[] */ public function resolve(PhpDocNode $originalPhpDocNode, PhpDocNode $currentPhpDocNode, array $tokens): array @@ -26,9 +26,14 @@ public function resolve(PhpDocNode $originalPhpDocNode, PhpDocNode $currentPhpDo $lastEndPosition = null; foreach ($removedChildNodes as $removedChildNode) { - /** @var StartAndEnd $removedPhpDocNodeInfo */ + /** @var StartAndEnd|null $removedPhpDocNodeInfo */ $removedPhpDocNodeInfo = $removedChildNode->getAttribute(PhpDocAttributeKey::START_AND_END); + // it's not there when comment block has empty row "\s\*\n" + if (! $removedPhpDocNodeInfo instanceof StartAndEnd) { + continue; + } + // change start position to start of the line, so the whole line is removed $seekPosition = $removedPhpDocNodeInfo->getStart(); @@ -37,7 +42,7 @@ public function resolve(PhpDocNode $originalPhpDocNode, PhpDocNode $currentPhpDo break; } - // do not colide + // do not collide if ($lastEndPosition < $seekPosition) { break; } diff --git a/packages/BetterPhpDocParser/ValueObject/DoctrineAnnotation/SilentKeyMap.php b/src/BetterPhpDocParser/ValueObject/DoctrineAnnotation/SilentKeyMap.php similarity index 82% rename from packages/BetterPhpDocParser/ValueObject/DoctrineAnnotation/SilentKeyMap.php rename to src/BetterPhpDocParser/ValueObject/DoctrineAnnotation/SilentKeyMap.php index 7709e836626..364a122078b 100644 --- a/packages/BetterPhpDocParser/ValueObject/DoctrineAnnotation/SilentKeyMap.php +++ b/src/BetterPhpDocParser/ValueObject/DoctrineAnnotation/SilentKeyMap.php @@ -9,7 +9,7 @@ final class SilentKeyMap /** * @var array */ - public const CLASS_NAMES_TO_SILENT_KEYS = [ + public const array CLASS_NAMES_TO_SILENT_KEYS = [ 'Symfony\Component\Routing\Annotation\Route' => 'path', ]; } diff --git a/packages/BetterPhpDocParser/ValueObject/NodeTypes.php b/src/BetterPhpDocParser/ValueObject/NodeTypes.php similarity index 78% rename from packages/BetterPhpDocParser/ValueObject/NodeTypes.php rename to src/BetterPhpDocParser/ValueObject/NodeTypes.php index 4951c81da06..be56587e82c 100644 --- a/packages/BetterPhpDocParser/ValueObject/NodeTypes.php +++ b/src/BetterPhpDocParser/ValueObject/NodeTypes.php @@ -8,27 +8,30 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use Rector\Enum\ClassName; final class NodeTypes { /** * @var array> */ - public const TYPE_AWARE_NODES = [ + public const array TYPE_AWARE_NODES = [ VarTagValueNode::class, ParamTagValueNode::class, ReturnTagValueNode::class, ThrowsTagValueNode::class, PropertyTagValueNode::class, + TemplateTagValueNode::class, ]; /** * @var string[] */ - public const TYPE_AWARE_DOCTRINE_ANNOTATION_CLASSES = [ - 'JMS\Serializer\Annotation\Type', + public const array TYPE_AWARE_DOCTRINE_ANNOTATION_CLASSES = [ + ClassName::JMS_TYPE, 'Doctrine\ORM\Mapping\OneToMany', 'Symfony\Component\Validator\Constraints\Choice', 'Symfony\Component\Validator\Constraints\Email', diff --git a/src/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php b/src/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php new file mode 100644 index 00000000000..55c25bafd35 --- /dev/null +++ b/src/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php @@ -0,0 +1,130 @@ + $tokens + */ + public function __construct(array $tokens, int $index = 0) + { + if ($tokens === []) { + $index = 0; + } + + parent::__construct($tokens, $index); + } + + /** + * @param int[] $types + */ + public function isNextTokenTypes(array $types): bool + { + foreach ($types as $type) { + if ($this->isNextTokenType($type)) { + return true; + } + } + + return false; + } + + public function isTokenTypeOnPosition(int $tokenType, int $position): bool + { + $tokens = $this->getTokens(); + $token = $tokens[$position] ?? null; + + if ($token === null) { + return false; + } + + return $token[1] === $tokenType; + } + + public function isNextTokenType(int $tokenType): bool + { + if ($this->nextTokenType() === null) { + return false; + } + + return $this->nextTokenType() === $tokenType; + } + + public function printFromTo(int $from, int $to): string + { + if ($to < $from) { + throw new ShouldNotHappenException('Arguments are flipped'); + } + + $tokens = $this->getTokens(); + + $content = ''; + + foreach ($tokens as $key => $token) { + if ($key < $from) { + continue; + } + + if ($key >= $to) { + continue; + } + + $content .= $token[0]; + } + + return $content; + } + + public function currentPosition(): int + { + return $this->currentTokenIndex(); + } + + public function count(): int + { + return count($this->getTokens()); + } + + /** + * @return array + */ + public function partialTokens(int $start, int $end): array + { + return array_slice($this->getTokens(), $start, $end - $start + 1); + } + + public function containsTokenType(int $type): bool + { + foreach ($this->getTokens() as $token) { + if ($token[1] === $type) { + return true; + } + } + + return false; + } + + private function nextTokenType(): ?int + { + $tokens = $this->getTokens(); + + // does next token exist? + $nextIndex = $this->currentPosition() + 1; + if (! isset($tokens[$nextIndex])) { + return null; + } + + $this->pushSavePoint(); + $this->next(); + $nextTokenType = $this->currentTokenType(); + $this->rollback(); + + return $nextTokenType; + } +} diff --git a/src/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php b/src/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php new file mode 100644 index 00000000000..94d92f64a6b --- /dev/null +++ b/src/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php @@ -0,0 +1,165 @@ +values as $key => $value) { + if (! $this->isValueKeyEquals($value, $desiredKey)) { + continue; + } + + unset($this->values[$key]); + + // invoke reprint + $this->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); + } + } + + /** + * @return ArrayItemNode[] + */ + public function getValues(): array + { + return $this->values; + } + + /** + * @return ArrayItemNode[] + */ + public function getValuesWithSilentKey(): array + { + if ($this->silentKey === null) { + return $this->values; + } + + // to keep original values untouched, unless not changed + $silentKeyAwareValues = $this->values; + + foreach ($silentKeyAwareValues as $silentKeyAwareValue) { + if ($silentKeyAwareValue->key === null) { + $silentKeyAwareValue->key = $this->silentKey; + break; + } + } + + return $silentKeyAwareValues; + } + + public function getValue(string $desiredKey): ?ArrayItemNode + { + foreach ($this->values as $value) { + if ($this->isValueKeyEquals($value, $desiredKey)) { + return $value; + } + } + + return null; + } + + public function getSilentValue(): ?ArrayItemNode + { + foreach ($this->values as $value) { + if ($value->key === null) { + return $value; + } + } + + return null; + } + + public function markAsChanged(): void + { + $this->hasChanged = true; + } + + public function getOriginalContent(): ?string + { + return $this->originalContent; + } + + /** + * @param mixed[] $values + */ + protected function printValuesContent(array $values): string + { + $itemContents = ''; + $lastItemKey = array_key_last($values); + + foreach ($values as $key => $value) { + if (is_int($key)) { + $itemContents .= $this->stringifyValue($value); + } else { + $itemContents .= $key . '=' . $this->stringifyValue($value); + } + + if ($lastItemKey !== $key) { + $itemContents .= ', '; + } + } + + return $itemContents; + } + + private function isValueKeyEquals(ArrayItemNode $arrayItemNode, string $desiredKey): bool + { + if ($arrayItemNode->key instanceof StringNode) { + return $arrayItemNode->key->value === $desiredKey; + } + + return $arrayItemNode->key === $desiredKey; + } + + private function stringifyValue(mixed $value): string + { + // @todo resolve original casing + if ($value === false) { + return 'false'; + } + + if ($value === true) { + return 'true'; + } + + if (is_int($value)) { + return (string) $value; + } + + if (is_float($value)) { + return (string) $value; + } + + if (is_array($value)) { + return $this->printValuesContent($value); + } + + return (string) $value; + } +} diff --git a/src/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/CurlyListNode.php b/src/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/CurlyListNode.php new file mode 100644 index 00000000000..233578cb552 --- /dev/null +++ b/src/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/CurlyListNode.php @@ -0,0 +1,51 @@ +arrayItemNodes, ArrayItemNode::class); + parent::__construct($this->arrayItemNodes); + } + + public function __toString(): string + { + // possibly list items + return $this->implode($this->values); + } + + /** + * @param ArrayItemNode[] $array + */ + private function implode(array $array): string + { + $itemContents = ''; + $lastItemKey = array_key_last($array); + + foreach ($array as $key => $value) { + if (is_int($key)) { + $itemContents .= (string) $value; + } else { + $itemContents .= $key . '=' . $value; + } + + if ($lastItemKey !== $key) { + $itemContents .= ', '; + } + } + + return '{' . $itemContents . '}'; + } +} diff --git a/packages/BetterPhpDocParser/ValueObject/PhpDoc/SpacingAwareTemplateTagValueNode.php b/src/BetterPhpDocParser/ValueObject/PhpDoc/SpacingAwareTemplateTagValueNode.php similarity index 80% rename from packages/BetterPhpDocParser/ValueObject/PhpDoc/SpacingAwareTemplateTagValueNode.php rename to src/BetterPhpDocParser/ValueObject/PhpDoc/SpacingAwareTemplateTagValueNode.php index e3d6e63ecbd..d0fa17733b9 100644 --- a/packages/BetterPhpDocParser/ValueObject/PhpDoc/SpacingAwareTemplateTagValueNode.php +++ b/src/BetterPhpDocParser/ValueObject/PhpDoc/SpacingAwareTemplateTagValueNode.php @@ -4,6 +4,7 @@ namespace Rector\BetterPhpDocParser\ValueObject\PhpDoc; +use Override; use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use Stringable; @@ -14,16 +15,17 @@ public function __construct( string $name, ?TypeNode $typeNode, string $description, - private string $preposition + private readonly string $preposition ) { parent::__construct($name, $typeNode, $description); } + #[Override] public function __toString(): string { // @see https://github.com/rectorphp/rector/issues/3438 # 'as'/'of' - $bound = $this->bound !== null ? ' ' . $this->preposition . ' ' . $this->bound : ''; + $bound = $this->bound instanceof TypeNode ? ' ' . $this->preposition . ' ' . $this->bound : ''; $content = $this->name . $bound . ' ' . $this->description; return trim($content); diff --git a/src/BetterPhpDocParser/ValueObject/PhpDocAttributeKey.php b/src/BetterPhpDocParser/ValueObject/PhpDocAttributeKey.php new file mode 100644 index 00000000000..3311c8273a0 --- /dev/null +++ b/src/BetterPhpDocParser/ValueObject/PhpDocAttributeKey.php @@ -0,0 +1,23 @@ +start; + } + + public function getEnd(): int + { + return $this->end; + } +} diff --git a/packages/BetterPhpDocParser/ValueObject/Type/BracketsAwareIntersectionTypeNode.php b/src/BetterPhpDocParser/ValueObject/Type/BracketsAwareIntersectionTypeNode.php similarity index 92% rename from packages/BetterPhpDocParser/ValueObject/Type/BracketsAwareIntersectionTypeNode.php rename to src/BetterPhpDocParser/ValueObject/Type/BracketsAwareIntersectionTypeNode.php index d1a0afae706..f0544b25f94 100644 --- a/packages/BetterPhpDocParser/ValueObject/Type/BracketsAwareIntersectionTypeNode.php +++ b/src/BetterPhpDocParser/ValueObject/Type/BracketsAwareIntersectionTypeNode.php @@ -4,11 +4,13 @@ namespace Rector\BetterPhpDocParser\ValueObject\Type; +use Override; use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; use Stringable; final class BracketsAwareIntersectionTypeNode extends IntersectionTypeNode implements Stringable { + #[Override] public function __toString(): string { return implode('&', $this->types); diff --git a/src/BetterPhpDocParser/ValueObject/Type/BracketsAwareUnionTypeNode.php b/src/BetterPhpDocParser/ValueObject/Type/BracketsAwareUnionTypeNode.php new file mode 100644 index 00000000000..e4bff800d1a --- /dev/null +++ b/src/BetterPhpDocParser/ValueObject/Type/BracketsAwareUnionTypeNode.php @@ -0,0 +1,50 @@ +types as $type) { + $types[] = (string) $type; + } + + $types = array_unique($types); + if (! $this->isWrappedInBrackets) { + return implode('|', $types); + } + + return '(' . implode('|', $types) . ')'; + } + + public function isWrappedInBrackets(): bool + { + return $this->isWrappedInBrackets; + } +} diff --git a/packages/BetterPhpDocParser/ValueObject/Type/FullyQualifiedIdentifierTypeNode.php b/src/BetterPhpDocParser/ValueObject/Type/FullyQualifiedIdentifierTypeNode.php similarity index 79% rename from packages/BetterPhpDocParser/ValueObject/Type/FullyQualifiedIdentifierTypeNode.php rename to src/BetterPhpDocParser/ValueObject/Type/FullyQualifiedIdentifierTypeNode.php index 8d8a04ebffe..ba15a85d3df 100644 --- a/packages/BetterPhpDocParser/ValueObject/Type/FullyQualifiedIdentifierTypeNode.php +++ b/src/BetterPhpDocParser/ValueObject/Type/FullyQualifiedIdentifierTypeNode.php @@ -4,13 +4,15 @@ namespace Rector\BetterPhpDocParser\ValueObject\Type; +use Override; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use Stringable; final class FullyQualifiedIdentifierTypeNode extends IdentifierTypeNode implements Stringable { + #[Override] public function __toString(): string { - return '\\' . $this->name; + return '\\' . ltrim($this->name, '\\'); } } diff --git a/src/BetterPhpDocParser/ValueObject/Type/ShortenedIdentifierTypeNode.php b/src/BetterPhpDocParser/ValueObject/Type/ShortenedIdentifierTypeNode.php new file mode 100644 index 00000000000..8ffded9528b --- /dev/null +++ b/src/BetterPhpDocParser/ValueObject/Type/ShortenedIdentifierTypeNode.php @@ -0,0 +1,12 @@ +type instanceof CallableTypeNode) { + return sprintf('(%s)[]', (string) $this->type); + } + + $typeAsString = (string) $this->type; + + if ($this->isGenericArrayCandidate($this->type)) { + return sprintf('array<%s>', $typeAsString); + } + + if ($this->type instanceof ArrayTypeNode) { + return $this->printArrayType($this->type); + } + + if ($this->type instanceof BracketsAwareUnionTypeNode) { + return $this->printUnionType($this->type); + } + + return $typeAsString . '[]'; + } + + private function isGenericArrayCandidate(TypeNode $typeNode): bool + { + $hasGenericTypeParent = (bool) $this->getAttribute(ArrayTypeMapper::HAS_GENERIC_TYPE_PARENT); + if (! $hasGenericTypeParent) { + return false; + } + + return $typeNode instanceof UnionTypeNode || $typeNode instanceof ArrayTypeNode; + } + + private function printArrayType(ArrayTypeNode $arrayTypeNode): string + { + $typeAsString = (string) $arrayTypeNode; + + $singleTypesAsString = explode('|', $typeAsString); + foreach ($singleTypesAsString as $key => $singleTypeAsString) { + $singleTypesAsString[$key] = $singleTypeAsString . '[]'; + } + + return implode('|', $singleTypesAsString); + } + + private function printUnionType(BracketsAwareUnionTypeNode $bracketsAwareUnionTypeNode): string + { + if ($bracketsAwareUnionTypeNode->isWrappedInBrackets()) { + return $bracketsAwareUnionTypeNode . '[]'; + } + + // If all types in the union are GenericTypeNode, use array syntax + $allGeneric = true; + $firstGenericTypeName = null; + + foreach ($bracketsAwareUnionTypeNode->types as $unionedType) { + if (! $unionedType instanceof GenericTypeNode) { + $allGeneric = false; + break; + } + + // ensure only check on base level + // avoid mix usage without [] added + if (count($unionedType->genericTypes) !== 1) { + $allGeneric = false; + break; + } + + // ensure all generic types has the same base type + $currentTypeName = $unionedType->type->name; + + if ($firstGenericTypeName === null) { + $firstGenericTypeName = $currentTypeName; + } elseif ($firstGenericTypeName !== $currentTypeName) { + // Different generic base types (e.g., class-string vs array) + $allGeneric = false; + break; + } + } + + if ($allGeneric) { + return sprintf('array', (string) $bracketsAwareUnionTypeNode); + } + + $unionedTypes = []; + foreach ($bracketsAwareUnionTypeNode->types as $unionedType) { + $unionedTypes[] = $unionedType . '[]'; + } + + return implode('|', $unionedTypes); + } +} diff --git a/packages/BetterPhpDocParser/ValueObject/Type/SpacingAwareCallableTypeNode.php b/src/BetterPhpDocParser/ValueObject/Type/SpacingAwareCallableTypeNode.php similarity index 86% rename from packages/BetterPhpDocParser/ValueObject/Type/SpacingAwareCallableTypeNode.php rename to src/BetterPhpDocParser/ValueObject/Type/SpacingAwareCallableTypeNode.php index b66127d59a2..7eb7ddf16ec 100644 --- a/packages/BetterPhpDocParser/ValueObject/Type/SpacingAwareCallableTypeNode.php +++ b/src/BetterPhpDocParser/ValueObject/Type/SpacingAwareCallableTypeNode.php @@ -4,16 +4,13 @@ namespace Rector\BetterPhpDocParser\ValueObject\Type; -use PHPStan\PhpDocParser\Ast\NodeAttributes; +use Override; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; -use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; -use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use Stringable; final class SpacingAwareCallableTypeNode extends CallableTypeNode implements Stringable { - use NodeAttributes; - + #[Override] public function __toString(): string { // keep original (Psalm?) format, see https://github.com/rectorphp/rector/issues/2841 @@ -22,12 +19,9 @@ public function __toString(): string private function createExplicitCallable(): string { - /** @var IdentifierTypeNode|GenericTypeNode $returnType */ - $returnType = $this->returnType; - $parameterTypeString = $this->createParameterTypeString(); - $returnTypeAsString = (string) $returnType; + $returnTypeAsString = (string) $this->returnType; if (\str_contains($returnTypeAsString, '|')) { $returnTypeAsString = '(' . $returnTypeAsString . ')'; } @@ -55,12 +49,15 @@ private function normalizeParameterType(string $parameterTypeString, string $ret if ($parameterTypeString !== '') { return '(' . $parameterTypeString . ')'; } + if ($returnTypeAsString === 'mixed') { return $parameterTypeString; } + if ($returnTypeAsString === '') { return $parameterTypeString; } + return '()'; } @@ -69,9 +66,11 @@ private function normalizeReturnType(string $parameterTypeString, string $return if ($returnTypeAsString !== 'mixed') { return ':' . $returnTypeAsString; } + if ($parameterTypeString !== '') { return ':' . $returnTypeAsString; } + return ''; } } diff --git a/src/Bootstrap/ExtensionConfigResolver.php b/src/Bootstrap/ExtensionConfigResolver.php index fd85455eb5e..3d700341342 100644 --- a/src/Bootstrap/ExtensionConfigResolver.php +++ b/src/Bootstrap/ExtensionConfigResolver.php @@ -2,32 +2,33 @@ declare(strict_types=1); -namespace Rector\Core\Bootstrap; +namespace Rector\Bootstrap; use Rector\RectorInstaller\GeneratedConfig; use ReflectionClass; -use Symplify\SmartFileSystem\SmartFileInfo; final class ExtensionConfigResolver { /** - * @return SmartFileInfo[] + * @api + * @return string[] */ public function provide(): array { - $configFileInfos = []; + $configFilePaths = []; if (! class_exists('Rector\RectorInstaller\GeneratedConfig')) { - return $configFileInfos; + return $configFilePaths; } $generatedConfigReflectionClass = new ReflectionClass('Rector\RectorInstaller\GeneratedConfig'); if ($generatedConfigReflectionClass->getFileName() === false) { - return $configFileInfos; + return $configFilePaths; } $generatedConfigDirectory = dirname($generatedConfigReflectionClass->getFileName()); foreach (GeneratedConfig::EXTENSIONS as $extensionConfig) { + /** @var string[] $includedFiles */ $includedFiles = $extensionConfig['extra']['includes'] ?? []; foreach ($includedFiles as $includedFile) { @@ -38,14 +39,16 @@ public function provide(): array ); if ($includedFilePath === null) { - $includedFilePath = sprintf('%s/%s', $extensionConfig['install_path'], $includedFile); + /** @var string $installPath */ + $installPath = $extensionConfig['install_path']; + $includedFilePath = sprintf('%s/%s', $installPath, $includedFile); } - $configFileInfos[] = new SmartFileInfo($includedFilePath); + $configFilePaths[] = $includedFilePath; } } - return $configFileInfos; + return $configFilePaths; } /** @@ -69,9 +72,11 @@ private function resolveIncludeFilePath( if (! file_exists($includedFilePath)) { return null; } + if (! is_readable($includedFilePath)) { return null; } + return $includedFilePath; } } diff --git a/src/Bootstrap/RectorConfigsResolver.php b/src/Bootstrap/RectorConfigsResolver.php index 7db67243536..052f75a58f9 100644 --- a/src/Bootstrap/RectorConfigsResolver.php +++ b/src/Bootstrap/RectorConfigsResolver.php @@ -2,78 +2,62 @@ declare(strict_types=1); -namespace Rector\Core\Bootstrap; +namespace Rector\Bootstrap; -use Rector\Core\ValueObject\Bootstrap\BootstrapConfigs; +use Rector\ValueObject\Bootstrap\BootstrapConfigs; use Symfony\Component\Console\Input\ArgvInput; -use Symplify\SmartFileSystem\Exception\FileNotFoundException; -use Symplify\SmartFileSystem\SmartFileInfo; +use Webmozart\Assert\Assert; final class RectorConfigsResolver { + public const string DEFAULT_CONFIG_FILE = 'rector.php'; + + public const string DEFAULT_DIST_CONFIG_FILE = 'rector.dist.php'; + public function provide(): BootstrapConfigs { $argvInput = new ArgvInput(); - $mainConfigFileInfo = $this->resolveFromInputWithFallback($argvInput, 'rector.php'); + $mainConfigFile = $this->resolveFromInputWithFallback($argvInput); - $rectorRecipeConfigFileInfo = $this->resolveRectorRecipeConfig($argvInput); - - $configFileInfos = []; - if ($rectorRecipeConfigFileInfo !== null) { - $configFileInfos[] = $rectorRecipeConfigFileInfo; - } - - return new BootstrapConfigs($mainConfigFileInfo, $configFileInfos); + return new BootstrapConfigs($mainConfigFile, []); } - private function resolveRectorRecipeConfig(ArgvInput $argvInput): ?SmartFileInfo + private function resolveFromInput(ArgvInput $argvInput): ?string { - if ($argvInput->getFirstArgument() !== 'generate') { + $configFile = $this->getOptionValue($argvInput, ['--config', '-c']); + if ($configFile === null) { return null; } - // autoload rector recipe file if present, just for \Rector\RectorGenerator\Command\GenerateCommand - $rectorRecipeFilePath = getcwd() . '/rector-recipe.php'; - if (! file_exists($rectorRecipeFilePath)) { - return null; - } + Assert::fileExists($configFile); - return new SmartFileInfo($rectorRecipeFilePath); + return realpath($configFile); } - private function resolveFromInput(ArgvInput $argvInput): ?SmartFileInfo + private function resolveFromInputWithFallback(ArgvInput $argvInput): ?string { - $configValue = $this->getOptionValue($argvInput, ['--config', '-c']); - if ($configValue === null) { - return null; - } - - if (! file_exists($configValue)) { - $message = sprintf('File "%s" was not found', $configValue); - throw new FileNotFoundException($message); + $configFile = $this->resolveFromInput($argvInput); + if ($configFile !== null) { + return $configFile; } - return new SmartFileInfo($configValue); - } - - private function resolveFromInputWithFallback(ArgvInput $argvInput, string $fallbackFile): ?SmartFileInfo - { - $configFileInfo = $this->resolveFromInput($argvInput); - if ($configFileInfo !== null) { - return $configFileInfo; + // Try rector.php first, then fall back to rector.dist.php + $rectorConfigFile = $this->createFallbackFileInfoIfFound(self::DEFAULT_CONFIG_FILE); + if ($rectorConfigFile !== null) { + return $rectorConfigFile; } - return $this->createFallbackFileInfoIfFound($fallbackFile); + return $this->createFallbackFileInfoIfFound(self::DEFAULT_DIST_CONFIG_FILE); } - private function createFallbackFileInfoIfFound(string $fallbackFile): ?SmartFileInfo + private function createFallbackFileInfoIfFound(string $fallbackFile): ?string { $rootFallbackFile = getcwd() . DIRECTORY_SEPARATOR . $fallbackFile; if (! is_file($rootFallbackFile)) { return null; } - return new SmartFileInfo($rootFallbackFile); + return $rootFallbackFile; } /** diff --git a/src/Bridge/SetProviderCollector.php b/src/Bridge/SetProviderCollector.php new file mode 100644 index 00000000000..9ac1c7b9fe3 --- /dev/null +++ b/src/Bridge/SetProviderCollector.php @@ -0,0 +1,89 @@ +setProviders = array_merge($setProviders, $extraSetProviders); + } + + /** + * @return array + */ + public function provide(): array + { + return $this->setProviders; + } + + /** + * @return array + */ + public function provideSets(): array + { + $sets = []; + + foreach ($this->setProviders as $setProvider) { + $sets = array_merge($sets, $setProvider->provide()); + } + + return $sets; + } + + /** + * @return array + */ + public function provideComposerTriggeredSets(): array + { + return array_filter( + $this->provideSets(), + fn (SetInterface $set): bool => $set instanceof ComposerTriggeredSet + ); + } +} diff --git a/src/Bridge/SetRectorsResolver.php b/src/Bridge/SetRectorsResolver.php new file mode 100644 index 00000000000..c54c7240302 --- /dev/null +++ b/src/Bridge/SetRectorsResolver.php @@ -0,0 +1,81 @@ +|array, mixed[]>> + */ + public function resolveFromFilePathsIncludingConfiguration(array $configFilePaths): array + { + Assert::allString($configFilePaths); + Assert::allFileExists($configFilePaths); + + $combinedRectorRulesWithConfiguration = []; + + foreach ($configFilePaths as $configFilePath) { + $rectorRulesWithConfiguration = $this->resolveFromFilePathIncludingConfiguration($configFilePath); + + $combinedRectorRulesWithConfiguration = array_merge( + $combinedRectorRulesWithConfiguration, + $rectorRulesWithConfiguration + ); + } + + return $combinedRectorRulesWithConfiguration; + } + + /** + * @return array|array, mixed[]>> + */ + public function resolveFromFilePathIncludingConfiguration(string $configFilePath): array + { + $rectorConfig = $this->loadRectorConfigFromFilePath($configFilePath); + + $rectorClassesWithOptionalConfiguration = $rectorConfig->getMainRectorClasses(); + + foreach ($rectorConfig->getRuleConfigurations() as $rectorClass => $configuration) { + // remove from non-configurable, if added again with better config + if (in_array($rectorClass, $rectorClassesWithOptionalConfiguration)) { + $rectorRulePosition = array_search($rectorClass, $rectorClassesWithOptionalConfiguration, true); + if (is_int($rectorRulePosition)) { + unset($rectorClassesWithOptionalConfiguration[$rectorRulePosition]); + } + } + + $rectorClassesWithOptionalConfiguration[] = [ + $rectorClass => $configuration, + ]; + } + + // sort keys + return array_values($rectorClassesWithOptionalConfiguration); + } + + private function loadRectorConfigFromFilePath(string $configFilePath): RectorConfig + { + Assert::fileExists($configFilePath); + + $rectorConfig = new RectorConfig(); + + /** @var callable $configCallable */ + $configCallable = require $configFilePath; + $configCallable($rectorConfig); + + return $rectorConfig; + } +} diff --git a/packages/Caching/Cache.php b/src/Caching/Cache.php similarity index 76% rename from packages/Caching/Cache.php rename to src/Caching/Cache.php index 5733a49b1a0..33de20dd83c 100644 --- a/packages/Caching/Cache.php +++ b/src/Caching/Cache.php @@ -5,8 +5,9 @@ namespace Rector\Caching; use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface; +use Rector\Caching\Enum\CacheKey; -final class Cache +final readonly class Cache { public function __construct( private CacheStorageInterface $cacheStorage @@ -14,6 +15,7 @@ public function __construct( } /** + * @param CacheKey::* $variableKey * @return mixed|null */ public function load(string $key, string $variableKey) @@ -22,9 +24,9 @@ public function load(string $key, string $variableKey) } /** - * @param mixed $data + * @param CacheKey::* $variableKey */ - public function save(string $key, string $variableKey, $data): void + public function save(string $key, string $variableKey, mixed $data): void { $this->cacheStorage->save($key, $variableKey, $data); } diff --git a/src/Caching/CacheFactory.php b/src/Caching/CacheFactory.php new file mode 100644 index 00000000000..5277e982734 --- /dev/null +++ b/src/Caching/CacheFactory.php @@ -0,0 +1,45 @@ +fileSystem->exists($cacheDirectory)) { + $this->fileSystem->mkdir($cacheDirectory); + } + + $fileCacheStorage = new FileCacheStorage($cacheDirectory, $this->fileSystem); + return new Cache($fileCacheStorage); + } + + return new Cache(new MemoryCacheStorage()); + } +} diff --git a/src/Caching/Config/FileHashComputer.php b/src/Caching/Config/FileHashComputer.php new file mode 100644 index 00000000000..ce084d61ec3 --- /dev/null +++ b/src/Caching/Config/FileHashComputer.php @@ -0,0 +1,57 @@ +ensureIsPhp($filePath); + + $parametersHash = SimpleParameterProvider::hash(); + $extensionHash = $this->computeExtensionHash(); + return sha1($filePath . $parametersHash . $extensionHash . VersionResolver::PACKAGE_VERSION); + } + + private function computeExtensionHash(): string + { + $extensionHash = ''; + foreach ($this->cacheMetaExtensions as $cacheMetumExtension) { + $extensionHash .= $cacheMetumExtension->getKey() . ':' . $cacheMetumExtension->getHash(); + } + + return $extensionHash; + } + + private function ensureIsPhp(string $filePath): void + { + $fileExtension = pathinfo($filePath, PATHINFO_EXTENSION); + if ($fileExtension === 'php') { + return; + } + + throw new ShouldNotHappenException(sprintf( + // getRealPath() cannot be used, as it breaks in phar + 'Provide only PHP file, ready for Dependency Injection. "%s" given', + $filePath + )); + } +} diff --git a/src/Caching/Contract/CacheMetaExtensionInterface.php b/src/Caching/Contract/CacheMetaExtensionInterface.php new file mode 100644 index 00000000000..dd0e05657c7 --- /dev/null +++ b/src/Caching/Contract/CacheMetaExtensionInterface.php @@ -0,0 +1,26 @@ + + */ + private array $cacheableFiles = []; + + public function __construct( + private readonly FileHashComputer $fileHashComputer, + private readonly Cache $cache, + private readonly FileHasher $fileHasher + ) { + } + + public function cacheFile(string $filePath): void + { + $filePathCacheKey = $this->getFilePathCacheKey($filePath); + + if (! isset($this->cacheableFiles[$filePathCacheKey])) { + return; + } + + $hash = $this->hashFile($filePath); + + $this->cache->save($filePathCacheKey, CacheKey::FILE_HASH_KEY, $hash); + } + + public function addCacheableFile(string $filePath): void + { + $filePathCacheKey = $this->getFilePathCacheKey($filePath); + $this->cacheableFiles[$filePathCacheKey] = true; + } + + public function hasFileChanged(string $filePath): bool + { + $fileInfoCacheKey = $this->getFilePathCacheKey($filePath); + $cachedValue = $this->cache->load($fileInfoCacheKey, CacheKey::FILE_HASH_KEY); + + if ($cachedValue !== null) { + $currentFileHash = $this->hashFile($filePath); + return $currentFileHash !== $cachedValue; + } + + // we don't have a value to compare against. Be defensive and assume its changed + return true; + } + + public function invalidateFile(string $filePath): void + { + $fileInfoCacheKey = $this->getFilePathCacheKey($filePath); + $this->cache->clean($fileInfoCacheKey); + unset($this->cacheableFiles[$fileInfoCacheKey]); + } + + public function clear(): void + { + $this->cache->clear(); + } + + /** + * @api + */ + public function setFirstResolvedConfigFileInfo(string $filePath): void + { + // the first config is core to all → if it was changed, just invalidate it + $configHash = $this->fileHashComputer->compute($filePath); + $this->storeConfigurationDataHash($filePath, $configHash); + } + + private function resolvePath(string $filePath): string + { + $realPath = realpath($filePath); + if ($realPath === false) { + return $filePath; + } + + return $realPath; + } + + private function getFilePathCacheKey(string $filePath): string + { + return $this->fileHasher->hash($this->resolvePath($filePath)); + } + + private function hashFile(string $filePath): string + { + return $this->fileHasher->hashFiles([$this->resolvePath($filePath)]); + } + + private function storeConfigurationDataHash(string $filePath, string $configurationHash): void + { + $key = CacheKey::CONFIGURATION_HASH_KEY . '_' . $this->getFilePathCacheKey($filePath); + $this->invalidateCacheIfConfigurationChanged($key, $configurationHash); + + $this->cache->save($key, CacheKey::CONFIGURATION_HASH_KEY, $configurationHash); + } + + private function invalidateCacheIfConfigurationChanged(string $key, string $configurationHash): void + { + $oldCachedValue = $this->cache->load($key, CacheKey::CONFIGURATION_HASH_KEY); + if ($oldCachedValue === null) { + return; + } + + if ($oldCachedValue === $configurationHash) { + return; + } + + // should be unique per getcwd() + $this->clear(); + } +} diff --git a/src/Caching/Enum/CacheKey.php b/src/Caching/Enum/CacheKey.php new file mode 100644 index 00000000000..eaffc1c6c09 --- /dev/null +++ b/src/Caching/Enum/CacheKey.php @@ -0,0 +1,15 @@ +changedFilesDetector->hasFileChanged($filePath)) { + continue; + } + + $changedFileInfos[] = $filePath; + $this->changedFilesDetector->invalidateFile($filePath); + } + + return $changedFileInfos; + } +} diff --git a/packages/Caching/ValueObject/CacheFilePaths.php b/src/Caching/ValueObject/CacheFilePaths.php similarity index 93% rename from packages/Caching/ValueObject/CacheFilePaths.php rename to src/Caching/ValueObject/CacheFilePaths.php index 1d8933d60f6..93d6a7e4b0c 100644 --- a/packages/Caching/ValueObject/CacheFilePaths.php +++ b/src/Caching/ValueObject/CacheFilePaths.php @@ -4,7 +4,7 @@ namespace Rector\Caching\ValueObject; -final class CacheFilePaths +final readonly class CacheFilePaths { public function __construct( private string $firstDirectory, diff --git a/packages/Caching/ValueObject/CacheItem.php b/src/Caching/ValueObject/CacheItem.php similarity index 86% rename from packages/Caching/ValueObject/CacheItem.php rename to src/Caching/ValueObject/CacheItem.php index 17ba907fb2d..f344bd82b72 100644 --- a/packages/Caching/ValueObject/CacheItem.php +++ b/src/Caching/ValueObject/CacheItem.php @@ -8,19 +8,16 @@ * Inspired by * https://github.com/phpstan/phpstan-src/commit/eeae2da7999b2e8b7b04542c6175d46f80c6d0b9#diff-6dc14f6222bf150e6840ca44a7126653052a1cedc6a149b4e5c1e1a2c80eacdc */ -final class CacheItem +final readonly class CacheItem { - /** - * @param mixed $data - */ public function __construct( private string $variableKey, - private $data + private mixed $data ) { } /** - * @param mixed[] $properties + * @param array $properties */ public static function __set_state(array $properties): self { diff --git a/src/Caching/ValueObject/Storage/FileCacheStorage.php b/src/Caching/ValueObject/Storage/FileCacheStorage.php new file mode 100644 index 00000000000..94a4172cf8f --- /dev/null +++ b/src/Caching/ValueObject/Storage/FileCacheStorage.php @@ -0,0 +1,137 @@ +getCacheFilePaths($key); + + $filePath = $cacheFilePaths->getFilePath(); + if (! \is_file($filePath)) { + return null; + } + + $cacheItem = (require $filePath); + if (! $cacheItem instanceof CacheItem) { + return null; + } + + if (! $cacheItem->isVariableKeyValid($variableKey)) { + return null; + } + + return $cacheItem->getData(); + })($key, $variableKey); + } + + public function save(string $key, string $variableKey, mixed $data): void + { + $cacheFilePaths = $this->getCacheFilePaths($key); + $this->filesystem-> mkdir($cacheFilePaths->getFirstDirectory()); + $this->filesystem->mkdir($cacheFilePaths->getSecondDirectory()); + + $filePath = $cacheFilePaths->getFilePath(); + + $tmpPath = \sprintf('%s/%s.tmp', $this->directory, Random::generate()); + $errorBefore = \error_get_last(); + $exported = @\var_export(new CacheItem($variableKey, $data), true); + $errorAfter = \error_get_last(); + if ($errorAfter !== null && $errorBefore !== $errorAfter) { + throw new CachingException(\sprintf( + 'Error occurred while saving item %s (%s) to cache: %s', + $key, + $variableKey, + $errorAfter['message'] + )); + } + + // for performance reasons we don't use SmartFileSystem + FileSystem::write($tmpPath, \sprintf("getCacheFilePaths($key); + $this->processRemoveCacheFilePath($cacheFilePaths); + $this->processRemoveEmptyDirectory($cacheFilePaths->getSecondDirectory()); + $this->processRemoveEmptyDirectory($cacheFilePaths->getFirstDirectory()); + } + + public function clear(): void + { + FileSystem::delete($this->directory); + } + + private function processRemoveCacheFilePath(CacheFilePaths $cacheFilePaths): void + { + $filePath = $cacheFilePaths->getFilePath(); + if (! $this->filesystem->exists($filePath)) { + return; + } + + FileSystem::delete($filePath); + } + + private function processRemoveEmptyDirectory(string $directory): void + { + if (! $this->filesystem->exists($directory)) { + return; + } + + if ($this->isNotEmptyDirectory($directory)) { + return; + } + + FileSystem::delete($directory); + } + + private function isNotEmptyDirectory(string $directory): bool + { + // FilesystemIterator will initially point to the first file in the folder - if there are no files in the folder, valid() will return false + $filesystemIterator = new FilesystemIterator($directory); + return $filesystemIterator->valid(); + } + + private function getCacheFilePaths(string $key): CacheFilePaths + { + $keyHash = sha1($key); + $firstDirectory = sprintf('%s/%s', $this->directory, substr($keyHash, 0, 2)); + $secondDirectory = sprintf('%s/%s', $firstDirectory, substr($keyHash, 2, 2)); + $filePath = sprintf('%s/%s.php', $secondDirectory, $keyHash); + + return new CacheFilePaths($firstDirectory, $secondDirectory, $filePath); + } +} diff --git a/packages/Caching/ValueObject/Storage/MemoryCacheStorage.php b/src/Caching/ValueObject/Storage/MemoryCacheStorage.php similarity index 88% rename from packages/Caching/ValueObject/Storage/MemoryCacheStorage.php rename to src/Caching/ValueObject/Storage/MemoryCacheStorage.php index 815603132e3..65e24c37f13 100644 --- a/packages/Caching/ValueObject/Storage/MemoryCacheStorage.php +++ b/src/Caching/ValueObject/Storage/MemoryCacheStorage.php @@ -20,7 +20,7 @@ final class MemoryCacheStorage implements CacheStorageInterface /** * @return null|mixed */ - public function load(string $key, string $variableKey) + public function load(string $key, string $variableKey): mixed { if (! isset($this->storage[$key])) { return null; @@ -34,7 +34,7 @@ public function load(string $key, string $variableKey) return $item->getData(); } - public function save(string $key, string $variableKey, $data): void + public function save(string $key, string $variableKey, mixed $data): void { $this->storage[$key] = new CacheItem($variableKey, $data); } diff --git a/packages/ChangesReporting/Contract/Output/OutputFormatterInterface.php b/src/ChangesReporting/Contract/Output/OutputFormatterInterface.php similarity index 75% rename from packages/ChangesReporting/Contract/Output/OutputFormatterInterface.php rename to src/ChangesReporting/Contract/Output/OutputFormatterInterface.php index 6e7ed232e4f..1c35e56418a 100644 --- a/packages/ChangesReporting/Contract/Output/OutputFormatterInterface.php +++ b/src/ChangesReporting/Contract/Output/OutputFormatterInterface.php @@ -4,8 +4,8 @@ namespace Rector\ChangesReporting\Contract\Output; -use Rector\Core\ValueObject\Configuration; -use Rector\Core\ValueObject\ProcessResult; +use Rector\ValueObject\Configuration; +use Rector\ValueObject\ProcessResult; interface OutputFormatterInterface { diff --git a/src/ChangesReporting/Output/ConsoleOutputFormatter.php b/src/ChangesReporting/Output/ConsoleOutputFormatter.php new file mode 100644 index 00000000000..afd48e240d0 --- /dev/null +++ b/src/ChangesReporting/Output/ConsoleOutputFormatter.php @@ -0,0 +1,204 @@ +shouldShowDiffs()) { + $this->reportFileDiffs($processResult->getFileDiffs(), $configuration->isReportingWithRealPath()); + } + + $this->reportErrors($processResult->getSystemErrors(), $configuration->isReportingWithRealPath()); + + if ($processResult->getSystemErrors() !== []) { + return; + } + + // to keep space between progress bar and success message + if ($configuration->shouldShowProgressBar() && $processResult->getFileDiffs() === []) { + $this->symfonyStyle->newLine(); + } + + if ($configuration->shouldShowRulesSummary()) { + $this->reportRulesSummary($processResult, $configuration); + } + + $message = $this->createSuccessMessage($processResult, $configuration); + $this->symfonyStyle->success($message); + } + + public function getName(): string + { + return self::NAME; + } + + /** + * @param FileDiff[] $fileDiffs + */ + private function reportFileDiffs(array $fileDiffs, bool $absoluteFilePath): void + { + if (count($fileDiffs) <= 0) { + return; + } + + // normalize + ksort($fileDiffs); + $message = sprintf('%d file%s with changes', count($fileDiffs), count($fileDiffs) === 1 ? '' : 's'); + + $this->symfonyStyle->title($message); + + $i = 0; + foreach ($fileDiffs as $fileDiff) { + $filePath = $absoluteFilePath + ? ($fileDiff->getAbsoluteFilePath() ?? '') + : $fileDiff->getRelativeFilePath(); + + // append line number for faster file jump in diff + $firstLineNumber = $fileDiff->getFirstLineNumber(); + if ($firstLineNumber !== null) { + $filePath .= ':' . $firstLineNumber; + } + + $filePathWithUrl = $this->addEditorUrl( + $filePath, + $fileDiff->getAbsoluteFilePath(), + $fileDiff->getRelativeFilePath(), + (string) $fileDiff->getFirstLineNumber() + ); + + $message = sprintf('%d) %s', ++$i, $filePathWithUrl); + + $this->symfonyStyle->writeln($message); + $this->symfonyStyle->newLine(); + $this->symfonyStyle->writeln($fileDiff->getDiffConsoleFormatted()); + + if ($fileDiff->getRectorChanges() !== []) { + $this->symfonyStyle->writeln('Applied rules:'); + $this->symfonyStyle->listing($fileDiff->getRectorShortClasses()); + $this->symfonyStyle->newLine(); + } + } + } + + /** + * @param SystemError[] $errors + */ + private function reportErrors(array $errors, bool $absoluteFilePath): void + { + foreach ($errors as $error) { + $errorMessage = $error->getMessage(); + $errorMessage = $this->normalizePathsToRelativeWithLine($errorMessage); + $errorMessage = str_replace("\r\n", "\n", $errorMessage); + + $filePath = $absoluteFilePath ? $error->getAbsoluteFilePath() : $error->getRelativeFilePath(); + + $message = sprintf( + 'Could not process %s%s, due to: %s"%s".', + $filePath !== null ? '"' . $filePath . '" file' : 'some files', + $error->getRectorClass() !== null ? ' by "' . $error->getRectorClass() . '"' : '', + "\n", + $errorMessage + ); + + if ($error->getLine() !== null) { + $message .= ' On line: ' . $error->getLine(); + } + + $this->symfonyStyle->error($message); + } + } + + private function reportRulesSummary(ProcessResult $processResult, Configuration $configuration): void + { + $ruleApplicationCounts = $processResult->getRuleApplicationCounts(); + if ($ruleApplicationCounts === []) { + return; + } + + $verb = $configuration->isDryRun() ? 'would have been applied' : 'was applied'; + + $this->symfonyStyle->section('Rules Summary'); + + foreach ($ruleApplicationCounts as $ruleClass => $count) { + $ruleShortClass = (string) Strings::after($ruleClass, '\\', -1); + $this->symfonyStyle->writeln(sprintf( + ' * %s %s %d time%s', + $ruleShortClass, + $verb, + $count, + $count > 1 ? 's' : '' + )); + } + + $this->symfonyStyle->newLine(); + } + + private function normalizePathsToRelativeWithLine(string $errorMessage): string + { + $regex = '#' . preg_quote(getcwd(), '#') . '/#'; + $errorMessage = Strings::replace($errorMessage, $regex); + return Strings::replace($errorMessage, self::ON_LINE_REGEX); + } + + private function createSuccessMessage(ProcessResult $processResult, Configuration $configuration): string + { + $changeCount = $processResult->getTotalChanged(); + + if ($changeCount === 0) { + return 'Rector is done!'; + } + + return sprintf( + '%d file%s %s by Rector', + $changeCount, + $changeCount > 1 ? 's' : '', + $configuration->isDryRun() ? 'would have been changed (dry-run)' : ($changeCount === 1 ? 'has' : 'have') . ' been changed' + ); + } + + private function addEditorUrl( + string $filePath, + ?string $absoluteFilePath, + ?string $relativeFilePath, + ?string $lineNumber, + ): string { + $editorUrl = SimpleParameterProvider::provideStringParameter(Option::EDITOR_URL, ''); + if ($editorUrl !== '') { + $editorUrl = str_replace( + ['%file%', '%relFile%', '%line%'], + [(string) $absoluteFilePath, (string) $relativeFilePath, (string) $lineNumber], + $editorUrl, + ); + $filePath = '' . $filePath . ''; + } + + return $filePath; + } +} diff --git a/src/ChangesReporting/Output/Factory/JsonOutputFactory.php b/src/ChangesReporting/Output/Factory/JsonOutputFactory.php new file mode 100644 index 00000000000..aa75152d3ab --- /dev/null +++ b/src/ChangesReporting/Output/Factory/JsonOutputFactory.php @@ -0,0 +1,85 @@ + [ + 'changed_files' => $processResult->getTotalChanged(), + ], + ]; + + // We need onlyWithChanges: false to include all file diffs + $fileDiffs = $processResult->getFileDiffs(onlyWithChanges: false); + ksort($fileDiffs); + foreach ($fileDiffs as $fileDiff) { + $filePath = $configuration->isReportingWithRealPath() + ? ($fileDiff->getAbsoluteFilePath() ?? '') + : $fileDiff->getRelativeFilePath() + ; + + if ($configuration->shouldShowDiffs() && $fileDiff->getDiff() !== '') { + $errorsJson[Bridge::FILE_DIFFS][] = [ + 'file' => $filePath, + 'diff' => $fileDiff->getDiff(), + 'applied_rectors' => $fileDiff->getRectorClasses(), + ]; + } + + // for Rector CI + $errorsJson['changed_files'][] = $filePath; + } + + $systemErrors = $processResult->getSystemErrors(); + $errorsJson['totals']['errors'] = count($systemErrors); + + $errorsData = self::createErrorsData($systemErrors, $configuration->isReportingWithRealPath()); + if ($errorsData !== []) { + $errorsJson['errors'] = $errorsData; + } + + return Json::encode($errorsJson, pretty: true); + } + + /** + * @param SystemError[] $errors + * @return mixed[] + */ + private static function createErrorsData(array $errors, bool $absoluteFilePath): array + { + $errorsData = []; + + foreach ($errors as $error) { + $errorDataJson = [ + 'message' => $error->getMessage(), + 'file' => $absoluteFilePath ? $error->getAbsoluteFilePath() : $error->getRelativeFilePath(), + ]; + + if ($error->getRectorClass() !== null) { + $errorDataJson['caused_by'] = $error->getRectorClass(); + } + + if ($error->getLine() !== null) { + $errorDataJson['line'] = $error->getLine(); + } + + $errorsData[] = $errorDataJson; + } + + return $errorsData; + } +} diff --git a/src/ChangesReporting/Output/GitHubOutputFormatter.php b/src/ChangesReporting/Output/GitHubOutputFormatter.php new file mode 100644 index 00000000000..f0d3bb8359a --- /dev/null +++ b/src/ChangesReporting/Output/GitHubOutputFormatter.php @@ -0,0 +1,154 @@ +startGroup(); + + $this->reportSystemErrors($processResult, $configuration); + $this->reportFileDiffs($processResult, $configuration); + + $this->endGroup(); + } + + private function startGroup(): void + { + echo sprintf('::group::%s', self::GROUP_NAME) . PHP_EOL; + } + + private function endGroup(): void + { + echo '::endgroup::' . PHP_EOL; + } + + private function reportSystemErrors(ProcessResult $processResult, Configuration $configuration): void + { + foreach ($processResult->getSystemErrors() as $systemError) { + $filePath = $configuration->isReportingWithRealPath() + ? $systemError->getAbsoluteFilePath() + : $systemError->getRelativeFilePath(); + + $line = $systemError->getLine(); + $message = trim($systemError->getRectorShortClass() . PHP_EOL . $systemError->getMessage()); + + $this->reportErrorAnnotation($message, [ + 'file' => $filePath, + 'line' => $line, + ]); + } + } + + private function reportFileDiffs(ProcessResult $processResult, Configuration $configuration): void + { + + $fileDiffs = $processResult->getFileDiffs(); + ksort($fileDiffs); + + foreach ($fileDiffs as $fileDiff) { + $filePath = $configuration->isReportingWithRealPath() + ? $fileDiff->getAbsoluteFilePath() + : $fileDiff->getRelativeFilePath(); + + $line = $fileDiff->getFirstLineNumber(); + $endLine = $fileDiff->getLastLineNumber(); + + $message = trim( + implode(' / ', $fileDiff->getRectorShortClasses()) + ) . PHP_EOL . PHP_EOL . $fileDiff->getDiff(); + + $this->reportErrorAnnotation($message, [ + 'file' => $filePath, + 'line' => $line, + 'endLine' => $endLine, + ]); + } + } + + /** + * @param AnnotationProperties $annotationProperties + */ + private function reportErrorAnnotation(string $message, array $annotationProperties): void + { + $properties = $this->sanitizeAnnotationProperties($annotationProperties); + + $command = sprintf('::error %s::%s', $properties, $message); + + // Sanitize command + $command = str_replace(['%', "\r", "\n"], ['%25', '%0D', '%0A'], $command); + + echo $command . PHP_EOL; + } + + /** + * @param AnnotationProperties $annotationProperties + */ + private function sanitizeAnnotationProperties(array $annotationProperties): string + { + if (! isset($annotationProperties['line']) || ! $annotationProperties['line']) { + $annotationProperties['line'] = 0; + } + + // This is a workaround for buggy endLine. See https://github.com/orgs/community/discussions/129899 + // TODO: Should be removed once github will have fixed it issue. + unset($annotationProperties['endLine']); + + $nonNullProperties = array_filter( + $annotationProperties, + static fn (int|string|null $value): bool => $value !== null + ); + + $sanitizedProperties = array_map( + fn (string $key, string|int|null $value): string => sprintf( + '%s=%s', + $key, + $this->sanitizeAnnotationProperty($value) + ), + array_keys($nonNullProperties), + $nonNullProperties + ); + + return implode(',', $sanitizedProperties); + } + + private function sanitizeAnnotationProperty(string|int|null $value): string + { + if ($value === null || $value === '') { + return ''; + } + + $value = (string) $value; + + return str_replace(['%', "\r", "\n", ':', ','], ['%25', '%0D', '%0A', '%3A', '%2C'], $value); + } +} diff --git a/src/ChangesReporting/Output/GitlabOutputFormatter.php b/src/ChangesReporting/Output/GitlabOutputFormatter.php new file mode 100644 index 00000000000..d43ac4ba2a2 --- /dev/null +++ b/src/ChangesReporting/Output/GitlabOutputFormatter.php @@ -0,0 +1,153 @@ +appendSystemErrors($processResult, $configuration), + ...$this->appendFileDiffs($processResult, $configuration), + ]; + + $json = Json::encode($errorsJson, true); + + echo $json . PHP_EOL; + } + + /** + * @return array + */ + private function appendSystemErrors(ProcessResult $processResult, Configuration $configuration): array + { + $errorsJson = []; + + foreach ($processResult->getSystemErrors() as $systemError) { + $filePath = $configuration->isReportingWithRealPath() + ? ($systemError->getAbsoluteFilePath() ?? '') + : ($systemError->getRelativeFilePath() ?? '') + ; + + $fingerprint = $this->filehasher->hash( + $filePath . ';' . $systemError->getLine() . ';' . $systemError->getMessage() + ); + + $errorsJson[] = [ + 'fingerprint' => $fingerprint, + 'type' => self::ERROR_TYPE_ISSUE, + 'categories' => [self::ERROR_CATEGORY_BUG_RISK], + 'severity' => self::ERROR_SEVERITY_BLOCKER, + 'description' => $systemError->getMessage(), + 'check_name' => $systemError->getRectorClass() ?? '', + 'location' => [ + 'path' => $filePath, + 'lines' => [ + 'begin' => $systemError->getLine() ?? 0, + ], + ], + ]; + } + + return $errorsJson; + } + + /** + * @return array + */ + private function appendFileDiffs(ProcessResult $processResult, Configuration $configuration): array + { + $errorsJson = []; + + $fileDiffs = $processResult->getFileDiffs(); + ksort($fileDiffs); + + foreach ($fileDiffs as $fileDiff) { + $filePath = $configuration->isReportingWithRealPath() + ? ($fileDiff->getAbsoluteFilePath() ?? '') + : ($fileDiff->getRelativeFilePath() ?? '') + ; + + $rectorClasses = implode(' / ', $fileDiff->getRectorShortClasses()); + $fingerprint = $this->filehasher->hash($filePath . ';' . $fileDiff->getDiff()); + + $errorsJson[] = [ + 'fingerprint' => $fingerprint, + 'type' => self::ERROR_TYPE_ISSUE, + 'categories' => [self::ERROR_CATEGORY_STYLE], + 'severity' => self::ERROR_SEVERITY_MINOR, + 'description' => $rectorClasses, + 'content' => [ + 'body' => $fileDiff->getDiff(), + ], + 'check_name' => $rectorClasses, + 'location' => [ + 'path' => $filePath, + 'lines' => [ + 'begin' => $fileDiff->getFirstLineNumber() ?? 0, + ], + ], + ]; + } + + return $errorsJson; + } +} diff --git a/src/ChangesReporting/Output/JUnitOutputFormatter.php b/src/ChangesReporting/Output/JUnitOutputFormatter.php new file mode 100644 index 00000000000..2fbd7864967 --- /dev/null +++ b/src/ChangesReporting/Output/JUnitOutputFormatter.php @@ -0,0 +1,136 @@ +symfonyStyle->warning( + 'The "dom" extension is not loaded. The rector could not generate a response in the JUnit format', + ); + + return; + } + + $domDocument = new DOMDocument('1.0', 'UTF-8'); + + $domElement = $domDocument->createElement(self::XML_ELEMENT_TESTSUITE); + $domElement->setAttribute(self::XML_ATTRIBUTE_NAME, 'rector'); + + $xmlTestSuites = $domDocument->createElement(self::XML_ELEMENT_TESTSUITES); + $xmlTestSuites->appendChild($domElement); + + $domDocument->appendChild($xmlTestSuites); + + $this->appendSystemErrors($processResult, $configuration, $domDocument, $domElement); + $this->appendFileDiffs($processResult, $configuration, $domDocument, $domElement); + + echo $domDocument->saveXML() . PHP_EOL; + } + + private function appendSystemErrors( + ProcessResult $processResult, + Configuration $configuration, + DOMDocument $domDocument, + DOMElement $domElement, + ): void { + if ($processResult->getSystemErrors() === []) { + return; + } + + foreach ($processResult->getSystemErrors() as $systemError) { + $filePath = $configuration->isReportingWithRealPath() + ? ($systemError->getAbsoluteFilePath() ?? '') + : ($systemError->getRelativeFilePath() ?? '') + ; + + $xmlError = $domDocument->createElement(self::XML_ELEMENT_ERROR); + $xmlError->setAttribute(self::XML_ATTRIBUTE_TYPE, 'Error'); + $xmlError->appendChild($domDocument->createTextNode($systemError->getMessage())); + + $xmlTestCase = $domDocument->createElement(self::XML_ELEMENT_TESTCASE); + $xmlTestCase->setAttribute(self::XML_ATTRIBUTE_FILE, $filePath); + $xmlTestCase->setAttribute(self::XML_ATTRIBUTE_NAME, $filePath . ':' . $systemError->getLine()); + $xmlTestCase->appendChild($xmlError); + + $domElement->appendChild($xmlTestCase); + } + } + + private function appendFileDiffs( + ProcessResult $processResult, + Configuration $configuration, + DOMDocument $domDocument, + DOMElement $domElement, + ): void { + if ($processResult->getFileDiffs() === []) { + return; + } + + $fileDiffs = $processResult->getFileDiffs(); + ksort($fileDiffs); + + foreach ($fileDiffs as $fileDiff) { + $filePath = $configuration->isReportingWithRealPath() + ? ($fileDiff->getAbsoluteFilePath() ?? '') + : ($fileDiff->getRelativeFilePath() ?? '') + ; + + $rectorClasses = implode(' / ', $fileDiff->getRectorShortClasses()); + + $xmlError = $domDocument->createElement(self::XML_ELEMENT_ERROR); + $xmlError->setAttribute(self::XML_ATTRIBUTE_TYPE, $rectorClasses); + $xmlError->appendChild($domDocument->createTextNode($fileDiff->getDiff())); + + $xmlTestCase = $domDocument->createElement(self::XML_ELEMENT_TESTCASE); + $xmlTestCase->setAttribute(self::XML_ATTRIBUTE_FILE, $filePath); + $xmlTestCase->setAttribute(self::XML_ATTRIBUTE_NAME, $filePath . ':' . $fileDiff->getFirstLineNumber()); + $xmlTestCase->appendChild($xmlError); + + $domElement->appendChild($xmlTestCase); + } + } +} diff --git a/src/ChangesReporting/Output/JsonOutputFormatter.php b/src/ChangesReporting/Output/JsonOutputFormatter.php new file mode 100644 index 00000000000..82e1f94f84f --- /dev/null +++ b/src/ChangesReporting/Output/JsonOutputFormatter.php @@ -0,0 +1,25 @@ + $rectorClass + */ + public function __construct( + private string $rectorClass, + private int $line + ) { + } + + /** + * @return class-string + */ + public function getRectorClass(): string + { + return $this->rectorClass; + } + + /** + * @param array $json + */ + public static function decode(array $json): self + { + /** @var class-string $rectorClass */ + $rectorClass = $json[self::KEY_RECTOR_CLASS]; + Assert::string($rectorClass); + + $line = $json[self::KEY_LINE]; + Assert::integer($line); + + return new self($rectorClass, $line); + } + + /** + * @return array{rector_class: class-string, line: int} + */ + public function jsonSerialize(): array + { + return [ + self::KEY_RECTOR_CLASS => $this->rectorClass, + self::KEY_LINE => $this->line, + ]; + } +} diff --git a/src/ChangesReporting/ValueObjectFactory/ErrorFactory.php b/src/ChangesReporting/ValueObjectFactory/ErrorFactory.php new file mode 100644 index 00000000000..8b1cade7c12 --- /dev/null +++ b/src/ChangesReporting/ValueObjectFactory/ErrorFactory.php @@ -0,0 +1,36 @@ +createExceptionMessage($analysedCodeException); + $relativeFilePath = $this->filePathHelper->relativePath($filePath); + + return new SystemError($message, $relativeFilePath); + } + + private function createExceptionMessage(AnalysedCodeException $analysedCodeException): string + { + return sprintf( + 'Analyze error: "%s". Include your files in "$rectorConfig->autoloadPaths([...]);" or "$rectorConfig->bootstrapFiles([...]);" in "rector.php" config.%sSee https://github.com/rectorphp/rector#configuration', + $analysedCodeException->getMessage(), + PHP_EOL + ); + } +} diff --git a/src/ChangesReporting/ValueObjectFactory/FileDiffFactory.php b/src/ChangesReporting/ValueObjectFactory/FileDiffFactory.php new file mode 100644 index 00000000000..0a01e41aaae --- /dev/null +++ b/src/ChangesReporting/ValueObjectFactory/FileDiffFactory.php @@ -0,0 +1,41 @@ +filePathHelper->relativePath($file->getFilePath()); + + $diff = $shouldShowDiffs ? $this->defaultDiffer->diff($oldContent, $newContent) : ''; + $consoleDiff = $shouldShowDiffs ? $this->colorConsoleDiffFormatter->format($diff) : ''; + + // always keep the most recent diff + return new FileDiff($relativeFilePath, $diff, $consoleDiff, $rectorsWithLineChanges); + } +} diff --git a/src/Comments/CommentRemover.php b/src/Comments/CommentRemover.php new file mode 100644 index 00000000000..da2e5d00469 --- /dev/null +++ b/src/Comments/CommentRemover.php @@ -0,0 +1,33 @@ +commentRemovingNodeTraverser->traverse($nodes); + } +} diff --git a/src/Comments/CommentResolver.php b/src/Comments/CommentResolver.php new file mode 100644 index 00000000000..3aa7c7c4562 --- /dev/null +++ b/src/Comments/CommentResolver.php @@ -0,0 +1,36 @@ +getAttribute(AttributeKey::COMMENTS); + + if ($this->hasNoComment($comments)) { + return $rangeLine; + } + + /** @var Comment[] $comments */ + $firstComment = $comments[0]; + + $line = $firstComment->getStartLine(); + return $line - $endLine; + } + + /** + * @param Comment[]|null $comments + */ + private function hasNoComment(?array $comments): bool + { + return $comments === null || $comments === []; + } +} diff --git a/src/Comments/NodeDocBlock/DocBlockUpdater.php b/src/Comments/NodeDocBlock/DocBlockUpdater.php new file mode 100644 index 00000000000..1bf70ee1fd7 --- /dev/null +++ b/src/Comments/NodeDocBlock/DocBlockUpdater.php @@ -0,0 +1,86 @@ +getAttribute(AttributeKey::PHP_DOC_INFO); + if (! $phpDocInfo instanceof PhpDocInfo) { + return; + } + + $phpDocNode = $phpDocInfo->getPhpDocNode(); + if ($phpDocNode->children === []) { + $this->setCommentsAttribute($node); + return; + } + + $printedPhpDoc = $this->printPhpDocInfoToString($phpDocInfo); + + $node->setDocComment(new Doc($printedPhpDoc)); + + if ($printedPhpDoc === '') { + $this->clearEmptyDoc($node); + } + } + + private function setCommentsAttribute(Node $node): void + { + $docComment = $node->getDocComment(); + $docCommentText = $docComment instanceof Doc ? $docComment->getText() : null; + + $comments = array_filter( + $node->getComments(), + static function (Comment $comment) use ($docCommentText): bool { + if (! $comment instanceof Doc) { + return true; + } + + // remove only the docblock that belongs to the node itself; + // keep other preceding docblocks (possible with multiple @var docblocks before a statement) + if ($docCommentText !== null && $comment->getText() === $docCommentText) { + return false; + } + + return true; + } + ); + + $node->setAttribute(AttributeKey::COMMENTS, array_values($comments)); + } + + private function clearEmptyDoc(Node $node): void + { + $comments = array_filter( + $node->getComments(), + static fn (Comment $comment): bool => ! $comment instanceof Doc || $comment->getText() !== '' + ); + $node->setAttribute(AttributeKey::COMMENTS, array_values($comments)); + } + + private function printPhpDocInfoToString(PhpDocInfo $phpDocInfo): string + { + if ($phpDocInfo->isNewNode()) { + return $this->phpDocInfoPrinter->printNew($phpDocInfo); + } + + return $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); + } +} diff --git a/packages/Comments/NodeTraverser/CommentRemovingNodeTraverser.php b/src/Comments/NodeTraverser/CommentRemovingNodeTraverser.php similarity index 85% rename from packages/Comments/NodeTraverser/CommentRemovingNodeTraverser.php rename to src/Comments/NodeTraverser/CommentRemovingNodeTraverser.php index fecfb3aee34..5689b9affce 100644 --- a/packages/Comments/NodeTraverser/CommentRemovingNodeTraverser.php +++ b/src/Comments/NodeTraverser/CommentRemovingNodeTraverser.php @@ -11,6 +11,6 @@ final class CommentRemovingNodeTraverser extends NodeTraverser { public function __construct(CommentRemovingNodeVisitor $commentRemovingNodeVisitor) { - $this->addVisitor($commentRemovingNodeVisitor); + parent::__construct($commentRemovingNodeVisitor); } } diff --git a/packages/Comments/NodeVisitor/CommentRemovingNodeVisitor.php b/src/Comments/NodeVisitor/CommentRemovingNodeVisitor.php similarity index 100% rename from packages/Comments/NodeVisitor/CommentRemovingNodeVisitor.php rename to src/Comments/NodeVisitor/CommentRemovingNodeVisitor.php diff --git a/src/Composer/InstalledPackageResolver.php b/src/Composer/InstalledPackageResolver.php new file mode 100644 index 00000000000..c2046cfed6f --- /dev/null +++ b/src/Composer/InstalledPackageResolver.php @@ -0,0 +1,113 @@ + + */ + private ?array $resolvedInstalledPackages = null; + + public function __construct( + private readonly ?string $projectDirectory = null + ) { + // fallback to root project directory + if ($projectDirectory === null) { + $projectDirectory = getcwd(); + } + + Assert::directory($projectDirectory); + } + + /** + * @return array + */ + public function resolve(): array + { + // already cached, even only empty array + if ($this->resolvedInstalledPackages !== null) { + return $this->resolvedInstalledPackages; + } + + $installedPackagesFilePath = $this->resolveVendorDir() . '/composer/installed.json'; + if (! file_exists($installedPackagesFilePath)) { + throw new ShouldNotHappenException( + 'The installed package json not found. Make sure you run `composer update` and the "vendor/composer/installed.json" file exists' + ); + } + + $installedPackageFileContents = FileSystem::read($installedPackagesFilePath); + $installedPackagesFilePath = Json::decode($installedPackageFileContents, true); + + $installedPackages = $this->createInstalledPackages($installedPackagesFilePath['packages']); + + $this->resolvedInstalledPackages = $installedPackages; + + return $installedPackages; + } + + public function resolvePackageVersion(string $packageName): ?string + { + $package = $this->resolve()[$packageName] ?? null; + + if (! $package instanceof InstalledPackage) { + return null; + } + + return $package->getVersion(); + } + + /** + * @param mixed[] $packages + * @return array + */ + private function createInstalledPackages(array $packages): array + { + $installedPackages = []; + + foreach ($packages as $package) { + $name = $package['name']; + $installedPackages[$name] = new InstalledPackage($name, $package['version_normalized']); + } + + return $installedPackages; + } + + private function resolveVendorDir(): string + { + $projectComposerJsonFilePath = $this->projectDirectory . '/composer.json'; + + if (\file_exists($projectComposerJsonFilePath)) { + $projectComposerContents = FileSystem::read($projectComposerJsonFilePath); + $projectComposerJson = Json::decode($projectComposerContents, true); + + if (isset($projectComposerJson['config']['vendor-dir']) && + is_string($projectComposerJson['config']['vendor-dir']) + ) { + $realPathVendorDir = realpath($projectComposerJson['config']['vendor-dir']) ?: ''; + $normalizedRealPathVendorDir = PathNormalizer::normalize($realPathVendorDir); + $normalizedVendorDir = PathNormalizer::normalize($projectComposerJson['config']['vendor-dir']); + + return $normalizedRealPathVendorDir === $normalizedVendorDir + ? $projectComposerJson['config']['vendor-dir'] + : $this->projectDirectory . '/' . $projectComposerJson['config']['vendor-dir']; + } + + } + + return $this->projectDirectory . '/vendor'; + } +} diff --git a/src/Composer/ValueObject/InstalledPackage.php b/src/Composer/ValueObject/InstalledPackage.php new file mode 100644 index 00000000000..291c7c389ab --- /dev/null +++ b/src/Composer/ValueObject/InstalledPackage.php @@ -0,0 +1,27 @@ +name; + } + + public function getVersion(): string + { + return $this->version; + } +} diff --git a/src/Config/Level/CodeQualityLevel.php b/src/Config/Level/CodeQualityLevel.php new file mode 100644 index 00000000000..aae830a863f --- /dev/null +++ b/src/Config/Level/CodeQualityLevel.php @@ -0,0 +1,220 @@ +> + */ + public const array RULES = [ + CombinedAssignRector::class, + SimplifyEmptyArrayCheckRector::class, + ReplaceMultipleBooleanNotRector::class, + ReplaceConstantBooleanNotRector::class, + ForeachToInArrayRector::class, + RepeatedOrEqualToInArrayRector::class, + RepeatedAndNotEqualToNotInArrayRector::class, + SimplifyForeachToCoalescingRector::class, + SimplifyFuncGetArgsCountRector::class, + SimplifyInArrayValuesRector::class, + SimplifyStrposLowerRector::class, + SimplifyArraySearchRector::class, + SimplifyConditionsRector::class, + SimplifyIfNotNullReturnRector::class, + SimplifyIfReturnBoolRector::class, + UnnecessaryTernaryExpressionRector::class, + RemoveExtraParametersRector::class, + SimplifyDeMorganBinaryRector::class, + SimplifyTautologyTernaryRector::class, + SingleInArrayToCompareRector::class, + SimplifyIfElseToTernaryRector::class, + TernaryImplodeToImplodeRector::class, + JoinStringConcatRector::class, + ConsecutiveNullCompareReturnsToNullCoalesceQueueRector::class, + ExplicitBoolCompareRector::class, + CombineIfRector::class, + UseIdenticalOverEqualWithSameTypeRector::class, + SimplifyBoolIdenticalTrueRector::class, + SimplifyRegexPatternRector::class, + BooleanNotIdenticalToNotIdenticalRector::class, + AndAssignsToSeparateLinesRector::class, + CompactToVariablesRector::class, + CompleteDynamicPropertiesRector::class, + IsAWithStringWithThirdArgumentRector::class, + StrlenZeroToIdenticalEmptyStringRector::class, + ThrowWithPreviousExceptionRector::class, + RemoveSoleValueSprintfRector::class, + ShortenElseIfRector::class, + ExplicitReturnNullRector::class, + ArrayMergeOfNonArraysToSimpleArrayRector::class, + ArrayKeyExistsTernaryThenValueToCoalescingRector::class, + AbsolutizeRequireAndIncludePathRector::class, + ChangeArrayPushToArrayAssignRector::class, + ForRepeatedCountToOwnVariableRector::class, + ForeachItemsAssignToEmptyArrayToAssignRector::class, + InlineIfToExplicitIfRector::class, + UnusedForeachValueToArrayKeysRector::class, + CommonNotEqualRector::class, + SetTypeToCastRector::class, + LogicalToBooleanRector::class, + VarToPublicPropertyRector::class, + IssetOnPropertyObjectToPropertyExistsRector::class, + NewStaticToNewSelfRector::class, + UnwrapSprintfOneArgumentRector::class, + VariableConstFetchToClassConstFetchRector::class, + SwitchNegatedTernaryRector::class, + SingularSwitchToIfRector::class, + SimplifyIfNullableReturnRector::class, + CallUserFuncWithArrowFunctionToInlineRector::class, + FlipTypeControlToUseExclusiveTypeRector::class, + InlineArrayReturnAssignRector::class, + InlineIsAInstanceOfRector::class, + TernaryFalseExpressionToIfRector::class, + InlineConstructorDefaultToPropertyRector::class, + TernaryEmptyArrayArrayDimFetchToCoalesceRector::class, + OptionalParametersAfterRequiredRector::class, + SimplifyEmptyCheckOnEmptyArrayRector::class, + SwitchTrueToIfRector::class, + CleanupUnneededNullsafeOperatorRector::class, + DisallowedEmptyRuleFixerRector::class, + LocallyCalledStaticMethodToNonStaticRector::class, + NumberCompareToMaxFuncCallRector::class, + CompleteMissingIfElseBracketRector::class, + RemoveUselessIsObjectCheckRector::class, + ConvertStaticToSelfRector::class, + SortCallLikeNamedArgsRector::class, + SortAttributeNamedArgsRector::class, + RemoveReadonlyPropertyVisibilityOnReadonlyClassRector::class, + SafeDeclareStrictTypesRector::class, + ]; + + /** + * @var array, mixed[]> + */ + public const array RULES_WITH_CONFIGURATION = [ + RenameFunctionRector::class => [ + 'split' => 'explode', + 'join' => 'implode', + 'sizeof' => 'count', + # https://www.php.net/manual/en/aliases.php + 'chop' => 'rtrim', + 'doubleval' => 'floatval', + 'gzputs' => 'gzwrite', + 'fputs' => 'fwrite', + 'ini_alter' => 'ini_set', + 'is_double' => 'is_float', + 'is_integer' => 'is_int', + 'is_long' => 'is_int', + 'is_real' => 'is_float', + 'is_writeable' => 'is_writable', + 'key_exists' => 'array_key_exists', + 'pos' => 'current', + 'strchr' => 'strstr', + # mb + 'mbstrcut' => 'mb_strcut', + 'mbstrlen' => 'mb_strlen', + 'mbstrpos' => 'mb_strpos', + 'mbstrrpos' => 'mb_strrpos', + 'mbsubstr' => 'mb_substr', + ], + ]; +} diff --git a/src/Config/Level/CodingStyleLevel.php b/src/Config/Level/CodingStyleLevel.php new file mode 100644 index 00000000000..d5ffff62baf --- /dev/null +++ b/src/Config/Level/CodingStyleLevel.php @@ -0,0 +1,99 @@ +> + */ + public const array RULES = [ + SeparateMultiUseImportsRector::class, + NewlineBetweenClassLikeStmtsRector::class, + NewlineAfterStatementRector::class, + RemoveFinalFromConstRector::class, + NullableCompareToNullRector::class, + ConsistentImplodeRector::class, + TernaryConditionVariableAssignmentRector::class, + SimplifyQuoteEscapeRector::class, + StringClassNameToClassConstantRector::class, + CatchExceptionNameMatchingTypeRector::class, + SplitDoubleAssignRector::class, + EncapsedStringsToSprintfRector::class, + WrapEncapsedVariableInCurlyBracesRector::class, + NewlineBeforeNewAssignSetRector::class, + MakeInheritedMethodVisibilitySameAsParentRector::class, + CallUserFuncArrayToVariadicRector::class, + VersionCompareFuncCallToConstantRector::class, + CountArrayToEmptyArrayComparisonRector::class, + CallUserFuncToMethodCallRector::class, + FuncGetArgsToVariadicParamRector::class, + StrictArraySearchRector::class, + StrictInArrayRector::class, + UseClassKeywordForClassNameResolutionRector::class, + SplitGroupedPropertiesRector::class, + SplitGroupedClassConstantsRector::class, + ExplicitPublicClassMethodRector::class, + RemoveUselessAliasInUseStatementRector::class, + BinaryOpStandaloneAssignsToDirectRector::class, + MinMaxToClampRector::class, + ]; + + /** + * @var array, mixed[]> + */ + public const array RULES_WITH_CONFIGURATION = [ + FuncCallToConstFetchRector::class => [ + 'php_sapi_name' => 'PHP_SAPI', + 'pi' => 'M_PI', + ], + ]; +} diff --git a/src/Config/Level/DeadCodeLevel.php b/src/Config/Level/DeadCodeLevel.php new file mode 100644 index 00000000000..3bb5770ca97 --- /dev/null +++ b/src/Config/Level/DeadCodeLevel.php @@ -0,0 +1,166 @@ +> + */ + public const array RULES = [ + // easy picks + RemoveUnusedForeachKeyRector::class, + RemoveDuplicatedArrayKeyRector::class, + RecastingRemovalRector::class, + RemoveAndTrueRector::class, + SimplifyMirrorAssignRector::class, + RemoveDeadContinueRector::class, + RemoveUnusedNonEmptyArrayBeforeForeachRector::class, + RemoveNullPropertyInitializationRector::class, + RemoveUselessReturnExprInConstructRector::class, + ReplaceBlockToItsStmtsRector::class, + RemoveFilterVarOnExactTypeRector::class, + + RemoveTypedPropertyDeadInstanceOfRector::class, + TernaryToBooleanOrFalseToBooleanAndRector::class, + RemoveUselessTernaryRector::class, + RemoveDoubleAssignRector::class, + RemoveUselessAssignFromPropertyPromotionRector::class, + RemoveConcatAutocastRector::class, + SimplifyIfElseWithSameContentRector::class, + + RemoveNextSameValueConditionRector::class, + SimplifyUselessVariableRector::class, + RemoveDeadZeroAndOneOperationRector::class, + + // docblock + RemoveVoidDocblockFromMagicMethodRector::class, + RemoveUselessParamTagRector::class, + RemoveUselessReturnTagRector::class, + RemoveUselessReadOnlyTagRector::class, + RemoveNonExistingVarAnnotationRector::class, + RemoveUselessVarTagRector::class, + // prioritize safe belt on RemoveUseless*TagRector that registered previously first + RemoveNullTagValueNodeRector::class, + RemovePhpVersionIdCheckRector::class, + RemoveTypedPropertyNonMockDocblockRector::class, + + RemoveAlwaysTrueIfConditionRector::class, + ReduceAlwaysFalseIfOrRector::class, + RemoveUnusedPrivateClassConstantRector::class, + RemoveUnusedPrivatePropertyRector::class, + RemoveUnusedClosureVariableUseRector::class, + + RemoveDuplicatedCaseInSwitchRector::class, + RemoveDeadInstanceOfRector::class, + + RemoveDeadCatchRector::class, + RemoveDeadTryCatchRector::class, + RemoveDeadIfBlockRector::class, + RemoveDeadIfForeachForRector::class, + RemoveConditionExactReturnRector::class, + RemoveDeadStmtRector::class, + UnwrapFutureCompatibleIfPhpVersionRector::class, + + RemoveParentCallWithoutParentRector::class, + RemoveParentDelegatingConstructorRector::class, + + RemoveDeadConditionAboveReturnRector::class, + RemoveDeadLoopRector::class, + + // removing methods could be risky if there is some magic loading them + RemoveUnusedPromotedPropertyRector::class, + RemoveUnusedPrivateMethodParameterRector::class, + RemoveUnusedPublicMethodParameterRector::class, + RemoveUnusedPrivateMethodRector::class, + RemoveUnreachableStatementRector::class, + RemoveUnusedVariableAssignRector::class, + + // this could break framework magic autowiring in some cases + RemoveUnusedConstructorParamRector::class, + RemoveEmptyClassMethodRector::class, + RemoveDeadReturnRector::class, + + RemoveArgumentFromDefaultParentCallRector::class, + RemoveNullArgOnNullDefaultParamRector::class, + NarrowWideUnionReturnTypeRector::class, + ]; +} diff --git a/src/Config/Level/TypeDeclarationDocblocksLevel.php b/src/Config/Level/TypeDeclarationDocblocksLevel.php new file mode 100644 index 00000000000..42155b30aaf --- /dev/null +++ b/src/Config/Level/TypeDeclarationDocblocksLevel.php @@ -0,0 +1,72 @@ +> + */ + public const array RULES = [ + // start with rules based on native code + // property var + DocblockVarArrayFromPropertyDefaultsRector::class, + + // tests + AddParamArrayDocblockFromDataProviderRector::class, + AddReturnDocblockDataProviderRector::class, + + // param + AddParamArrayDocblockFromDimFetchAccessRector::class, + ClassMethodArrayDocblockParamFromLocalCallsRector::class, + AddParamArrayDocblockBasedOnArrayMapRector::class, + AddParamArrayDocblockFromAssignsParamToParamReferenceRector::class, + AddParamArrayDocblockBasedOnCallableNativeFuncCallRector::class, + + // return + AddReturnDocblockForCommonObjectDenominatorRector::class, + AddReturnArrayDocblockBasedOnArrayMapRector::class, + AddReturnDocblockForScalarArrayFromAssignsRector::class, + DocblockReturnArrayFromDirectArrayInstanceRector::class, + AddReturnDocblockForArrayDimAssignedObjectRector::class, + AddReturnDocblockForJsonArrayRector::class, + + // move to rules based on existing docblocks, as more risky + // property var + DocblockVarFromParamDocblockInConstructorRector::class, + DocblockVarArrayFromGetterReturnRector::class, + AddVarArrayDocblockFromDimFetchAssignRector::class, + + // return + DocblockGetterReturnArrayFromPropertyDocblockVarRector::class, + + // run latter after other rules, as more generic + AddReturnDocblockForDimFetchArrayFromAssignsRector::class, + + // @todo test first, 2026-01 + // AddReturnDocblockFromMethodCallDocblockRector::class, + ]; +} diff --git a/src/Config/Level/TypeDeclarationLevel.php b/src/Config/Level/TypeDeclarationLevel.php new file mode 100644 index 00000000000..630af35b4a2 --- /dev/null +++ b/src/Config/Level/TypeDeclarationLevel.php @@ -0,0 +1,175 @@ +> + */ + public const array RULES = [ + // php 7.1, start with closure first, as safest + AddClosureVoidReturnTypeWhereNoReturnRector::class, + AddFunctionVoidReturnTypeWhereNoReturnRector::class, + AddTestsVoidReturnTypeWhereNoReturnRector::class, + ReturnIteratorInDataProviderRector::class, + + ReturnTypeFromMockObjectRector::class, + TypedPropertyFromCreateMockAssignRector::class, + + AddArrowFunctionReturnTypeRector::class, + BoolReturnTypeFromBooleanConstReturnsRector::class, + ReturnTypeFromStrictNewArrayRector::class, + + // scalar and array from constant + ReturnTypeFromStrictConstantReturnRector::class, + StringReturnTypeFromStrictScalarReturnsRector::class, + NumericReturnTypeFromStrictScalarReturnsRector::class, + + BoolReturnTypeFromBooleanStrictReturnsRector::class, + StringReturnTypeFromStrictStringReturnsRector::class, + NumericReturnTypeFromStrictReturnsRector::class, + + ReturnTypeFromStrictTernaryRector::class, + ReturnTypeFromReturnDirectArrayRector::class, + + ResponseReturnTypeControllerActionRector::class, + ReturnTypeFromReturnNewRector::class, + ReturnTypeFromReturnCastRector::class, + ReturnTypeFromSymfonySerializerRector::class, + AddVoidReturnTypeWhereNoReturnRector::class, + ReturnTypeFromStrictTypedPropertyRector::class, + + ReturnNullableTypeRector::class, + + // php 7.4 + EmptyOnNullableObjectToInstanceOfRector::class, + + // php 7.4 + TypedPropertyFromStrictConstructorRector::class, + AddParamTypeSplFixedArrayRector::class, + AddReturnTypeDeclarationFromYieldsRector::class, + AddParamTypeBasedOnPHPUnitDataProviderRector::class, + TypedPropertyFromStrictSetUpRector::class, + ReturnTypeFromStrictNativeCallRector::class, + AddReturnTypeFromTryCatchTypeRector::class, + ReturnTypeFromStrictTypedCallRector::class, + ChildDoctrineRepositoryClassTypeRector::class, + + // php native types + KnownMagicClassMethodTypeRector::class, + + // param + AddMethodCallBasedStrictParamTypeRector::class, + ParamTypeByParentCallTypeRector::class, + + NarrowObjectReturnTypeRector::class, + + // multi types (nullable, union) + ReturnUnionTypeRector::class, + + // closures + AddClosureNeverReturnTypeRector::class, + AddClosureParamTypeForArrayMapRector::class, + AddClosureParamTypeForArrayReduceRector::class, + ClosureReturnTypeRector::class, + AddArrowFunctionParamArrayWhereDimFetchRector::class, + + // more risky rules + ReturnTypeFromStrictParamRector::class, + AddParamTypeFromPropertyTypeRector::class, + MergeDateTimePropertyTypeDeclarationRector::class, + PropertyTypeFromStrictSetterGetterRector::class, + ParamTypeByMethodCallTypeRector::class, + TypedPropertyFromAssignsRector::class, + AddReturnTypeDeclarationBasedOnParentClassMethodRector::class, + ReturnTypeFromStrictFluentReturnRector::class, + ReturnNeverTypeRector::class, + StrictStringParamConcatRector::class, + + // jms attributes + ObjectTypedPropertyFromJMSSerializerAttributeTypeRector::class, + ScalarTypedPropertyFromJMSSerializerAttributeTypeRector::class, + + // array parameter from dim fetch assign inside + StrictArrayParamDimFetchRector::class, + AddParamFromDimFetchKeyUseRector::class, + AddParamStringTypeFromSprintfUseRector::class, + + // possibly based on docblocks, but also helpful, intentionally last + AddArrayFunctionClosureParamTypeRector::class, + TypedPropertyFromDocblockSetUpDefinedRector::class, + AddClosureParamTypeFromIterableMethodCallRector::class, + TypedStaticPropertyInBehatContextRector::class, + ]; +} diff --git a/src/Config/RectorConfig.php b/src/Config/RectorConfig.php new file mode 100644 index 00000000000..82a05485e0d --- /dev/null +++ b/src/Config/RectorConfig.php @@ -0,0 +1,472 @@ +, mixed[]> + */ + private array $ruleConfigurations = []; + + /** + * @var string[] + */ + private array $autotagInterfaces = [Command::class, ResettableInterface::class]; + + private static ?bool $recreated = null; + + public static function configure(): RectorConfigBuilder + { + if (self::$recreated === null) { + self::$recreated = false; + } elseif (self::$recreated === false) { + self::$recreated = true; + } + + SimpleParameterProvider::setParameter(Option::IS_RECTORCONFIG_BUILDER_RECREATED, self::$recreated); + + return new RectorConfigBuilder(); + } + + /** + * @param string[] $paths + */ + public function paths(array $paths): void + { + Assert::allString($paths); + + // ensure paths exist + foreach ($paths as $path) { + if (str_contains($path, '*')) { + continue; + } + + Assert::fileExists($path); + } + + SimpleParameterProvider::setParameter(Option::PATHS, $paths); + } + + /** + * @param string[] $sets + */ + public function sets(array $sets): void + { + Assert::allString($sets); + + foreach ($sets as $set) { + Assert::fileExists($set); + $this->import($set); + } + + // for cache invalidation in case of sets change + SimpleParameterProvider::addParameter(Option::REGISTERED_RECTOR_SETS, $sets); + } + + public function disableParallel(): void + { + SimpleParameterProvider::setParameter(Option::PARALLEL, false); + } + + public function parallel( + int $processTimeout = 120, + int $maxNumberOfProcess = Defaults::PARALLEL_MAX_NUMBER_OF_PROCESS, + int $jobSize = 16 + ): void { + SimpleParameterProvider::setParameter(Option::PARALLEL, true); + SimpleParameterProvider::setParameter(Option::PARALLEL_JOB_TIMEOUT_IN_SECONDS, $processTimeout); + SimpleParameterProvider::setParameter(Option::PARALLEL_MAX_NUMBER_OF_PROCESSES, $maxNumberOfProcess); + SimpleParameterProvider::setParameter(Option::PARALLEL_JOB_SIZE, $jobSize); + } + + public function noDiffs(): void + { + SimpleParameterProvider::setParameter(Option::NO_DIFFS, true); + } + + public function memoryLimit(string $memoryLimit): void + { + SimpleParameterProvider::setParameter(Option::MEMORY_LIMIT, $memoryLimit); + } + + /** + * @see https://getrector.com/documentation/ignoring-rules-or-paths + * @param array $skip + */ + public function skip(array $skip): void + { + RectorConfigValidator::ensureRectorRulesExist($skip); + + SimpleParameterProvider::addParameter(Option::SKIP, $skip); + } + + public function removeUnusedImports(bool $removeUnusedImports = true): void + { + SimpleParameterProvider::setParameter(Option::REMOVE_UNUSED_IMPORTS, $removeUnusedImports); + } + + public function importNames(bool $importNames = true, bool $importDocBlockNames = true): void + { + SimpleParameterProvider::setParameter(Option::AUTO_IMPORT_NAMES, $importNames); + SimpleParameterProvider::setParameter(Option::AUTO_IMPORT_DOC_BLOCK_NAMES, $importDocBlockNames); + } + + public function importShortClasses(bool $importShortClasses = true): void + { + SimpleParameterProvider::setParameter(Option::IMPORT_SHORT_CLASSES, $importShortClasses); + } + + /** + * Add PHPStan custom config to load extensions and custom configuration to Rector. + */ + public function phpstanConfig(string $filePath): void + { + Assert::fileExists($filePath); + SimpleParameterProvider::addParameter(Option::PHPSTAN_FOR_RECTOR_PATHS, [$filePath]); + } + + /** + * Add PHPStan custom configs to load extensions and custom configuration to Rector. + * + * @param string[] $filePaths + */ + public function phpstanConfigs(array $filePaths): void + { + Assert::allString($filePaths); + Assert::allFileExists($filePaths); + SimpleParameterProvider::addParameter(Option::PHPSTAN_FOR_RECTOR_PATHS, $filePaths); + } + + /** + * @param class-string $rectorClass + * @param mixed[] $configuration + */ + public function ruleWithConfiguration(string $rectorClass, array $configuration): void + { + Assert::classExists($rectorClass); + Assert::isAOf($rectorClass, RectorInterface::class); + Assert::isAOf($rectorClass, ConfigurableRectorInterface::class); + + // store configuration to cache + $this->ruleConfigurations[$rectorClass] = array_merge( + $this->ruleConfigurations[$rectorClass] ?? [], + $configuration + ); + + $this->rule($rectorClass); + + $this->afterResolving($rectorClass, function (ConfigurableRectorInterface $configurableRector) use ( + $rectorClass + ): void { + $ruleConfiguration = $this->ruleConfigurations[$rectorClass]; + $configurableRector->configure($ruleConfiguration); + }); + + // for cache invalidation in case of sets change + SimpleParameterProvider::addParameter(Option::REGISTERED_RECTOR_RULES, $rectorClass); + } + + /** + * @param class-string $rectorClass + */ + public function rule(string $rectorClass): void + { + Assert::classExists($rectorClass); + Assert::isAOf($rectorClass, RectorInterface::class); + + $this->singleton($rectorClass); + $this->tag($rectorClass, RectorInterface::class); + + // for cache invalidation in case of change + SimpleParameterProvider::addParameter(Option::REGISTERED_RECTOR_RULES, $rectorClass); + + if (is_a($rectorClass, RelatedConfigInterface::class, true)) { + $configFile = $rectorClass::getConfigFile(); + + Assert::file($configFile, sprintf( + 'The config path "%s" in "%s::getConfigFile()" could not be found', + $configFile, + $rectorClass + )); + + $this->import($configFile); + } + } + + /** + * @param class-string $commandClass + */ + public function command(string $commandClass): void + { + $this->singleton($commandClass); + $this->tag($commandClass, Command::class); + } + + public function import(string $filePath): void + { + /** + * Only stop when filePath realpath is false and contains glob patterns + * @see https://github.com/rectorphp/rector/issues/9156#issuecomment-2869130541 + */ + if (realpath($filePath) === false && str_contains($filePath, '*')) { + throw new ShouldNotHappenException( + 'Matching file paths by using glob-patterns is no longer supported. Use specific file path instead.' + ); + } + + Assert::fileExists($filePath); + + $self = $this; + $callable = (require $filePath); + + Assert::isCallable($callable); + /** @var callable(Container $container): void $callable */ + $callable($self); + } + + /** + * @param array> $rectorClasses + */ + public function rules(array $rectorClasses): void + { + Assert::allString($rectorClasses); + + RectorConfigValidator::ensureNoDuplicatedClasses($rectorClasses); + + foreach ($rectorClasses as $rectorClass) { + $this->rule($rectorClass); + } + } + + /** + * @param PhpVersion::* $phpVersion + */ + public function phpVersion(int $phpVersion): void + { + SimpleParameterProvider::setParameter(Option::PHP_VERSION_FEATURES, $phpVersion); + } + + /** + * @internal + * + * @api only for testing. It is parsed from composer.json "require" packages by default + * @param array $polyfillPackages + */ + public function polyfillPackages(array $polyfillPackages): void + { + SimpleParameterProvider::setParameter(Option::POLYFILL_PACKAGES, $polyfillPackages); + } + + /** + * @param string[] $autoloadPaths + */ + public function autoloadPaths(array $autoloadPaths): void + { + Assert::allString($autoloadPaths); + + SimpleParameterProvider::setParameter(Option::AUTOLOAD_PATHS, $autoloadPaths); + } + + /** + * @param string[] $bootstrapFiles + */ + public function bootstrapFiles(array $bootstrapFiles): void + { + Assert::allString($bootstrapFiles); + + SimpleParameterProvider::setParameter(Option::BOOTSTRAP_FILES, $bootstrapFiles); + } + + public function symfonyContainerXml(string $filePath): void + { + SimpleParameterProvider::setParameter(Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER, $filePath); + } + + public function symfonyContainerPhp(string $filePath): void + { + SimpleParameterProvider::setParameter(Option::SYMFONY_CONTAINER_PHP_PATH_PARAMETER, $filePath); + } + + public function newLineOnFluentCall(bool $enabled = true): void + { + SimpleParameterProvider::setParameter(Option::NEW_LINE_ON_FLUENT_CALL, $enabled); + } + + public function treatClassesAsFinal(bool $treatClassesAsFinal = true): void + { + SimpleParameterProvider::setParameter(Option::TREAT_CLASSES_AS_FINAL, $treatClassesAsFinal); + } + + /** + * @param string[] $extensions + */ + public function fileExtensions(array $extensions): void + { + Assert::allString($extensions); + + SimpleParameterProvider::setParameter(Option::FILE_EXTENSIONS, $extensions); + } + + public function cacheDirectory(string $directoryPath): void + { + // cache directory path is created via mkdir in CacheFactory + // when not exists, so no need to validate $directoryPath is a directory + SimpleParameterProvider::setParameter(Option::CACHE_DIR, $directoryPath); + } + + public function containerCacheDirectory(string $directoryPath): void + { + // container cache directory path must be a directory on the first place + Assert::directory($directoryPath); + + SimpleParameterProvider::setParameter(Option::CONTAINER_CACHE_DIRECTORY, $directoryPath); + } + + /** + * @param class-string $cacheClass + */ + public function cacheClass(string $cacheClass): void + { + Assert::isAOf($cacheClass, CacheStorageInterface::class); + + SimpleParameterProvider::setParameter(Option::CACHE_CLASS, $cacheClass); + } + + /** + * @param class-string $cacheMetaExtensionClass + */ + public function cacheMetaExtension(string $cacheMetaExtensionClass): void + { + Assert::isAOf($cacheMetaExtensionClass, CacheMetaExtensionInterface::class); + + $this->singleton($cacheMetaExtensionClass); + $this->tag($cacheMetaExtensionClass, CacheMetaExtensionInterface::class); + } + + /** + * @see https://github.com/nikic/PHP-Parser/issues/723#issuecomment-712401963 + */ + public function indent(string $character, int $count): void + { + SimpleParameterProvider::setParameter(Option::INDENT_CHAR, $character); + SimpleParameterProvider::setParameter(Option::INDENT_SIZE, $count); + } + + /** + * @internal + * @api used only in tests + */ + public function resetRuleConfigurations(): void + { + $this->ruleConfigurations = []; + } + + /** + * Compiler passes-like method + */ + public function boot(): void + { + $skippedClassResolver = new SkippedClassResolver(); + $skippedElements = $skippedClassResolver->resolve(); + + foreach ($skippedElements as $skippedClass => $path) { + if ($path !== null) { + continue; + } + + // completely forget the Rector rule only when no path specified + ContainerMemento::forgetService($this, $skippedClass); + } + } + + /** + * @internal Use to add tag on service registrations + */ + public function autotagInterface(string $interface): void + { + $this->autotagInterfaces[] = $interface; + } + + /** + * @param string $abstract + */ + #[Override] + public function singleton($abstract, mixed $concrete = null): void + { + parent::singleton($abstract, $concrete); + + foreach ($this->autotagInterfaces as $autotagInterface) { + if (! is_a($abstract, $autotagInterface, true)) { + continue; + } + + $this->tag($abstract, $autotagInterface); + } + } + + public function reportingRealPath(bool $absolute = true): void + { + SimpleParameterProvider::setParameter(Option::ABSOLUTE_FILE_PATH, $absolute); + } + + public function editorUrl(string $editorUrl): void + { + SimpleParameterProvider::setParameter(Option::EDITOR_URL, $editorUrl); + } + + /** + * @internal Used only for bridge + * @return array, mixed> + */ + public function getRuleConfigurations(): array + { + return $this->ruleConfigurations; + } + + /** + * @internal Used only for bridge + * @return array> + */ + public function getMainRectorClasses(): array + { + return $this->tags[RectorInterface::class] ?? []; + } + + /** + * @internal used to report level overflows in configuration + * @param LevelOverflow[] $levelOverflows + */ + public function setOverflowLevels(array $levelOverflows): void + { + SimpleParameterProvider::addParameter(Option::LEVEL_OVERFLOWS, $levelOverflows); + } +} diff --git a/src/Config/RegisteredService.php b/src/Config/RegisteredService.php new file mode 100644 index 00000000000..e94efece663 --- /dev/null +++ b/src/Config/RegisteredService.php @@ -0,0 +1,30 @@ +className; + } + + public function getAlias(): ?string + { + return $this->alias; + } + + public function getTag(): ?string + { + return $this->tag; + } +} diff --git a/src/Configuration/ConfigInitializer.php b/src/Configuration/ConfigInitializer.php new file mode 100644 index 00000000000..339923a6b45 --- /dev/null +++ b/src/Configuration/ConfigInitializer.php @@ -0,0 +1,99 @@ +symfonyStyle->warning( + 'Register rules or sets in your "' . RectorConfigsResolver::DEFAULT_CONFIG_FILE . '" config' + ); + return; + } + + if (file_exists($distRectorConfigPath)) { + $this->symfonyStyle->warning( + 'Register rules or sets in your "' . RectorConfigsResolver::DEFAULT_DIST_CONFIG_FILE . '" config' + ); + return; + } + + $response = $this->symfonyStyle->ask( + 'No "' . RectorConfigsResolver::DEFAULT_CONFIG_FILE . '" config found. Should we generate it for you?', + 'yes' + ); + // be tolerant about input + if (! in_array($response, ['yes', 'YES', 'y', 'Y'], true)) { + // okay, nothing we can do + return; + } + + $configContents = FileSystem::read(__DIR__ . '/../../templates/rector.php.dist'); + $configContents = $this->replacePathsContents($configContents, $projectDirectory); + + FileSystem::write($commonRectorConfigPath, $configContents, null); + $this->symfonyStyle->success('The config is added now. Re-run command to make Rector do the work!'); + } + + public function areSomeRectorsLoaded(): bool + { + $activeRectors = $this->filterActiveRectors($this->rectors); + + return $activeRectors !== []; + } + + /** + * @param RectorInterface[] $rectors + * @return RectorInterface[] + */ + private function filterActiveRectors(array $rectors): array + { + return array_filter( + $rectors, + static fn (RectorInterface $rector): bool => ! $rector instanceof PostRectorInterface + ); + } + + private function replacePathsContents(string $rectorPhpTemplateContents, string $projectDirectory): string + { + $projectPhpDirectories = $this->initFilePathsResolver->resolve($projectDirectory); + + // fallback to default 'src' in case of empty one + if ($projectPhpDirectories === []) { + $projectPhpDirectories[] = 'src'; + } + + $projectPhpDirectoriesContents = ''; + foreach ($projectPhpDirectories as $projectPhpDirectory) { + $projectPhpDirectoriesContents .= " __DIR__ . '/" . $projectPhpDirectory . "'," . PHP_EOL; + } + + $projectPhpDirectoriesContents = rtrim($projectPhpDirectoriesContents); + + return str_replace('__PATHS__', $projectPhpDirectoriesContents, $rectorPhpTemplateContents); + } +} diff --git a/src/Configuration/ConfigurationFactory.php b/src/Configuration/ConfigurationFactory.php index e3cbf96be4e..2128b23f11d 100644 --- a/src/Configuration/ConfigurationFactory.php +++ b/src/Configuration/ConfigurationFactory.php @@ -2,27 +2,48 @@ declare(strict_types=1); -namespace Rector\Core\Configuration; +namespace Rector\Configuration; use Rector\ChangesReporting\Output\ConsoleOutputFormatter; -use Rector\Core\ValueObject\Configuration; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\ValueObject\Configuration; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symplify\PackageBuilder\Parameter\ParameterProvider; -final class ConfigurationFactory +/** + * @see \Rector\Tests\Configuration\ConfigurationFactoryTest + */ +final readonly class ConfigurationFactory { public function __construct( - private ParameterProvider $parameterProvider, - private SymfonyStyle $symfonyStyle + private SymfonyStyle $symfonyStyle, + private OnlyRuleResolver $onlyRuleResolver, ) { } - public function createForTests(): Configuration + /** + * @api used in tests + * @param string[] $paths + */ + public function createForTests(array $paths): Configuration { - $fileExtensions = $this->parameterProvider->provideArrayParameter(Option::FILE_EXTENSIONS); + $fileExtensions = SimpleParameterProvider::provideArrayParameter(Option::FILE_EXTENSIONS); - return new Configuration(isDryRun: true, fileExtensions: $fileExtensions); + return new Configuration( + false, + true, + false, + ConsoleOutputFormatter::NAME, + $fileExtensions, + $paths, + true, + null, + null, + false, + null, + false, + false + ); } /** @@ -36,11 +57,37 @@ public function createFromInput(InputInterface $input): Configuration $outputFormat = (string) $input->getOption(Option::OUTPUT_FORMAT); $showProgressBar = $this->shouldShowProgressBar($input, $outputFormat); - $showDiffs = ! (bool) $input->getOption(Option::NO_DIFFS); + $showDiffs = $this->shouldShowDiffs($input); $paths = $this->resolvePaths($input); - $fileExtensions = $this->parameterProvider->provideArrayParameter(Option::FILE_EXTENSIONS); + $fileExtensions = SimpleParameterProvider::provideArrayParameter(Option::FILE_EXTENSIONS); + + // filter rule and path + $onlyRule = $input->getOption(Option::ONLY); + if ($onlyRule !== null) { + $onlyRule = $this->onlyRuleResolver->resolve($onlyRule); + } + + $onlySuffix = $input->getOption(Option::ONLY_SUFFIX); + + $isParallel = SimpleParameterProvider::provideBoolParameter(Option::PARALLEL); + $parallelPort = (string) $input->getOption(Option::PARALLEL_PORT); + $parallelIdentifier = (string) $input->getOption(Option::PARALLEL_IDENTIFIER); + $isDebug = (bool) $input->getOption(Option::DEBUG); + + // using debug disables parallel, so emitting exception is straightforward and easier to debug + if ($isDebug) { + $isParallel = false; + } + + $memoryLimit = $this->resolveMemoryLimit($input); + + $isReportingWithRealPath = SimpleParameterProvider::provideBoolParameter(Option::ABSOLUTE_FILE_PATH); + + $levelOverflows = SimpleParameterProvider::provideArrayParameter(Option::LEVEL_OVERFLOWS); + + $showRulesSummary = (bool) $input->getOption(Option::RULES_SUMMARY); return new Configuration( $isDryRun, @@ -49,7 +96,17 @@ public function createFromInput(InputInterface $input): Configuration $outputFormat, $fileExtensions, $paths, - $showDiffs + $showDiffs, + $parallelPort, + $parallelIdentifier, + $isParallel, + $memoryLimit, + $isDebug, + $isReportingWithRealPath, + $onlyRule, + $onlySuffix, + $levelOverflows, + $showRulesSummary, ); } @@ -67,20 +124,15 @@ private function shouldShowProgressBar(InputInterface $input, string $outputForm return $outputFormat === ConsoleOutputFormatter::NAME; } - /** - * @param string[] $commandLinePaths - * @return string[] - */ - private function correctBashSpacePaths(array $commandLinePaths): array + private function shouldShowDiffs(InputInterface $input): bool { - // fixes bash edge-case that to merges string with space to one - foreach ($commandLinePaths as $commandLinePath) { - if (\str_contains($commandLinePath, ' ')) { - $commandLinePaths = explode(' ', $commandLinePath); - } + $noDiffs = (bool) $input->getOption(Option::NO_DIFFS); + if ($noDiffs) { + return false; } - return $commandLinePaths; + // fallback to parameter + return ! SimpleParameterProvider::provideBoolParameter(Option::NO_DIFFS, false); } /** @@ -90,12 +142,48 @@ private function resolvePaths(InputInterface $input): array { $commandLinePaths = (array) $input->getArgument(Option::SOURCE); - // command line has priority + // give priority to command line if ($commandLinePaths !== []) { - return $this->correctBashSpacePaths($commandLinePaths); + $this->setFilesWithoutExtensionParameter($commandLinePaths); + return $commandLinePaths; } // fallback to parameter - return $this->parameterProvider->provideArrayParameter(Option::PATHS); + $configPaths = SimpleParameterProvider::provideArrayParameter(Option::PATHS); + $this->setFilesWithoutExtensionParameter($configPaths); + + return $configPaths; + } + + /** + * @param string[] $paths + */ + private function setFilesWithoutExtensionParameter(array $paths): void + { + foreach ($paths as $path) { + if (is_file($path) && pathinfo($path, PATHINFO_EXTENSION) === '') { + $path = realpath($path); + + if ($path === false) { + continue; + } + + SimpleParameterProvider::addParameter(Option::FILES_WITHOUT_EXTENSION, $path); + } + } + } + + private function resolveMemoryLimit(InputInterface $input): string | null + { + $memoryLimit = $input->getOption(Option::MEMORY_LIMIT); + if ($memoryLimit !== null) { + return (string) $memoryLimit; + } + + if (! SimpleParameterProvider::hasParameter(Option::MEMORY_LIMIT)) { + return null; + } + + return SimpleParameterProvider::provideStringParameter(Option::MEMORY_LIMIT); } } diff --git a/src/Configuration/ConfigurationRuleFilter.php b/src/Configuration/ConfigurationRuleFilter.php new file mode 100644 index 00000000000..83e351ce575 --- /dev/null +++ b/src/Configuration/ConfigurationRuleFilter.php @@ -0,0 +1,55 @@ +configuration = $configuration; + } + + /** + * @param list $rectors + * @return list + */ + public function filter(array $rectors): array + { + if (! $this->configuration instanceof Configuration) { + return $rectors; + } + + $onlyRule = $this->configuration->getOnlyRule(); + if ($onlyRule !== null) { + return $this->filterOnlyRule($rectors, $onlyRule); + } + + return $rectors; + } + + /** + * @param list $rectors + * @return list + */ + public function filterOnlyRule(array $rectors, string $onlyRule): array + { + $activeRectors = []; + foreach ($rectors as $rector) { + if ($rector instanceof $onlyRule) { + $activeRectors[] = $rector; + } + } + + return $activeRectors; + } +} diff --git a/src/Configuration/CurrentNodeProvider.php b/src/Configuration/CurrentNodeProvider.php deleted file mode 100644 index aeb4a8df148..00000000000 --- a/src/Configuration/CurrentNodeProvider.php +++ /dev/null @@ -1,22 +0,0 @@ -node = $node; - } - - public function getNode(): ?Node - { - return $this->node; - } -} diff --git a/src/Configuration/Deprecation/Contract/DeprecatedInterface.php b/src/Configuration/Deprecation/Contract/DeprecatedInterface.php new file mode 100644 index 00000000000..668828f4281 --- /dev/null +++ b/src/Configuration/Deprecation/Contract/DeprecatedInterface.php @@ -0,0 +1,15 @@ +> $availableRules + * @return array> + */ + public static function resolve(int $level, array $availableRules, string $methodName): array + { + // level < 0 is not allowed + Assert::natural($level, sprintf('Level must be >= 0 on %s', $methodName)); + + Assert::allIsAOf($availableRules, RectorInterface::class); + + $rulesCount = count($availableRules); + + if ($availableRules === []) { + throw new ShouldNotHappenException(sprintf( + 'There are no available rules in "%s()", define the available rules first', + $methodName + )); + } + + // start with 0 + $maxLevel = $rulesCount - 1; + if ($level > $maxLevel) { + $level = $maxLevel; + } + + $levelRules = []; + + for ($i = 0; $i <= $level; ++$i) { + $levelRules[] = $availableRules[$i]; + } + + return $levelRules; + } +} diff --git a/src/Configuration/OnlyRuleResolver.php b/src/Configuration/OnlyRuleResolver.php new file mode 100644 index 00000000000..e4f3ad52c1f --- /dev/null +++ b/src/Configuration/OnlyRuleResolver.php @@ -0,0 +1,81 @@ +rectors as $rector) { + if ($rector::class === $rule) { + return $rule; + } + } + + //allow short rule names if there are not duplicates + $matching = []; + foreach ($this->rectors as $rector) { + if (str_ends_with($rector::class, '\\' . $rule)) { + $matching[] = $rector::class; + } + } + + $matching = array_unique($matching); + if (count($matching) === 1) { + return $matching[0]; + } + + if (count($matching) > 1) { + sort($matching); + $message = sprintf( + 'Short rule name "%s" is ambiguous. Specify the full rule name:' . PHP_EOL + . '- ' . implode(PHP_EOL . '- ', $matching), + $rule + ); + throw new RectorRuleNameAmbiguousException($message); + } + + if (! str_contains($rule, '\\')) { + $message = sprintf( + 'Rule "%s" was not found.%sThe rule has no namespace. Make sure to escape the backslashes, and add quotes around the rule name: --only="My\\Rector\\Rule"', + $rule, + PHP_EOL + ); + } else { + $message = sprintf( + 'Rule "%s" was not found.%sMake sure it is registered in your config or in one of the sets', + $rule, + PHP_EOL + ); + } + + throw new RectorRuleNotFoundException($message); + } +} diff --git a/src/Configuration/Option.php b/src/Configuration/Option.php index e57dccd3b13..a0021391dbc 100644 --- a/src/Configuration/Option.php +++ b/src/Configuration/Option.php @@ -2,172 +2,264 @@ declare(strict_types=1); -namespace Rector\Core\Configuration; +namespace Rector\Configuration; -use JetBrains\PhpStorm\Immutable; use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface; use Rector\Caching\ValueObject\Storage\FileCacheStorage; -use Symplify\Skipper\ValueObject\Option as SkipperOption; -#[Immutable] final class Option { + public const string SOURCE = 'source'; + + public const string AUTOLOAD_FILE = 'autoload-file'; + /** + * @internal Use @see \Rector\Config\RectorConfig::bootstrapFiles() instead * @var string */ - public const SOURCE = 'source'; + public const string BOOTSTRAP_FILES = 'bootstrap_files'; + + public const string DRY_RUN = 'dry-run'; + + public const string DRY_RUN_SHORT = 'n'; + + public const string OUTPUT_FORMAT = 'output-format'; + + public const string NO_PROGRESS_BAR = 'no-progress-bar'; /** + * @internal Use @see \Rector\Config\RectorConfig::phpVersion() instead * @var string */ - public const AUTOLOAD_FILE = 'autoload-file'; + public const string PHP_VERSION_FEATURES = 'php_version_features'; /** + * @internal Use @see \Rector\Config\RectorConfig::importNames() instead * @var string */ - public const BOOTSTRAP_FILES = 'bootstrap_files'; + public const string AUTO_IMPORT_NAMES = 'auto_import_names'; /** + * @internal Use @see \Rector\Config\RectorConfig::polyfillPackages() instead * @var string */ - public const DRY_RUN = 'dry-run'; + public const string POLYFILL_PACKAGES = 'polyfill_packages'; /** + * @internal Use @see \Rector\Config\RectorConfig::importNames() instead * @var string */ - public const DRY_RUN_SHORT = 'n'; + public const string AUTO_IMPORT_DOC_BLOCK_NAMES = 'auto_import_doc_block_names'; /** + * @internal Use @see \Rector\Config\RectorConfig::importShortClasses() instead * @var string */ - public const OUTPUT_FORMAT = 'output-format'; + public const string IMPORT_SHORT_CLASSES = 'import_short_classes'; /** + * @internal Use @see \Rector\Config\RectorConfig::symfonyContainerXml() instead * @var string */ - public const NO_PROGRESS_BAR = 'no-progress-bar'; + public const string SYMFONY_CONTAINER_XML_PATH_PARAMETER = 'symfony_container_xml_path'; /** + * @internal Use @see \Rector\Config\RectorConfig::symfonyContainerPhp() * @var string */ - public const PHP_VERSION_FEATURES = 'php_version_features'; + public const string SYMFONY_CONTAINER_PHP_PATH_PARAMETER = 'symfony_container_php_path'; /** + * @internal Use @see \Rector\Config\RectorConfig::newLineOnFluentCall() * @var string */ - public const AUTO_IMPORT_NAMES = 'auto_import_names'; + public const string NEW_LINE_ON_FLUENT_CALL = 'new_line_on_fluent_call'; + + public const string CLEAR_CACHE = 'clear-cache'; + + public const string ONLY = 'only'; /** + * @internal Use @see \Rector\Config\RectorConfig::parallel() instead * @var string */ - public const IMPORT_SHORT_CLASSES = 'import_short_classes'; + public const string PARALLEL = 'parallel'; /** + * @internal Use @see \Rector\Config\RectorConfig::paths() instead * @var string */ - public const IMPORT_DOC_BLOCKS = 'import_doc_blocks'; + public const string PATHS = 'paths'; /** + * @internal Use @see \Rector\Config\RectorConfig::autoloadPaths() instead * @var string */ - public const SYMFONY_CONTAINER_XML_PATH_PARAMETER = 'symfony_container_xml_path'; + public const string AUTOLOAD_PATHS = 'autoload_paths'; /** + * @internal Use @see \Rector\Config\RectorConfig::skip() instead * @var string */ - public const CLEAR_CACHE = 'clear-cache'; + public const string SKIP = 'skip'; /** - * @deprecated Cache is enabled by default - * @var string + * @internal Use RectorConfig::fileExtensions() instead */ - public const ENABLE_CACHE = 'enable_cache'; + public const string FILE_EXTENSIONS = 'file_extensions'; /** - * @var string + * @internal Use RectorConfig::cacheDirectory() instead */ - public const PATHS = 'paths'; + public const string CACHE_DIR = 'cache_dir'; /** - * @var string + * Cache backend. Most of the time we cache in files, but in ephemeral environment (e.g. CI), a faster `MemoryCacheStorage` can be useful. + * @internal Use RectorConfig::cacheClass() instead + * + * @var class-string + * @internal */ - public const AUTOLOAD_PATHS = 'autoload_paths'; + public const string CACHE_CLASS = FileCacheStorage::class; + + public const string DEBUG = 'debug'; + + public const string XDEBUG = 'xdebug'; + + public const string RULES_SUMMARY = 'rules-summary'; + + public const string CONFIG = 'config'; /** + * @internal Use @see \Rector\Config\RectorConfig::phpstanConfig() instead * @var string */ - public const SKIP = SkipperOption::SKIP; + public const string PHPSTAN_FOR_RECTOR_PATHS = 'phpstan_for_rector_paths'; + + public const string NO_DIFFS = 'no-diffs'; + + public const string AUTOLOAD_FILE_SHORT = 'a'; + + public const string PARALLEL_IDENTIFIER = 'identifier'; + + public const string PARALLEL_PORT = 'port'; /** + * @internal Use @see \Rector\Config\RectorConfig::parallel() instead with pass int $jobSize parameter * @var string */ - public const FILE_EXTENSIONS = 'file_extensions'; + public const string PARALLEL_JOB_SIZE = 'parallel-job-size'; /** + * @internal Use @see \Rector\Config\RectorConfig::parallel() instead with pass int $maxNumberOfProcess parameter * @var string */ - public const NESTED_CHAIN_METHOD_CALL_LIMIT = 'nested_chain_method_call_limit'; + public const string PARALLEL_MAX_NUMBER_OF_PROCESSES = 'parallel-max-number-of-processes'; /** + * @internal Use @see \Rector\Config\RectorConfig::parallel() instead with pass int $seconds parameter * @var string */ - public const CACHE_DIR = 'cache_dir'; + public const string PARALLEL_JOB_TIMEOUT_IN_SECONDS = 'parallel-job-timeout-in-seconds'; + + public const string MEMORY_LIMIT = 'memory-limit'; /** - * Cache backend. Most of the time we cache in files, but in ephemeral environment (e.g. CI), a faster `MemoryCacheStorage` can be usefull. - * - * @var class-string - * @internal + * @internal Use @see \Rector\Config\RectorConfig::indent() method + * @var string */ - public const CACHE_CLASS = FileCacheStorage::class; + public const string INDENT_CHAR = 'indent-char'; /** + * @internal Use @see \Rector\Config\RectorConfig::indent() method * @var string */ - public const DEBUG = 'debug'; + public const string INDENT_SIZE = 'indent-size'; /** + * @internal Use @see \Rector\Config\RectorConfig::removeUnusedImports() method * @var string */ - public const XDEBUG = 'xdebug'; + public const string REMOVE_UNUSED_IMPORTS = 'remove-unused-imports'; /** + * @internal Use @see \Rector\Config\RectorConfig::containerCacheDirectory() method * @var string */ - public const CONFIG = 'config'; + public const string CONTAINER_CACHE_DIRECTORY = 'container-cache-directory'; /** - * @var string + * @internal For cache invalidation in case of change */ - public const PHPSTAN_FOR_RECTOR_PATH = 'phpstan_for_rector_path'; + public const string REGISTERED_RECTOR_RULES = 'registered_rector_rules'; /** - * @var string + * @internal For cache invalidation in case of change */ - public const TYPES_TO_REMOVE_STATIC_FROM = 'types_to_remove_static_from'; + public const string REGISTERED_RECTOR_SETS = 'registered_rector_sets'; /** - * @var string + * @internal For verify RectorConfigBuilder instance recreated */ - public const NO_DIFFS = 'no-diffs'; + public const string IS_RECTORCONFIG_BUILDER_RECREATED = 'is_rectorconfig_builder_recreated'; /** - * @var string + * @internal For verify skipped rules exists in registered rules */ - public const TEMPLATE_TYPE = 'template-type'; + public const string SKIPPED_RECTOR_RULES = 'skipped_rector_rules'; /** - * @var string + * @internal For collect skipped start with short open tag files to be reported */ - public const ENABLE_EDITORCONFIG = 'enable_editorconfig'; + public const string SKIPPED_START_WITH_SHORT_OPEN_TAG_FILES = 'skipped_start_with_short_open_tag_files'; /** - * @var string + * @internal For reporting with absolute paths instead of relative paths (default behaviour) + * @see \Rector\Config\RectorConfig::reportingRealPath() + */ + public const string ABSOLUTE_FILE_PATH = 'absolute_file_path'; + + /** + * @internal To add editor links to console output + * @see \Rector\Config\RectorConfig::editorUrl() */ - public const AUTOLOAD_FILE_SHORT = 'a'; + public const string EDITOR_URL = 'editor_url'; /** + * @internal Use @see \Rector\Config\RectorConfig::treatClassesAsFinal() method * @var string */ - public const OUTPUT_FORMAT_SHORT = 'o'; + public const string TREAT_CLASSES_AS_FINAL = 'treat_classes_as_final'; + + /** + * @internal To report composer based loaded sets + * @see \Rector\Configuration\RectorConfigBuilder::withComposerBased() + */ + public const string COMPOSER_BASED_SETS = 'composer_based_sets'; + + /** + * @internal To filter files by specific suffix + */ + public const string ONLY_SUFFIX = 'only-suffix'; + + /** + * @internal To report overflow levels in ->with*Level() methods + */ + public const string LEVEL_OVERFLOWS = 'level_overflows'; + + /** + * @internal To avoid registering rules via ->withRules(), that are already loaded in sets, + * and keep rector.php clean + */ + public const string ROOT_STANDALONE_REGISTERED_RULES = 'root_standalone_registered_rules'; + + /** + * @internal The other half of ROOT_STANDALONE_REGISTERED_RULES to compare + */ + public const string SET_REGISTERED_RULES = 'set_registered_rules'; + + /** + * @internal to allow process file without extension if explicitly registered + */ + public const string FILES_WITHOUT_EXTENSION = 'files_without_extension'; } diff --git a/src/Configuration/Parameter/FeatureFlags.php b/src/Configuration/Parameter/FeatureFlags.php new file mode 100644 index 00000000000..8326b037b11 --- /dev/null +++ b/src/Configuration/Parameter/FeatureFlags.php @@ -0,0 +1,26 @@ +isAbstract()) { + return false; + } + + return SimpleParameterProvider::provideBoolParameter(Option::TREAT_CLASSES_AS_FINAL); + } +} diff --git a/src/Configuration/Parameter/SimpleParameterProvider.php b/src/Configuration/Parameter/SimpleParameterProvider.php new file mode 100644 index 00000000000..2382c339465 --- /dev/null +++ b/src/Configuration/Parameter/SimpleParameterProvider.php @@ -0,0 +1,118 @@ + + */ + private static array $parameters = []; + + /** + * @param Option::* $name + */ + public static function addParameter(string $name, mixed $value): void + { + if (is_array($value)) { + $mergedParameters = array_merge(self::$parameters[$name] ?? [], $value); + self::$parameters[$name] = $mergedParameters; + } else { + self::$parameters[$name][] = $value; + } + } + + /** + * @param Option::* $name + */ + public static function setParameter(string $name, mixed $value): void + { + self::$parameters[$name] = $value; + } + + /** + * @param Option::* $name + * @return mixed[] + */ + public static function provideArrayParameter(string $name): array + { + $parameter = self::$parameters[$name] ?? []; + Assert::isArray($parameter); + + if (array_is_list($parameter)) { + // remove duplicates + $uniqueParameters = array_unique($parameter, SORT_REGULAR); + return array_values($uniqueParameters); + } + + return $parameter; + } + + /** + * @param Option::* $name + */ + public static function hasParameter(string $name): bool + { + return array_key_exists($name, self::$parameters); + } + + /** + * @param Option::* $name + */ + public static function provideStringParameter(string $name, ?string $default = null): string + { + if ($default === null) { + self::ensureParameterIsSet($name); + } + + return self::$parameters[$name] ?? $default; + } + + public static function provideIntParameter(string $key): int + { + return self::$parameters[$key]; + } + + /** + * @param Option::* $name + */ + public static function provideBoolParameter(string $name, ?bool $default = null): bool + { + if ($default === null) { + self::ensureParameterIsSet($name); + } + + return self::$parameters[$name] ?? $default; + } + + /** + * @api + * For cache invalidation + */ + public static function hash(): string + { + $parameterKeys = self::$parameters; + return sha1(serialize($parameterKeys)); + } + + /** + * @param Option::* $name + */ + private static function ensureParameterIsSet(string $name): void + { + if (array_key_exists($name, self::$parameters)) { + return; + } + + throw new ShouldNotHappenException(sprintf('Parameter "%s" was not found', $name)); + } +} diff --git a/src/Configuration/PhpLevelSetResolver.php b/src/Configuration/PhpLevelSetResolver.php new file mode 100644 index 00000000000..001806d2088 --- /dev/null +++ b/src/Configuration/PhpLevelSetResolver.php @@ -0,0 +1,59 @@ + + */ + private const array VERSION_LOWER_BOUND_CONFIGS = [ + PhpVersion::PHP_52 => SetList::PHP_52, + PhpVersion::PHP_53 => SetList::PHP_53, + PhpVersion::PHP_54 => SetList::PHP_54, + PhpVersion::PHP_55 => SetList::PHP_55, + PhpVersion::PHP_56 => SetList::PHP_56, + PhpVersion::PHP_70 => SetList::PHP_70, + PhpVersion::PHP_71 => SetList::PHP_71, + PhpVersion::PHP_72 => SetList::PHP_72, + PhpVersion::PHP_73 => SetList::PHP_73, + PhpVersion::PHP_74 => SetList::PHP_74, + PhpVersion::PHP_80 => SetList::PHP_80, + PhpVersion::PHP_81 => SetList::PHP_81, + PhpVersion::PHP_82 => SetList::PHP_82, + PhpVersion::PHP_83 => SetList::PHP_83, + PhpVersion::PHP_84 => SetList::PHP_84, + PhpVersion::PHP_85 => SetList::PHP_85, + PhpVersion::PHP_86 => SetList::PHP_86, + ]; + + /** + * @param PhpVersion::* $phpVersion + * @return string[] + */ + public static function resolveFromPhpVersion(int $phpVersion): array + { + $configFilePaths = []; + + foreach (self::VERSION_LOWER_BOUND_CONFIGS as $versionLowerBound => $phpSetFilePath) { + if ($versionLowerBound <= $phpVersion) { + $configFilePaths[] = $phpSetFilePath; + } + } + + Assert::allFileExists($configFilePaths); + + return $configFilePaths; + } +} diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php new file mode 100644 index 00000000000..b1b3649dd6f --- /dev/null +++ b/src/Configuration/RectorConfigBuilder.php @@ -0,0 +1,1349 @@ + + */ + private array $skip = []; + + /** + * @var array> + */ + private array $rules = []; + + /** + * @var array, mixed[]> + */ + private array $rulesWithConfigurations = []; + + /** + * @var string[] + */ + private array $fileExtensions = []; + + /** + * @var null|class-string + */ + private ?string $cacheClass = null; + + private ?string $cacheDirectory = null; + + private ?string $containerCacheDirectory = null; + + /** + * @var array> + */ + private array $cacheMetaExtensions = []; + + private ?bool $parallel = null; + + private int $parallelTimeoutSeconds = 120; + + private int $parallelMaxNumberOfProcess = Defaults::PARALLEL_MAX_NUMBER_OF_PROCESS; + + private int $parallelJobSize = 16; + + private bool $importNames = false; + + private bool $importDocBlockNames = false; + + private bool $importShortClasses = true; + + private bool $removeUnusedImports = false; + + private bool $noDiffs = false; + + private ?string $memoryLimit = null; + + /** + * @var string[] + */ + private array $autoloadPaths = []; + + /** + * @var string[] + */ + private array $bootstrapFiles = []; + + private string $indentChar = ' '; + + private int $indentSize = 4; + + /** + * @var string[] + */ + private array $phpstanConfigs = []; + + /** + * @var null|PhpVersion::* + */ + private ?int $phpVersion = null; + + private ?string $symfonyContainerXmlFile = null; + + private ?string $symfonyContainerPhpFile = null; + + /** + * To make sure type declarations set and level are not duplicated, + * as both contain same rules + */ + private ?bool $isTypeCoverageLevelUsed = null; + + private ?bool $isTypeCoverageDocblockLevelUsed = null; + + private ?bool $isDeadCodeLevelUsed = null; + + private ?bool $isCodeQualityLevelUsed = null; + + private ?bool $isCodingStyleLevelUsed = null; + + private ?bool $isFluentNewLine = null; + + private ?bool $isTreatClassesAsFinal = null; + + /** + * @var RegisteredService[] + */ + private array $registerServices = []; + + /** + * @var array + */ + private array $setGroups = []; + + private ?bool $reportingRealPath = null; + + /** + * @var string[] + */ + private array $groupLoadedSets = []; + + private ?string $editorUrl = null; + + private ?bool $isWithPhpSetsUsed = null; + + private ?bool $isWithPhpLevelUsed = null; + + /** + * @var array,bool> + */ + private array $setProviders = []; + + /** + * @var LevelOverflow[] + */ + private array $levelOverflows = []; + + public function __invoke(RectorConfig $rectorConfig): void + { + if ($this->setGroups !== [] || $this->setProviders !== []) { + $setProviderCollector = new SetProviderCollector(array_map( + $rectorConfig->make(...), + \array_keys($this->setProviders) + )); + + $setManager = new SetManager($setProviderCollector, new InstalledPackageResolver(getcwd())); + + $this->groupLoadedSets = $setManager->matchBySetGroups($this->setGroups); + + SimpleParameterProvider::addParameter(Option::COMPOSER_BASED_SETS, $this->groupLoadedSets); + } + + // not to miss it by accident + if ($this->isWithPhpSetsUsed === true) { + $this->sets[] = SetList::PHP_POLYFILLS; + } + + // merge sets together + $this->sets = array_merge($this->sets, $this->groupLoadedSets); + + $uniqueSets = array_unique($this->sets); + + if ($this->isWithPhpLevelUsed && $this->isWithPhpSetsUsed) { + throw new InvalidConfigurationException(sprintf( + 'Your config uses "withPhp*()" and "withPhpLevel()" methods at the same time.%sPick one of them to avoid rule conflicts.', + PHP_EOL + )); + } + + if (in_array(SetList::TYPE_DECLARATION, $uniqueSets, true) && $this->isTypeCoverageLevelUsed === true) { + throw new InvalidConfigurationException(sprintf( + 'Your config already enables type declarations set.%sRemove "->withTypeCoverageLevel()" as it only duplicates it, or remove type declaration set.', + PHP_EOL + )); + } + + if (in_array( + SetList::TYPE_DECLARATION_DOCBLOCKS, + $uniqueSets, + true + ) && $this->isTypeCoverageDocblockLevelUsed === true) { + throw new InvalidConfigurationException(sprintf( + 'Your config already enables type declarations set.%sRemove "->withTypeCoverageDocblockLevel()" as it only duplicates it, or remove type declaration set.', + PHP_EOL + )); + } + + if (in_array(SetList::DEAD_CODE, $uniqueSets, true) && $this->isDeadCodeLevelUsed === true) { + throw new InvalidConfigurationException(sprintf( + 'Your config already enables dead code set.%sRemove "->withDeadCodeLevel()" as it only duplicates it, or remove dead code set.', + PHP_EOL + )); + } + + if (in_array(SetList::CODE_QUALITY, $uniqueSets, true) && $this->isCodeQualityLevelUsed === true) { + throw new InvalidConfigurationException(sprintf( + 'Your config already enables code quality set.%sRemove "->withCodeQualityLevel()" as it only duplicates it, or remove code quality set.', + PHP_EOL + )); + } + + if (in_array(SetList::CODING_STYLE, $uniqueSets, true) && $this->isCodingStyleLevelUsed === true) { + throw new InvalidConfigurationException(sprintf( + 'Your config already enables coding style set.%sRemove "->withCodingStyleLevel()" as it only duplicates it, or remove coding style set.', + PHP_EOL + )); + } + + if ($uniqueSets !== []) { + $rectorConfig->sets($uniqueSets); + } + + // log rules from sets and compare them with explicit rules + $setRegisteredRectorClasses = $rectorConfig->getMainRectorClasses(); + SimpleParameterProvider::addParameter(Option::SET_REGISTERED_RULES, $setRegisteredRectorClasses); + + if ($this->paths !== []) { + $rectorConfig->paths($this->paths); + } + + // must be in upper part, as these services might be used by rule registered bellow + foreach ($this->registerServices as $registerService) { + $rectorConfig->singleton($registerService->getClassName()); + + if ($registerService->getAlias()) { + $rectorConfig->alias($registerService->getClassName(), $registerService->getAlias()); + } + + if ($registerService->getTag()) { + $rectorConfig->tag($registerService->getClassName(), $registerService->getTag()); + } + } + + if ($this->skip !== []) { + $rectorConfig->skip($this->skip); + } + + if ($this->rules !== []) { + $rectorConfig->rules($this->rules); + } + + foreach ($this->rulesWithConfigurations as $rectorClass => $configurations) { + foreach ($configurations as $configuration) { + $rectorConfig->ruleWithConfiguration($rectorClass, $configuration); + } + } + + if ($this->fileExtensions !== []) { + $rectorConfig->fileExtensions($this->fileExtensions); + } + + if ($this->cacheClass !== null) { + $rectorConfig->cacheClass($this->cacheClass); + } + + if ($this->cacheDirectory !== null) { + $rectorConfig->cacheDirectory($this->cacheDirectory); + } + + if ($this->containerCacheDirectory !== null) { + $rectorConfig->containerCacheDirectory($this->containerCacheDirectory); + } + + foreach ($this->cacheMetaExtensions as $cacheMetumExtension) { + $rectorConfig->cacheMetaExtension($cacheMetumExtension); + } + + if ($this->importNames || $this->importDocBlockNames) { + $rectorConfig->importNames($this->importNames, $this->importDocBlockNames); + $rectorConfig->importShortClasses($this->importShortClasses); + } + + if ($this->removeUnusedImports) { + $rectorConfig->removeUnusedImports($this->removeUnusedImports); + } + + if ($this->noDiffs) { + $rectorConfig->noDiffs(); + } + + if ($this->memoryLimit !== null) { + $rectorConfig->memoryLimit($this->memoryLimit); + } + + if ($this->autoloadPaths !== []) { + $rectorConfig->autoloadPaths($this->autoloadPaths); + } + + if ($this->bootstrapFiles !== []) { + $rectorConfig->bootstrapFiles($this->bootstrapFiles); + } + + if ($this->indentChar !== ' ' || $this->indentSize !== 4) { + $rectorConfig->indent($this->indentChar, $this->indentSize); + } + + if ($this->phpstanConfigs !== []) { + $rectorConfig->phpstanConfigs($this->phpstanConfigs); + } + + if ($this->phpVersion !== null) { + $rectorConfig->phpVersion($this->phpVersion); + } + + if ($this->parallel !== null) { + if ($this->parallel) { + $rectorConfig->parallel( + processTimeout: $this->parallelTimeoutSeconds, + maxNumberOfProcess: $this->parallelMaxNumberOfProcess, + jobSize: $this->parallelJobSize + ); + } else { + $rectorConfig->disableParallel(); + } + } + + if ($this->symfonyContainerXmlFile !== null) { + $rectorConfig->symfonyContainerXml($this->symfonyContainerXmlFile); + } + + if ($this->symfonyContainerPhpFile !== null) { + $rectorConfig->symfonyContainerPhp($this->symfonyContainerPhpFile); + } + + if ($this->isFluentNewLine !== null) { + $rectorConfig->newLineOnFluentCall($this->isFluentNewLine); + } + + if ($this->isTreatClassesAsFinal !== null) { + $rectorConfig->treatClassesAsFinal($this->isTreatClassesAsFinal); + } + + if ($this->reportingRealPath !== null) { + $rectorConfig->reportingRealPath($this->reportingRealPath); + } + + if ($this->editorUrl !== null) { + $rectorConfig->editorUrl($this->editorUrl); + } + + if ($this->levelOverflows !== []) { + $rectorConfig->setOverflowLevels($this->levelOverflows); + } + } + + /** + * @param string[] $paths + */ + public function withPaths(array $paths): self + { + $this->paths = $paths; + + return $this; + } + + /** + * @param array $skip + */ + public function withSkip(array $skip): self + { + $this->skip = array_merge($this->skip, $skip); + + return $this; + } + + public function withSkipPath(string $skipPath): self + { + if (! str_contains($skipPath, '*')) { + Assert::fileExists($skipPath); + } + + return $this->withSkip([$skipPath]); + } + + /** + * Include PHP files from the root directory (including hidden ones), + * typically ecs.php, rector.php, .php-cs-fixer.dist.php etc. + */ + public function withRootFiles(): self + { + $rootPhpFilesFinder = (new Finder())->files() + ->in(getcwd()) + ->depth(0) + ->ignoreDotFiles(false) + ->ignoreVCSIgnored(true) + ->name('*.php') + ->name('.*.php') + // this file cannot be interpreted as PHP file + // https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html#expected-arguments + ->notName('.phpstorm.meta.php') + ; + + foreach ($rootPhpFilesFinder as $rootPhpFileFinder) { + $path = $rootPhpFileFinder->getRealPath(); + $this->paths[] = $path; + } + + return $this; + } + + /** + * @param string[] $sets + */ + public function withSets(array $sets): self + { + $this->sets = array_merge($this->sets, $sets); + + return $this; + } + + /** + * Upgrade your annotations to attributes + */ + public function withAttributesSets( + bool $symfony = false, + bool $doctrine = false, + bool $mongoDb = false, + bool $gedmo = false, + bool $phpunit = false, + bool $fosRest = false, + bool $jms = false, + bool $sensiolabs = false, + bool $behat = false, + bool $all = false, + bool $symfonyRoute = false, + bool $symfonyValidator = false + ): self { + // if nothing is passed, enable all as convention in other method + if (func_get_args() === []) { + $all = true; + } + + if ($symfony || $all) { + $this->sets[] = SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES; + } + + // dx for more granular upgrade + if ($symfonyRoute) { + if ($symfony) { + throw new InvalidConfigurationException( + '$symfonyRoute is already included in $symfony. Use $symfony only' + ); + } + + $this->withConfiguredRule(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Symfony\Component\Routing\Annotation\Route'), + ]); + } + + if ($symfonyValidator) { + if ($symfony) { + throw new InvalidConfigurationException( + '$symfonyValidator is already included in $symfony. Use $symfony only' + ); + } + + $this->sets[] = SymfonySetList::SYMFONY_52_VALIDATOR_ATTRIBUTES; + } + + if ($doctrine || $all) { + $this->sets[] = DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES; + } + + if ($mongoDb || $all) { + $this->sets[] = DoctrineSetList::MONGODB__ANNOTATIONS_TO_ATTRIBUTES; + } + + if ($gedmo || $all) { + $this->sets[] = DoctrineSetList::GEDMO_ANNOTATIONS_TO_ATTRIBUTES; + } + + if ($fosRest || $all) { + $this->sets[] = SymfonyInternalSetList::FOS_REST_ANNOTATIONS_TO_ATTRIBUTES; + } + + if ($jms || $all) { + $this->sets[] = SymfonyInternalSetList::JMS_ANNOTATIONS_TO_ATTRIBUTES; + } + + if ($sensiolabs || $all) { + $this->sets[] = SymfonyInternalSetList::SENSIOLABS_ANNOTATIONS_TO_ATTRIBUTES; + } + + if ($phpunit || $all) { + $this->sets[] = PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES; + } + + if ($behat || $all) { + $this->sets[] = SetList::BEHAT_ANNOTATIONS_TO_ATTRIBUTES; + } + + return $this; + } + + /** + * What PHP sets should be applied? By default the same version + * as composer.json has is used + */ + public function withPhpSets( + bool $php83 = false, + bool $php82 = false, + bool $php81 = false, + bool $php80 = false, + bool $php74 = false, + bool $php73 = false, + bool $php72 = false, + bool $php71 = false, + bool $php70 = false, + bool $php56 = false, + bool $php55 = false, + bool $php54 = false, + bool $php53 = false, + // place on later as BC break when used in php 7.x without named arg + bool $php84 = false, + bool $php85 = false, + bool $php86 = false, + ): self { + if ($this->isWithPhpSetsUsed === true) { + throw new InvalidConfigurationException(sprintf( + 'Method "%s()" can be called only once. It always includes all previous sets UP TO the defined version.%sThe best practise is to call it once with no argument. That way it will pick up PHP version from composer.json and your project will always stay up to date.', + __METHOD__, + PHP_EOL + )); + } + + $this->isWithPhpSetsUsed = true; + + $pickedArguments = array_filter(func_get_args()); + if ($pickedArguments !== []) { + Notifier::errorWithPhpSetsNotSuitableForPHP74AndLower(); + } + + if (count($pickedArguments) > 1) { + throw new InvalidConfigurationException( + sprintf( + 'Pick only one version target in "withPhpSets()". All rules up to this version will be used.%sTo use your composer.json PHP version, keep arguments empty.', + PHP_EOL + ) + ); + } + + if ($pickedArguments === []) { + $projectPhpVersion = ComposerJsonPhpVersionResolver::resolveFromCwdOrFail(); + $phpLevelSets = PhpLevelSetResolver::resolveFromPhpVersion($projectPhpVersion); + + $this->sets = array_merge($this->sets, $phpLevelSets); + + return $this; + } + + if ($php53) { + $this->withPhp53Sets(); + return $this; + } + + if ($php54) { + $this->withPhp54Sets(); + return $this; + } + + if ($php55) { + $this->withPhp55Sets(); + return $this; + } + + if ($php56) { + $this->withPhp56Sets(); + return $this; + } + + if ($php70) { + $this->withPhp70Sets(); + return $this; + } + + if ($php71) { + $this->withPhp71Sets(); + return $this; + } + + if ($php72) { + $this->withPhp72Sets(); + return $this; + } + + if ($php73) { + $this->withPhp73Sets(); + return $this; + } + + if ($php74) { + $this->withPhp74Sets(); + return $this; + } + + if ($php80) { + $targetPhpVersion = PhpVersion::PHP_80; + } elseif ($php81) { + $targetPhpVersion = PhpVersion::PHP_81; + } elseif ($php82) { + $targetPhpVersion = PhpVersion::PHP_82; + } elseif ($php83) { + $targetPhpVersion = PhpVersion::PHP_83; + } elseif ($php84) { + $targetPhpVersion = PhpVersion::PHP_84; + } elseif ($php85) { + $targetPhpVersion = PhpVersion::PHP_85; + } elseif ($php86) { + $targetPhpVersion = PhpVersion::PHP_86; + } else { + throw new InvalidConfigurationException('Invalid PHP version set'); + } + + $phpLevelSets = PhpLevelSetResolver::resolveFromPhpVersion($targetPhpVersion); + $this->sets = array_merge($this->sets, $phpLevelSets); + + return $this; + } + + /** + * Following methods are suitable for PHP 7.4 and lower, before named args + * Let's keep them without warning, in case Rector is run on both PHP 7.4 and PHP 8.0 in CI + */ + public function withPhp53Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_53)); + + return $this; + } + + public function withPhp54Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_54)); + + return $this; + } + + public function withPhp55Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_55)); + + return $this; + } + + public function withPhp56Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_56)); + + return $this; + } + + public function withPhp70Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_70)); + + return $this; + } + + public function withPhp71Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_71)); + + return $this; + } + + public function withPhp72Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_72)); + + return $this; + } + + public function withPhp73Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_73)); + + return $this; + } + + public function withPhp74Sets(): self + { + $this->isWithPhpSetsUsed = true; + + $this->sets = array_merge($this->sets, PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_74)); + + return $this; + } + + // there is no withPhp80Sets() and above, + // as we already use PHP 8.0 and should go with withPhpSets() instead + + public function withPreparedSets( + bool $deadCode = false, + bool $codeQuality = false, + bool $codingStyle = false, + bool $typeDeclarations = false, + bool $typeDeclarationDocblocks = false, + bool $privatization = false, + bool $naming = false, + bool $instanceOf = false, + bool $earlyReturn = false, + /** @deprecated */ + bool $strictBooleans = false, + bool $carbon = false, + bool $rectorPreset = false, + bool $phpunitCodeQuality = false, + bool $doctrineCodeQuality = false, + bool $symfonyCodeQuality = false, + bool $symfonyConfigs = false, + ): self { + Notifier::notifyNotSuitableMethodForPHP74(__METHOD__); + + if ($strictBooleans) { + $message = 'The "strictBooleans" set is deprecated as mostly risky and not practical. Remove it from withPreparedSets() method and use "codeQuality" and "codingStyle" sets instead. They already contain more granular and stable rules on same note.'; + + $symfonyStyle = new SymfonyStyle(new ArgvInput(), new ConsoleOutput()); + $symfonyStyle->warning($message); + } + + $setMap = [ + SetList::DEAD_CODE => $deadCode, + SetList::CODE_QUALITY => $codeQuality, + SetList::CODING_STYLE => $codingStyle, + SetList::TYPE_DECLARATION => $typeDeclarations, + SetList::TYPE_DECLARATION_DOCBLOCKS => $typeDeclarationDocblocks, + SetList::PRIVATIZATION => $privatization, + SetList::NAMING => $naming, + SetList::INSTANCEOF => $instanceOf, + SetList::EARLY_RETURN => $earlyReturn, + SetList::CARBON => $carbon, + SetList::RECTOR_PRESET => $rectorPreset, + PHPUnitSetList::PHPUNIT_CODE_QUALITY => $phpunitCodeQuality, + DoctrineSetList::DOCTRINE_CODE_QUALITY => $doctrineCodeQuality, + SymfonySetList::SYMFONY_CODE_QUALITY => $symfonyCodeQuality, + SymfonySetList::CONFIGS => $symfonyConfigs, + ]; + + foreach ($setMap as $setPath => $isEnabled) { + if ($isEnabled) { + $this->sets[] = $setPath; + } + } + + return $this; + } + + public function withComposerBased( + bool $twig = false, + bool $doctrine = false, + bool $phpunit = false, + bool $symfony = false, + bool $netteUtils = false, + bool $laravel = false, + ): self { + $setMap = [ + SetGroup::TWIG => $twig, + SetGroup::DOCTRINE => $doctrine, + SetGroup::PHPUNIT => $phpunit, + SetGroup::SYMFONY => $symfony, + SetGroup::NETTE_UTILS => $netteUtils, + SetGroup::LARAVEL => $laravel, + ]; + + foreach ($setMap as $setPath => $isEnabled) { + if ($isEnabled) { + $this->setGroups[] = $setPath; + } + } + + return $this; + } + + /** + * @param array> $rules + */ + public function withRules(array $rules): self + { + $this->rules = array_merge($this->rules, $rules); + + if (SimpleParameterProvider::provideBoolParameter(Option::IS_RECTORCONFIG_BUILDER_RECREATED, false) === false) { + // log all explicitly registered rules on root rector.php + // we only check the non-configurable rules, as the configurable ones might override them + $nonConfigurableRules = array_filter( + $rules, + fn (string $rule): bool => ! is_a($rule, ConfigurableRectorInterface::class, true) + ); + + SimpleParameterProvider::addParameter(Option::ROOT_STANDALONE_REGISTERED_RULES, $nonConfigurableRules); + } + + return $this; + } + + /** + * @param string[] $fileExtensions + */ + public function withFileExtensions(array $fileExtensions): self + { + $this->fileExtensions = $fileExtensions; + + return $this; + } + + /** + * @param class-string|null $cacheClass + */ + public function withCache( + ?string $cacheDirectory = null, + ?string $cacheClass = null, + ?string $containerCacheDirectory = null + ): self { + $this->cacheDirectory = $cacheDirectory; + $this->cacheClass = $cacheClass; + $this->containerCacheDirectory = $containerCacheDirectory; + + return $this; + } + + /** + * @param class-string $cacheMetaExtensionClass + */ + public function withCacheMetaExtension(string $cacheMetaExtensionClass): self + { + $this->cacheMetaExtensions[] = $cacheMetaExtensionClass; + + return $this; + } + + /** + * @param class-string $rectorClass + * @param mixed[] $configuration + */ + public function withConfiguredRule(string $rectorClass, array $configuration): self + { + $this->rulesWithConfigurations[$rectorClass][] = $configuration; + + return $this; + } + + public function withParallel( + ?int $timeoutSeconds = null, + ?int $maxNumberOfProcess = null, + ?int $jobSize = null + ): self { + $this->parallel = true; + + if (is_int($timeoutSeconds)) { + $this->parallelTimeoutSeconds = $timeoutSeconds; + } + + if (is_int($maxNumberOfProcess)) { + $this->parallelMaxNumberOfProcess = $maxNumberOfProcess; + } + + if (is_int($jobSize)) { + $this->parallelJobSize = $jobSize; + } + + return $this; + } + + public function withoutParallel(): self + { + $this->parallel = false; + + return $this; + } + + public function withImportNames( + bool $importNames = true, + bool $importDocBlockNames = true, + bool $importShortClasses = true, + bool $removeUnusedImports = false + ): self { + $this->importNames = $importNames; + $this->importDocBlockNames = $importDocBlockNames; + $this->importShortClasses = $importShortClasses; + $this->removeUnusedImports = $removeUnusedImports; + return $this; + } + + public function withNoDiffs(): self + { + $this->noDiffs = true; + return $this; + } + + public function withMemoryLimit(string $memoryLimit): self + { + $this->memoryLimit = $memoryLimit; + return $this; + } + + public function withIndent(string $indentChar = ' ', int $indentSize = 4): self + { + $this->indentChar = $indentChar; + $this->indentSize = $indentSize; + + return $this; + } + + /** + * @param string[] $autoloadPaths + */ + public function withAutoloadPaths(array $autoloadPaths): self + { + $this->autoloadPaths = $autoloadPaths; + return $this; + } + + /** + * @param string[] $bootstrapFiles + */ + public function withBootstrapFiles(array $bootstrapFiles): self + { + $this->bootstrapFiles = $bootstrapFiles; + return $this; + } + + /** + * @param string[] $phpstanConfigs + */ + public function withPHPStanConfigs(array $phpstanConfigs): self + { + $this->phpstanConfigs = $phpstanConfigs; + return $this; + } + + /** + * @param PhpVersion::* $phpVersion + */ + public function withPhpVersion(int $phpVersion): self + { + $this->phpVersion = $phpVersion; + return $this; + } + + public function withSymfonyContainerXml(string $symfonyContainerXmlFile): self + { + if (! str_ends_with($symfonyContainerXmlFile, '.xml')) { + throw new InvalidConfigurationException(sprintf( + 'Provided dumped Symfony container must have "xml" suffix. "%s" given', + $symfonyContainerXmlFile + )); + } + + $this->symfonyContainerXmlFile = $symfonyContainerXmlFile; + return $this; + } + + public function withSymfonyContainerPhp(string $symfonyContainerPhpFile): self + { + if (! str_ends_with($symfonyContainerPhpFile, '.php')) { + throw new InvalidConfigurationException(sprintf( + 'Provided dumped Symfony container must have "php" suffix. "%s" given', + $symfonyContainerPhpFile + )); + } + + $this->symfonyContainerPhpFile = $symfonyContainerPhpFile; + return $this; + } + + /** + * Raise your type coverage from the safest type rules + * to more affecting ones, one level at a time + */ + public function withTypeCoverageLevel(int $level): self + { + Assert::natural($level); + + $this->isTypeCoverageLevelUsed = true; + + $levelRules = LevelRulesResolver::resolve($level, TypeDeclarationLevel::RULES, __METHOD__); + + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + 'withTypeCoverageLevel', + $level, + $levelRulesCount, + 'typeDeclarations', + 'TYPE_DECLARATION' + ); + } + + $this->rules = array_merge($this->rules, $levelRules); + + return $this; + } + + /** + * Raise your type coverage docblock from the safest type rules + * to more affecting ones, one level at a time + */ + public function withTypeCoverageDocblockLevel(int $level): self + { + Assert::natural($level); + + $this->isTypeCoverageDocblockLevelUsed = true; + + $levelRules = LevelRulesResolver::resolve($level, TypeDeclarationDocblocksLevel::RULES, __METHOD__); + + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + __METHOD__, + $level, + $levelRulesCount, + 'typeDeclarationDocblocks', + 'TYPE_DECLARATION_DOCBLOCKS' + ); + } + + $this->rules = array_merge($this->rules, $levelRules); + + return $this; + } + + /** + * Raise your dead-code coverage from the safest rules + * to more affecting ones, one level at a time + */ + public function withDeadCodeLevel(int $level): self + { + Assert::natural($level); + + $this->isDeadCodeLevelUsed = true; + + $levelRules = LevelRulesResolver::resolve($level, DeadCodeLevel::RULES, __METHOD__); + + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + 'withDeadCodeLevel', + $level, + $levelRulesCount, + 'deadCode', + 'DEAD_CODE' + ); + } + + $this->rules = array_merge($this->rules, $levelRules); + + return $this; + } + + /** + * Raise your PHP level from, one level at a time + */ + public function withPhpLevel(int $level): self + { + Assert::natural($level); + + $this->isWithPhpLevelUsed = true; + + $phpVersion = ComposerJsonPhpVersionResolver::resolveFromCwdOrFail(); + + $setRectorsResolver = new SetRectorsResolver(); + $setFilePaths = PhpLevelSetResolver::resolveFromPhpVersion($phpVersion); + + $rectorRulesWithConfiguration = $setRectorsResolver->resolveFromFilePathsIncludingConfiguration($setFilePaths); + + foreach ($rectorRulesWithConfiguration as $position => $rectorRuleWithConfiguration) { + // add rules until level is reached + if ($position > $level) { + break; + } + + if (is_string($rectorRuleWithConfiguration)) { + $this->rules[] = $rectorRuleWithConfiguration; + } elseif (is_array($rectorRuleWithConfiguration)) { + foreach ($rectorRuleWithConfiguration as $rectorRule => $rectorRuleConfiguration) { + /** @var class-string $rectorRule */ + $this->withConfiguredRule($rectorRule, $rectorRuleConfiguration); + } + } + } + + return $this; + } + + /** + * Raise your code quality from the safest rules + * to more affecting ones, one level at a time + */ + public function withCodeQualityLevel(int $level): self + { + Assert::natural($level); + + $this->isCodeQualityLevelUsed = true; + + $levelRules = LevelRulesResolver::resolve($level, CodeQualityLevel::RULES, __METHOD__); + + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + 'withCodeQualityLevel', + $level, + $levelRulesCount, + 'codeQuality', + 'CODE_QUALITY' + ); + } + + $this->rules = array_merge($this->rules, $levelRules); + + foreach (CodeQualityLevel::RULES_WITH_CONFIGURATION as $rectorClass => $configuration) { + $this->rulesWithConfigurations[$rectorClass][] = $configuration; + } + + return $this; + } + + /** + * Raise your coding style from the safest rules + * to more affecting ones, one level at a time + */ + public function withCodingStyleLevel(int $level): self + { + Assert::natural($level); + + $this->isCodingStyleLevelUsed = true; + + $levelRules = LevelRulesResolver::resolve($level, CodingStyleLevel::RULES, __METHOD__); + + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + 'withCodingStyleLevel', + $level, + $levelRulesCount, + 'codingStyle', + 'CODING_STYLE' + ); + } + + $this->rules = array_merge($this->rules, $levelRules); + + foreach (CodingStyleLevel::RULES_WITH_CONFIGURATION as $rectorClass => $configuration) { + $this->rulesWithConfigurations[$rectorClass][] = $configuration; + } + + return $this; + } + + public function withFluentCallNewLine(bool $isFluentNewLine = true): self + { + $this->isFluentNewLine = $isFluentNewLine; + return $this; + } + + public function withTreatClassesAsFinal(bool $isTreatClassesAsFinal = true): self + { + $this->isTreatClassesAsFinal = $isTreatClassesAsFinal; + return $this; + } + + public function registerService(string $className, ?string $alias = null, ?string $tag = null): self + { + // BC layer since 2.2.9 + if ($tag === ScopeResolverNodeVisitorInterface::class) { + $tag = DecoratingNodeVisitorInterface::class; + } + + $this->registerServices[] = new RegisteredService($className, $alias, $tag); + + return $this; + } + + /** + * DX helper + * @see https://getrector.com/documentation/creating-a-node-visitor + * @param class-string $decoratingNodeVisitorClass + */ + public function registerDecoratingNodeVisitor(string $decoratingNodeVisitorClass): self + { + Assert::isAOf($decoratingNodeVisitorClass, NodeVisitor::class); + + $this->registerServices[] = new RegisteredService( + $decoratingNodeVisitorClass, + null, + DecoratingNodeVisitorInterface::class + ); + + return $this; + } + + public function withDowngradeSets( + bool $php84 = false, + bool $php83 = false, + bool $php82 = false, + bool $php81 = false, + bool $php80 = false, + bool $php74 = false, + bool $php73 = false, + bool $php72 = false, + bool $php71 = false, + ): self { + $pickedArguments = array_filter(func_get_args()); + if (count($pickedArguments) !== 1) { + throw new InvalidConfigurationException( + 'Pick only one PHP version target in "withDowngradeSets()". All rules down to this version will be used.', + ); + } + + if ($php84) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_84; + } elseif ($php83) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_83; + } elseif ($php82) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_82; + } elseif ($php81) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_81; + } elseif ($php80) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_80; + } elseif ($php74) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_74; + } elseif ($php73) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_73; + } elseif ($php72) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_72; + } elseif ($php71) { + $this->sets[] = DowngradeLevelSetList::DOWN_TO_PHP_71; + } + + return $this; + } + + public function withRealPathReporting(bool $absolutePath = true): self + { + $this->reportingRealPath = $absolutePath; + + return $this; + } + + public function withEditorUrl(string $editorUrl): self + { + $this->editorUrl = $editorUrl; + + return $this; + } + + /** + * @param class-string ...$setProviders + */ + public function withSetProviders(string ...$setProviders): self + { + foreach ($setProviders as $setProvider) { + if (\array_key_exists($setProvider, $this->setProviders)) { + continue; + } + + if (! is_a($setProvider, SetProviderInterface::class, true)) { + throw new InvalidConfigurationException(sprintf( + 'Set provider "%s" must implement "%s"', + $setProvider, + SetProviderInterface::class + )); + } + + $this->setProviders[$setProvider] = true; + } + + return $this; + } +} diff --git a/src/Configuration/RenamedClassesDataCollector.php b/src/Configuration/RenamedClassesDataCollector.php index 1505482ace2..93ee93cef56 100644 --- a/src/Configuration/RenamedClassesDataCollector.php +++ b/src/Configuration/RenamedClassesDataCollector.php @@ -2,22 +2,27 @@ declare(strict_types=1); -namespace Rector\Core\Configuration; +namespace Rector\Configuration; use PHPStan\Type\ObjectType; +use Rector\Contract\DependencyInjection\ResettableInterface; -final class RenamedClassesDataCollector +final class RenamedClassesDataCollector implements ResettableInterface { /** * @var array */ private array $oldToNewClasses = []; - public function addOldToNewClass(string $oldClass, string $newClass): void + public function reset(): void { - $this->oldToNewClasses[$oldClass] = $newClass; + $this->oldToNewClasses = []; } + /** + * keep public modifier and use internally on matchClassName() method + * to keep API as on Configuration level + */ public function hasOldClass(string $oldClass): bool { return isset($this->oldToNewClasses[$oldClass]); @@ -28,7 +33,9 @@ public function hasOldClass(string $oldClass): bool */ public function addOldToNewClasses(array $oldToNewClasses): void { - $this->oldToNewClasses = array_merge($this->oldToNewClasses, $oldToNewClasses); + /** @var array $oldToNewClasses */ + $oldToNewClasses = [...$this->oldToNewClasses, ...$oldToNewClasses]; + $this->oldToNewClasses = $oldToNewClasses; } /** @@ -43,12 +50,11 @@ public function matchClassName(ObjectType $objectType): ?ObjectType { $className = $objectType->getClassName(); - $renamedClassName = $this->oldToNewClasses[$className] ?? null; - if ($renamedClassName === null) { + if (! $this->hasOldClass($className)) { return null; } - return new ObjectType($renamedClassName); + return new ObjectType($this->oldToNewClasses[$className]); } /** diff --git a/src/Configuration/VendorMissAnalyseGuard.php b/src/Configuration/VendorMissAnalyseGuard.php new file mode 100644 index 00000000000..d0261e8e4e0 --- /dev/null +++ b/src/Configuration/VendorMissAnalyseGuard.php @@ -0,0 +1,53 @@ +hasDowngradeSets()) { + return false; + } + + return $this->containsVendorPath($filePaths); + } + + private function hasDowngradeSets(): bool + { + $registeredRectorSets = SimpleParameterProvider::provideArrayParameter(Option::REGISTERED_RECTOR_SETS); + + foreach ($registeredRectorSets as $registeredRectorSet) { + if (str_contains((string) $registeredRectorSet, 'downgrade-')) { + return true; + } + } + + return false; + } + + /** + * @param string[] $filePaths + */ + private function containsVendorPath(array $filePaths): bool + { + $cwdLength = strlen(getcwd()); + + foreach ($filePaths as $filePath) { + $normalizedPath = PathNormalizer::normalize(realpath($filePath)); + if (str_starts_with(substr($normalizedPath, $cwdLength), '/vendor/')) { + return true; + } + } + + return false; + } +} diff --git a/src/Console/Command/CustomRuleCommand.php b/src/Console/Command/CustomRuleCommand.php new file mode 100644 index 00000000000..c51909f1e25 --- /dev/null +++ b/src/Console/Command/CustomRuleCommand.php @@ -0,0 +1,146 @@ +setName('custom-rule'); + $this->setDescription('Create base of local custom rule with tests'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + // ask for rule name + $rectorName = $this->symfonyStyle->ask( + 'What is the rule class name? (e.g. "LegacyCallToDbalMethodCall")?', + null, + static function (?string $answer): string { + if ($answer === '' || $answer === null) { + throw new ShouldNotHappenException('Rector name cannot be empty'); + } + + return $answer; + } + ); + + // suffix with Rector by convention + if (! str_ends_with((string) $rectorName, 'Rector')) { + $rectorName .= 'Rector'; + } + + $rectorName = ucfirst((string) $rectorName); + + // find all files in templates directory + $finder = Finder::create() + ->files() + ->in(__DIR__ . '/../../../templates/custom-rule') + ->notName('__Name__Test.php.phtml'); + + // 0. resolve if local phpunit is at least PHPUnit 10 (which supports #[DataProvider]) + // to provide annotation if not + if ($this->isPHPUnitAttributeSupported()) { + $finder->append([ + new SplFileInfo( + __DIR__ . '/../../../templates/custom-rule/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml', + 'utils/rector/tests/Rector/__Name__', + 'utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml', + ), + ]); + } else { + // use @annotations for PHPUnit 9 and bellow + $finder->append([ + new SplFileInfo( + __DIR__ . '/../../../templates/custom-rules-annotations/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml', + 'utils/rector/tests/Rector/__Name__', + 'utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml', + ), + ]); + } + + $currentDirectory = getcwd(); + + $generatedFilePaths = []; + + $fileInfos = iterator_to_array($finder->getIterator()); + + foreach ($fileInfos as $fileInfo) { + // replace "__Name__" with $rectorName + $newContent = $this->replaceNameVariable($rectorName, $fileInfo->getContents()); + $newFilePath = $this->replaceNameVariable($rectorName, $fileInfo->getRelativePathname()); + + // remove "phtml" suffix + $newFilePath = Strings::substring($newFilePath, 0, -strlen('.phtml')); + + FileSystem::write($currentDirectory . '/' . $newFilePath, $newContent, null); + + $generatedFilePaths[] = $newFilePath; + } + + $title = sprintf('Skeleton for "%s" rule was created. Now write rule logic to solve your problem', $rectorName); + $this->symfonyStyle->title($title); + $this->symfonyStyle->listing($generatedFilePaths); + + // 2. update autoload-dev in composer.json + $composerJsonFilePath = $currentDirectory . '/composer.json'; + if (file_exists($composerJsonFilePath)) { + $hasChanged = false; + $composerJson = JsonFileSystem::readFilePath($composerJsonFilePath); + + if (! isset($composerJson['autoload-dev']['psr-4']['Utils\\Rector\\'])) { + $composerJson['autoload-dev']['psr-4']['Utils\\Rector\\'] = 'utils/rector/src'; + $composerJson['autoload-dev']['psr-4']['Utils\\Rector\\Tests\\'] = 'utils/rector/tests'; + $hasChanged = true; + } + + if ($hasChanged) { + $this->symfonyStyle->writeln('We updated "composer.json" autoload-dev to load Rector rules.'); + $this->symfonyStyle->writeln('Now run "composer dump-autoload" to update paths'); + JsonFileSystem::writeFile($composerJsonFilePath, $composerJson); + } + } + + $this->symfonyStyle->newLine(1); + + // 3. update phpunit.xml(.dist) to include rector test suite + $this->symfonyStyle->writeln('Run Rector tests via PHPUnit:'); + $this->symfonyStyle->newLine(1); + $this->symfonyStyle->writeln(' vendor/bin/phpunit utils/rector/tests'); + $this->symfonyStyle->newLine(1); + + return Command::SUCCESS; + } + + private function replaceNameVariable(string $rectorName, string $contents): string + { + return str_replace('__Name__', $rectorName, $contents); + } + + private function isPHPUnitAttributeSupported(): bool + { + return $this->reflectionProvider->hasClass(ClassName::DATA_PROVIDER); + } +} diff --git a/src/Console/Command/InitCommand.php b/src/Console/Command/InitCommand.php deleted file mode 100644 index 1ee8173715d..00000000000 --- a/src/Console/Command/InitCommand.php +++ /dev/null @@ -1,94 +0,0 @@ -setDescription('Generate rector.php configuration file'); - - $this->addOption( - Option::TEMPLATE_TYPE, - null, - InputOption::VALUE_OPTIONAL, - 'A template type like default, nette, doctrine etc.', - DefaultResolver::TYPE, - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $templateType = (string) $input->getOption(Option::TEMPLATE_TYPE); - - $rectorTemplateFilePath = $this->resolveTemplateFilePathByType($templateType); - - $this->fileSystemGuard->ensureFileExists($rectorTemplateFilePath, __METHOD__); - - $rectorRootFilePath = getcwd() . '/rector.php'; - - $doesFileExist = $this->smartFileSystem->exists($rectorRootFilePath); - if ($doesFileExist) { - $this->symfonyStyle->warning('Config file "rector.php" already exists'); - } else { - $this->smartFileSystem->copy($rectorTemplateFilePath, $rectorRootFilePath); - $this->symfonyStyle->success('"rector.php" config file was added'); - } - - return Command::SUCCESS; - } - - private function resolveTemplateFilePathByType(string $templateType): string - { - $rectorTemplateFilePath = null; - - foreach ($this->templateResolvers as $templateResolver) { - if ($templateResolver->supports($templateType)) { - $rectorTemplateFilePath = $templateResolver->provide(); - break; - } - } - - if ($rectorTemplateFilePath === null) { - $templateResolverTypes = []; - foreach ($this->templateResolvers as $templateResolver) { - if (method_exists($templateResolver, 'getType')) { - $templateResolverTypes[] = $templateResolver->getType(); - } elseif ($templateResolver instanceof Stringable) { - $templateResolverTypes[] = (string) $templateResolver; - } - } - - throw new TemplateTypeNotFoundException($templateType, $templateResolverTypes); - } - - return $rectorTemplateFilePath; - } -} diff --git a/src/Console/Command/ListRulesCommand.php b/src/Console/Command/ListRulesCommand.php new file mode 100644 index 00000000000..965e21de21a --- /dev/null +++ b/src/Console/Command/ListRulesCommand.php @@ -0,0 +1,115 @@ +setName('list-rules'); + $this->setDescription('Show loaded Rectors'); + + $this->setAliases(['show-rules']); + + $this->addOption( + Option::OUTPUT_FORMAT, + null, + InputOption::VALUE_REQUIRED, + 'Select output format', + ConsoleOutputFormatter::NAME + ); + + $this->addOption(Option::ONLY, null, InputOption::VALUE_REQUIRED, 'Fully qualified rule class name'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $rectorClasses = $this->resolveRectorClasses(); + + $skippedClasses = $this->getSkippedRectorClasses(); + + $outputFormat = $input->getOption(Option::OUTPUT_FORMAT); + if ($outputFormat === 'json') { + $data = [ + 'rectors' => $rectorClasses, + 'skipped-rectors' => $skippedClasses, + ]; + + echo Json::encode($data, pretty: true) . PHP_EOL; + return Command::SUCCESS; + } + + $this->symfonyStyle->title('Loaded Rector rules'); + $this->symfonyStyle->listing($rectorClasses); + + if ($skippedClasses !== []) { + $this->symfonyStyle->title('Skipped Rector rules'); + $this->symfonyStyle->listing($skippedClasses); + } + + $this->symfonyStyle->newLine(); + $this->symfonyStyle->note(sprintf('Loaded %d rules', count($rectorClasses))); + + return Command::SUCCESS; + } + + /** + * @return array> + */ + private function resolveRectorClasses(): array + { + $customRectors = array_filter( + $this->rectors, + static fn (RectorInterface $rector): bool => ! $rector instanceof PostRectorInterface + ); + + $rectorClasses = array_map(static fn (RectorInterface $rector): string => $rector::class, $customRectors); + sort($rectorClasses); + + return array_unique($rectorClasses); + } + + /** + * @return array + */ + private function getSkippedRectorClasses(): array + { + $skippedRectorClasses = []; + + foreach ($this->skippedClassResolver->resolve() as $rectorClass => $fileList) { + // ignore specific skips + if ($fileList !== null) { + continue; + } + + $skippedRectorClasses[] = $rectorClass; + } + + return $skippedRectorClasses; + } +} diff --git a/src/Console/Command/ProcessCommand.php b/src/Console/Command/ProcessCommand.php index 0fd4c33ad69..a8e7451628f 100644 --- a/src/Console/Command/ProcessCommand.php +++ b/src/Console/Command/ProcessCommand.php @@ -2,154 +2,191 @@ declare(strict_types=1); -namespace Rector\Core\Console\Command; +namespace Rector\Console\Command; -use PHPStan\Analyser\NodeScopeResolver; +use Rector\Application\ApplicationFileProcessor; +use Rector\Autoloading\AdditionalAutoloader; use Rector\Caching\Detector\ChangedFilesDetector; -use Rector\ChangesReporting\Output\ConsoleOutputFormatter; -use Rector\Core\Application\ApplicationFileProcessor; -use Rector\Core\Autoloading\AdditionalAutoloader; -use Rector\Core\Autoloading\BootstrapFilesIncluder; -use Rector\Core\Configuration\ConfigurationFactory; -use Rector\Core\Configuration\Option; -use Rector\Core\Console\Output\OutputFormatterCollector; -use Rector\Core\Contract\Rector\RectorInterface; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\Reporting\MissingRectorRulesReporter; -use Rector\Core\StaticReflection\DynamicSourceLocatorDecorator; -use Rector\Core\ValueObject\Application\File; -use Rector\Core\ValueObject\Configuration; -use Rector\Core\ValueObject\ProcessResult; -use Rector\Core\ValueObjectFactory\Application\FileFactory; -use Rector\Core\ValueObjectFactory\ProcessResultFactory; -use Rector\VersionBonding\Application\MissedRectorDueVersionChecker; +use Rector\ChangesReporting\Output\JsonOutputFormatter; +use Rector\Configuration\ConfigInitializer; +use Rector\Configuration\ConfigurationFactory; +use Rector\Configuration\ConfigurationRuleFilter; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\Console\ExitCode; +use Rector\Console\Output\OutputFormatterCollector; +use Rector\Console\ProcessConfigureDecorator; +use Rector\Exception\ShouldNotHappenException; +use Rector\Reporting\DeprecatedRulesReporter; +use Rector\Reporting\MissConfigurationReporter; +use Rector\Skipper\SkipCriteriaResolver\SkippedClassResolver; +use Rector\StaticReflection\DynamicSourceLocatorDecorator; +use Rector\Util\MemoryLimiter; +use Rector\ValueObject\Configuration; +use Rector\ValueObject\Configuration\LevelOverflow; +use Rector\ValueObject\ProcessResult; use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; final class ProcessCommand extends Command { - /** - * @param RectorInterface[] $rectors - */ public function __construct( - private AdditionalAutoloader $additionalAutoloader, - private ChangedFilesDetector $changedFilesDetector, - private OutputFormatterCollector $outputFormatterCollector, - private MissingRectorRulesReporter $missingRectorRulesReporter, - private ApplicationFileProcessor $applicationFileProcessor, - private FileFactory $fileFactory, - private BootstrapFilesIncluder $bootstrapFilesIncluder, - private ProcessResultFactory $processResultFactory, - private NodeScopeResolver $nodeScopeResolver, - private DynamicSourceLocatorDecorator $dynamicSourceLocatorDecorator, - private ConfigurationFactory $configurationFactory, - private MissedRectorDueVersionChecker $missedRectorDueVersionChecker, - private array $rectors + private readonly AdditionalAutoloader $additionalAutoloader, + private readonly ChangedFilesDetector $changedFilesDetector, + private readonly ConfigInitializer $configInitializer, + private readonly ApplicationFileProcessor $applicationFileProcessor, + private readonly DynamicSourceLocatorDecorator $dynamicSourceLocatorDecorator, + private readonly OutputFormatterCollector $outputFormatterCollector, + private readonly SymfonyStyle $symfonyStyle, + private readonly MemoryLimiter $memoryLimiter, + private readonly ConfigurationFactory $configurationFactory, + private readonly DeprecatedRulesReporter $deprecatedRulesReporter, + private readonly MissConfigurationReporter $missConfigurationReporter, + private readonly ConfigurationRuleFilter $configurationRuleFilter, + private readonly SkippedClassResolver $skippedClassResolver, ) { parent::__construct(); } protected function configure(): void { - $this->setDescription('Upgrades or refactors source code with provided rectors'); + $this->setName('process'); + $this->setAliases(['p']); + $this->setDescription('Upgrades or refactors source code with provided Rector rules'); + $this->setHelp( + <<<'EOF' +The %command.name% command will run Rector main feature: - $this->addArgument( - Option::SOURCE, - InputArgument::OPTIONAL | InputArgument::IS_ARRAY, - 'Files or directories to be upgraded.' - ); + vendor/bin/rector - $this->addOption( - Option::DRY_RUN, - Option::DRY_RUN_SHORT, - InputOption::VALUE_NONE, - 'Only see the diff of changes, do not save them to files.' - ); +To specify a folder or a file, you can run: - $this->addOption( - Option::AUTOLOAD_FILE, - Option::AUTOLOAD_FILE_SHORT, - InputOption::VALUE_REQUIRED, - 'Path to file with extra autoload (will be included)' - ); + vendor/bin/rector src/Controller - $names = $this->outputFormatterCollector->getNames(); +You can also dry run to see the changes that Rector will make with the --dry-run option: - $description = sprintf('Select output format: "%s".', implode('", "', $names)); - $this->addOption( - Option::OUTPUT_FORMAT, - Option::OUTPUT_FORMAT_SHORT, - InputOption::VALUE_OPTIONAL, - $description, - ConsoleOutputFormatter::NAME - ); + vendor/bin/rector src/Controller --dry-run - $this->addOption( - Option::NO_PROGRESS_BAR, - null, - InputOption::VALUE_NONE, - 'Hide progress bar. Useful e.g. for nicer CI output.' - ); +It's also possible to get debug via the --debug option: - $this->addOption( - Option::NO_DIFFS, - null, - InputOption::VALUE_NONE, - 'Hide diffs of changed files. Useful e.g. for nicer CI output.' + vendor/bin/rector src/Controller --dry-run --debug +EOF ); - $this->addOption(Option::CLEAR_CACHE, null, InputOption::VALUE_NONE, 'Clear unchaged files cache'); + ProcessConfigureDecorator::decorate($this); + + parent::configure(); } protected function execute(InputInterface $input, OutputInterface $output): int { - $exitCode = $this->missingRectorRulesReporter->reportIfMissing(); - if ($exitCode !== null) { - return $exitCode; + // missing config? add it :) + if (! $this->configInitializer->areSomeRectorsLoaded()) { + $this->configInitializer->createConfig(getcwd()); + return self::SUCCESS; } $configuration = $this->configurationFactory->createFromInput($input); - // register autoloaded and included files - $this->bootstrapFilesIncluder->includeBootstrapFiles(); + $this->memoryLimiter->adjust($configuration); + $this->configurationRuleFilter->setConfiguration($configuration); + + // disable console output in case of json output formatter + if ($configuration->getOutputFormat() === JsonOutputFormatter::NAME) { + $this->symfonyStyle->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } $this->additionalAutoloader->autoloadInput($input); - $this->additionalAutoloader->autoloadPaths(); $paths = $configuration->getPaths(); - // 0. add files and directories to static locator + // 0. warn about too high levels + foreach ($configuration->getLevelOverflows() as $levelOverflow) { + $this->reportLevelOverflow($levelOverflow); + } + + // 0. warn about skipped rules that are deprecated + if ($this->skippedClassResolver->resolveDeprecatedSkippedClasses() !== []) { + $this->symfonyStyle->warning(sprintf( + 'These rules are skipped, but are deprecated. Most likely you do not need to skip them anymore as not part of any set and remove them: %s* %s', + "\n\n", + implode(' * ', $this->skippedClassResolver->resolveDeprecatedSkippedClasses()) . "\n" + )); + } + + // 1. warn about rules registered in both withRules() and sets to avoid bloated rector.php configs + $setAndRulesDuplicatedRegistrations = $configuration->getBothSetAndRulesDuplicatedRegistrations(); + if ($setAndRulesDuplicatedRegistrations !== []) { + $this->symfonyStyle->warning(sprintf( + 'These rules are registered in both sets and "withRules()". Remove them from "withRules()" to avoid duplications: %s* %s', + "\n\n", + implode(' * ', $setAndRulesDuplicatedRegistrations) . "\n" + )); + } + + // 2. add files and directories to static locator $this->dynamicSourceLocatorDecorator->addPaths($paths); + if ($this->dynamicSourceLocatorDecorator->arePathsEmpty()) { + // read from rector.php, no paths definition needs withPaths() config + if ($paths === []) { + $this->symfonyStyle->error( + 'Provide paths in Rector config. See ways: https://getrector.com/documentation/define-paths' + ); + + return ExitCode::FAILURE; + } - // 1. inform user about non-runnable rules - $this->missedRectorDueVersionChecker->check($this->rectors); + // read from cli paths arguments, eg: vendor/bin/rector process A B C which A, B, and C not exists + $isSingular = count($paths) === 1; + $this->symfonyStyle->error( + sprintf( + 'The following given path%s do%s not match any file%s or director%s: %s%s', + $isSingular ? '' : 's', + $isSingular ? 'es' : '', + $isSingular ? '' : 's', + $isSingular ? 'y' : 'ies', + "\n\n" . ' - ', + implode("\n" . ' - ', $paths) + ) + ); + + return ExitCode::FAILURE; + } - // 2. collect all files from files+dirs provided paths - $files = $this->fileFactory->createFromPaths($paths, $configuration); + // autoload paths is register to DynamicSourceLocatorProvider, + // so check after arePathsEmpty() above + // check in no parallel since parallel will require register on its own process + if (! $configuration->isParallel()) { + $this->additionalAutoloader->autoloadPaths(); + } - // 3. PHPStan has to know about all files too - $this->configurePHPStanNodeScopeResolver($files); + // show debug info + if ($configuration->isDebug()) { + $this->reportLoadedComposerBasedSets(); + } // MAIN PHASE - // 4. run Rector - $this->applicationFileProcessor->run($files, $configuration); + // 2. run Rector + $processResult = $this->applicationFileProcessor->run($configuration, $input); // REPORTING PHASE - // 5. reporting phase + // 3. reporting phaseRunning 2nd time with collectors data // report diffs and errors $outputFormat = $configuration->getOutputFormat(); $outputFormatter = $this->outputFormatterCollector->getByName($outputFormat); - // here should be value obect factory - $processResult = $this->processResultFactory->create($files); $outputFormatter->report($processResult, $configuration); - // invalidate affected files - $this->invalidateCacheChangedFiles($processResult); + // 4. Deprecations reporter + $this->deprecatedRulesReporter->reportDeprecatedRules(); + $this->deprecatedRulesReporter->reportDeprecatedSkippedRules(); + $this->deprecatedRulesReporter->reportDeprecatedNodeTypes(); + $this->deprecatedRulesReporter->reportDeprecatedRectorUnsupportedMethods(); + + $this->missConfigurationReporter->reportSkippedNeverRegisteredRules(); return $this->resolveReturnCode($processResult, $configuration); } @@ -173,43 +210,61 @@ protected function initialize(InputInterface $input, OutputInterface $output): v } } - private function invalidateCacheChangedFiles(ProcessResult $processResult): void - { - foreach ($processResult->getChangedFileInfos() as $changedFileInfo) { - $this->changedFilesDetector->invalidateFile($changedFileInfo); - } - } - + /** + * @return ExitCode::* + */ private function resolveReturnCode(ProcessResult $processResult, Configuration $configuration): int { - // some errors were found → fail - if ($processResult->getErrors() !== []) { - return Command::FAILURE; + // some system errors were found → fail + if ($processResult->getSystemErrors() !== []) { + return ExitCode::FAILURE; } // inverse error code for CI dry-run if (! $configuration->isDryRun()) { - return Command::SUCCESS; + return ExitCode::SUCCESS; + } + + if ($processResult->getFileDiffs() !== []) { + return ExitCode::CHANGED_CODE; } - return $processResult->getFileDiffs() === [] ? Command::SUCCESS : Command::FAILURE; + if ($processResult->getTotalChanged() > 0) { + return ExitCode::CHANGED_CODE; + } + + return ExitCode::SUCCESS; } - /** - * @param File[] $files - */ - private function configurePHPStanNodeScopeResolver(array $files): void + private function reportLoadedComposerBasedSets(): void { - $filePaths = []; - foreach ($files as $file) { - $smartFileInfo = $file->getSmartFileInfo(); - $pathName = $smartFileInfo->getPathname(); + if (! SimpleParameterProvider::hasParameter(Option::COMPOSER_BASED_SETS)) { + return; + } - if (\str_ends_with($pathName, '.php')) { - $filePaths[] = $pathName; - } + $composerBasedSets = SimpleParameterProvider::provideArrayParameter(Option::COMPOSER_BASED_SETS); + if ($composerBasedSets === []) { + return; } - $this->nodeScopeResolver->setAnalysedFiles($filePaths); + $this->symfonyStyle->writeln('[info] Sets loaded based on installed packages:'); + $this->symfonyStyle->listing($composerBasedSets); + } + + private function reportLevelOverflow(LevelOverflow $levelOverflow): void + { + $suggestedSetMethod = PHP_VERSION_ID >= 80000 ? sprintf( + '->withPreparedSets(%s: true)', + $levelOverflow->getSuggestedRuleset() + ) : sprintf('->withSets(SetList::%s)', $levelOverflow->getSuggestedSetListConstant()); + + $this->symfonyStyle->warning(sprintf( + 'The "->%s()" level contains only %d rules, but you set level to %d.%sYou are using the full set now! Time to switch to more efficient "%s".', + $levelOverflow->getConfigurationName(), + $levelOverflow->getRuleCount(), + $levelOverflow->getLevel(), + "\n", + $suggestedSetMethod, + )); } } diff --git a/src/Console/Command/SetupCICommand.php b/src/Console/Command/SetupCICommand.php new file mode 100644 index 00000000000..94a39b5cfc5 --- /dev/null +++ b/src/Console/Command/SetupCICommand.php @@ -0,0 +1,157 @@ +setName('setup-ci'); + $this->setDescription('Add CI workflow to let Rector work for you'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + // detect current CI + $ci = $this->resolveCurrentCI(); + + if ($ci === CiDetector::CI_GITLAB) { + return $this->handleGitlabCi(); + } + + if ($ci === CiDetector::CI_GITHUB_ACTIONS) { + return $this->handleGithubActions(); + } + + $noteMessage = sprintf( + 'Only GitHub and GitLab are currently supported.%s Contribute your CI template to Rector to make this work: %s', + "\n", + 'https://github.com/rectorphp/rector-src/' + ); + + $this->symfonyStyle->note($noteMessage); + return self::SUCCESS; + } + + /** + * @return CiDetector::CI_*|null + */ + private function resolveCurrentCI(): ?string + { + if (file_exists(getcwd() . '/.github')) { + return CiDetector::CI_GITHUB_ACTIONS; + } + + if (file_exists(getcwd() . '/.gitlab-ci.yml')) { + return CiDetector::CI_GITLAB; + } + + return null; + } + + private function addGithubActionsWorkflow(string $currentRepository, string $targetWorkflowFilePath): void + { + $workflowTemplate = FileSystem::read(__DIR__ . '/../../../templates/rector-github-action-check.yaml'); + $workflowContents = strtr($workflowTemplate, [ + '__CURRENT_REPOSITORY__' => $currentRepository, + ]); + + FileSystem::write($targetWorkflowFilePath, $workflowContents, null); + + $this->symfonyStyle->newLine(); + $this->symfonyStyle->success('The ".github/workflows/rector.yaml" file was added'); + + $this->symfonyStyle->writeln('2 more steps to run Rector in CI:'); + + $this->symfonyStyle->newLine(); + $this->symfonyStyle->writeln( + '1) Generate token with "repo" scope:' . \PHP_EOL . 'https://github.com/settings/tokens/new' + ); + + $this->symfonyStyle->newLine(); + + $repositoryNewSecretsLink = sprintf('https://github.com/%s/settings/secrets/actions/new', $currentRepository); + $this->symfonyStyle->writeln( + '2) Add the token to Action secrets as "ACCESS_TOKEN":' . \PHP_EOL . $repositoryNewSecretsLink + ); + } + + private function addGitlabFile(string $targetGitlabFilePath): void + { + $gitlabTemplate = FileSystem::read(__DIR__ . '/../../../templates/rector-gitlab-check.yaml'); + FileSystem::write($targetGitlabFilePath, $gitlabTemplate, null); + + $this->symfonyStyle->newLine(); + $this->symfonyStyle->success('The "gitlab/rector.yaml" file was added'); + + $this->symfonyStyle->newLine(); + $this->symfonyStyle->writeln( + '1) Register it in your ".gitlab-ci.yml" file:' . \PHP_EOL . 'include:' . \PHP_EOL . ' - local: gitlab/rector.yaml' + ); + } + + /** + * @return self::SUCCESS + */ + private function handleGitlabCi(): int + { + // add snippet in the end of file or include it? + $ciRectorFilePath = getcwd() . '/gitlab/rector.yaml'; + + if (file_exists($ciRectorFilePath)) { + $response = $this->symfonyStyle->ask( + 'The "gitlab/rector.yaml" workflow already exists. Overwrite it?', + 'Yes' + ); + if (! in_array($response, ['y', 'yes', 'Yes'], true)) { + $this->symfonyStyle->note('Nothing changed'); + return self::SUCCESS; + } + } + + $this->addGitlabFile($ciRectorFilePath); + return self::SUCCESS; + } + + /** + * @return self::SUCCESS|self::FAILURE + */ + private function handleGithubActions(): int + { + $rectorWorkflowFilePath = getcwd() . '/.github/workflows/rector.yaml'; + if (file_exists($rectorWorkflowFilePath)) { + $response = $this->symfonyStyle->ask('The "rector.yaml" workflow already exists. Overwrite it?', 'Yes'); + if (! in_array($response, ['y', 'yes', 'Yes'], true)) { + $this->symfonyStyle->note('Nothing changed'); + return self::SUCCESS; + } + } + + $currentRepository = RepositoryHelper::resolveGithubRepositoryName(getcwd()); + if ($currentRepository === null) { + $this->symfonyStyle->error('Current repository name could not be resolved'); + + return self::FAILURE; + } + + $this->addGithubActionsWorkflow($currentRepository, $rectorWorkflowFilePath); + return self::SUCCESS; + } +} diff --git a/src/Console/Command/ShowCommand.php b/src/Console/Command/ShowCommand.php deleted file mode 100644 index 7db3884a032..00000000000 --- a/src/Console/Command/ShowCommand.php +++ /dev/null @@ -1,86 +0,0 @@ -setDescription('Show loaded Rectors with their configuration'); - - $names = $this->showOutputFormatterCollector->getNames(); - - $description = sprintf('Select output format: "%s".', implode('", "', $names)); - $this->addOption( - Option::OUTPUT_FORMAT, - 'o', - InputOption::VALUE_OPTIONAL, - $description, - ConsoleOutputFormatter::NAME - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $outputFormat = (string) $input->getOption(Option::OUTPUT_FORMAT); - - $this->reportLoadedRectors($outputFormat); - - return Command::SUCCESS; - } - - private function reportLoadedRectors(string $outputFormat): void - { - $rectors = array_filter( - $this->rectors, - function (RectorInterface $rector): bool { - if ($rector instanceof PostRectorInterface) { - return false; - } - - return ! $rector instanceof ComplementaryRectorInterface; - } - ); - - $rectorCount = count($rectors); - - if ($rectorCount === 0) { - $warningMessage = sprintf( - 'No Rectors were loaded.%sAre sure your "rector.php" config is in the root?%sTry "--config " option to include it.', - PHP_EOL . PHP_EOL, - PHP_EOL - ); - $this->outputStyle->warning($warningMessage); - - return; - } - - $outputFormatter = $this->showOutputFormatterCollector->getByName($outputFormat); - $outputFormatter->list($rectors); - } -} diff --git a/src/Console/Command/WorkerCommand.php b/src/Console/Command/WorkerCommand.php new file mode 100644 index 00000000000..b86320a9cd8 --- /dev/null +++ b/src/Console/Command/WorkerCommand.php @@ -0,0 +1,175 @@ +setName('worker'); + $this->setDescription('[INTERNAL] Support for parallel process'); + + ProcessConfigureDecorator::decorate($this); + + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $configuration = $this->configurationFactory->createFromInput($input); + $this->memoryLimiter->adjust($configuration); + $this->configurationRuleFilter->setConfiguration($configuration); + + $streamSelectLoop = new StreamSelectLoop(); + $parallelIdentifier = $configuration->getParallelIdentifier(); + + $tcpConnector = new TcpConnector($streamSelectLoop); + + $promise = $tcpConnector->connect('127.0.0.1:' . $configuration->getParallelPort()); + $promise->then(function (ConnectionInterface $connection) use ( + $parallelIdentifier, + $configuration, + $input, + $output + ): void { + $inDecoder = new Decoder($connection, true, 512, JSON_INVALID_UTF8_IGNORE); + $outEncoder = new Encoder($connection, JSON_INVALID_UTF8_IGNORE); + + $outEncoder->write([ + ReactCommand::ACTION => Action::HELLO, + ReactCommand::IDENTIFIER => $parallelIdentifier, + ]); + + $this->runWorker($outEncoder, $inDecoder, $configuration, $input, $output); + }); + + $streamSelectLoop->run(); + + return self::SUCCESS; + } + + private function runWorker( + Encoder $encoder, + Decoder $decoder, + Configuration $configuration, + InputInterface $input, + OutputInterface $output + ): void { + $this->additionalAutoloader->autoloadPaths(); + $this->dynamicSourceLocatorDecorator->addPaths($configuration->getPaths()); + + if ($configuration->isDebug()) { + $preFileCallback = static function (string $filePath) use ($output): void { + $output->writeln($filePath); + }; + } else { + $preFileCallback = null; + } + + // 1. handle system error + $handleErrorCallback = static function (Throwable $throwable) use ($encoder): void { + $systemError = new SystemError($throwable->getMessage(), $throwable->getFile(), $throwable->getLine()); + + $encoder->write([ + ReactCommand::ACTION => Action::RESULT, + self::RESULT => [ + Bridge::SYSTEM_ERRORS => [$systemError], + Bridge::FILES_COUNT => 0, + Bridge::SYSTEM_ERRORS_COUNT => 1, + ], + ]); + $encoder->end(); + }; + + $encoder->on(ReactEvent::ERROR, $handleErrorCallback); + + // 2. collect diffs + errors from file processor + $decoder->on(ReactEvent::DATA, function (array $json) use ( + $preFileCallback, + $encoder, + $configuration, + $input + ): void { + $action = $json[ReactCommand::ACTION]; + if ($action !== Action::MAIN) { + return; + } + + /** @var string[] $filePaths */ + $filePaths = $json[Bridge::FILES] ?? []; + + Assert::notEmpty($filePaths); + + $processResult = $this->applicationFileProcessor->processFiles( + $filePaths, + $configuration, + $preFileCallback + ); + + /** + * this invokes all listeners listening $decoder->on(...) @see \Symplify\EasyParallel\Enum\ReactEvent::DATA + */ + $encoder->write([ + ReactCommand::ACTION => Action::RESULT, + self::RESULT => [ + Bridge::FILE_DIFFS => $processResult->getFileDiffs( + $input->getOption(Option::OUTPUT_FORMAT) !== 'json' + ), + Bridge::FILES_COUNT => count($filePaths), + Bridge::SYSTEM_ERRORS => $processResult->getSystemErrors(), + Bridge::SYSTEM_ERRORS_COUNT => count($processResult->getSystemErrors()), + Bridge::TOTAL_CHANGED => $processResult->getTotalChanged(), + ], + ]); + }); + + $decoder->on(ReactEvent::ERROR, $handleErrorCallback); + } +} diff --git a/src/Console/ConsoleApplication.php b/src/Console/ConsoleApplication.php index 74fe7cdb804..40c945b40a9 100644 --- a/src/Console/ConsoleApplication.php +++ b/src/Console/ConsoleApplication.php @@ -2,70 +2,52 @@ declare(strict_types=1); -namespace Rector\Core\Console; +namespace Rector\Console; use Composer\XdebugHandler\XdebugHandler; +use Override; +use Rector\Application\VersionResolver; use Rector\ChangesReporting\Output\ConsoleOutputFormatter; -use Rector\Core\Application\VersionResolver; -use Rector\Core\Configuration\Option; -use Rector\Core\Console\Command\ProcessCommand; -use Rector\Core\Exception\Configuration\InvalidConfigurationException; +use Rector\Configuration\Option; +use Rector\Util\Reflection\PrivatesAccessor; use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symplify\PackageBuilder\Console\Command\CommandNaming; +use Symfony\Component\Console\Style\SymfonyStyle; +use Webmozart\Assert\Assert; final class ConsoleApplication extends Application { - /** - * @var string - */ - private const NAME = 'Rector'; + private const string NAME = 'Rector'; /** * @param Command[] $commands */ - public function __construct(CommandNaming $commandNaming, array $commands = []) - { - $version = VersionResolver::PACKAGE_VERSION; - parent::__construct(self::NAME, $version); + public function __construct( + array $commands, + private readonly SymfonyStyle $symfonyStyle + ) { + parent::__construct(self::NAME, VersionResolver::PACKAGE_VERSION); - foreach ($commands as $command) { - $commandName = $commandNaming->resolveFromCommand($command); - $command->setName($commandName); - } + Assert::notEmpty($commands); + Assert::allIsInstanceOf($commands, Command::class); $this->addCommands($commands); - $this->setDefaultCommand(CommandNaming::classToName(ProcessCommand::class)); + + // run this command, if no command name is provided + $this->setDefaultCommand('process'); } + #[Override] public function doRun(InputInterface $input, OutputInterface $output): int { - // @fixes https://github.com/rectorphp/rector/issues/2205 - $isXdebugAllowed = $input->hasParameterOption('--xdebug'); - if (! $isXdebugAllowed) { - $xdebugHandler = new XdebugHandler('rector'); - $xdebugHandler->check(); - unset($xdebugHandler); - } + $this->enableXdebug($input); $shouldFollowByNewline = false; - // switch working dir - $newWorkDir = $this->getNewWorkingDir($input); - if ($newWorkDir !== '') { - $oldWorkingDir = getcwd(); - chdir($newWorkDir); - - if ($output->isDebug()) { - $message = sprintf('Changed working directory from "%s" to "%s"', $oldWorkingDir, getcwd()); - $output->writeln($message); - } - } - // skip in this case, since generate content must be clear from meta-info if ($this->shouldPrintMetaInformation($input)) { $output->writeln($this->getLongVersion()); @@ -76,9 +58,44 @@ public function doRun(InputInterface $input, OutputInterface $output): int $output->write(PHP_EOL); } + $commandName = $input->getFirstArgument(); + + if ($commandName === null) { + return parent::doRun($input, $output); + } + + // if paths exist or if the command name is not the first argument but with --option, eg: + // bin/rector src + // bin/rector --only "RemovePhpVersionIdCheckRector" + // file_exists() can check directory and file + if (( + file_exists($commandName) || isset($_SERVER['argv'][1]) + && $commandName !== $_SERVER['argv'][1] + // ensure verify has parameter option, eg: --only + && $input->hasParameterOption($_SERVER['argv'][1]) + ) + ) { + // prepend command name if implicit + $privatesAccessor = new PrivatesAccessor(); + $tokens = $privatesAccessor->getPrivateProperty($input, 'tokens'); + $tokens = array_merge(['process'], $tokens); + $privatesAccessor->setPrivateProperty($input, 'tokens', $tokens); + } elseif (! $this->has($commandName)) { + $this->symfonyStyle->error( + sprintf( + 'The following given path does not match any files or directories: %s%s', + "\n\n - ", + $commandName + ) + ); + + return ExitCode::FAILURE; + } + return parent::doRun($input, $output); } + #[Override] protected function getDefaultInputDefinition(): InputDefinition { $defaultInputDefinition = parent::getDefaultInputDefinition(); @@ -89,17 +106,6 @@ protected function getDefaultInputDefinition(): InputDefinition return $defaultInputDefinition; } - private function getNewWorkingDir(InputInterface $input): string - { - $workingDir = $input->getParameterOption('--working-dir'); - if (is_string($workingDir) && ! is_dir($workingDir)) { - $errorMessage = sprintf('Invalid working directory specified, "%s" does not exist.', $workingDir); - throw new InvalidConfigurationException($errorMessage); - } - - return (string) $workingDir; - } - private function shouldPrintMetaInformation(InputInterface $input): bool { $hasNoArguments = $input->getFirstArgument() === null; @@ -120,7 +126,7 @@ private function removeUnusedOptions(InputDefinition $inputDefinition): void { $options = $inputDefinition->getOptions(); - unset($options['quiet'], $options['no-interaction']); + unset($options['quiet'], $options['verbose'], $options['no-interaction']); $inputDefinition->setOptions($options); } @@ -131,15 +137,14 @@ private function addCustomOptions(InputDefinition $inputDefinition): void Option::CONFIG, 'c', InputOption::VALUE_REQUIRED, - 'Path to config file', - $this->getDefaultConfigPath() + 'Path to config file' )); $inputDefinition->addOption(new InputOption( Option::DEBUG, null, InputOption::VALUE_NONE, - 'Enable debug verbosity (-vvv)' + 'Enable debug verbosity' )); $inputDefinition->addOption(new InputOption( @@ -153,19 +158,18 @@ private function addCustomOptions(InputDefinition $inputDefinition): void Option::CLEAR_CACHE, null, InputOption::VALUE_NONE, - 'Clear cache' - )); - - $inputDefinition->addOption(new InputOption( - 'working-dir', - null, - InputOption::VALUE_REQUIRED, - 'If specified, use the given directory as working directory.' + 'Clear cache before starting the execution of the command' )); } - private function getDefaultConfigPath(): string + private function enableXdebug(InputInterface $input): void { - return getcwd() . '/rector.php'; + $isXdebugAllowed = $input->hasParameterOption('--xdebug'); + if (! $isXdebugAllowed) { + $xdebugHandler = new XdebugHandler('rector'); + $xdebugHandler->setPersistent(); + $xdebugHandler->check(); + unset($xdebugHandler); + } } } diff --git a/src/Console/ExitCode.php b/src/Console/ExitCode.php new file mode 100644 index 00000000000..046efbd7711 --- /dev/null +++ b/src/Console/ExitCode.php @@ -0,0 +1,19 @@ + + * + * @see \Rector\Tests\Console\Formatter\ColorConsoleDiffFormatterTest + */ +final readonly class ColorConsoleDiffFormatter +{ + /** + * @see https://regex101.com/r/ovLMDF/1 + */ + private const string PLUS_START_REGEX = '#^(\+.*)#'; + + /** + * @see https://regex101.com/r/xwywpa/1 + */ + private const string MINUS_START_REGEX = '#^(\-.*)#'; + + /** + * @see https://regex101.com/r/CMlwa8/1 + */ + private const string AT_START_REGEX = '#^(@.*)#'; + + /** + * @see https://regex101.com/r/8MXnfa/2 + */ + private const string AT_DIFF_LINE_REGEX = '#^\@@ \-(\d+),\d+ \+\d+,\d+ @@\<\/fg=cyan\>$#'; + + private string $template; + + public function __construct() + { + $this->template = sprintf( + ' ---------- begin diff ----------%s%%s%s ----------- end diff -----------' . PHP_EOL, + PHP_EOL, + PHP_EOL + ); + } + + public function format(string $diff): string + { + return $this->formatWithTemplate($diff, $this->template); + } + + private function formatWithTemplate(string $diff, string $template): string + { + $escapedDiff = OutputFormatter::escape(rtrim($diff)); + + $escapedDiffLines = NewLineSplitter::split($escapedDiff); + + // remove description of added + remove, obvious on diffs + // decorize lines + foreach ($escapedDiffLines as $key => $escapedDiffLine) { + if ($escapedDiffLine === '--- Original') { + unset($escapedDiffLines[$key]); + continue; + } + + if ($escapedDiffLine === '+++ New') { + unset($escapedDiffLines[$key]); + continue; + } + + if ($escapedDiffLine === ' ') { + $escapedDiffLines[$key] = ''; + continue; + } + + $escapedDiffLine = $this->makePlusLinesGreen($escapedDiffLine); + $escapedDiffLine = $this->makeMinusLinesRed($escapedDiffLine); + $escapedDiffLine = $this->makeAtNoteCyan($escapedDiffLine); + $escapedDiffLine = $this->normalizeLineAtDiff($escapedDiffLine); + + // final decorized line + $escapedDiffLines[$key] = $escapedDiffLine; + } + + return sprintf($template, implode(PHP_EOL, $escapedDiffLines)); + } + + /** + * Simplify diff line info, eg; @@ -67,6 +67,8 @@ to become @@ Line 67 @@ + */ + private function normalizeLineAtDiff(string $string): string + { + return Strings::replace($string, self::AT_DIFF_LINE_REGEX, '@@ Line $1 @@'); + } + + private function makePlusLinesGreen(string $string): string + { + return Strings::replace($string, self::PLUS_START_REGEX, '$1'); + } + + private function makeMinusLinesRed(string $string): string + { + return Strings::replace($string, self::MINUS_START_REGEX, '$1'); + } + + private function makeAtNoteCyan(string $string): string + { + return Strings::replace($string, self::AT_START_REGEX, '$1'); + } +} diff --git a/src/Console/Notifier.php b/src/Console/Notifier.php new file mode 100644 index 00000000000..2e4dd184c93 --- /dev/null +++ b/src/Console/Notifier.php @@ -0,0 +1,41 @@ += 80000) { + return; + } + + $message = sprintf( + 'The "%s()" method uses named arguments. Its suitable for PHP 8.0+. In lower PHP versions, use "withSets([...])" method instead', + $calledMethod + ); + + $symfonyStyle = new SymfonyStyle(new ArgvInput(), new ConsoleOutput()); + $symfonyStyle->warning($message); + + sleep(3); + } + + public static function errorWithPhpSetsNotSuitableForPHP74AndLower(): void + { + if (PHP_VERSION_ID >= 80000) { + return; + } + + throw new InvalidConfigurationException( + 'The "->withPhpSets()" method uses named arguments. Its suitable for PHP 8.0+. Use more explicit "->withPhp53Sets()" ... "->withPhp74Sets()" in lower PHP versions instead.' + ); + } +} diff --git a/src/Console/Output/OutputFormatterCollector.php b/src/Console/Output/OutputFormatterCollector.php index 5c585c34895..8102612d3b9 100644 --- a/src/Console/Output/OutputFormatterCollector.php +++ b/src/Console/Output/OutputFormatterCollector.php @@ -2,22 +2,22 @@ declare(strict_types=1); -namespace Rector\Core\Console\Output; +namespace Rector\Console\Output; use Rector\ChangesReporting\Contract\Output\OutputFormatterInterface; -use Rector\Core\Exception\Configuration\InvalidConfigurationException; +use Rector\Exception\Configuration\InvalidConfigurationException; final class OutputFormatterCollector { /** - * @var OutputFormatterInterface[] + * @var array */ private array $outputFormatters = []; /** * @param OutputFormatterInterface[] $outputFormatters */ - public function __construct(array $outputFormatters) + public function __construct(iterable $outputFormatters) { foreach ($outputFormatters as $outputFormatter) { $this->outputFormatters[$outputFormatter->getName()] = $outputFormatter; @@ -31,24 +31,18 @@ public function getByName(string $name): OutputFormatterInterface return $this->outputFormatters[$name]; } - /** - * @return int[]|string[] - */ - public function getNames(): array - { - return array_keys($this->outputFormatters); - } - private function ensureOutputFormatExists(string $name): void { if (isset($this->outputFormatters[$name])) { return; } + $outputFormatterNames = array_keys($this->outputFormatters); + throw new InvalidConfigurationException(sprintf( 'Output formatter "%s" was not found. Pick one of "%s".', $name, - implode('", "', $this->getNames()) + implode('", "', $outputFormatterNames) )); } } diff --git a/src/Console/Output/RectorOutputStyle.php b/src/Console/Output/RectorOutputStyle.php deleted file mode 100644 index 115a5e8069a..00000000000 --- a/src/Console/Output/RectorOutputStyle.php +++ /dev/null @@ -1,63 +0,0 @@ -symfonyStyle->error($message); - } - - public function warning(string $message): void - { - $this->symfonyStyle->warning($message); - } - - public function success(string $message): void - { - $this->symfonyStyle->success($message); - } - - public function note(string $message): void - { - $this->symfonyStyle->note($message); - } - - public function title(string $message): void - { - $this->symfonyStyle->title($message); - } - - public function writeln(string $message): void - { - $this->symfonyStyle->writeln($message); - } - - public function newline(int $count = 1): void - { - $this->symfonyStyle->newLine($count); - } - - /** - * @param string[] $elements - */ - public function listing(array $elements): void - { - $this->symfonyStyle->listing($elements); - } -} diff --git a/src/Console/Output/ShowOutputFormatterCollector.php b/src/Console/Output/ShowOutputFormatterCollector.php deleted file mode 100644 index b0dd6c2851b..00000000000 --- a/src/Console/Output/ShowOutputFormatterCollector.php +++ /dev/null @@ -1,54 +0,0 @@ -outputFormatters[$showOutputFormatter->getName()] = $showOutputFormatter; - } - } - - public function getByName(string $name): ShowOutputFormatterInterface - { - $this->ensureOutputFormatExists($name); - - return $this->outputFormatters[$name]; - } - - /** - * @return int[]|string[] - */ - public function getNames(): array - { - return array_keys($this->outputFormatters); - } - - private function ensureOutputFormatExists(string $name): void - { - if (isset($this->outputFormatters[$name])) { - return; - } - - throw new InvalidConfigurationException(sprintf( - 'Output formatter "%s" was not found. Pick one of "%s".', - $name, - implode('", "', $this->getNames()) - )); - } -} diff --git a/src/Console/ProcessConfigureDecorator.php b/src/Console/ProcessConfigureDecorator.php new file mode 100644 index 00000000000..7b3f20f107e --- /dev/null +++ b/src/Console/ProcessConfigureDecorator.php @@ -0,0 +1,85 @@ +addArgument( + Option::SOURCE, + InputArgument::OPTIONAL | InputArgument::IS_ARRAY, + 'Files or directories to be upgraded.' + ); + + $command->addOption( + Option::DRY_RUN, + Option::DRY_RUN_SHORT, + InputOption::VALUE_NONE, + 'Only see the diff of changes, do not save them to files.' + ); + + $command->addOption( + Option::AUTOLOAD_FILE, + Option::AUTOLOAD_FILE_SHORT, + InputOption::VALUE_REQUIRED, + 'Path to file with extra autoload (will be included)' + ); + + $command->addOption( + Option::NO_PROGRESS_BAR, + null, + InputOption::VALUE_NONE, + 'Hide progress bar. Useful e.g. for nicer CI output.' + ); + + $command->addOption( + Option::NO_DIFFS, + null, + InputOption::VALUE_NONE, + 'Hide diffs of changed files. Useful e.g. for nicer CI output.' + ); + + $command->addOption( + Option::OUTPUT_FORMAT, + null, + InputOption::VALUE_REQUIRED, + 'Select output format', + ConsoleOutputFormatter::NAME + ); + + // filter by rule and path + $command->addOption(Option::ONLY, null, InputOption::VALUE_REQUIRED, 'Fully qualified rule class name'); + + $command->addOption( + Option::ONLY_SUFFIX, + null, + InputOption::VALUE_REQUIRED, + 'Filter only files with specific suffix in name, e.g. "Controller"' + ); + + $command->addOption(Option::DEBUG, null, InputOption::VALUE_NONE, 'Display debug output.'); + $command->addOption(Option::MEMORY_LIMIT, null, InputOption::VALUE_REQUIRED, 'Memory limit for process'); + $command->addOption(Option::CLEAR_CACHE, null, InputOption::VALUE_NONE, 'Clear unchanged files cache'); + + $command->addOption(Option::PARALLEL_PORT, null, InputOption::VALUE_REQUIRED); + $command->addOption(Option::PARALLEL_IDENTIFIER, null, InputOption::VALUE_REQUIRED); + + $command->addOption(Option::XDEBUG, null, InputOption::VALUE_NONE, 'Display xdebug output.'); + + $command->addOption( + Option::RULES_SUMMARY, + null, + InputOption::VALUE_NONE, + 'Show summary of rules applied during the run.' + ); + } +} diff --git a/src/Console/Style/RectorStyle.php b/src/Console/Style/RectorStyle.php new file mode 100644 index 00000000000..e3a4db616b8 --- /dev/null +++ b/src/Console/Style/RectorStyle.php @@ -0,0 +1,87 @@ +setVerbosity(OutputInterface::VERBOSITY_QUIET); + } + } + + /** + * @see https://github.com/phpstan/phpstan-src/commit/0993d180e5a15a17631d525909356081be59ffeb + */ + #[Override] + public function createProgressBar(int $max = 0): ProgressBar + { + $progressBar = parent::createProgressBar($max); + $progressBar->setOverwrite(! $this->isCiDetected()); + + $isCiDetected = $this->isCiDetected(); + $progressBar->setOverwrite(! $isCiDetected); + + if ($isCiDetected) { + $progressBar->minSecondsBetweenRedraws(15); + $progressBar->maxSecondsBetweenRedraws(30); + } elseif (DIRECTORY_SEPARATOR === '\\') { + // windows + $progressBar->minSecondsBetweenRedraws(0.5); + $progressBar->maxSecondsBetweenRedraws(2); + } else { + // *nix + $progressBar->minSecondsBetweenRedraws(0.1); + $progressBar->maxSecondsBetweenRedraws(0.5); + } + + $this->progressBar = $progressBar; + + return $progressBar; + } + + #[Override] + public function progressAdvance(int $step = 1): void + { + // hide progress bar in tests + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + return; + } + + $progressBar = $this->getProgressBar(); + $progressBar->advance($step); + } + + private function isCiDetected(): bool + { + if ($this->isCiDetected === null) { + $ciDetector = new CiDetector(); + $this->isCiDetected = $ciDetector->isCiDetected(); + } + + return $this->isCiDetected; + } + + private function getProgressBar(): ProgressBar + { + return $this->progressBar ?? throw new RuntimeException('The ProgressBar is not started.'); + } +} diff --git a/src/Console/Style/SymfonyStyleFactory.php b/src/Console/Style/SymfonyStyleFactory.php index 7c0323bb4af..aa9ae877b0e 100644 --- a/src/Console/Style/SymfonyStyleFactory.php +++ b/src/Console/Style/SymfonyStyleFactory.php @@ -2,36 +2,55 @@ declare(strict_types=1); -namespace Rector\Core\Console\Style; +namespace Rector\Console\Style; +use Rector\Util\Reflection\PrivatesAccessor; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symplify\PackageBuilder\Reflection\PrivatesCaller; -final class SymfonyStyleFactory +final readonly class SymfonyStyleFactory { public function __construct( - private PrivatesCaller $privatesCaller + private PrivatesAccessor $privatesAccessor ) { } - public function create(): SymfonyStyle + /** + * @api + */ + public function create(): RectorStyle { + // to prevent missing argv indexes + if (! isset($_SERVER['argv'])) { + $_SERVER['argv'] = []; + } + $argvInput = new ArgvInput(); $consoleOutput = new ConsoleOutput(); // to configure all -v, -vv, -vvv options without memory-lock to Application run() arguments - $this->privatesCaller->callPrivateMethod(new Application(), 'configureIO', [$argvInput, $consoleOutput]); - $debugArgvInputParameterOption = $argvInput->getParameterOption('--debug'); + $this->privatesAccessor->callPrivateMethod(new Application(), 'configureIO', [$argvInput, $consoleOutput]); // --debug is called - if ($debugArgvInputParameterOption === null) { + if ($argvInput->hasParameterOption('--debug')) { $consoleOutput->setVerbosity(OutputInterface::VERBOSITY_DEBUG); } - return new SymfonyStyle($argvInput, $consoleOutput); + // disable output for tests + if ($this->isPHPUnitRun()) { + $consoleOutput->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } + + return new RectorStyle($argvInput, $consoleOutput); + } + + /** + * Never ever used static methods if not necessary, this is just handy for tests + src to prevent duplication. + */ + private function isPHPUnitRun(): bool + { + return defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__'); } } diff --git a/src/Contract/Application/FileDecoratorInterface.php b/src/Contract/Application/FileDecoratorInterface.php deleted file mode 100644 index 39059942a07..00000000000 --- a/src/Contract/Application/FileDecoratorInterface.php +++ /dev/null @@ -1,15 +0,0 @@ - $configuration + * @param mixed[] $configuration */ public function configure(array $configuration): void; } diff --git a/src/Contract/Rector/HTMLAverseRectorInterface.php b/src/Contract/Rector/HTMLAverseRectorInterface.php new file mode 100644 index 00000000000..a456e29647a --- /dev/null +++ b/src/Contract/Rector/HTMLAverseRectorInterface.php @@ -0,0 +1,13 @@ +> - */ - public function getNodeTypes(): array; - - /** - * Process Node of matched type - * @return Node|Node[]|null - */ - public function refactor(Node $node); -} diff --git a/src/Contract/Rector/RectorInterface.php b/src/Contract/Rector/RectorInterface.php index d6a721d846c..2602bd2a67e 100644 --- a/src/Contract/Rector/RectorInterface.php +++ b/src/Contract/Rector/RectorInterface.php @@ -2,10 +2,25 @@ declare(strict_types=1); -namespace Rector\Core\Contract\Rector; +namespace Rector\Contract\Rector; +use PhpParser\Node; +use PhpParser\NodeVisitor; use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; -interface RectorInterface extends DocumentedRuleInterface +interface RectorInterface extends NodeVisitor, DocumentedRuleInterface { + /** + * List of nodes this class checks, classes that implements \PhpParser\Node + * See beautiful map of all nodes https://github.com/rectorphp/php-parser-nodes-docs#node-overview + * + * @return array> + */ + public function getNodeTypes(): array; + + /** + * Process Node of matched type + * @return Node|Node[]|null|NodeVisitor::REMOVE_NODE + */ + public function refactor(Node $node); } diff --git a/src/Contract/Template/TemplateResolverInterface.php b/src/Contract/Template/TemplateResolverInterface.php deleted file mode 100644 index 8f1cace9abc..00000000000 --- a/src/Contract/Template/TemplateResolverInterface.php +++ /dev/null @@ -1,14 +0,0 @@ - + */ + private const array INCLUDE_TYPE_MAP = [ + Include_::TYPE_INCLUDE => 'TYPE_INCLUDE', + Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE', + Include_::TYPE_REQUIRE => 'TYPE_REQUIRE', + Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE', + ]; + + /** + * @param Node[]|Node|mixed[] $node + */ + public static function dump(array|Node $node, bool $rootNode = true): string + { + // display single root node directly to avoid useless nesting in output + if (is_array($node) && count($node) === 1 && $rootNode) { + $node = $node[0]; + } + + if ($node instanceof Node) { + return self::dumpSingleNode($node); + } + + if (self::isStringList($node)) { + return json_encode($node, JSON_THROW_ON_ERROR); + } + + $result = '['; + foreach ($node as $key => $value) { + $result .= "\n " . $key . ': '; + + if ($value === null) { + $result .= 'null'; + } elseif ($value === false) { + $result .= 'false'; + } elseif ($value === true) { + $result .= 'true'; + } elseif (is_string($value)) { + $result .= '"' . $value . '"'; + } elseif (is_scalar($value)) { + $result .= $value; + } else { + $result .= str_replace("\n", "\n ", self::dump($value, false)); + } + } + + if (count($node) === 0) { + $result .= ']'; + } else { + $result .= "\n]"; + } + + return $result; + } + + /** + * @param mixed[] $items + */ + private static function isStringList(array $items): bool + { + foreach ($items as $item) { + if (! is_string($item)) { + return false; + } + } + + return true; + } + + private static function dumpFlags(mixed $flags): string + { + $strs = []; + if (($flags & Modifiers::PUBLIC) !== 0) { + $strs[] = 'MODIFIER_PUBLIC'; + } + + if (($flags & Modifiers::PROTECTED) !== 0) { + $strs[] = 'MODIFIER_PROTECTED'; + } + + if (($flags & Modifiers::PRIVATE) !== 0) { + $strs[] = 'MODIFIER_PRIVATE'; + } + + if (($flags & Modifiers::ABSTRACT) !== 0) { + $strs[] = 'MODIFIER_ABSTRACT'; + } + + if (($flags & Modifiers::STATIC) !== 0) { + $strs[] = 'MODIFIER_STATIC'; + } + + if (($flags & Modifiers::FINAL) !== 0) { + $strs[] = 'MODIFIER_FINAL'; + } + + if (($flags & Modifiers::READONLY) !== 0) { + $strs[] = 'MODIFIER_READONLY'; + } + + if ($strs !== []) { + return implode(' | ', $strs) . ' (' . $flags . ')'; + } + + return (string) $flags; + } + + private static function dumpIncludeType(int|float|string $type): string + { + if (! isset(self::INCLUDE_TYPE_MAP[$type])) { + return (string) $type; + } + + return self::INCLUDE_TYPE_MAP[$type] . ' (' . $type . ')'; + } + + private static function dumpUseType(mixed $type): string + { + $map = [ + Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN', + Use_::TYPE_NORMAL => 'TYPE_NORMAL', + Use_::TYPE_FUNCTION => 'TYPE_FUNCTION', + Use_::TYPE_CONSTANT => 'TYPE_CONSTANT', + ]; + + if (! isset($map[$type])) { + return (string) $type; + } + + return $map[$type] . ' (' . $type . ')'; + } + + private static function dumpSingleNode(Node $node): string + { + $result = $node::class; + + // print simple nodes on same line, to make output more readable + if ($node instanceof Variable && is_string($node->name)) { + $result .= '( name: "' . $node->name . '" )'; + } elseif ($node instanceof Identifier) { + $result .= '( name: "' . $node->name . '" )'; + } elseif ($node instanceof Name) { + $result .= '( parts: ' . json_encode($node->getParts(), JSON_THROW_ON_ERROR) . ' )'; + } elseif ($node instanceof Scalar && $node->getSubNodeNames() === ['value']) { + if (is_string($node->value)) { + $result .= '( value: "' . $node->value . '" )'; + } else { + $result .= '( value: ' . $node->value . ' )'; + } + } else { + $result .= '('; + + foreach ($node->getSubNodeNames() as $key) { + $result .= "\n " . $key . ': '; + + $value = $node->{$key}; + if ($value === null) { + $result .= 'null'; + } elseif ($value === false) { + $result .= 'false'; + } elseif ($value === true) { + $result .= 'true'; + } elseif (is_scalar($value)) { + if ($key === 'flags' || $key === 'newModifier') { + $result .= self::dumpFlags($value); + } elseif ($key === 'type' && $node instanceof Include_) { + $result .= self::dumpIncludeType($value); + } elseif ($key === 'type' + && ($node instanceof Use_ || $node instanceof UseItem || $node instanceof GroupUse)) { + $result .= self::dumpUseType($value); + } elseif (is_string($value)) { + $result .= '"' . $value . '"'; + } else { + $result .= $value; + } + } else { + $result .= str_replace("\n", "\n ", self::dump($value, false)); + } + } + + $result .= "\n)"; + } + + return $result; + } +} diff --git a/src/DependencyInjection/Collector/ConfigureCallValuesCollector.php b/src/DependencyInjection/Collector/ConfigureCallValuesCollector.php deleted file mode 100644 index db5fe5acd36..00000000000 --- a/src/DependencyInjection/Collector/ConfigureCallValuesCollector.php +++ /dev/null @@ -1,61 +0,0 @@ -parametersMerger = new ParametersMerger(); - } - - /** - * @return mixed[] - */ - public function getConfigureCallValues(string $rectorClass): array - { - return $this->configureCallValuesByRectorClass[$rectorClass] ?? []; - } - - public function collectFromServiceAndClassName(string $className, Definition $definition): void - { - foreach ($definition->getMethodCalls() as $methodCall) { - if ($methodCall[0] !== 'configure') { - continue; - } - - $this->addConfigureCallValues($className, $methodCall[1]); - } - } - - /** - * @param mixed[] $configureValues - */ - private function addConfigureCallValues(string $rectorClass, array $configureValues): void - { - foreach ($configureValues as $configureValue) { - if (! isset($this->configureCallValuesByRectorClass[$rectorClass])) { - $this->configureCallValuesByRectorClass[$rectorClass] = $configureValue; - } else { - $mergedParameters = $this->parametersMerger->merge( - $this->configureCallValuesByRectorClass[$rectorClass], - $configureValue - ); - - $this->configureCallValuesByRectorClass[$rectorClass] = $mergedParameters; - } - } - } -} diff --git a/src/DependencyInjection/CompilerPass/MakeRectorsPublicCompilerPass.php b/src/DependencyInjection/CompilerPass/MakeRectorsPublicCompilerPass.php deleted file mode 100644 index 3950c1b23a7..00000000000 --- a/src/DependencyInjection/CompilerPass/MakeRectorsPublicCompilerPass.php +++ /dev/null @@ -1,27 +0,0 @@ -getDefinitions() as $definition) { - if ($definition->getClass() === null) { - continue; - } - - if (! is_a($definition->getClass(), RectorInterface::class, true)) { - continue; - } - - $definition->setPublic(true); - } - } -} diff --git a/src/DependencyInjection/CompilerPass/MergeImportedRectorConfigureCallValuesCompilerPass.php b/src/DependencyInjection/CompilerPass/MergeImportedRectorConfigureCallValuesCompilerPass.php deleted file mode 100644 index 75cc2e3e94c..00000000000 --- a/src/DependencyInjection/CompilerPass/MergeImportedRectorConfigureCallValuesCompilerPass.php +++ /dev/null @@ -1,41 +0,0 @@ -getDefinitions() as $id => $definition) { - $this->completeCollectedArguments($id, $definition); - } - } - - private function completeCollectedArguments(string $serviceClass, Definition $definition): void - { - $configureCallValues = $this->configureCallValuesCollector->getConfigureCallValues($serviceClass); - if ($configureCallValues === []) { - return; - } - - $definition->removeMethodCall(self::CONFIGURE_METHOD_NAME); - $definition->addMethodCall(self::CONFIGURE_METHOD_NAME, [$configureCallValues]); - } -} diff --git a/src/DependencyInjection/CompilerPass/RemoveSkippedRectorsCompilerPass.php b/src/DependencyInjection/CompilerPass/RemoveSkippedRectorsCompilerPass.php deleted file mode 100644 index efae72c703c..00000000000 --- a/src/DependencyInjection/CompilerPass/RemoveSkippedRectorsCompilerPass.php +++ /dev/null @@ -1,56 +0,0 @@ -resolveSkippedRectorClasses($containerBuilder); - - foreach ($containerBuilder->getDefinitions() as $id => $definition) { - if ($definition->getClass() === null) { - continue; - } - - if (! in_array($definition->getClass(), $skippedRectorClasses, true)) { - continue; - } - - $containerBuilder->removeDefinition($id); - } - } - - /** - * @return string[] - */ - private function resolveSkippedRectorClasses(ContainerBuilder $containerBuilder): array - { - $skipParameters = (array) $containerBuilder->getParameter(Option::SKIP); - - return array_filter($skipParameters, fn ($element): bool => $this->isRectorClass($element)); - } - - /** - * @param mixed $element - */ - private function isRectorClass($element): bool - { - if (! is_string($element)) { - return false; - } - - return is_a($element, RectorInterface::class, true); - } -} diff --git a/src/DependencyInjection/CompilerPass/VerifyRectorServiceExistsCompilerPass.php b/src/DependencyInjection/CompilerPass/VerifyRectorServiceExistsCompilerPass.php deleted file mode 100644 index f2e70c69647..00000000000 --- a/src/DependencyInjection/CompilerPass/VerifyRectorServiceExistsCompilerPass.php +++ /dev/null @@ -1,48 +0,0 @@ -getDefinitions() as $definition) { - $class = $definition->getClass(); - if ($class === null) { - continue; - } - - if (! \str_ends_with($class, 'Rector')) { - continue; - } - - if (! class_exists($class)) { - throw new ShouldNotHappenException( - sprintf( - 'Rector rule "%s" not found, please verify that the class exists and is autoloadable.', - $class - ) - ); - } - - if (! is_a($class, RectorInterface::class, true)) { - throw new ShouldNotHappenException( - sprintf( - 'Rector rule "%s" should extend "%s" or implement "%s".', - $class, - AbstractRector::class, - RectorInterface::class - ) - ); - } - } - } -} diff --git a/src/DependencyInjection/Laravel/ContainerMemento.php b/src/DependencyInjection/Laravel/ContainerMemento.php new file mode 100644 index 00000000000..607edcf8fad --- /dev/null +++ b/src/DependencyInjection/Laravel/ContainerMemento.php @@ -0,0 +1,58 @@ +tagged($tagToForget); + foreach ($taggedClasses as $taggedClass) { + $container->offsetUnset($taggedClass::class); + } + + // 2. forget tagged references + $privatesAccessor = new PrivatesAccessor(); + $privatesAccessor->propertyClosure($container, 'tags', static function (array $tags) use ( + $tagToForget + ): array { + unset($tags[$tagToForget]); + return $tags; + }); + } + + public static function forgetService(Container $container, string $typeToForget): void + { + // 1. remove the service + $container->offsetUnset($typeToForget); + + // 2. remove all tagged rules + $privatesAccessor = new PrivatesAccessor(); + $privatesAccessor->propertyClosure($container, 'tags', static function (array $tags) use ( + $typeToForget + ): array { + foreach ($tags as $tagName => $taggedClasses) { + foreach ($taggedClasses as $key => $taggedClass) { + if (is_a($taggedClass, $typeToForget, true)) { + unset($tags[$tagName][$key]); + } + } + } + + return $tags; + }); + } +} diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php new file mode 100644 index 00000000000..04d7da27c22 --- /dev/null +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -0,0 +1,753 @@ +> + */ + private const array NODE_NAME_RESOLVER_CLASSES = [ + ClassConstFetchNameResolver::class, + ClassConstNameResolver::class, + ClassNameResolver::class, + FuncCallNameResolver::class, + FunctionNameResolver::class, + NameNameResolver::class, + ParamNameResolver::class, + PropertyNameResolver::class, + UseNameResolver::class, + VariableNameResolver::class, + ]; + + /** + * @var array> + */ + private const array BASE_PHP_DOC_NODE_VISITORS = [ + ArrayTypePhpDocNodeVisitor::class, + CallableTypePhpDocNodeVisitor::class, + IntersectionTypeNodePhpDocNodeVisitor::class, + TemplatePhpDocNodeVisitor::class, + UnionTypeNodePhpDocNodeVisitor::class, + ]; + + /** + * @var array> + */ + private const array ANNOTATION_TO_ATTRIBUTE_MAPPER_CLASSES = [ + ArrayAnnotationToAttributeMapper::class, + ArrayItemNodeAnnotationToAttributeMapper::class, + ClassConstFetchAnnotationToAttributeMapper::class, + ConstExprNodeAnnotationToAttributeMapper::class, + CurlyListNodeAnnotationToAttributeMapper::class, + DoctrineAnnotationAnnotationToAttributeMapper::class, + StringAnnotationToAttributeMapper::class, + StringNodeAnnotationToAttributeMapper::class, + ]; + + /** + * @var array> + */ + private const array DECORATING_NODE_VISITOR_CLASSES = [ + ArgNodeVisitor::class, + ClosureWithVariadicParametersNodeVisitor::class, + PhpVersionConditionNodeVisitor::class, + AssignedToNodeVisitor::class, + SymfonyClosureNodeVisitor::class, + ByRefReturnNodeVisitor::class, + ByRefVariableNodeVisitor::class, + ContextNodeVisitor::class, + GlobalVariableNodeVisitor::class, + NameNodeVisitor::class, + StaticVariableNodeVisitor::class, + PropertyOrClassConstDefaultNodeVisitor::class, + ParamDefaultNodeVisitor::class, + ClassConstFetchNodeVisitor::class, + CallLikeThisBoundClosureArgsNodeVisitor::class, + ]; + + /** + * @var array> + */ + private const array PHPDOC_TYPE_MAPPER_CLASSES = [ + IdentifierPhpDocTypeMapper::class, + IntersectionPhpDocTypeMapper::class, + NullablePhpDocTypeMapper::class, + UnionPhpDocTypeMapper::class, + ]; + + /** + * @var array> + */ + private const array CLASS_NAME_IMPORT_SKIPPER_CLASSES = [ + AliasClassNameImportSkipVoter::class, + ClassLikeNameClassNameImportSkipVoter::class, + FullyQualifiedNameClassNameImportSkipVoter::class, + UsesClassNameImportSkipVoter::class, + ReservedClassNameImportSkipVoter::class, + ShortClassImportSkipVoter::class, + OriginalNameImportSkipVoter::class, + ]; + + /** + * @var array> + */ + private const array TYPE_MAPPER_CLASSES = [ + AccessoryLiteralStringTypeMapper::class, + AccessoryNonEmptyStringTypeMapper::class, + AccessoryNonFalsyStringTypeMapper::class, + AccessoryNumericStringTypeMapper::class, + ConstantArrayTypeMapper::class, + ArrayTypeMapper::class, + BooleanTypeMapper::class, + CallableTypeMapper::class, + ClassStringTypeMapper::class, + ClosureTypeMapper::class, + ConditionalTypeForParameterMapper::class, + ConditionalTypeMapper::class, + FloatTypeMapper::class, + GenericClassStringTypeMapper::class, + HasMethodTypeMapper::class, + HasOffsetTypeMapper::class, + HasOffsetValueTypeTypeMapper::class, + HasPropertyTypeMapper::class, + IntegerTypeMapper::class, + IntersectionTypeMapper::class, + IterableTypeMapper::class, + MixedTypeMapper::class, + NeverTypeMapper::class, + NonEmptyArrayTypeMapper::class, + NullTypeMapper::class, + ObjectTypeMapper::class, + ObjectWithoutClassTypeMapper::class, + OversizedArrayTypeMapper::class, + ParentStaticTypeMapper::class, + ResourceTypeMapper::class, + SelfObjectTypeMapper::class, + StaticTypeMapper::class, + StrictMixedTypeMapper::class, + StringTypeMapper::class, + ThisTypeMapper::class, + TypeWithClassNameTypeMapper::class, + UnionTypeMapper::class, + VoidTypeMapper::class, + ]; + + /** + * @var array> + */ + private const array PHP_DOC_NODE_DECORATOR_CLASSES = [ + ConstExprClassNameDecorator::class, + DoctrineAnnotationDecorator::class, + ArrayItemClassNameDecorator::class, + PhpDocTagGenericUsesDecorator::class, + ]; + + /** + * @var array + */ + private const array PUBLIC_PHPSTAN_SERVICE_TYPES = [ + ScopeFactory::class, + TypeNodeResolver::class, + NodeScopeResolver::class, + ReflectionProvider::class, + ]; + + /** + * @var array> + */ + private const array OUTPUT_FORMATTER_CLASSES = [ + ConsoleOutputFormatter::class, + JsonOutputFormatter::class, + GitlabOutputFormatter::class, + JUnitOutputFormatter::class, + GitHubOutputFormatter::class, + ]; + + /** + * @var array> + */ + private const array NODE_TYPE_RESOLVER_CLASSES = [ + CastTypeResolver::class, + StaticCallMethodCallTypeResolver::class, + ClassAndInterfaceTypeResolver::class, + IdentifierTypeResolver::class, + NameTypeResolver::class, + NewTypeResolver::class, + ParamTypeResolver::class, + PropertyFetchTypeResolver::class, + ClassConstFetchTypeResolver::class, + PropertyTypeResolver::class, + ScalarTypeResolver::class, + TraitTypeResolver::class, + ]; + + /** + * @var array> + */ + private const array PHP_PARSER_NODE_MAPPER_CLASSES = [ + FullyQualifiedNodeMapper::class, + IdentifierNodeMapper::class, + IntersectionTypeNodeMapper::class, + NameNodeMapper::class, + NullableTypeNodeMapper::class, + StringNodeMapper::class, + UnionTypeNodeMapper::class, + ExprNodeMapper::class, + ]; + + /** + * @var array> + */ + private const array CONVERTER_ATTRIBUTE_DECORATOR_CLASSES = [ + SensioParamConverterAttributeDecorator::class, + DoctrineConverterAttributeDecorator::class, + ]; + + /** + * @api used as next rectorConfig factory + */ + public function create(): RectorConfig + { + $rectorConfig = new RectorConfig(); + + $rectorConfig->import(__DIR__ . '/../../config/config.php'); + + $rectorConfig->singleton(Application::class, static function (Container $container): Application { + $consoleApplication = $container->make(ConsoleApplication::class); + + $commandNamesToHide = ['list', 'completion', 'help', 'worker']; + foreach ($commandNamesToHide as $commandNameToHide) { + $commandToHide = $consoleApplication->get($commandNameToHide); + $commandToHide->setHidden(); + } + + return $consoleApplication; + }); + + $rectorConfig->when(ConsoleApplication::class) + ->needs('$commands') + ->giveTagged(Command::class); + + $rectorConfig->singleton(Inflector::class, static function (): Inflector { + $inflectorFactory = new InflectorFactory(); + return $inflectorFactory->build(); + }); + + $rectorConfig->singleton(ConfigurationRuleFilter::class); + + $rectorConfig->singleton(ProcessCommand::class); + $rectorConfig->singleton(WorkerCommand::class); + $rectorConfig->singleton(SetupCICommand::class); + $rectorConfig->singleton(ListRulesCommand::class); + $rectorConfig->singleton(CustomRuleCommand::class); + + $rectorConfig->when(ListRulesCommand::class) + ->needs('$rectors') + ->giveTagged(RectorInterface::class); + + $rectorConfig->when(OnlyRuleResolver::class) + ->needs('$rectors') + ->giveTagged(RectorInterface::class); + + $rectorConfig->when(DeprecatedRulesReporter::class) + ->needs('$rectors') + ->giveTagged(RectorInterface::class); + + $rectorConfig->singleton(FileProcessor::class); + $rectorConfig->singleton(PostFileProcessor::class); + + $rectorConfig->when(RectorNodeTraverser::class) + ->needs('$rectors') + ->giveTagged(RectorInterface::class); + + $rectorConfig->when(ConfigInitializer::class) + ->needs('$rectors') + ->giveTagged(RectorInterface::class); + + $rectorConfig->when(ClassNameImportSkipper::class) + ->needs('$classNameImportSkipVoters') + ->giveTagged(ClassNameImportSkipVoterInterface::class); + + $rectorConfig->singleton( + DynamicSourceLocatorProvider::class, + static function (Container $container): DynamicSourceLocatorProvider { + $phpStanServicesFactory = $container->make(PHPStanServicesFactory::class); + return $phpStanServicesFactory->createDynamicSourceLocatorProvider(); + } + ); + + // resettable + $rectorConfig->tag(DynamicSourceLocatorProvider::class, ResettableInterface::class); + $rectorConfig->tag(RenamedClassesDataCollector::class, ResettableInterface::class); + + // caching + $rectorConfig->singleton(Cache::class, static function (Container $container): Cache { + /** @var CacheFactory $cacheFactory */ + $cacheFactory = $container->make(CacheFactory::class); + return $cacheFactory->create(); + }); + + $rectorConfig->when(FileHashComputer::class) + ->needs('$cacheMetaExtensions') + ->giveTagged(CacheMetaExtensionInterface::class); + + // tagged services + $rectorConfig->when(BetterPhpDocParser::class) + ->needs('$phpDocNodeDecorators') + ->giveTagged(PhpDocNodeDecoratorInterface::class); + + $rectorConfig->afterResolving( + ArrayTypeMapper::class, + static function (ArrayTypeMapper $arrayTypeMapper, Container $container): void { + $arrayTypeMapper->autowire($container->make(PHPStanStaticTypeMapper::class)); + } + ); + + $rectorConfig->afterResolving( + ConditionalTypeForParameterMapper::class, + static function ( + ConditionalTypeForParameterMapper $conditionalTypeForParameterMapper, + Container $container + ): void { + $phpStanStaticTypeMapper = $container->make(PHPStanStaticTypeMapper::class); + $conditionalTypeForParameterMapper->autowire($phpStanStaticTypeMapper); + } + ); + + $rectorConfig->afterResolving( + ConditionalTypeMapper::class, + static function (ConditionalTypeMapper $conditionalTypeMapper, Container $container): void { + $phpStanStaticTypeMapper = $container->make(PHPStanStaticTypeMapper::class); + $conditionalTypeMapper->autowire($phpStanStaticTypeMapper); + } + ); + + $rectorConfig->afterResolving( + UnionTypeMapper::class, + static function (UnionTypeMapper $unionTypeMapper, Container $container): void { + $phpStanStaticTypeMapper = $container->make(PHPStanStaticTypeMapper::class); + $unionTypeMapper->autowire($phpStanStaticTypeMapper); + } + ); + + $rectorConfig->when(PHPStanStaticTypeMapper::class) + ->needs('$typeMappers') + ->giveTagged(TypeMapperInterface::class); + + $rectorConfig->when(PhpDocTypeMapper::class) + ->needs('$phpDocTypeMappers') + ->giveTagged(PhpDocTypeMapperInterface::class); + + $rectorConfig->when(PhpParserNodeMapper::class) + ->needs('$phpParserNodeMappers') + ->giveTagged(PhpParserNodeMapperInterface::class); + + $rectorConfig->when(NodeTypeResolver::class) + ->needs('$nodeTypeResolvers') + ->giveTagged(NodeTypeResolverInterface::class); + + // node name resolvers + $rectorConfig->when(NodeNameResolver::class) + ->needs('$nodeNameResolvers') + ->giveTagged(NodeNameResolverInterface::class); + + $rectorConfig->when(AttributeGroupNamedArgumentManipulator::class) + ->needs('$converterAttributeDecorators') + ->giveTagged(ConverterAttributeDecoratorInterface::class); + + $this->registerTagged( + $rectorConfig, + self::CONVERTER_ATTRIBUTE_DECORATOR_CLASSES, + ConverterAttributeDecoratorInterface::class + ); + + $rectorConfig->afterResolving( + AbstractRector::class, + static function (AbstractRector $rector, Container $container): void { + $rector->autowire( + $container->get(NodeNameResolver::class), + $container->get(NodeTypeResolver::class), + $container->get(SimpleCallableNodeTraverser::class), + $container->get(NodeFactory::class), + $container->get(Skipper::class), + $container->get(NodeComparator::class), + $container->get(CurrentFileProvider::class), + $container->get(CreatedByRuleDecorator::class), + $container->get(ChangedNodeScopeRefresher::class), + $container->get(CommentsMerger::class), + ); + } + ); + + $this->registerTagged( + $rectorConfig, + self::PHP_PARSER_NODE_MAPPER_CLASSES, + PhpParserNodeMapperInterface::class + ); + + $this->registerTagged( + $rectorConfig, + self::PHP_DOC_NODE_DECORATOR_CLASSES, + PhpDocNodeDecoratorInterface::class + ); + + $this->registerTagged( + $rectorConfig, + self::BASE_PHP_DOC_NODE_VISITORS, + BasePhpDocNodeVisitorInterface::class + ); + + // PHP 8.0 attributes + $this->registerTagged( + $rectorConfig, + self::ANNOTATION_TO_ATTRIBUTE_MAPPER_CLASSES, + AnnotationToAttributeMapperInterface::class + ); + + $this->registerTagged($rectorConfig, self::TYPE_MAPPER_CLASSES, TypeMapperInterface::class); + $this->registerTagged($rectorConfig, self::PHPDOC_TYPE_MAPPER_CLASSES, PhpDocTypeMapperInterface::class); + $this->registerTagged($rectorConfig, self::NODE_NAME_RESOLVER_CLASSES, NodeNameResolverInterface::class); + $this->registerTagged($rectorConfig, self::NODE_TYPE_RESOLVER_CLASSES, NodeTypeResolverInterface::class); + $this->registerTagged($rectorConfig, self::OUTPUT_FORMATTER_CLASSES, OutputFormatterInterface::class); + $this->registerTagged($rectorConfig, self::BASE_PHP_DOC_NODE_VISITORS, BasePhpDocNodeVisitorInterface::class); + $this->registerTagged( + $rectorConfig, + self::CLASS_NAME_IMPORT_SKIPPER_CLASSES, + ClassNameImportSkipVoterInterface::class + ); + + $rectorConfig->alias(SymfonyStyle::class, RectorStyle::class); + + $rectorConfig->singleton( + SymfonyStyle::class, + static function (Container $container): SymfonyStyle { + $symfonyStyleFactory = $container->make(SymfonyStyleFactory::class); + return $symfonyStyleFactory->create(); + } + ); + + $rectorConfig->when(AnnotationToAttributeMapper::class) + ->needs('$annotationToAttributeMappers') + ->giveTagged(AnnotationToAttributeMapperInterface::class); + + $rectorConfig->when(OutputFormatterCollector::class) + ->needs('$outputFormatters') + ->giveTagged(OutputFormatterInterface::class); + + // required-like setter + $rectorConfig->afterResolving( + ArrayAnnotationToAttributeMapper::class, + static function ( + ArrayAnnotationToAttributeMapper $arrayAnnotationToAttributeMapper, + Container $container + ): void { + $annotationToAttributeMapper = $container->make(AnnotationToAttributeMapper::class); + $arrayAnnotationToAttributeMapper->autowire($annotationToAttributeMapper); + } + ); + + $rectorConfig->afterResolving( + ArrayItemNodeAnnotationToAttributeMapper::class, + static function ( + ArrayItemNodeAnnotationToAttributeMapper $arrayItemNodeAnnotationToAttributeMapper, + Container $container + ): void { + $annotationToAttributeMapper = $container->make(AnnotationToAttributeMapper::class); + $arrayItemNodeAnnotationToAttributeMapper->autowire($annotationToAttributeMapper); + } + ); + + $rectorConfig->afterResolving( + PlainValueParser::class, + static function (PlainValueParser $plainValueParser, Container $container): void { + $plainValueParser->autowire( + $container->make(StaticDoctrineAnnotationParser::class), + $container->make(ArrayParser::class), + ); + } + ); + + $rectorConfig->afterResolving( + CurlyListNodeAnnotationToAttributeMapper::class, + static function ( + CurlyListNodeAnnotationToAttributeMapper $curlyListNodeAnnotationToAttributeMapper, + Container $container + ): void { + $annotationToAttributeMapper = $container->make(AnnotationToAttributeMapper::class); + $curlyListNodeAnnotationToAttributeMapper->autowire($annotationToAttributeMapper); + } + ); + + $rectorConfig->afterResolving( + DoctrineAnnotationAnnotationToAttributeMapper::class, + static function ( + DoctrineAnnotationAnnotationToAttributeMapper $doctrineAnnotationAnnotationToAttributeMapper, + Container $container + ): void { + $annotationToAttributeMapper = $container->make(AnnotationToAttributeMapper::class); + $doctrineAnnotationAnnotationToAttributeMapper->autowire($annotationToAttributeMapper); + } + ); + + $rectorConfig->when(PHPStanNodeScopeResolver::class) + ->needs('$decoratingNodeVisitors') + ->giveTagged(DecoratingNodeVisitorInterface::class); + + $this->registerTagged( + $rectorConfig, + self::DECORATING_NODE_VISITOR_CLASSES, + DecoratingNodeVisitorInterface::class + ); + + $this->createPHPStanServices($rectorConfig); + + $rectorConfig->when(PhpDocNodeMapper::class) + ->needs('$phpDocNodeVisitors') + ->giveTagged(BasePhpDocNodeVisitorInterface::class); + + // phpdoc-parser + $rectorConfig->singleton( + ParserConfig::class, + static fn (Container $container): ParserConfig => new ParserConfig([ + 'lines' => true, + 'indexes' => true, + 'comments' => true, + ]) + ); + + return $rectorConfig; + } + + /** + * @param array $classes + * @param class-string $tagInterface + */ + private function registerTagged(Container $container, array $classes, string $tagInterface): void + { + foreach ($classes as $class) { + Assert::isAOf($class, $tagInterface); + + $container->singleton($class); + $container->tag($class, $tagInterface); + } + } + + private function createPHPStanServices(RectorConfig $rectorConfig): void + { + $rectorConfig->singleton(Parser::class, static function (Container $container) { + $phpStanServicesFactory = $container->make(PHPStanServicesFactory::class); + return $phpStanServicesFactory->createPHPStanParser(); + }); + + $rectorConfig->singleton(Lexer::class, static function (Container $container) { + $phpStanServicesFactory = $container->make(PHPStanServicesFactory::class); + return $phpStanServicesFactory->createEmulativeLexer(); + }); + + foreach (self::PUBLIC_PHPSTAN_SERVICE_TYPES as $publicPhpstanServiceType) { + $rectorConfig->singleton($publicPhpstanServiceType, static function (Container $container) use ( + $publicPhpstanServiceType + ) { + $phpStanServicesFactory = $container->make(PHPStanServicesFactory::class); + return $phpStanServicesFactory->getByType($publicPhpstanServiceType); + }); + } + } +} diff --git a/src/DependencyInjection/Loader/ConfigurableCallValuesCollectingPhpFileLoader.php b/src/DependencyInjection/Loader/ConfigurableCallValuesCollectingPhpFileLoader.php deleted file mode 100644 index 0141abb4fc9..00000000000 --- a/src/DependencyInjection/Loader/ConfigurableCallValuesCollectingPhpFileLoader.php +++ /dev/null @@ -1,65 +0,0 @@ -collectConfigureCallsFromJustImportedConfigurableRectorDefinitions(); - - parent::load($resource, $type); - - $this->collectConfigureCallsFromJustImportedConfigurableRectorDefinitions(); - } - - public function import( - $resource, - string $type = null, - $ignoreErrors = false, - string $sourceResource = null, - $exclude = null - ): void { - // this call collects root values - $this->collectConfigureCallsFromJustImportedConfigurableRectorDefinitions(); - - parent::import($resource, $type, $ignoreErrors, $sourceResource, $exclude); - - $this->collectConfigureCallsFromJustImportedConfigurableRectorDefinitions(); - } - - private function collectConfigureCallsFromJustImportedConfigurableRectorDefinitions(): void - { - foreach ($this->container->getDefinitions() as $class => $definition) { - if (! is_string($class)) { - continue; - } - - if (! is_a($class, ConfigurableRectorInterface::class, true)) { - continue; - } - - $this->configureCallValuesCollector->collectFromServiceAndClassName($class, $definition); - } - } -} diff --git a/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php b/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php new file mode 100644 index 00000000000..387750afaa7 --- /dev/null +++ b/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php @@ -0,0 +1,49 @@ +getPrivateProperty($richParser, 'container'); + + /** @var NetteContainer $originalContainer */ + $originalContainer = $privatesAccessor->getPrivateProperty($container, 'originalContainer'); + + /** @var NetteContainer $originalContainer */ + $deeperContainer = $privatesAccessor->getPrivateProperty($originalContainer, 'container'); + + // get tags property + $tags = $privatesAccessor->getPrivateProperty($deeperContainer, 'tags'); + + // keep visitors that are useful + // remove all the rest, https://github.com/phpstan/phpstan-src/tree/1d86de8bb9371534983a8dbcd879e057d2ff028f/src/Parser + $nodeVisitorsToKeep = [ + $container->findServiceNamesByType(AnonymousClassVisitor::class)[0] => true, + $container->findServiceNamesByType(ArrayMapArgVisitor::class)[0] => true, + ]; + + $tags[RichParser::VISITOR_SERVICE_TAG] = $nodeVisitorsToKeep; + + $privatesAccessor->setPrivateProperty($deeperContainer, 'tags', $tags); + } +} diff --git a/src/DependencyInjection/RectorContainerFactory.php b/src/DependencyInjection/RectorContainerFactory.php index f6d256ec4d4..8e2f7a21296 100644 --- a/src/DependencyInjection/RectorContainerFactory.php +++ b/src/DependencyInjection/RectorContainerFactory.php @@ -2,72 +2,48 @@ declare(strict_types=1); -namespace Rector\Core\DependencyInjection; +namespace Rector\DependencyInjection; +use Rector\Autoloading\BootstrapFilesIncluder; use Rector\Caching\Detector\ChangedFilesDetector; -use Rector\Core\HttpKernel\RectorKernel; -use Rector\Core\Stubs\PHPStanStubLoader; -use Rector\Core\ValueObject\Bootstrap\BootstrapConfigs; -use Rector\Core\ValueObject\Configuration; -use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symplify\PackageBuilder\Console\Input\StaticInputDetector; -use Symplify\SmartFileSystem\SmartFileInfo; +use Rector\Config\RectorConfig; +use Rector\ValueObject\Bootstrap\BootstrapConfigs; final class RectorContainerFactory { - /** - * @param SmartFileInfo[] $configFileInfos - * @api - */ - public function createFromConfigs(array $configFileInfos): ContainerInterface + public function createFromBootstrapConfigs(BootstrapConfigs $bootstrapConfigs): RectorConfig { - // to override the configs without clearing cache - $isDebug = StaticInputDetector::isDebug(); + $rectorConfig = $this->createFromConfigs($bootstrapConfigs->getConfigFiles()); - $environment = $this->createEnvironment($configFileInfos); - - // mt_rand is needed to invalidate container cache in case of class changes to be registered as services - $isPHPUnitRun = StaticPHPUnitEnvironment::isPHPUnitRun(); - if (! $isPHPUnitRun) { - $environment .= mt_rand(0, 10000); - } + $mainConfigFile = $bootstrapConfigs->getMainConfigFile(); - $phpStanStubLoader = new PHPStanStubLoader(); - $phpStanStubLoader->loadStubs(); - - $rectorKernel = new RectorKernel($environment, $isDebug, $configFileInfos); - $rectorKernel->boot(); - - return $rectorKernel->getContainer(); - } - - public function createFromBootstrapConfigs(BootstrapConfigs $bootstrapConfigs): ContainerInterface - { - $container = $this->createFromConfigs($bootstrapConfigs->getConfigFileInfos()); - - $mainConfigFileInfo = $bootstrapConfigs->getMainConfigFileInfo(); - if ($mainConfigFileInfo !== null) { + if ($mainConfigFile !== null) { /** @var ChangedFilesDetector $changedFilesDetector */ - $changedFilesDetector = $container->get(ChangedFilesDetector::class); - $changedFilesDetector->setFirstResolvedConfigFileInfo($mainConfigFileInfo); + $changedFilesDetector = $rectorConfig->make(ChangedFilesDetector::class); + $changedFilesDetector->setFirstResolvedConfigFileInfo($mainConfigFile); } - return $container; + /** @var BootstrapFilesIncluder $bootstrapFilesIncluder */ + $bootstrapFilesIncluder = $rectorConfig->get(BootstrapFilesIncluder::class); + $bootstrapFilesIncluder->includeBootstrapFiles(); + + return $rectorConfig; } /** - * @see https://symfony.com/doc/current/components/dependency_injection/compilation.html#dumping-the-configuration-for-performance - * @param SmartFileInfo[] $configFileInfos + * @param string[] $configFiles */ - private function createEnvironment(array $configFileInfos): string + private function createFromConfigs(array $configFiles): RectorConfig { - $configHashes = []; - foreach ($configFileInfos as $configFileInfo) { - $configHashes[] = md5_file($configFileInfo->getRealPath()); + $lazyContainerFactory = new LazyContainerFactory(); + $rectorConfig = $lazyContainerFactory->create(); + + foreach ($configFiles as $configFile) { + $rectorConfig->import($configFile); } - $configHashString = implode('', $configHashes); - return sha1($configHashString); + $rectorConfig->boot(); + + return $rectorConfig; } } diff --git a/src/Differ/DefaultDiffer.php b/src/Differ/DefaultDiffer.php index 5ee8d9dd640..948fe20c7c1 100644 --- a/src/Differ/DefaultDiffer.php +++ b/src/Differ/DefaultDiffer.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Rector\Core\Differ; +namespace Rector\Differ; use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder; -final class DefaultDiffer +final readonly class DefaultDiffer { private Differ $differ; @@ -22,9 +22,6 @@ public function __construct() public function diff(string $old, string $new): string { - if ($old === $new) { - return ''; - } return $this->differ->diff($old, $new); } } diff --git a/src/Enum/ApplicationPhase.php b/src/Enum/ApplicationPhase.php deleted file mode 100644 index 69711abded6..00000000000 --- a/src/Enum/ApplicationPhase.php +++ /dev/null @@ -1,42 +0,0 @@ -getTrace()[0])) { - return null; - } - - if (! isset($throwable->getTrace()[0]['class'])) { - return null; - } - - /** @var string $class */ - $class = $throwable->getTrace()[0]['class']; - if (! is_a($class, RectorInterface::class, true)) { - return null; - } - - return $class; - } - - public function getAutoloadExceptionMessageAndAddLocation(AnalysedCodeException $analysedCodeException): string - { - return sprintf( - 'Analyze error: "%s". Include your files in "$parameters->set(Option::AUTOLOAD_PATHS, [...]);" in "rector.php" config.%sSee https://github.com/rectorphp/rector#configuration', - $analysedCodeException->getMessage(), - PHP_EOL - ); - } -} diff --git a/src/Exception/Application/FileProcessingException.php b/src/Exception/Application/FileProcessingException.php deleted file mode 100644 index a0edf8c50a0..00000000000 --- a/src/Exception/Application/FileProcessingException.php +++ /dev/null @@ -1,24 +0,0 @@ -getRealPath(), - PHP_EOL . PHP_EOL, - $throwable - ); - - parent::__construct($message); - } -} diff --git a/src/Exception/Cache/CachingException.php b/src/Exception/Cache/CachingException.php new file mode 100644 index 00000000000..702e87839dc --- /dev/null +++ b/src/Exception/Cache/CachingException.php @@ -0,0 +1,11 @@ +createDefaultMessageWithLocation(); @@ -27,11 +26,13 @@ private function createDefaultMessageWithLocation(): string $debugBacktrace = debug_backtrace(); $class = $debugBacktrace[2]['class'] ?? null; - $function = (string) $debugBacktrace[2]['function']; - $line = (int) $debugBacktrace[1]['line']; + $function = $debugBacktrace[2]['function']; + $line = $debugBacktrace[1]['line'] ?? 0; - $method = $class ? ($class . '::' . $function) : $function; + $method = $class !== null ? ($class . '::' . $function) : $function; + /** @var string $method */ + /** @var int $line */ return sprintf('Look at "%s()" on line %d', $method, $line); } } diff --git a/src/Exception/Template/TemplateTypeNotFoundException.php b/src/Exception/Template/TemplateTypeNotFoundException.php deleted file mode 100644 index 4452cd478f8..00000000000 --- a/src/Exception/Template/TemplateTypeNotFoundException.php +++ /dev/null @@ -1,22 +0,0 @@ -getAttribute(AttributeKey::PARENT_NODE); - if (! $node instanceof Node) { - return false; - } - } - - if ($this->hasNoRectorPhpDocTagMatch($node, $phpRector)) { - return true; - } - - if ($node instanceof Stmt) { - return false; - } - - // recurse up until a Stmt node is found since it might contain a noRector - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode === null) { - return false; - } - - return $this->isNodeSkippedByRector($parentNode, $phpRector); - } - - private function hasNoRectorPhpDocTagMatch(Node $node, PhpRectorInterface $phpRector): bool - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - /** @var PhpDocTagNode[] $noRectorTags */ - $noRectorTags = array_merge($phpDocInfo->getTagsByName('noRector'), $phpDocInfo->getTagsByName('norector')); - - $rectorClass = $phpRector::class; - - if ($this->matchesNoRectorTag($noRectorTags, $rectorClass)) { - return true; - } - - return $this->matchesNoRectorComment($node, $rectorClass); - } - - /** - * @param PhpDocTagNode[] $noRectorPhpDocTagNodes - * @param class-string $rectorClass - */ - private function matchesNoRectorTag(array $noRectorPhpDocTagNodes, string $rectorClass): bool - { - foreach ($noRectorPhpDocTagNodes as $noRectorPhpDocTagNode) { - if (! $noRectorPhpDocTagNode->value instanceof GenericTagValueNode) { - throw new ShouldNotHappenException(); - } - - $description = $noRectorPhpDocTagNode->value->value; - if ($description === '') { - return true; - } - - $description = ltrim($description, '\\'); - if ($description === $rectorClass) { - return true; - } - - if (! is_a($description, RectorInterface::class, true)) { - return true; - } - } - - return false; - } - - /** - * @param class-string $rectorClass - */ - private function matchesNoRectorComment(Node $node, string $rectorClass): bool - { - foreach ($node->getComments() as $comment) { - if (Strings::match($comment->getText(), self::NO_RECTOR_START_REGEX)) { - return true; - } - - $noRectorWithRule = '#@noRector \\\\?' . preg_quote($rectorClass, '#') . '$#'; - if (Strings::match($comment->getText(), $noRectorWithRule)) { - return true; - } - } - - return false; - } -} diff --git a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php new file mode 100644 index 00000000000..8255677d41e --- /dev/null +++ b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php @@ -0,0 +1,92 @@ +resolveParentClassMethods($classReflection, $methodName); + if ($parentClassMethods === []) { + return false; + } + + foreach ($parentClassMethods as $parentClassMethod) { + if ($parentClassMethod->isAbstract()) { + return true; + } + } + + return false; + } + + /** + * @api downgrade + */ + public function resolveParentClassMethodReturnType(ClassReflection $classReflection, string $methodName): Type + { + $parentClassMethods = $this->resolveParentClassMethods($classReflection, $methodName); + if ($parentClassMethods === []) { + return new MixedType(); + } + + foreach ($parentClassMethods as $parentClassMethod) { + $parametersAcceptor = ParametersAcceptorSelector::combineAcceptors($parentClassMethod->getVariants()); + $nativeReturnType = $parametersAcceptor->getNativeReturnType(); + + if (! $nativeReturnType instanceof MixedType) { + return $nativeReturnType; + } + } + + return new MixedType(); + } + + /** + * @return PhpMethodReflection[] + */ + private function resolveParentClassMethods(ClassReflection $classReflection, string $methodName): array + { + if ($classReflection->hasNativeMethod($methodName) && $classReflection->getNativeMethod( + $methodName + )->isPrivate()) { + return []; + } + + $parentClassMethods = []; + $parents = [...$classReflection->getParents(), ...$classReflection->getInterfaces()]; + foreach ($parents as $parent) { + if (! $parent->hasNativeMethod($methodName)) { + continue; + } + + $methodReflection = $parent->getNativeMethod($methodName); + if (! $methodReflection instanceof PhpMethodReflection) { + continue; + } + + $methodDeclaringMethodClass = $methodReflection->getDeclaringClass(); + if ($methodDeclaringMethodClass->getName() === $parent->getName()) { + $parentClassMethods[] = $methodReflection; + } + } + + return $parentClassMethods; + } +} diff --git a/src/FamilyTree/Reflection/FamilyRelationsAnalyzer.php b/src/FamilyTree/Reflection/FamilyRelationsAnalyzer.php new file mode 100644 index 00000000000..a2a5282a60b --- /dev/null +++ b/src/FamilyTree/Reflection/FamilyRelationsAnalyzer.php @@ -0,0 +1,67 @@ +nodeNameResolver->getName($classOrName); + if (! $this->reflectionProvider->hasClass($fullName)) { + return []; + } + + $classReflection = $this->reflectionProvider->getClass($fullName); + $ancestors = [...$classReflection->getParents(), ...$classReflection->getInterfaces()]; + + return array_map( + static fn (ClassReflection $classReflection): string => $classReflection->getName(), + $ancestors + ); + } + + if ($classOrName instanceof Interface_) { + foreach ($classOrName->extends as $extendInterfaceName) { + $ancestorNames[] = $this->nodeNameResolver->getName($extendInterfaceName); + $ancestorNames = array_merge($ancestorNames, $this->getClassLikeAncestorNames($extendInterfaceName)); + } + } + + if ($classOrName instanceof Class_) { + if ($classOrName->extends instanceof Name) { + $ancestorNames[] = $this->nodeNameResolver->getName($classOrName->extends); + $ancestorNames = array_merge($ancestorNames, $this->getClassLikeAncestorNames($classOrName->extends)); + } + + foreach ($classOrName->implements as $implement) { + $ancestorNames[] = $this->nodeNameResolver->getName($implement); + $ancestorNames = array_merge($ancestorNames, $this->getClassLikeAncestorNames($implement)); + } + } + + /** @var string[] $ancestorNames */ + return $ancestorNames; + } +} diff --git a/src/FileSystem/FileAndDirectoryFilter.php b/src/FileSystem/FileAndDirectoryFilter.php new file mode 100644 index 00000000000..641eea46b76 --- /dev/null +++ b/src/FileSystem/FileAndDirectoryFilter.php @@ -0,0 +1,33 @@ +filesystem->isAbsolutePath($fileRealPath)) { + return $fileRealPath; + } + + return $this->relativeFilePathFromDirectory($fileRealPath, getcwd()); + } + + /** + * Used from + * https://github.com/phpstan/phpstan-src/blob/02425e61aa48f0668b4efb3e73d52ad544048f65/src/File/FileHelper.php#L40, with custom modifications + */ + public function normalizePathAndSchema(string $originalPath): string + { + $directorySeparator = DIRECTORY_SEPARATOR; + + $matches = Strings::match($originalPath, self::SCHEME_PATH_REGEX); + if ($matches !== null) { + [, $scheme, $path] = $matches; + } else { + $scheme = self::SCHEME_UNDEFINED; + $path = $originalPath; + } + + $normalizedPath = PathNormalizer::normalize((string) $path); + $path = Strings::replace($normalizedPath, self::TWO_AND_MORE_SLASHES_REGEX, '/'); + + $pathRoot = str_starts_with($path, '/') ? $directorySeparator : ''; + $pathParts = explode('/', trim($path, '/')); + + /** @var string $scheme */ + $normalizedPathParts = $this->normalizePathParts($pathParts, $scheme); + + $pathStart = ($scheme !== self::SCHEME_UNDEFINED ? $scheme . '://' : ''); + return PathNormalizer::normalize($pathStart . $pathRoot . implode($directorySeparator, $normalizedPathParts)); + } + + private function relativeFilePathFromDirectory(string $fileRealPath, string $directory): string + { + Assert::directory($directory); + $normalizedFileRealPath = PathNormalizer::normalize($fileRealPath); + + $relativeFilePath = $this->filesystem->makePathRelative($normalizedFileRealPath, $directory); + return rtrim($relativeFilePath, '/'); + } + + /** + * @param string[] $pathParts + * @return string[] + */ + private function normalizePathParts(array $pathParts, string $scheme): array + { + $normalizedPathParts = []; + + foreach ($pathParts as $pathPart) { + if ($pathPart === '.') { + continue; + } + + if ($pathPart !== '..') { + $normalizedPathParts[] = $pathPart; + continue; + } + + /** @var string $removedPart */ + $removedPart = array_pop($normalizedPathParts); + if ($scheme !== 'phar') { + continue; + } + + if (! \str_ends_with($removedPart, '.phar')) { + continue; + } + + $scheme = self::SCHEME_UNDEFINED; + } + + return $normalizedPathParts; + } +} diff --git a/src/FileSystem/FilesFinder.php b/src/FileSystem/FilesFinder.php index 43691927d79..8a8a5871f7b 100644 --- a/src/FileSystem/FilesFinder.php +++ b/src/FileSystem/FilesFinder.php @@ -2,158 +2,204 @@ declare(strict_types=1); -namespace Rector\Core\FileSystem; +namespace Rector\FileSystem; -use Nette\Utils\Strings; +use Nette\Utils\FileSystem; +use Rector\Caching\Detector\ChangedFilesDetector; use Rector\Caching\UnchangedFilesFilter; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\Skipper\Skipper\PathSkipper; +use Rector\ValueObject\Configuration; use Symfony\Component\Finder\Finder; -use Symfony\Component\Finder\SplFileInfo; -use Symplify\Skipper\SkipCriteriaResolver\SkippedPathsResolver; -use Symplify\SmartFileSystem\FileSystemFilter; -use Symplify\SmartFileSystem\Finder\FinderSanitizer; -use Symplify\SmartFileSystem\SmartFileInfo; /** - * @see \Rector\Core\Tests\FileSystem\FilesFinder\FilesFinderTest + * @see \Rector\Tests\FileSystem\FilesFinder\FilesFinderTest */ -final class FilesFinder +final readonly class FilesFinder { - /** - * @var string - * @see https://regex101.com/r/e1jm7v/1 - */ - private const STARTS_WITH_ASTERISK_REGEX = '#^\*(.*?)[^*]$#'; - - /** - * @var string - * @see https://regex101.com/r/EgJQyZ/1 - */ - private const ENDS_WITH_ASTERISK_REGEX = '#^[^*](.*?)\*$#'; - public function __construct( private FilesystemTweaker $filesystemTweaker, - private FinderSanitizer $finderSanitizer, - private FileSystemFilter $fileSystemFilter, - private SkippedPathsResolver $skippedPathsResolver, - private UnchangedFilesFilter $unchangedFilesFilter + private UnchangedFilesFilter $unchangedFilesFilter, + private FileAndDirectoryFilter $fileAndDirectoryFilter, + private PathSkipper $pathSkipper, + private FilePathHelper $filePathHelper, + private ChangedFilesDetector $changedFilesDetector, ) { } /** * @param string[] $source * @param string[] $suffixes - * @return SmartFileInfo[] + * @return string[] */ - public function findInDirectoriesAndFiles(array $source, array $suffixes = []): array - { + public function findInDirectoriesAndFiles( + array $source, + array $suffixes = [], + bool $sortByName = true, + ?string $onlySuffix = null, + ): array { $filesAndDirectories = $this->filesystemTweaker->resolveWithFnmatch($source); - $filePaths = $this->fileSystemFilter->filterFiles($filesAndDirectories); - $directories = $this->fileSystemFilter->filterDirectories($filesAndDirectories); + // filtering files in files collection + $filteredFilePaths = $this->fileAndDirectoryFilter->filterFiles($filesAndDirectories); + $filteredFilePaths = array_filter( + $filteredFilePaths, + fn (string $filePath): bool => ! $this->pathSkipper->shouldSkip($filePath) + ); + + // fallback append `.php` to be used for both $filteredFilePaths and $filteredFilePathsInDirectories + $hasOnlySuffix = $onlySuffix !== null && $onlySuffix !== ''; - $smartFileInfos = []; - foreach ($filePaths as $filePath) { - $smartFileInfos[] = new SmartFileInfo($filePath); + if ($hasOnlySuffix && ! str_ends_with($onlySuffix, '.php')) { + $onlySuffix .= '.php'; } - $smartFileInfos = $this->unchangedFilesFilter->filterAndJoinWithDependentFileInfos($smartFileInfos); + // filter files by specific suffix + if ($hasOnlySuffix) { + /** @var string $onlySuffix */ + $fileWithSuffixFilter = (static fn (string $filePath): bool => str_ends_with($filePath, $onlySuffix)); + } elseif ($suffixes !== []) { + $fileWithSuffixFilter = static function (string $filePath) use ($suffixes): bool { + $filePathExtension = pathinfo($filePath, PATHINFO_EXTENSION); + return in_array($filePathExtension, $suffixes, true); + }; + } else { + $fileWithSuffixFilter = fn (): bool => true; + } + + $filteredFilePaths = array_filter($filteredFilePaths, $fileWithSuffixFilter); + + // add file without extension after file extension filter + $filteredFilePaths = array_merge( + $filteredFilePaths, + SimpleParameterProvider::provideArrayParameter(Option::FILES_WITHOUT_EXTENSION) + ); + + $filteredFilePaths = array_filter( + $filteredFilePaths, + function (string $file): bool { + if ($this->isStartWithShortPHPTag(FileSystem::read($file))) { + SimpleParameterProvider::addParameter( + Option::SKIPPED_START_WITH_SHORT_OPEN_TAG_FILES, + $this->filePathHelper->relativePath($file) + ); + return false; + } - return array_merge($smartFileInfos, $this->findInDirectories($directories, $suffixes)); + return true; + } + ); + + // filtering files in directories collection + $directories = $this->fileAndDirectoryFilter->filterDirectories($filesAndDirectories); + $filteredFilePathsInDirectories = $this->findInDirectories( + $directories, + $suffixes, + $hasOnlySuffix, + $onlySuffix, + $sortByName + ); + + $filePaths = [...$filteredFilePaths, ...$filteredFilePathsInDirectories]; + return $this->unchangedFilesFilter->filterFilePaths($filePaths); + } + + /** + * @param string[] $paths + * @return string[] + */ + public function findFilesInPaths(array $paths, Configuration $configuration): array + { + if ($configuration->shouldClearCache()) { + $this->changedFilesDetector->clear(); + } + + return $this->findInDirectoriesAndFiles( + $paths, + $configuration->getFileExtensions(), + true, + $configuration->getOnlySuffix(), + ); + } + + /** + * Exclude short "followLinks() ->files() // skip empty files ->size('> 0') - ->in($directories) - ->sortByName(); + ->in($directories); - if ($suffixes !== []) { + // filter files by specific suffix + if ($hasOnlySuffix) { + $finder->name('*' . $onlySuffix); + } elseif ($suffixes !== []) { $suffixesPattern = $this->normalizeSuffixesToPattern($suffixes); $finder->name($suffixesPattern); } - $this->addFilterWithExcludedPaths($finder); - - $smartFileInfos = $this->finderSanitizer->sanitize($finder); - - return $this->unchangedFilesFilter->filterAndJoinWithDependentFileInfos($smartFileInfos); - } - - /** - * @param string[] $suffixes - */ - private function normalizeSuffixesToPattern(array $suffixes): string - { - $suffixesPattern = implode('|', $suffixes); - return '#\.(' . $suffixesPattern . ')$#'; - } - - private function addFilterWithExcludedPaths(Finder $finder): void - { - $excludePaths = $this->skippedPathsResolver->resolve(); - if ($excludePaths === []) { - return; + if ($sortByName) { + $finder->sortByName(); } - $finder->filter(function (SplFileInfo $splFileInfo) use ($excludePaths): bool { - /** @var string|false $realPath */ - $realPath = $splFileInfo->getRealPath(); - if (! $realPath) { - //dead symlink - return false; - } + $filePaths = []; + foreach ($finder as $fileInfo) { + // getRealPath() function will return false when it checks broken symlinks. + // So we should check if this file exists or we got broken symlink - // make the path work accross different OSes - $realPath = str_replace('\\', '/', $realPath); - - // return false to remove file - foreach ($excludePaths as $excludePath) { - // make the path work accross different OSes - $excludePath = str_replace('\\', '/', $excludePath); + /** @var string|false $path */ + $path = $fileInfo->getRealPath(); + if ($path === false) { + continue; + } - if (Strings::match($realPath, '#' . preg_quote($excludePath, '#') . '#')) { - return false; - } + if ($this->pathSkipper->shouldSkip($path)) { + continue; + } - $excludePath = $this->normalizeForFnmatch($excludePath); - if (fnmatch($excludePath, $realPath)) { - return false; - } + if ($this->isStartWithShortPHPTag($fileInfo->getContents())) { + SimpleParameterProvider::addParameter( + Option::SKIPPED_START_WITH_SHORT_OPEN_TAG_FILES, + $this->filePathHelper->relativePath($path) + ); + continue; } - return true; - }); + $filePaths[] = $path; + } + + return $filePaths; } /** - * "value*" → "*value*" - * "*value" → "*value*" + * @param string[] $suffixes */ - private function normalizeForFnmatch(string $path): string + private function normalizeSuffixesToPattern(array $suffixes): string { - // ends with * - if (Strings::match($path, self::ENDS_WITH_ASTERISK_REGEX)) { - return '*' . $path; - } - - // starts with * - if (Strings::match($path, self::STARTS_WITH_ASTERISK_REGEX)) { - return $path . '*'; - } - - return $path; + $suffixesPattern = implode('|', $suffixes); + return '#\.(' . $suffixesPattern . ')$#'; } } diff --git a/src/FileSystem/FilesystemTweaker.php b/src/FileSystem/FilesystemTweaker.php index 0b4870bd940..07804e2efa3 100644 --- a/src/FileSystem/FilesystemTweaker.php +++ b/src/FileSystem/FilesystemTweaker.php @@ -2,41 +2,10 @@ declare(strict_types=1); -namespace Rector\Core\FileSystem; - -use Symplify\SmartFileSystem\FileSystemGuard; +namespace Rector\FileSystem; final class FilesystemTweaker { - public function __construct( - private FileSystemGuard $fileSystemGuard - ) { - } - - /** - * This will turn paths like "src/Symfony/Component/*\/Tests" to existing directory paths - * - * @param string[] $directories - * @return string[] - */ - public function resolveDirectoriesWithFnmatch(array $directories): array - { - $absoluteDirectories = []; - foreach ($directories as $directory) { - // is fnmatch for directories - if (\str_contains($directory, '*')) { - $foundDirectories = $this->findDirectoriesInGlob($directory); - $absoluteDirectories = array_merge($absoluteDirectories, $foundDirectories); - } else { - // is classic directory - $this->fileSystemGuard->ensureDirectoryExists($directory); - $absoluteDirectories[] = $directory; - } - } - - return $absoluteDirectories; - } - /** * This will turn paths like "src/Symfony/Component/*\/Tests" to existing directory paths * @@ -50,9 +19,9 @@ public function resolveWithFnmatch(array $paths): array foreach ($paths as $path) { if (\str_contains($path, '*')) { $foundPaths = $this->foundInGlob($path); - $absolutePathsFound = array_merge($absolutePathsFound, $foundPaths); + $absolutePathsFound = $this->appendPaths($foundPaths, $absolutePathsFound); } else { - $absolutePathsFound[] = $path; + $absolutePathsFound = $this->appendPaths([$path], $absolutePathsFound); } } @@ -60,21 +29,23 @@ public function resolveWithFnmatch(array $paths): array } /** + * @param string[] $foundPaths + * @param string[] $absolutePathsFound * @return string[] */ - private function findDirectoriesInGlob(string $directory): array + private function appendPaths(array $foundPaths, array $absolutePathsFound): array { - $foundDirectories = []; + foreach ($foundPaths as $foundPath) { + $foundPath = realpath($foundPath); - foreach ((array) glob($directory, GLOB_ONLYDIR) as $foundDirectory) { - if (! is_string($foundDirectory)) { + if ($foundPath === false) { continue; } - $foundDirectories[] = $foundDirectory; + $absolutePathsFound[] = $foundPath; } - return $foundDirectories; + return $absolutePathsFound; } /** @@ -82,20 +53,9 @@ private function findDirectoriesInGlob(string $directory): array */ private function foundInGlob(string $path): array { - $foundPaths = []; - - foreach ((array) glob($path) as $foundPath) { - if (! is_string($foundPath)) { - continue; - } - - if (! file_exists($foundPath)) { - continue; - } - - $foundPaths[] = $foundPath; - } + /** @var string[] $paths */ + $paths = (array) glob($path); - return $foundPaths; + return array_filter($paths, file_exists(...)); } } diff --git a/src/FileSystem/InitFilePathsResolver.php b/src/FileSystem/InitFilePathsResolver.php new file mode 100644 index 00000000000..69d51f4ce72 --- /dev/null +++ b/src/FileSystem/InitFilePathsResolver.php @@ -0,0 +1,58 @@ +directories() + ->depth(0) + // system files + ->notPath(self::DO_NOT_INCLUDE_PATHS_REGEX) + ->in($projectDirectory) + ->sortByName(); + + /** @var SplFileInfo[] $rootDirectoryFileInfos */ + $rootDirectoryFileInfos = iterator_to_array($rootDirectoryFinder); + + $projectDirectories = []; + + foreach ($rootDirectoryFileInfos as $rootDirectoryFileInfo) { + if (! $this->hasDirectoryFileInfoPhpFiles($rootDirectoryFileInfo)) { + continue; + } + + $projectDirectories[] = $rootDirectoryFileInfo->getRelativePathname(); + } + + return $projectDirectories; + } + + private function hasDirectoryFileInfoPhpFiles(SplFileInfo $rootDirectoryFileInfo): bool + { + // is directory with PHP files? + return Finder::create() + ->files() + ->in($rootDirectoryFileInfo->getPathname()) + ->name('*.php') + ->hasResults(); + } +} diff --git a/src/FileSystem/JsonFileSystem.php b/src/FileSystem/JsonFileSystem.php new file mode 100644 index 00000000000..052add9ba9a --- /dev/null +++ b/src/FileSystem/JsonFileSystem.php @@ -0,0 +1,30 @@ + + */ + public static function readFilePath(string $filePath): array + { + $fileContents = FileSystem::read($filePath); + + return Json::decode($fileContents, forceArrays: true); + } + + /** + * @param array $data + */ + public static function writeFile(string $filePath, array $data): void + { + $json = Json::encode($data, pretty: true); + FileSystem::write($filePath, $json, null); + } +} diff --git a/src/FileSystem/PhpFilesFinder.php b/src/FileSystem/PhpFilesFinder.php deleted file mode 100644 index f430ab1e44f..00000000000 --- a/src/FileSystem/PhpFilesFinder.php +++ /dev/null @@ -1,49 +0,0 @@ -filesFinder->findInDirectoriesAndFiles($paths); - - // filter out non-PHP files - foreach ($phpFileInfos as $key => $phpFileInfo) { - $pathName = $phpFileInfo->getPathname(); - foreach (self::NON_PHP_FILE_EXTENSIONS as $nonPHPFileExtension) { - if (str_ends_with($pathName, $nonPHPFileExtension)) { - unset($phpFileInfos[$key]); - continue 2; - } - } - } - - return $this->unchangedFilesFilter->filterAndJoinWithDependentFileInfos($phpFileInfos); - } -} diff --git a/src/Git/RepositoryHelper.php b/src/Git/RepositoryHelper.php new file mode 100644 index 00000000000..207ee28ad59 --- /dev/null +++ b/src/Git/RepositoryHelper.php @@ -0,0 +1,28 @@ +.*?)\.git#'; + + public static function resolveGithubRepositoryName(string $currentDirectory): ?string + { + // resolve current repository name + $process = new Process(['git', 'remote', 'get-url', 'origin'], $currentDirectory, null, null, null); + $process->run(); + + $output = $process->getOutput(); + + $match = Strings::match($output, self::GITHUB_REPOSITORY_REGEX); + return $match['repository_name'] ?? null; + } +} diff --git a/src/HttpKernel/RectorKernel.php b/src/HttpKernel/RectorKernel.php deleted file mode 100644 index 713c439a81d..00000000000 --- a/src/HttpKernel/RectorKernel.php +++ /dev/null @@ -1,131 +0,0 @@ -configureCallValuesCollector = new ConfigureCallValuesCollector(); - - parent::__construct($environment, $debug); - } - - public function getCacheDir(): string - { - $cacheDirectory = $_ENV['KERNEL_CACHE_DIRECTORY'] ?? null; - if ($cacheDirectory !== null) { - return $cacheDirectory . '/' . $this->environment; - } - - // manually configured, so it can be replaced in phar - return sys_get_temp_dir() . '/rector/cache'; - } - - public function getLogDir(): string - { - // manually configured, so it can be replaced in phar - return sys_get_temp_dir() . '/rector/log'; - } - - public function registerContainerConfiguration(LoaderInterface $loader): void - { - $loader->load(__DIR__ . '/../../config/config.php'); - foreach ($this->configFileInfos as $configFileInfo) { - $loader->load($configFileInfo->getRealPath()); - } - } - - /** - * @return iterable - */ - public function registerBundles(): iterable - { - return [ - new ConsoleColorDiffBundle(), - new ComposerJsonManipulatorBundle(), - new SkipperBundle(), - new SimplePhpDocParserBundle(), - ]; - } - - protected function build(ContainerBuilder $containerBuilder): void - { - // @see https://symfony.com/blog/new-in-symfony-4-4-dependency-injection-improvements-part-1 - $containerBuilder->setParameter('container.dumper.inline_factories', true); - // to fix reincluding files again - $containerBuilder->setParameter('container.dumper.inline_class_loader', false); - - // must run before AutowireArrayParameterCompilerPass, as the autowired array cannot contain removed services - $containerBuilder->addCompilerPass(new RemoveSkippedRectorsCompilerPass()); - $containerBuilder->addCompilerPass(new AutowireArrayParameterCompilerPass()); - - // autowire Rectors by default (mainly for tests) - $containerBuilder->addCompilerPass(new AutowireInterfacesCompilerPass([RectorInterface::class])); - $containerBuilder->addCompilerPass(new MakeRectorsPublicCompilerPass()); - - // add all merged arguments of Rector services - $containerBuilder->addCompilerPass( - new MergeImportedRectorConfigureCallValuesCompilerPass($this->configureCallValuesCollector) - ); - - $containerBuilder->addCompilerPass(new VerifyRectorServiceExistsCompilerPass()); - } - - /** - * This allows to use "%vendor%" variables in imports - * @param ContainerInterface|ContainerBuilder $container - */ - protected function getContainerLoader(ContainerInterface $container): DelegatingLoader - { - $fileLocator = new FileLocator($this); - - $loaderResolver = new LoaderResolver([ - new GlobFileLoader($fileLocator), - new ConfigurableCallValuesCollectingPhpFileLoader( - $container, - $fileLocator, - $this->configureCallValuesCollector - ), - ]); - - return new DelegatingLoader($loaderResolver); - } -} diff --git a/src/Logging/CurrentRectorProvider.php b/src/Logging/CurrentRectorProvider.php deleted file mode 100644 index b13bffa0857..00000000000 --- a/src/Logging/CurrentRectorProvider.php +++ /dev/null @@ -1,22 +0,0 @@ -currentRector = $rector; - } - - public function getCurrentRector(): ?RectorInterface - { - return $this->currentRector; - } -} diff --git a/src/NodeAnalyzer/ArgsAnalyzer.php b/src/NodeAnalyzer/ArgsAnalyzer.php new file mode 100644 index 00000000000..d5ff196b867 --- /dev/null +++ b/src/NodeAnalyzer/ArgsAnalyzer.php @@ -0,0 +1,68 @@ +name instanceof Identifier) { + return true; + } + } + + return false; + } + + /** + * @param Arg[] $args + */ + public function resolveArgPosition(array $args, string $name, int $defaultPosition): int + { + foreach ($args as $position => $arg) { + if (! $arg->name instanceof Identifier) { + continue; + } + + if (! $this->nodeNameResolver->isName($arg->name, $name)) { + continue; + } + + return $position; + } + + return $defaultPosition; + } + + /** + * @param Arg[] $args + */ + public function resolveFirstNamedArgPosition(array $args): ?int + { + $position = 0; + foreach ($args as $arg) { + if ($arg->name instanceof Identifier) { + return $position; + } + + ++$position; + } + + return null; + } +} diff --git a/src/NodeAnalyzer/BinaryOpAnalyzer.php b/src/NodeAnalyzer/BinaryOpAnalyzer.php new file mode 100644 index 00000000000..5fb78b5e23a --- /dev/null +++ b/src/NodeAnalyzer/BinaryOpAnalyzer.php @@ -0,0 +1,39 @@ +left instanceof FuncCall) { + if (! $this->nodeNameResolver->isName($binaryOp->left, $funcCallName)) { + return null; + } + + return new FuncCallAndExpr($binaryOp->left, $binaryOp->right); + } + + if ($binaryOp->right instanceof FuncCall) { + if (! $this->nodeNameResolver->isName($binaryOp->right, $funcCallName)) { + return null; + } + + return new FuncCallAndExpr($binaryOp->right, $binaryOp->left); + } + + return null; + } +} diff --git a/src/NodeAnalyzer/CallAnalyzer.php b/src/NodeAnalyzer/CallAnalyzer.php index bc2c2d45ba7..641f6fee790 100644 --- a/src/NodeAnalyzer/CallAnalyzer.php +++ b/src/NodeAnalyzer/CallAnalyzer.php @@ -2,31 +2,30 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; -use PhpParser\Node; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BooleanNot; -use PhpParser\Node\Expr\Clone_; use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\If_; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Type\ObjectType; +use Rector\NodeTypeResolver\Node\AttributeKey; -final class CallAnalyzer +final readonly class CallAnalyzer { /** * @var array> */ - private const OBJECT_CALL_TYPES = [MethodCall::class, NullsafeMethodCall::class, StaticCall::class]; + private const array OBJECT_CALL_TYPES = [MethodCall::class, NullsafeMethodCall::class, StaticCall::class]; public function __construct( - private NodeComparator $nodeComparator + private ReflectionProvider $reflectionProvider ) { } @@ -44,7 +43,7 @@ public function isObjectCall(Expr $expr): bool } foreach (self::OBJECT_CALL_TYPES as $objectCallType) { - if (is_a($expr, $objectCallType, true)) { + if ($expr instanceof $objectCallType) { return true; } } @@ -66,25 +65,25 @@ public function doesIfHasObjectCall(array $ifs): bool return false; } - /** - * Inject BetterNodeFinder due Circular reference - */ - public function isNewInstance(BetterNodeFinder $betterNodeFinder, Expr $expr): bool + public function isNewInstance(Variable $variable): bool { - if ($expr instanceof Clone_ || $expr instanceof New_) { - return true; + $scope = $variable->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return false; } - return (bool) $betterNodeFinder->findFirstPreviousOfNode($expr, function (Node $node) use ($expr): bool { - if (! $node instanceof Assign) { - return false; - } + $type = $scope->getNativeType($variable); + if (! $type instanceof ObjectType) { + return false; + } - if (! $this->nodeComparator->areNodesEqual($node->var, $expr)) { - return false; - } + $className = $type->getClassName(); + if (! $this->reflectionProvider->hasClass($className)) { + return false; + } - return $node->expr instanceof Clone_ || $node->expr instanceof New_; - }); + $classReflection = $this->reflectionProvider->getClass($className); + return $classReflection->getNativeReflection() + ->isInstantiable(); } } diff --git a/src/NodeAnalyzer/CallLikeExpectsThisBoundClosureArgsAnalyzer.php b/src/NodeAnalyzer/CallLikeExpectsThisBoundClosureArgsAnalyzer.php new file mode 100644 index 00000000000..a5039df661c --- /dev/null +++ b/src/NodeAnalyzer/CallLikeExpectsThisBoundClosureArgsAnalyzer.php @@ -0,0 +1,91 @@ +isFirstClassCallable() || $callLike->getArgs() === []) { + return []; + } + + $callArgs = $callLike->getArgs(); + $hasClosureArg = (bool) array_filter($callArgs, fn (Arg $arg): bool => $arg->value instanceof Closure); + + if (! $hasClosureArg) { + return []; + } + + $argsUsingThisBoundClosure = []; + + $reflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($callLike); + + if ($reflection === null) { + return []; + } + + $scope = $callLike->getAttribute(AttributeKey::SCOPE); + + if ($scope === null) { + return []; + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($reflection, $callLike, $scope); + $parameters = $parametersAcceptor->getParameters(); + + foreach ($callArgs as $index => $arg) { + if (! $arg->value instanceof Closure) { + continue; + } + + if ($arg->name?->name !== null) { + foreach ($parameters as $parameter) { + if (! $parameter instanceof ExtendedParameterReflection) { + continue; + } + + $hasObjectBinding = (bool) $parameter->getClosureThisType(); + if ($hasObjectBinding && $arg->name->name === $parameter->getName()) { + $argsUsingThisBoundClosure[] = $arg; + } + } + + continue; + } + + if (! is_string($arg->name?->name)) { + $parameter = $parameters[$index] ?? null; + + if (! $parameter instanceof ExtendedParameterReflection) { + continue; + } + + $hasObjectBinding = (bool) $parameter->getClosureThisType(); + if ($hasObjectBinding) { + $argsUsingThisBoundClosure[] = $arg; + } + } + } + + return $argsUsingThisBoundClosure; + } +} diff --git a/src/NodeAnalyzer/ChangedNodeAnalyzer.php b/src/NodeAnalyzer/ChangedNodeAnalyzer.php deleted file mode 100644 index ac5bde4ee38..00000000000 --- a/src/NodeAnalyzer/ChangedNodeAnalyzer.php +++ /dev/null @@ -1,55 +0,0 @@ -isNameIdentical($node, $originalNode)) { - return false; - } - - // @see https://github.com/rectorphp/rector/issues/6169 - special check, as php-parser skips brackets - if ($node instanceof Encapsed) { - foreach ($node->parts as $encapsedPart) { - $originalEncapsedPart = $encapsedPart->getAttribute(AttributeKey::ORIGINAL_NODE); - if ($originalEncapsedPart === null) { - return true; - } - } - } - - // php-parser has no idea about changed docblocks, so to report it correctly, we have to set up this attribute - if ($node->hasAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED)) { - $node->setAttribute(AttributeKey::HAS_PHP_DOC_INFO_JUST_CHANGED, null); - return true; - } - - return ! $this->nodeComparator->areNodesEqual($originalNode, $node); - } - - private function isNameIdentical(Node $node, Node $originalNode): bool - { - if (! $originalNode instanceof Name) { - return false; - } - - // names are the same - $originalName = $originalNode->getAttribute(AttributeKey::ORIGINAL_NAME); - return $this->nodeComparator->areNodesEqual($originalName, $node); - } -} diff --git a/src/NodeAnalyzer/ClassAnalyzer.php b/src/NodeAnalyzer/ClassAnalyzer.php index bceed963921..f4ddb6fc666 100644 --- a/src/NodeAnalyzer/ClassAnalyzer.php +++ b/src/NodeAnalyzer/ClassAnalyzer.php @@ -2,49 +2,24 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; -use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Expr\New_; use PhpParser\Node\Stmt\Class_; -use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; final class ClassAnalyzer { - /** - * @var string - * @see https://regex101.com/r/FQH6RT/1 - */ - private const ANONYMOUS_CLASS_REGEX = '#AnonymousClass\w+$#'; - - public function __construct( - private NodeNameResolver $nodeNameResolver - ) { - } - public function isAnonymousClass(Node $node): bool { - if (! $node instanceof Class_) { - return false; - } - - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof New_) { - return false; - } - - if ($node->isAnonymous()) { - return true; + if ($node instanceof New_) { + return $this->isAnonymousClass($node->class); } - $className = $this->nodeNameResolver->getName($node); - if ($className === null) { - return true; + if ($node instanceof Class_) { + return $node->isAnonymous(); } - // match PHPStan pattern for anonymous classes - return (bool) Strings::match($className, self::ANONYMOUS_CLASS_REGEX); + return false; } } diff --git a/src/NodeAnalyzer/CoalesceAnalyzer.php b/src/NodeAnalyzer/CoalesceAnalyzer.php deleted file mode 100644 index 1fa0695fbc4..00000000000 --- a/src/NodeAnalyzer/CoalesceAnalyzer.php +++ /dev/null @@ -1,31 +0,0 @@ -> - */ - private const ISSETABLE_EXPR = [ - Variable::class, - ArrayDimFetch::class, - PropertyFetch::class, - StaticPropertyFetch::class, - ]; - - public function hasIssetableLeft(Coalesce $coalesce): bool - { - $leftClass = $coalesce->left::class; - return in_array($leftClass, self::ISSETABLE_EXPR, true); - } -} diff --git a/src/NodeAnalyzer/CompactFuncCallAnalyzer.php b/src/NodeAnalyzer/CompactFuncCallAnalyzer.php index c5037bcb877..000c8a5ac24 100644 --- a/src/NodeAnalyzer/CompactFuncCallAnalyzer.php +++ b/src/NodeAnalyzer/CompactFuncCallAnalyzer.php @@ -2,17 +2,18 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; use PhpParser\Node\Arg; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Scalar\String_; +use PhpParser\Node\VariadicPlaceholder; use Rector\NodeNameResolver\NodeNameResolver; -final class CompactFuncCallAnalyzer +final readonly class CompactFuncCallAnalyzer { public function __construct( private NodeNameResolver $nodeNameResolver @@ -25,24 +26,24 @@ public function isInCompact(FuncCall $funcCall, Variable $variable): bool return false; } - $variableName = $variable->name; - if (! is_string($variableName)) { + if (! is_string($variable->name)) { return false; } - return $this->isInArgOrArrayItemNodes($funcCall->args, $variableName); + return $this->isInArgOrArrayItemNodes($funcCall->args, $variable->name); } /** - * @param array $nodes + * @param array $nodes */ private function isInArgOrArrayItemNodes(array $nodes, string $variableName): bool { foreach ($nodes as $node) { - if ($node === null) { + if ($this->shouldSkip($node)) { continue; } + /** @var Arg|ArrayItem $node */ if ($node->value instanceof Array_) { if ($this->isInArgOrArrayItemNodes($node->value->items, $variableName)) { return true; @@ -62,4 +63,13 @@ private function isInArgOrArrayItemNodes(array $nodes, string $variableName): bo return false; } + + private function shouldSkip(Arg|VariadicPlaceholder|ArrayItem|null $node): bool + { + if ($node === null) { + return true; + } + + return $node instanceof VariadicPlaceholder; + } } diff --git a/src/NodeAnalyzer/ConstFetchAnalyzer.php b/src/NodeAnalyzer/ConstFetchAnalyzer.php index 60d12a4e470..9edef3c82a4 100644 --- a/src/NodeAnalyzer/ConstFetchAnalyzer.php +++ b/src/NodeAnalyzer/ConstFetchAnalyzer.php @@ -2,9 +2,10 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ConstFetch; /** @@ -13,27 +14,28 @@ */ final class ConstFetchAnalyzer { - public function isTrueOrFalse(Node $node): bool + public function isTrueOrFalse(Expr $expr): bool { - if ($this->isTrue($node)) { + if ($this->isTrue($expr)) { return true; } - return $this->isFalse($node); + + return $this->isFalse($expr); } - public function isFalse(Node $node): bool + public function isFalse(Expr $expr): bool { - return $this->isConstantWithLowercasedName($node, 'false'); + return $this->isConstantWithLowercasedName($expr, 'false'); } - public function isTrue(Node $node): bool + public function isTrue(Expr $expr): bool { - return $this->isConstantWithLowercasedName($node, 'true'); + return $this->isConstantWithLowercasedName($expr, 'true'); } - public function isNull(Node $node): bool + public function isNull(Expr $expr): bool { - return $this->isConstantWithLowercasedName($node, 'null'); + return $this->isConstantWithLowercasedName($expr, 'null'); } private function isConstantWithLowercasedName(Node $node, string $name): bool diff --git a/src/NodeAnalyzer/DoctrineEntityAnalyzer.php b/src/NodeAnalyzer/DoctrineEntityAnalyzer.php new file mode 100644 index 00000000000..5f3c18cd402 --- /dev/null +++ b/src/NodeAnalyzer/DoctrineEntityAnalyzer.php @@ -0,0 +1,62 @@ +phpDocInfoFactory->createFromNode($class); + if (! $phpDocInfo instanceof PhpDocInfo) { + return false; + } + + return $phpDocInfo->hasByAnnotationClasses(self::DOCTRINE_MAPPING_CLASSES); + } + + public function hasClassReflectionAttribute(ClassReflection $classReflection): bool + { + /** @var ReflectionClass $nativeReflectionClass */ + $nativeReflectionClass = $classReflection->getNativeReflection(); + + // skip early in case of no attributes at all + if ($nativeReflectionClass->getAttributes() === []) { + return false; + } + + foreach (self::DOCTRINE_MAPPING_CLASSES as $doctrineMappingClass) { + // skip entities + if ($nativeReflectionClass->getAttributes($doctrineMappingClass) !== []) { + return true; + } + } + + return false; + } +} diff --git a/src/NodeAnalyzer/EnumAnalyzer.php b/src/NodeAnalyzer/EnumAnalyzer.php deleted file mode 100644 index 1f98d11fc2b..00000000000 --- a/src/NodeAnalyzer/EnumAnalyzer.php +++ /dev/null @@ -1,35 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return false; - } - - if ($classLike->extends === null) { - return false; - } - - return $this->nodeNameResolver->isName($classLike->extends, '*Enum'); - } -} diff --git a/src/NodeAnalyzer/ExprAnalyzer.php b/src/NodeAnalyzer/ExprAnalyzer.php new file mode 100644 index 00000000000..7eeb4005cf7 --- /dev/null +++ b/src/NodeAnalyzer/ExprAnalyzer.php @@ -0,0 +1,250 @@ +getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return false; + } + + $nativeType = $scope->getNativeType($expr); + return $nativeType->isBoolean() + ->yes(); + } + + /** + * Verify that Expr has ->expr property that can be wrapped by parentheses + */ + public function isExprWithExprPropertyWrappable(Node $node): bool + { + if (! $node instanceof Expr) { + return false; + } + + // ensure only verify on reprint, using token start verification is more reliable for its check + if ($node->getStartTokenPos() > 0) { + return false; + } + + if ( + $node instanceof Cast || + $node instanceof YieldFrom || + $node instanceof UnaryMinus || + $node instanceof UnaryPlus || + $node instanceof Throw_ || + $node instanceof Empty_ || + $node instanceof BooleanNot || + $node instanceof Clone_ || + $node instanceof ErrorSuppress || + $node instanceof BitwiseNot || + $node instanceof Eval_ || + $node instanceof Print_ || + $node instanceof Exit_ || + $node instanceof Include_ || + $node instanceof Instanceof_) { + return $node->expr instanceof BinaryOp; + } + + return false; + } + + public function isNonTypedFromParam(Expr $expr): bool + { + if (! $expr instanceof Variable) { + return false; + } + + $scope = $expr->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + // uncertainty when scope not yet filled/overlapped on just refactored + return true; + } + + $nativeType = $scope->getNativeType($expr); + $type = $scope->getType($expr); + if ( + ($nativeType instanceof MixedType && ! $nativeType->isExplicitMixed()) + || + ($nativeType instanceof MixedType && ! $type instanceof MixedType) + ) { + return true; + } + + if ($nativeType instanceof ObjectWithoutClassType && ! $type instanceof ObjectWithoutClassType) { + return true; + } + + if ($nativeType instanceof UnionType) { + return ! $nativeType->equals($type); + } + + return ! $nativeType->isSuperTypeOf($type) + ->yes(); + } + + public function isDynamicExpr(Expr $expr): bool + { + // Unwrap UnaryPlus and UnaryMinus + if ($expr instanceof UnaryPlus || $expr instanceof UnaryMinus) { + $expr = $expr->expr; + } + + if ($expr instanceof Array_) { + return $this->isDynamicArray($expr); + } + + if ($expr instanceof Scalar) { + // string interpolation is true, otherwise false + return $expr instanceof InterpolatedString; + } + + return ! $this->isAllowedConstFetchOrClassConstFetch($expr); + } + + public function isDynamicArray(Array_ $array): bool + { + foreach ($array->items as $item) { + if (! $item instanceof ArrayItem) { + continue; + } + + if (! $this->isAllowedArrayKey($item->key)) { + return true; + } + + if (! $this->isAllowedArrayValue($item->value)) { + return true; + } + } + + return false; + } + + private function isAllowedConstFetchOrClassConstFetch(Expr $expr): bool + { + if ($expr instanceof ConstFetch) { + return true; + } + + if ($expr instanceof ClassConstFetch) { + if (! $expr->class instanceof Name) { + return false; + } + + if (! $expr->name instanceof Identifier) { + return false; + } + + // static::class cannot be used for compile-time class name resolution + return $expr->class->toString() !== ObjectReference::STATIC; + } + + return false; + } + + private function isAllowedArrayKey(?Expr $expr): bool + { + if (! $expr instanceof Expr) { + return true; + } + + if ($expr instanceof String_) { + return true; + } + + return $expr instanceof Int_; + } + + private function isAllowedArrayValue(Expr $expr): bool + { + if ($expr instanceof Array_) { + return ! $this->isDynamicArray($expr); + } + + return ! $this->isDynamicExpr($expr); + } +} diff --git a/src/NodeAnalyzer/InlineHTMLAnalyzer.php b/src/NodeAnalyzer/InlineHTMLAnalyzer.php deleted file mode 100644 index 771e3928025..00000000000 --- a/src/NodeAnalyzer/InlineHTMLAnalyzer.php +++ /dev/null @@ -1,27 +0,0 @@ -betterNodeFinder->findInstanceOf((array) $node->getStmts(), InlineHTML::class); - } - - return (bool) $this->betterNodeFinder->findInstanceOf($node, InlineHTML::class); - } -} diff --git a/src/NodeAnalyzer/MagicClassMethodAnalyzer.php b/src/NodeAnalyzer/MagicClassMethodAnalyzer.php new file mode 100644 index 00000000000..537c7ca15ab --- /dev/null +++ b/src/NodeAnalyzer/MagicClassMethodAnalyzer.php @@ -0,0 +1,26 @@ +nodeNameResolver->isName($classMethod, MethodName::INVOKE)) { + return false; + } + + return $classMethod->isMagic(); + } +} diff --git a/src/NodeAnalyzer/ParamAnalyzer.php b/src/NodeAnalyzer/ParamAnalyzer.php index 7b412d7e65b..779a125fd85 100644 --- a/src/NodeAnalyzer/ParamAnalyzer.php +++ b/src/NodeAnalyzer/ParamAnalyzer.php @@ -2,38 +2,99 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; use PhpParser\Node; -use PhpParser\Node\Expr\ConstFetch; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\CallLike; +use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\Error; +use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\Variable; use PhpParser\Node\NullableType; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\Node\Value\ValueResolver; +use PhpParser\Node\Stmt\Function_; +use PhpParser\NodeVisitor; +use Rector\NodeManipulator\FuncCallManipulator; +use Rector\NodeNameResolver\NodeNameResolver; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Node\BetterNodeFinder; -final class ParamAnalyzer +final readonly class ParamAnalyzer { + /** + * @var string[] + */ + private const array VARIADIC_FUNCTION_NAMES = [ + 'func_get_arg', + 'func_get_args', + 'func_num_args', + 'get_defined_vars', + ]; + public function __construct( - private BetterNodeFinder $betterNodeFinder, private NodeComparator $nodeComparator, - private ValueResolver $valueResolver + private NodeNameResolver $nodeNameResolver, + private FuncCallManipulator $funcCallManipulator, + private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, + private BetterNodeFinder $betterNodeFinder ) { } public function isParamUsedInClassMethod(ClassMethod $classMethod, Param $param): bool { - return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node) use ( + if ($param->isPromoted()) { + return true; + } + + $isParamUsed = false; + + if ($param->var instanceof Error) { + return false; + } + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod->stmts, function (Node $node) use ( + &$isParamUsed, $param - ): bool { - if (! $node instanceof Variable) { - return false; + ): ?int { + if ($this->isVariadicFuncCall($node)) { + $isParamUsed = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + if ($this->isUsedAsArg($node, $param)) { + $isParamUsed = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + // skip nested anonymous class + if ($node instanceof Class_ || $node instanceof Function_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($node instanceof Variable && $this->nodeComparator->areNodesEqual($node, $param->var)) { + $isParamUsed = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + if ($node instanceof Closure && $this->isVariableInClosureUses($node, $param->var)) { + $isParamUsed = true; + return NodeVisitor::STOP_TRAVERSAL; + } + + if ($this->isParamUsed($node, $param)) { + $isParamUsed = true; + return NodeVisitor::STOP_TRAVERSAL; } - return $this->nodeComparator->areNodesEqual($node, $param->var); + return null; }); + + return $isParamUsed; } /** @@ -42,7 +103,7 @@ public function isParamUsedInClassMethod(ClassMethod $classMethod, Param $param) public function hasPropertyPromotion(array $params): bool { foreach ($params as $param) { - if ($param->flags !== 0) { + if ($param->isPromoted()) { return true; } } @@ -56,15 +117,79 @@ public function isNullable(Param $param): bool return false; } - if ($param->type === null) { + if (! $param->type instanceof Node) { return false; } return $param->type instanceof NullableType; } - public function hasDefaultNull(Param $param): bool + public function isParamReassign(ClassMethod $classMethod, Param $param): bool { - return $param->default instanceof ConstFetch && $this->valueResolver->isNull($param->default); + $paramName = $this->nodeNameResolver->getName($param); + return (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped($classMethod, function (Node $node) use ( + $paramName + ): bool { + if (! $node instanceof Assign) { + return false; + } + + if (! $node->var instanceof Variable) { + return false; + } + + return $this->nodeNameResolver->isName($node->var, $paramName); + }); + } + + private function isVariableInClosureUses(Closure $closure, Variable $variable): bool + { + foreach ($closure->uses as $use) { + if ($this->nodeComparator->areNodesEqual($use->var, $variable)) { + return true; + } + } + + return false; + } + + private function isUsedAsArg(Node $node, Param $param): bool + { + if ($node instanceof New_ || $node instanceof CallLike) { + if ($node->isFirstClassCallable()) { + return false; + } + + foreach ($node->getArgs() as $arg) { + if ($this->nodeComparator->areNodesEqual($param->var, $arg->value)) { + return true; + } + } + } + + return false; + } + + private function isParamUsed(Node $node, Param $param): bool + { + if (! $node instanceof FuncCall) { + return false; + } + + if (! $this->nodeNameResolver->isName($node, 'compact')) { + return false; + } + + $arguments = $this->funcCallManipulator->extractArgumentsFromCompactFuncCalls([$node]); + return $this->nodeNameResolver->isNames($param, $arguments); + } + + private function isVariadicFuncCall(Node $node): bool + { + if (! $node instanceof FuncCall) { + return false; + } + + return $this->nodeNameResolver->isNames($node, self::VARIADIC_FUNCTION_NAMES); } } diff --git a/src/NodeAnalyzer/PromotedPropertyParamCleaner.php b/src/NodeAnalyzer/PromotedPropertyParamCleaner.php deleted file mode 100644 index 044099edaf7..00000000000 --- a/src/NodeAnalyzer/PromotedPropertyParamCleaner.php +++ /dev/null @@ -1,26 +0,0 @@ -flags = 0; - $cleanParams[] = $cleanParam; - } - - return $cleanParams; - } -} diff --git a/src/NodeAnalyzer/PropertyAnalyzer.php b/src/NodeAnalyzer/PropertyAnalyzer.php new file mode 100644 index 00000000000..ecbf9dfec96 --- /dev/null +++ b/src/NodeAnalyzer/PropertyAnalyzer.php @@ -0,0 +1,64 @@ +nodeTypeResolver->getType($property); + if ($propertyType->isNull()->yes()) { + return true; + } + + if ($this->isForbiddenType($propertyType)) { + return true; + } + + if (! $propertyType instanceof UnionType) { + return false; + } + + $types = $propertyType->getTypes(); + foreach ($types as $type) { + if ($this->isForbiddenType($type)) { + return true; + } + } + + return false; + } + + public function isForbiddenType(Type $type): bool + { + if ($type instanceof NonExistingObjectType) { + return true; + } + + return $this->isCallableType($type); + } + + private function isCallableType(Type $type): bool + { + if (ClassNameFromObjectTypeResolver::resolve($type) === 'Closure') { + return false; + } + + return $type->isCallable() + ->yes(); + } +} diff --git a/src/NodeAnalyzer/PropertyFetchAnalyzer.php b/src/NodeAnalyzer/PropertyFetchAnalyzer.php index 2681b709979..9c612d652ed 100644 --- a/src/NodeAnalyzer/PropertyFetchAnalyzer.php +++ b/src/NodeAnalyzer/PropertyFetchAnalyzer.php @@ -2,98 +2,143 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\NullsafePropertyFetch; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Param; -use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Name; +use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\ValueObject\MethodName; +use PhpParser\Node\Stmt\Trait_; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Type\ObjectType; +use PHPStan\Type\StaticType; +use PHPStan\Type\ThisType; +use Rector\Enum\ObjectReference; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; - -final class PropertyFetchAnalyzer +use Rector\NodeNestingScope\ContextAnalyzer; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\AstResolver; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\MethodName; + +final readonly class PropertyFetchAnalyzer { + private const string THIS = 'this'; + public function __construct( private NodeNameResolver $nodeNameResolver, private BetterNodeFinder $betterNodeFinder, - private NodeComparator $nodeComparator + private AstResolver $astResolver, + private NodeTypeResolver $nodeTypeResolver, + private ReflectionResolver $reflectionResolver, + private ContextAnalyzer $contextAnalyzer ) { } public function isLocalPropertyFetch(Node $node): bool { - if ($node instanceof PropertyFetch) { - if ($node->var instanceof MethodCall) { - return false; + if ( + ! $node instanceof PropertyFetch + && ! $node instanceof StaticPropertyFetch + && ! $node instanceof NullsafePropertyFetch + ) { + return false; + } + + $variableType = $node instanceof StaticPropertyFetch + ? $this->nodeTypeResolver->getType($node->class) + : $this->nodeTypeResolver->getType($node->var); + + // patch clone usage + // @see https://github.com/phpstan/phpstan-src/commit/020adb548011c098cdb2e061019346b0a838c6a4 + // @see https://github.com/rectorphp/rector-src/pull/7622 + if ($variableType instanceof StaticType && ! $variableType instanceof ThisType) { + $variableType = $variableType->getStaticObjectType(); + } + + if ($variableType instanceof ObjectType) { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if ($classReflection instanceof ClassReflection) { + return $classReflection->getName() === $variableType->getClassName(); } - return $this->nodeNameResolver->isName($node->var, 'this'); + return false; } - if ($node instanceof StaticPropertyFetch) { - return $this->nodeNameResolver->isName($node->class, 'self'); + if (! $variableType instanceof ThisType) { + return $this->isTraitLocalPropertyFetch($node); } - return false; + return true; } public function isLocalPropertyFetchName(Node $node, string $desiredPropertyName): bool { - if (! $this->isLocalPropertyFetch($node)) { + if ( + ! $node instanceof PropertyFetch + && ! $node instanceof StaticPropertyFetch + && ! $node instanceof NullsafePropertyFetch + ) { return false; } - /** @var PropertyFetch|StaticPropertyFetch $node */ - return $this->nodeNameResolver->isName($node->name, $desiredPropertyName); + if (! $this->nodeNameResolver->isName($node->name, $desiredPropertyName)) { + return false; + } + + return $this->isLocalPropertyFetch($node); } - public function containsLocalPropertyFetchName(ClassMethod $classMethod, string $propertyName): bool + public function containsLocalPropertyFetchName(Trait_ $trait, string $propertyName): bool { - return (bool) $this->betterNodeFinder->findFirst($classMethod, function (Node $node) use ($propertyName): bool { - if (! $node instanceof PropertyFetch) { - return false; - } + if ($trait->getProperty($propertyName) instanceof Property) { + return true; + } - return $this->nodeNameResolver->isName($node->name, $propertyName); - }); + return (bool) $this->betterNodeFinder->findFirst( + $trait, + fn (Node $node): bool => $this->isLocalPropertyFetchName($node, $propertyName) + ); } - public function isPropertyToSelf(PropertyFetch | StaticPropertyFetch $expr): bool + public function containsWrittenPropertyFetchName(Trait_ $trait, string $propertyName): bool { - if ($expr instanceof PropertyFetch && ! $this->nodeNameResolver->isName($expr->var, 'this')) { - return false; + if ($trait->getProperty($propertyName) instanceof Property) { + return true; } - if ($expr instanceof StaticPropertyFetch && ! $this->nodeNameResolver->isName($expr->class, 'self')) { - return false; - } + return (bool) $this->betterNodeFinder->findFirst( + $trait, + function (Node $node) use ($propertyName): bool { + if (! $this->isLocalPropertyFetchName($node, $propertyName)) { + return false; + } - $classLike = $expr->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return false; - } + /** + * @var PropertyFetch|StaticPropertyFetch|NullsafePropertyFetch $node + */ + if ($this->contextAnalyzer->isChangeableContext($node)) { + return true; + } - foreach ($classLike->getProperties() as $property) { - if (! $this->nodeNameResolver->areNamesEqual($property->props[0], $expr)) { - continue; + return $this->contextAnalyzer->isLeftPartOfAssign($node); } - - return true; - } - - return false; + ); } + /** + * @phpstan-assert-if-true PropertyFetch|StaticPropertyFetch $node + */ public function isPropertyFetch(Node $node): bool { if ($node instanceof PropertyFetch) { @@ -107,97 +152,118 @@ public function isPropertyFetch(Node $node): bool * Matches: * "$this->someValue = $;" */ - public function isVariableAssignToThisPropertyFetch(Node $node, string $variableName): bool + public function isVariableAssignToThisPropertyFetch(Assign $assign, string $variableName): bool { - if (! $node instanceof Assign) { + if (! $assign->expr instanceof Variable) { return false; } - if (! $node->expr instanceof Variable) { + if (! $this->nodeNameResolver->isName($assign->expr, $variableName)) { return false; } - if (! $this->nodeNameResolver->isName($node->expr, $variableName)) { - return false; - } - - return $this->isLocalPropertyFetch($node->var); + return $this->isLocalPropertyFetch($assign->var); } - public function isFilledByConstructParam(Property $property): bool + public function isFilledViaMethodCallInConstructStmts(ClassLike $classLike, string $propertyName): bool { - $class = $property->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { - return false; - } - - $classMethod = $class->getMethod(MethodName::CONSTRUCT); + $classMethod = $classLike->getMethod(MethodName::CONSTRUCT); if (! $classMethod instanceof ClassMethod) { return false; } - $params = $classMethod->params; - if ($params === []) { - return false; - } - + $className = (string) $this->nodeNameResolver->getName($classLike); $stmts = (array) $classMethod->stmts; - if ($stmts === []) { - return false; - } - /** @var string $propertyName */ - $propertyName = $this->nodeNameResolver->getName($property->props[0]->name); - $kindPropertyFetch = $property->isStatic() - ? StaticPropertyFetch::class - : PropertyFetch::class; + foreach ($stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } - return $this->isParamFilledStmts($params, $stmts, $propertyName, $kindPropertyFetch); + if (! $stmt->expr instanceof MethodCall && ! $stmt->expr instanceof StaticCall) { + continue; + } + + $callerClassMethod = $this->astResolver->resolveClassMethodFromCall($stmt->expr); + if (! $callerClassMethod instanceof ClassMethod) { + continue; + } + + $callerClassReflection = $this->reflectionResolver->resolveClassReflection($callerClassMethod); + if (! $callerClassReflection instanceof ClassReflection) { + continue; + } + + if (! $callerClassReflection->isClass()) { + continue; + } + + $callerClassName = $callerClassReflection->getName(); + $isFound = $this->isPropertyAssignFoundInClassMethod( + $classLike, + $className, + $callerClassName, + $callerClassMethod, + $propertyName + ); + if ($isFound) { + return true; + } + } + + return false; } - /** - * @param string[] $propertyNames - */ - public function isLocalPropertyOfNames(Node $node, array $propertyNames): bool + private function isTraitLocalPropertyFetch(Node $node): bool { - if (! $this->isLocalPropertyFetch($node)) { - return false; + if ($node instanceof PropertyFetch) { + if (! $node->var instanceof Variable) { + return false; + } + + return $this->nodeNameResolver->isName($node->var, self::THIS); } - /** @var PropertyFetch $node */ - return $this->nodeNameResolver->isNames($node->name, $propertyNames); + if ($node instanceof StaticPropertyFetch) { + if (! $node->class instanceof Name) { + return false; + } + + return $this->nodeNameResolver->isNames($node->class, [ + ObjectReference::SELF, + ObjectReference::STATIC, + ]); + } + + return false; } - /** - * @param Param[] $params - * @param Stmt[] $stmts - */ - private function isParamFilledStmts( - array $params, - array $stmts, - string $propertyName, - string $kindPropertyFetch + private function isPropertyAssignFoundInClassMethod( + ClassLike $classLike, + string $className, + string $callerClassName, + ClassMethod $classMethod, + string $propertyName ): bool { - foreach ($params as $param) { - $paramVariable = $param->var; - $isAssignWithParamVarName = $this->betterNodeFinder->findFirst($stmts, function (Node $node) use ( - $propertyName, - $paramVariable, - $kindPropertyFetch - ): bool { - if (! $node instanceof Assign) { - return false; - } - if ($kindPropertyFetch !== $node->var::class) { - return false; - } - if (! $this->nodeNameResolver->isName($node->var, $propertyName)) { - return false; - } - return $this->nodeComparator->areNodesEqual($node->expr, $paramVariable); - }); + if ($className !== $callerClassName && ! $classLike instanceof Trait_) { + $objectType = new ObjectType($className); + $callerObjectType = new ObjectType($callerClassName); + + if (! $callerObjectType->isSuperTypeOf($objectType)->yes()) { + return false; + } + } + + foreach ((array) $classMethod->stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof Assign) { + continue; + } - if ($isAssignWithParamVarName !== null) { + if ($this->isLocalPropertyFetchName($stmt->expr->var, $propertyName)) { return true; } } diff --git a/src/NodeAnalyzer/PropertyPresenceChecker.php b/src/NodeAnalyzer/PropertyPresenceChecker.php index 55672c4cd87..434c47a6865 100644 --- a/src/NodeAnalyzer/PropertyPresenceChecker.php +++ b/src/NodeAnalyzer/PropertyPresenceChecker.php @@ -2,151 +2,58 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Property; -use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Type\TypeWithClassName; -use Rector\Core\PhpParser\AstResolver; +use Rector\CodeQuality\ValueObject\DefinedPropertyWithType; use Rector\NodeNameResolver\NodeNameResolver; use Rector\Php80\NodeAnalyzer\PromotedPropertyResolver; use Rector\PostRector\ValueObject\PropertyMetadata; -use ReflectionNamedType; -use ReflectionProperty; /** * Can be local property, parent property etc. */ -final class PropertyPresenceChecker +final readonly class PropertyPresenceChecker { public function __construct( private PromotedPropertyResolver $promotedPropertyResolver, private NodeNameResolver $nodeNameResolver, - private ReflectionProvider $reflectionProvider, - private AstResolver $astResolver ) { } /** * Includes parent classes and traits */ - public function hasClassContextProperty(Class_ $class, PropertyMetadata $propertyMetadata): bool + public function hasClassContextProperty(Class_ $class, DefinedPropertyWithType $definedPropertyWithType): bool { - $propertyOrParam = $this->getClassContextProperty($class, $propertyMetadata); + $propertyOrParam = $this->getClassContextProperty($class, $definedPropertyWithType); return $propertyOrParam !== null; } - public function getClassContextProperty(Class_ $class, PropertyMetadata $propertyMetadata): Property | Param | null - { + public function getClassContextProperty( + Class_ $class, + DefinedPropertyWithType|PropertyMetadata $definedPropertyWithType + ): Property | Param | null { $className = $this->nodeNameResolver->getName($class); if ($className === null) { return null; } - if (! $this->reflectionProvider->hasClass($className)) { - return null; - } - - $property = $class->getProperty($propertyMetadata->getName()); + $property = $class->getProperty($definedPropertyWithType->getName()); if ($property instanceof Property) { return $property; } - $property = $this->matchPropertyByParentPublicOrProtectedProperties($className, $propertyMetadata); - if ($property instanceof Property || $property instanceof Param) { - return $property; - } - $promotedPropertyParams = $this->promotedPropertyResolver->resolveFromClass($class); + foreach ($promotedPropertyParams as $promotedPropertyParam) { - if ($this->nodeNameResolver->isName($promotedPropertyParam, $propertyMetadata->getName())) { + if ($this->nodeNameResolver->isName($promotedPropertyParam, $definedPropertyWithType->getName())) { return $promotedPropertyParam; } } return null; } - - /** - * @return ReflectionProperty[] - */ - private function getParentClassPublicAndProtectedPropertyReflections(string $className): array - { - if (! $this->reflectionProvider->hasClass($className)) { - return []; - } - - $classReflection = $this->reflectionProvider->getClass($className); - - $propertyReflections = []; - foreach ($classReflection->getParents() as $parentClassReflection) { - $nativeReflectionClass = $parentClassReflection->getNativeReflection(); - - $currentPropertyReflections = $nativeReflectionClass->getProperties( - ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED - ); - - $propertyReflections = [...$propertyReflections, ...$currentPropertyReflections]; - } - - return $propertyReflections; - } - - private function matchPropertyByType( - PropertyMetadata $propertyMetadata, - ReflectionProperty $reflectionProperty - ): Property | Param | null { - if ($propertyMetadata->getType() === null) { - return null; - } - - if (! $reflectionProperty->getType() instanceof ReflectionNamedType) { - return null; - } - - if (! $propertyMetadata->getType() instanceof TypeWithClassName) { - return null; - } - - $propertyObjectType = $propertyMetadata->getType(); - $propertyObjectTypeClassName = $propertyObjectType->getClassName(); - - if ($propertyObjectTypeClassName !== (string) $reflectionProperty->getType()) { - return null; - } - - $propertyObjectType = $propertyMetadata->getType(); - $propertyObjectTypeClassName = $propertyObjectType->getClassName(); - - if ($propertyObjectTypeClassName !== (string) $reflectionProperty->getType()) { - return null; - } - - return $this->astResolver->resolvePropertyFromPropertyReflection($reflectionProperty); - } - - private function matchPropertyByParentPublicOrProtectedProperties( - string $className, - PropertyMetadata $propertyMetadata - ): Property | Param | null { - $availablePropertyReflections = $this->getParentClassPublicAndProtectedPropertyReflections($className); - - foreach ($availablePropertyReflections as $availablePropertyReflection) { - // 1. match type by priority - $property = $this->matchPropertyByType($propertyMetadata, $availablePropertyReflection); - - if ($property instanceof Property || $property instanceof Param) { - return $property; - } - - // 2. match by name - if ($availablePropertyReflection->getName() === $propertyMetadata->getName()) { - return $this->astResolver->resolvePropertyFromPropertyReflection($availablePropertyReflection); - } - } - - return null; - } } diff --git a/src/NodeAnalyzer/ScopeAnalyzer.php b/src/NodeAnalyzer/ScopeAnalyzer.php new file mode 100644 index 00000000000..8bc7252f827 --- /dev/null +++ b/src/NodeAnalyzer/ScopeAnalyzer.php @@ -0,0 +1,29 @@ +> + */ + private const array NON_REFRESHABLE_NODES = [Name::class, Identifier::class, ComplexType::class]; + + public function isRefreshable(Node $node): bool + { + foreach (self::NON_REFRESHABLE_NODES as $noScopeNode) { + if ($node instanceof $noScopeNode) { + return false; + } + } + + return true; + } +} diff --git a/src/NodeAnalyzer/TerminatedNodeAnalyzer.php b/src/NodeAnalyzer/TerminatedNodeAnalyzer.php new file mode 100644 index 00000000000..444df395921 --- /dev/null +++ b/src/NodeAnalyzer/TerminatedNodeAnalyzer.php @@ -0,0 +1,181 @@ +> + */ + private const array TERMINABLE_NODES = [Return_::class, Break_::class, Continue_::class]; + + /** + * @var array> + */ + private const array TERMINABLE_NODES_BY_ITS_STMTS = [TryCatch::class, If_::class, Switch_::class]; + + /** + * @var array> + */ + private const array ALLOWED_CONTINUE_CURRENT_STMTS = [InlineHTML::class, Nop::class]; + + /** + * @param StmtsAware $stmtsAware + */ + public function isAlwaysTerminated(Node $stmtsAware, Stmt $node, Stmt $currentStmt): bool + { + if (in_array($currentStmt::class, self::ALLOWED_CONTINUE_CURRENT_STMTS, true)) { + return false; + } + + if (($stmtsAware instanceof FileNode || $stmtsAware instanceof Namespace_) && ($currentStmt instanceof ClassLike || $currentStmt instanceof Function_)) { + return false; + } + + if (! in_array($node::class, self::TERMINABLE_NODES_BY_ITS_STMTS, true)) { + return $this->isTerminatedNode($node, $currentStmt); + } + + if ($node instanceof TryCatch) { + return $this->isTerminatedInLastStmtsTryCatch($node, $currentStmt); + } + + if ($node instanceof If_) { + return $this->isTerminatedInLastStmtsIf($node, $currentStmt); + } + + /** @var Switch_ $node */ + return $this->isTerminatedInLastStmtsSwitch($node, $currentStmt); + } + + private function isTerminatedNode(Node $previousNode, Node $currentStmt): bool + { + if (in_array($previousNode::class, self::TERMINABLE_NODES, true)) { + return true; + } + + if ($previousNode instanceof Expression && ($previousNode->expr instanceof Exit_ || $previousNode->expr instanceof Throw_)) { + return true; + } + + if ($previousNode instanceof Goto_) { + return ! $currentStmt instanceof Label; + } + + return false; + } + + private function isTerminatedInLastStmtsSwitch(Switch_ $switch, Stmt $stmt): bool + { + if ($switch->cases === []) { + return false; + } + + $hasDefault = false; + foreach ($switch->cases as $key => $case) { + if (! $case->cond instanceof Expr) { + $hasDefault = true; + } + + if ($case->stmts === [] && isset($switch->cases[$key + 1])) { + continue; + } + + if (! $this->isTerminatedInLastStmts($case->stmts, $stmt)) { + return false; + } + } + + return $hasDefault; + } + + private function isTerminatedInLastStmtsTryCatch(TryCatch $tryCatch, Stmt $stmt): bool + { + if ($tryCatch->finally instanceof Finally_ && $this->isTerminatedInLastStmts( + $tryCatch->finally->stmts, + $stmt + )) { + return true; + } + + foreach ($tryCatch->catches as $catch) { + if (! $this->isTerminatedInLastStmts($catch->stmts, $stmt)) { + return false; + } + } + + return $this->isTerminatedInLastStmts($tryCatch->stmts, $stmt); + } + + private function isTerminatedInLastStmtsIf(If_ $if, Stmt $stmt): bool + { + // Without ElseIf_[] and Else_, after If_ is possibly executable + if ($if->elseifs === [] && ! $if->else instanceof Else_) { + return false; + } + + foreach ($if->elseifs as $elseif) { + if (! $this->isTerminatedInLastStmts($elseif->stmts, $stmt)) { + return false; + } + } + + if (! $this->isTerminatedInLastStmts($if->stmts, $stmt)) { + return false; + } + + if (! $if->else instanceof Else_) { + return false; + } + + return $this->isTerminatedInLastStmts($if->else->stmts, $stmt); + } + + /** + * @param Stmt[] $stmts + */ + private function isTerminatedInLastStmts(array $stmts, Node $node): bool + { + if ($stmts === []) { + return false; + } + + $lastKey = array_key_last($stmts); + $lastNode = $stmts[$lastKey]; + + if (isset($stmts[$lastKey - 1]) && ! $this->isTerminatedNode($stmts[$lastKey - 1], $node)) { + return false; + } + + if ($lastNode instanceof Expression) { + return $lastNode->expr instanceof Exit_ || $lastNode->expr instanceof Throw_; + } + + return $lastNode instanceof Return_; + } +} diff --git a/src/NodeAnalyzer/VariableAnalyzer.php b/src/NodeAnalyzer/VariableAnalyzer.php index 1dea847dd9d..6d2ae946854 100644 --- a/src/NodeAnalyzer/VariableAnalyzer.php +++ b/src/NodeAnalyzer/VariableAnalyzer.php @@ -2,48 +2,24 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; -use PhpParser\Node; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Stmt\Global_; -use PhpParser\Node\Stmt\Static_; -use PhpParser\Node\Stmt\StaticVar; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use Rector\NodeTypeResolver\Node\AttributeKey; final class VariableAnalyzer { - public function __construct( - private BetterNodeFinder $betterNodeFinder, - private NodeComparator $nodeComparator - ) { - } - public function isStaticOrGlobal(Variable $variable): bool { - return (bool) $this->betterNodeFinder->findFirstPreviousOfNode($variable, function (Node $n) use ( - $variable - ): bool { - if (! $n instanceof Static_ && ! $n instanceof Global_) { - return false; - } - - /** - * @var StaticVar[]|Variable[] $vars - */ - $vars = $n->vars; - foreach ($vars as $var) { - $staticVarVariable = $var instanceof StaticVar - ? $var->var - : $var; + if ($variable->getAttribute(AttributeKey::IS_GLOBAL_VAR) === true) { + return true; + } - if ($this->nodeComparator->areNodesEqual($staticVarVariable, $variable)) { - return true; - } - } + return $variable->getAttribute(AttributeKey::IS_STATIC_VAR) === true; + } - return false; - }); + public function isUsedByReference(Variable $variable): bool + { + return $variable->getAttribute(AttributeKey::IS_BYREF_VAR) === true; } } diff --git a/src/NodeAnalyzer/VariadicAnalyzer.php b/src/NodeAnalyzer/VariadicAnalyzer.php index 6f74db0d6d6..b120be23fc5 100644 --- a/src/NodeAnalyzer/VariadicAnalyzer.php +++ b/src/NodeAnalyzer/VariadicAnalyzer.php @@ -2,27 +2,18 @@ declare(strict_types=1); -namespace Rector\Core\NodeAnalyzer; +namespace Rector\NodeAnalyzer; -use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\Stmt\Function_; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\Php\PhpFunctionReflection; -use Rector\Core\PhpParser\AstResolver; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\Reflection\ReflectionResolver; -use Rector\NodeNameResolver\NodeNameResolver; +use Rector\Reflection\ReflectionResolver; -final class VariadicAnalyzer +final readonly class VariadicAnalyzer { public function __construct( - private BetterNodeFinder $betterNodeFinder, - private NodeNameResolver $nodeNameResolver, - private AstResolver $astResolver, private ReflectionResolver $reflectionResolver ) { } @@ -34,26 +25,7 @@ public function hasVariadicParameters(FuncCall | StaticCall | MethodCall $call): return false; } - if ($this->hasVariadicVariant($functionLikeReflection)) { - return true; - } - - if ($functionLikeReflection instanceof PhpFunctionReflection) { - $function = $this->astResolver->resolveFunctionFromFunctionReflection($functionLikeReflection); - if (! $function instanceof Function_) { - return false; - } - - return (bool) $this->betterNodeFinder->findFirst($function->stmts, function (Node $node): bool { - if (! $node instanceof FuncCall) { - return false; - } - - return $this->nodeNameResolver->isNames($node, ['func_get_args', 'func_num_args', 'func_get_arg']); - }); - } - - return false; + return $this->hasVariadicVariant($functionLikeReflection); } private function hasVariadicVariant(MethodReflection | FunctionReflection $functionLikeReflection): bool diff --git a/src/NodeCollector/BinaryOpConditionsCollector.php b/src/NodeCollector/BinaryOpConditionsCollector.php new file mode 100644 index 00000000000..ac258021059 --- /dev/null +++ b/src/NodeCollector/BinaryOpConditionsCollector.php @@ -0,0 +1,51 @@ + $binaryOpClass + * @return array + */ + public function findConditions(Expr $expr, string $binaryOpClass): array + { + if ($expr::class !== $binaryOpClass) { + // Different binary operators, as well as non-BinaryOp expressions + // are considered trivial case of a single operand (no operators). + return [$expr]; + } + + $conditions = []; + /** @var BinaryOp|Expr $expr */ + while ($expr instanceof BinaryOp) { + $conditions[] = $expr->right; + $expr = $expr->left; + + if ($binaryOpClass !== $expr::class) { + $conditions[] = $expr; + break; + } + } + + krsort($conditions); + return $conditions; + } +} diff --git a/src/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php b/src/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php new file mode 100644 index 00000000000..d655ca56113 --- /dev/null +++ b/src/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php @@ -0,0 +1,214 @@ +items) !== 2) { + return null; + } + + if ($this->shouldSkipNullItems($array)) { + return null; + } + + /** @var ArrayItem[] $items */ + $items = $array->items; + + // $this, self, static, FQN + $firstItemValue = $items[0]->value; + + $callerType = $this->resolveCallerType($firstItemValue, $scope, $classMethodName); + if (! $callerType instanceof TypeWithClassName) { + return null; + } + + if ($array->getAttribute(AttributeKey::IS_ARRAY_IN_ATTRIBUTE) === true) { + return null; + } + + $values = $this->valueResolver->getValue($array); + $className = $callerType->getClassName(); + $secondItemValue = $items[1]->value; + + if ($values === null) { + return new ArrayCallableDynamicMethod(); + } + + if ($this->shouldSkipAssociativeArray($values)) { + return null; + } + + if (! $secondItemValue instanceof String_) { + return null; + } + + if ($this->isCallbackAtFunctionNames($array, ['register_shutdown_function', 'forward_static_call'])) { + return null; + } + + $methodName = $secondItemValue->value; + if ($methodName === MethodName::CONSTRUCT) { + return null; + } + + // skip non-existing methods + if (! $callerType->hasMethod($methodName)->yes()) { + return null; + } + + return new ArrayCallable($firstItemValue, $className, $methodName); + } + + private function shouldSkipNullItems(Array_ $array): bool + { + if (! $array->items[0] instanceof ArrayItem) { + return true; + } + + return ! $array->items[1] instanceof ArrayItem; + } + + private function shouldSkipAssociativeArray(mixed $values): bool + { + if (! is_array($values)) { + return false; + } + + $keys = array_keys($values); + return $keys !== [0, 1] && $keys !== [1]; + } + + /** + * @param string[] $functionNames + */ + private function isCallbackAtFunctionNames(Array_ $array, array $functionNames): bool + { + $fromFuncCallName = $array->getAttribute(AttributeKey::FROM_FUNC_CALL_NAME); + if ($fromFuncCallName === null) { + return false; + } + + return in_array($fromFuncCallName, $functionNames, true); + } + + private function resolveClassContextType( + ClassConstFetch|Class_ $classContext, + Scope $scope, + ?string $classMethodName + ): MixedType | ObjectType { + $classConstantReference = $this->valueResolver->getValue($classContext); + + // non-class value + if (! is_string($classConstantReference)) { + return new MixedType(); + } + + if ($this->isRequiredClassReflectionResolution($classConstantReference)) { + $classReflection = $this->reflectionResolver->resolveClassReflection($classContext); + if (! $classReflection instanceof ClassReflection || ! $classReflection->isClass()) { + return new MixedType(); + } + + $classConstantReference = $classReflection->getName(); + } + + if (! $this->reflectionProvider->hasClass($classConstantReference)) { + return new MixedType(); + } + + $classReflection = $this->reflectionProvider->getClass($classConstantReference); + $hasConstruct = $classReflection->hasMethod(MethodName::CONSTRUCT); + + if (! $hasConstruct) { + return new ObjectType($classConstantReference, null, $classReflection); + } + + if (is_string($classMethodName) && $classReflection->hasNativeMethod($classMethodName)) { + return new ObjectType($classConstantReference, null, $classReflection); + } + + $extendedMethodReflection = $classReflection->getMethod(MethodName::CONSTRUCT, $scope); + $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors( + $extendedMethodReflection->getVariants() + ); + + foreach ($extendedParametersAcceptor->getParameters() as $extendedParameterReflection) { + if (! $extendedParameterReflection->getDefaultValue() instanceof Type) { + return new MixedType(); + } + } + + return new ObjectType($classConstantReference, null, $classReflection); + } + + private function resolveCallerType(Expr $expr, Scope $scope, ?string $classMethodName): Type + { + if ($expr instanceof ClassConstFetch || $expr instanceof Class_) { + // class context means self|static ::class or __CLASS__ + $callerType = $this->resolveClassContextType($expr, $scope, $classMethodName); + } else { + $callerType = $this->nodeTypeResolver->getType($expr); + } + + if ($callerType instanceof ThisType) { + return $callerType->getStaticObjectType(); + } + + return $callerType; + } + + private function isRequiredClassReflectionResolution(string $classConstantReference): bool + { + if ($classConstantReference === ObjectReference::STATIC) { + return true; + } + + return $classConstantReference === '__CLASS__'; + } +} diff --git a/src/NodeCollector/ScopeResolver/ParentClassScopeResolver.php b/src/NodeCollector/ScopeResolver/ParentClassScopeResolver.php new file mode 100644 index 00000000000..d02bc1643d7 --- /dev/null +++ b/src/NodeCollector/ScopeResolver/ParentClassScopeResolver.php @@ -0,0 +1,31 @@ +resolveParentClassReflection($scope); + if (! $parentClassReflection instanceof ClassReflection) { + return null; + } + + return $parentClassReflection->getName(); + } + + public function resolveParentClassReflection(Scope $scope): ?ClassReflection + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + return $classReflection->getParentClass(); + } +} diff --git a/src/NodeCollector/StaticAnalyzer.php b/src/NodeCollector/StaticAnalyzer.php new file mode 100644 index 00000000000..ab91b18cff9 --- /dev/null +++ b/src/NodeCollector/StaticAnalyzer.php @@ -0,0 +1,51 @@ +hasNativeMethod($methodName)) { + $extendedMethodReflection = $classReflection->getNativeMethod($methodName); + if ($extendedMethodReflection->isStatic()) { + // use cached ClassReflection + if (! $class instanceof Class_) { + return true; + } + + // use non-cached Class_ + $classMethod = $class->getMethod($methodName); + if ($classMethod instanceof ClassMethod && $classMethod->isStatic()) { + return true; + } + } + } + + // could be static in doc type magic + // @see https://regex101.com/r/tlvfTB/1 + return $this->hasStaticAnnotation($methodName, $classReflection); + } + + private function hasStaticAnnotation(string $methodName, ClassReflection $classReflection): bool + { + $resolvedPhpDocBlock = $classReflection->getResolvedPhpDoc(); + if (! $resolvedPhpDocBlock instanceof ResolvedPhpDocBlock) { + return false; + } + + // @see https://regex101.com/r/7Zkej2/1 + return StringUtils::isMatch( + $resolvedPhpDocBlock->getPhpDocString(), + '#@method\s*static\s*((([\w\|\\\\]+)|\$this)*+(\[\])*)*\s+\b' . $methodName . '\b#' + ); + } +} diff --git a/src/NodeCollector/ValueObject/ArrayCallable.php b/src/NodeCollector/ValueObject/ArrayCallable.php new file mode 100644 index 00000000000..37cb036d8e8 --- /dev/null +++ b/src/NodeCollector/ValueObject/ArrayCallable.php @@ -0,0 +1,34 @@ +class; + } + + public function getMethod(): string + { + return $this->method; + } + + public function getCallerExpr(): Expr + { + return $this->callerExpr; + } +} diff --git a/src/NodeCollector/ValueObject/ArrayCallableDynamicMethod.php b/src/NodeCollector/ValueObject/ArrayCallableDynamicMethod.php new file mode 100644 index 00000000000..3f5df2f2093 --- /dev/null +++ b/src/NodeCollector/ValueObject/ArrayCallableDynamicMethod.php @@ -0,0 +1,10 @@ +|Node $node + * @param class-string $rectorClass + */ + public function decorate(array | Node $node, Node $originalNode, string $rectorClass): void + { + if ($node instanceof Node && $node === $originalNode) { + $this->createByRule($node, $rectorClass); + return; + } + + if ($node instanceof Node) { + $node = [$node]; + } + + foreach ($node as $singleNode) { + if ($singleNode::class === $originalNode::class) { + $this->createByRule($singleNode, $rectorClass); + } + } + + $this->createByRule($originalNode, $rectorClass); + } + + /** + * @param class-string $rectorClass + */ + private function createByRule(Node $node, string $rectorClass): void + { + /** @var class-string[] $createdByRule */ + $createdByRule = $node->getAttribute(AttributeKey::CREATED_BY_RULE) ?? []; + + // empty array, insert + if ($createdByRule === []) { + $node->setAttribute(AttributeKey::CREATED_BY_RULE, [$rectorClass]); + return; + } + + // consecutive, no need to refill + if (end($createdByRule) === $rectorClass) { + return; + } + + // filter out when exists, then append + $createdByRule = array_filter( + $createdByRule, + static fn (string $rectorRule): bool => $rectorRule !== $rectorClass + ); + $node->setAttribute(AttributeKey::CREATED_BY_RULE, [...$createdByRule, $rectorClass]); + } +} diff --git a/src/NodeDecorator/PropertyTypeDecorator.php b/src/NodeDecorator/PropertyTypeDecorator.php new file mode 100644 index 00000000000..9daa733df42 --- /dev/null +++ b/src/NodeDecorator/PropertyTypeDecorator.php @@ -0,0 +1,52 @@ +phpDocInfoFactory->createFromNodeOrEmpty($property); + + if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::TYPED_PROPERTIES)) { + $phpParserType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PROPERTY); + + if ($phpParserType instanceof Node) { + $property->type = $phpParserType; + + if ($type instanceof GenericObjectType) { + $this->phpDocTypeChanger->changeVarType($property, $phpDocInfo, $type); + } + + return; + } + } + + $this->phpDocTypeChanger->changeVarType($property, $phpDocInfo, $type); + } +} diff --git a/src/NodeDecorator/StatementDepthAttributeDecorator.php b/src/NodeDecorator/StatementDepthAttributeDecorator.php new file mode 100644 index 00000000000..d26a8e28a25 --- /dev/null +++ b/src/NodeDecorator/StatementDepthAttributeDecorator.php @@ -0,0 +1,28 @@ +stmts as $methodStmt) { + $methodStmt->setAttribute(AttributeKey::IS_FIRST_LEVEL_STATEMENT, true); + + if ($methodStmt instanceof Expression) { + $methodStmt->expr->setAttribute(AttributeKey::IS_FIRST_LEVEL_STATEMENT, true); + } + } + } + } +} diff --git a/src/NodeFactory/ClassWithPublicPropertiesFactory.php b/src/NodeFactory/ClassWithPublicPropertiesFactory.php deleted file mode 100644 index cc4fe1c1d5c..00000000000 --- a/src/NodeFactory/ClassWithPublicPropertiesFactory.php +++ /dev/null @@ -1,72 +0,0 @@ - $properties - * @param string|null $parent fully qualified name of parent class - * @param string[] $traits list of fully qualified names of traits used in class - */ - public function createNode( - string $fullyQualifiedName, - array $properties, - ?string $parent = null, - array $traits = [] - ): Namespace_ | Class_ { - $namespaceParts = explode('\\', ltrim($fullyQualifiedName, '\\')); - $className = array_pop($namespaceParts); - $namespace = implode('\\', $namespaceParts); - - $namespaceBuilder = null; - if ($namespace !== '') { - $namespaceBuilder = new NamespaceBuilder($namespace); - } - $classBuilder = new ClassBuilder($className); - if ($parent !== null && $parent !== '') { - $classBuilder->extend($this->fixFullyQualifiedName($parent)); - } - - foreach ($traits as $trait) { - $classBuilder->addStmt(new TraitUseBuilder($this->fixFullyQualifiedName($trait))); - } - - foreach ($properties as $propertyName => $propertySettings) { - $propertyType = $propertySettings['type']; - $nullable = $propertySettings['nullable'] ?? false; - if ($nullable) { - $propertyType = new NullableType($propertyType); - } - $propertyBuilder = new PropertyBuilder($propertyName); - $propertyBuilder->setType($propertyType); - $classBuilder->addStmt($propertyBuilder); - } - - if ($namespaceBuilder !== null) { - $namespaceBuilder->addStmt($classBuilder); - return $namespaceBuilder->getNode(); - } - - return $classBuilder->getNode(); - } - - private function fixFullyQualifiedName(string $fullyQualifiedName): string - { - return '\\' . ltrim($fullyQualifiedName, '\\'); - } -} diff --git a/src/NodeManipulator/ArrayDestructVariableFilter.php b/src/NodeManipulator/ArrayDestructVariableFilter.php deleted file mode 100644 index f207b5c57ba..00000000000 --- a/src/NodeManipulator/ArrayDestructVariableFilter.php +++ /dev/null @@ -1,67 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod, function (Node $node) use ( - &$arrayDestructionCreatedVariables - ) { - if (! $node instanceof Assign) { - return null; - } - - if (! $node->var instanceof Array_ && ! $node->var instanceof List_) { - return null; - } - - foreach ($node->var->items as $arrayItem) { - // empty item - if ($arrayItem === null) { - continue; - } - - if (! $arrayItem->value instanceof Variable) { - continue; - } - - /** @var string $variableName */ - $variableName = $this->nodeNameResolver->getName($arrayItem->value); - $arrayDestructionCreatedVariables[] = $variableName; - } - }); - - return array_filter( - $variableAssigns, - fn (Assign $assign): bool => ! $this->nodeNameResolver->isNames( - $assign->var, - $arrayDestructionCreatedVariables - ) - ); - } -} diff --git a/src/NodeManipulator/ArrayManipulator.php b/src/NodeManipulator/ArrayManipulator.php deleted file mode 100644 index 4a4cb03eee1..00000000000 --- a/src/NodeManipulator/ArrayManipulator.php +++ /dev/null @@ -1,99 +0,0 @@ -items as $arrayItem) { - if (! $arrayItem instanceof ArrayItem) { - continue; - } - - if ($arrayItem->value instanceof Array_) { - if (! $this->isArrayOnlyScalarValues($arrayItem->value)) { - return false; - } - - continue; - } - - if ($arrayItem->value instanceof Scalar) { - continue; - } - - return false; - } - - return true; - } - - public function addItemToArrayUnderKey(Array_ $array, ArrayItem $newArrayItem, string $key): void - { - foreach ($array->items as $item) { - if ($item === null) { - continue; - } - - if ($this->hasKeyName($item, $key)) { - if (! $item->value instanceof Array_) { - continue; - } - - $item->value->items[] = $newArrayItem; - return; - } - } - - $array->items[] = new ArrayItem(new Array_([$newArrayItem]), new String_($key)); - } - - public function findItemInInArrayByKeyAndUnset(Array_ $array, string $keyName): ?ArrayItem - { - foreach ($array->items as $i => $item) { - if ($item === null) { - continue; - } - - if (! $this->hasKeyName($item, $keyName)) { - continue; - } - - $removedArrayItem = $array->items[$i]; - if (! $removedArrayItem instanceof ArrayItem) { - continue; - } - - // remove + recount for the printer - unset($array->items[$i]); - - $this->rectorChangeCollector->notifyNodeFileInfo($removedArrayItem); - - return $item; - } - - return null; - } - - public function hasKeyName(ArrayItem $arrayItem, string $name): bool - { - if (! $arrayItem->key instanceof String_) { - return false; - } - return $arrayItem->key->value === $name; - } -} diff --git a/src/NodeManipulator/AssignManipulator.php b/src/NodeManipulator/AssignManipulator.php index db17966f9c8..7240eb46b5e 100644 --- a/src/NodeManipulator/AssignManipulator.php +++ b/src/NodeManipulator/AssignManipulator.php @@ -2,117 +2,68 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; use PhpParser\Node; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\AssignOp; +use PhpParser\Node\Expr\ErrorSuppress; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\List_; -use PhpParser\Node\Expr\PostDec; -use PhpParser\Node\Expr\PostInc; -use PhpParser\Node\Expr\PreDec; -use PhpParser\Node\Expr\PreInc; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\FunctionLike; -use PhpParser\Node\Stmt\Expression; -use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Symplify\PackageBuilder\Php\TypeChecker; +use Rector\NodeNestingScope\ContextAnalyzer; +use Rector\Php72\ValueObject\ListAndEach; +use Rector\PhpParser\Node\BetterNodeFinder; -final class AssignManipulator +final readonly class AssignManipulator { - /** - * @var array> - */ - private const MODIFYING_NODE_TYPES = [ - AssignOp::class, - PreDec::class, - PostDec::class, - PreInc::class, - PostInc::class, - ]; - public function __construct( private NodeNameResolver $nodeNameResolver, - private NodeComparator $nodeComparator, private BetterNodeFinder $betterNodeFinder, private PropertyFetchAnalyzer $propertyFetchAnalyzer, - private TypeChecker $typeChecker + private ContextAnalyzer $contextAnalyzer ) { } /** * Matches: - * each() = [1, 2]; + * list([1, 2]) = each($items) */ - public function isListToEachAssign(Assign $assign): bool + public function matchListAndEach(Assign $assign): ?ListAndEach { - if (! $assign->expr instanceof FuncCall) { - return false; + // could be behind error suppress + if ($assign->expr instanceof ErrorSuppress) { + $errorSuppress = $assign->expr; + $bareExpr = $errorSuppress->expr; + } else { + $bareExpr = $assign->expr; } - if (! $assign->var instanceof List_) { - return false; + if (! $bareExpr instanceof FuncCall) { + return null; } - return $this->nodeNameResolver->isName($assign->expr, 'each'); - } - - public function isLeftPartOfAssign(Node $node): bool - { - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Assign && $this->nodeComparator->areNodesEqual($parent->var, $node)) { - return true; + if (! $assign->var instanceof List_) { + return null; } - if ($parent !== null && $this->typeChecker->isInstanceOf($parent, self::MODIFYING_NODE_TYPES)) { - return true; + if (! $this->nodeNameResolver->isName($bareExpr, 'each')) { + return null; } - // traverse up to array dim fetches - if ($parent instanceof ArrayDimFetch) { - $previousParent = $parent; - while ($parent instanceof ArrayDimFetch) { - $previousParent = $parent; - $parent = $parent->getAttribute(AttributeKey::PARENT_NODE); - } - - if ($parent instanceof Assign) { - return $parent->var === $previousParent; - } - } - - return false; - } - - public function isNodePartOfAssign(Node $node): bool - { - $previousNode = $node; - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - - while ($parentNode !== null && ! $parentNode instanceof Expression) { - if ($parentNode instanceof Assign && $this->nodeComparator->areNodesEqual( - $parentNode->var, - $previousNode - )) { - return true; - } - - $previousNode = $parentNode; - $parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE); + // no placeholders + if ($bareExpr->isFirstClassCallable()) { + return null; } - return false; + return new ListAndEach($assign->var, $bareExpr); } /** + * @api doctrine * @return array */ public function resolveAssignsToLocalPropertyFetches(FunctionLike $functionLike): array @@ -122,7 +73,7 @@ public function resolveAssignsToLocalPropertyFetches(FunctionLike $functionLike) return false; } - return $this->isLeftPartOfAssign($node); + return $this->contextAnalyzer->isLeftPartOfAssign($node); }); } } diff --git a/src/NodeManipulator/BinaryOpManipulator.php b/src/NodeManipulator/BinaryOpManipulator.php index a39046285ae..3c16cb570e6 100644 --- a/src/NodeManipulator/BinaryOpManipulator.php +++ b/src/NodeManipulator/BinaryOpManipulator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; use PhpParser\Node; use PhpParser\Node\Expr; @@ -10,11 +10,11 @@ use PhpParser\Node\Expr\BinaryOp\BooleanAnd; use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BooleanNot; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\PhpParser\Node\AssignAndBinaryMap; +use Rector\Exception\ShouldNotHappenException; use Rector\Php71\ValueObject\TwoNodeMatch; +use Rector\PhpParser\Node\AssignAndBinaryMap; -final class BinaryOpManipulator +final readonly class BinaryOpManipulator { public function __construct( private AssignAndBinaryMap $assignAndBinaryMap @@ -25,13 +25,13 @@ public function __construct( * Tries to match left or right parts (xor), * returns null or match on first condition and then second condition. No matter what the origin order is. * - * @param callable|class-string $firstCondition - * @param callable|class-string $secondCondition + * @param callable(Node $firstNode, Node $secondNode): bool|class-string $firstCondition + * @param callable(Node $firstNode, Node $secondNode): bool|class-string $secondCondition */ public function matchFirstAndSecondConditionNode( BinaryOp $binaryOp, - $firstCondition, - $secondCondition + callable|string $firstCondition, + callable|string $secondCondition ): ?TwoNodeMatch { $this->validateCondition($firstCondition); $this->validateCondition($secondCondition); @@ -42,35 +42,38 @@ public function matchFirstAndSecondConditionNode( if ($firstCondition($binaryOp->left, $binaryOp->right) && $secondCondition($binaryOp->right, $binaryOp->left)) { return new TwoNodeMatch($binaryOp->left, $binaryOp->right); } + if (! $firstCondition($binaryOp->right, $binaryOp->left)) { return null; } + if (! $secondCondition($binaryOp->left, $binaryOp->right)) { return null; } + return new TwoNodeMatch($binaryOp->right, $binaryOp->left); } - public function inverseBinaryOp(BinaryOp $binaryOp): ?BinaryOp + public function inverseBooleanOr(BooleanOr $booleanOr): ?BinaryOp { // no nesting - if ($binaryOp->left instanceof BooleanOr) { + if ($booleanOr->left instanceof BooleanOr) { return null; } - if ($binaryOp->right instanceof BooleanOr) { + if ($booleanOr->right instanceof BooleanOr) { return null; } - $inversedNodeClass = $this->resolveInversedNodeClass($binaryOp); + $inversedNodeClass = $this->resolveInversedNodeClass($booleanOr); if ($inversedNodeClass === null) { return null; } - $firstInversedNode = $this->inverseNode($binaryOp->left); - $secondInversedNode = $this->inverseNode($binaryOp->right); + $firstInversedExpr = $this->inverseNode($booleanOr->left); + $secondInversedExpr = $this->inverseNode($booleanOr->right); - return new $inversedNodeClass($firstInversedNode, $secondInversedNode); + return new $inversedNodeClass($firstInversedExpr, $secondInversedExpr); } public function invertCondition(BinaryOp $binaryOp): ?BinaryOp @@ -92,11 +95,11 @@ public function invertCondition(BinaryOp $binaryOp): ?BinaryOp return new $inversedNodeClass($binaryOp->left, $binaryOp->right); } - public function inverseNode(Expr $expr): Node + public function inverseNode(Expr $expr): BinaryOp|Expr|BooleanNot { if ($expr instanceof BinaryOp) { $inversedBinaryOp = $this->assignAndBinaryMap->getInversed($expr); - if ($inversedBinaryOp) { + if ($inversedBinaryOp !== null) { return new $inversedBinaryOp($expr->left, $expr->right); } } @@ -109,9 +112,9 @@ public function inverseNode(Expr $expr): Node } /** - * @param callable|class-string $firstCondition + * @param callable(Node $firstNode, Node $secondNode): bool|class-string $firstCondition */ - private function validateCondition($firstCondition): void + private function validateCondition(callable|string $firstCondition): void { if (is_callable($firstCondition)) { return; @@ -125,17 +128,21 @@ private function validateCondition($firstCondition): void } /** - * @param callable|class-string $condition + * @param callable(Node $firstNode, Node $secondNode): bool|class-string $condition + * @return callable(Node $firstNode, Node $secondNode): bool */ - private function normalizeCondition($condition): callable + private function normalizeCondition(callable|string $condition): callable { if (is_callable($condition)) { return $condition; } - return fn (Node $node): bool => is_a($node, $condition, true); + return static fn (Node $node): bool => $node instanceof $condition; } + /** + * @return class-string|null + */ private function resolveInversedNodeClass(BinaryOp $binaryOp): ?string { $inversedNodeClass = $this->assignAndBinaryMap->getInversed($binaryOp); diff --git a/src/NodeManipulator/ClassConstManipulator.php b/src/NodeManipulator/ClassConstManipulator.php index 358bb8853fd..5a88103c032 100644 --- a/src/NodeManipulator/ClassConstManipulator.php +++ b/src/NodeManipulator/ClassConstManipulator.php @@ -2,55 +2,57 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; use PhpParser\Node; use PhpParser\Node\Expr\ClassConstFetch; -use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\ClassLike; use PHPStan\Reflection\ClassReflection; -use Rector\Core\PhpParser\AstResolver; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use PHPStan\Type\ObjectType; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\AstResolver; +use Rector\PhpParser\Node\BetterNodeFinder; -final class ClassConstManipulator +final readonly class ClassConstManipulator { public function __construct( private BetterNodeFinder $betterNodeFinder, private NodeNameResolver $nodeNameResolver, - private AstResolver $astResolver + private AstResolver $astResolver, + private NodeTypeResolver $nodeTypeResolver ) { } public function hasClassConstFetch(ClassConst $classConst, ClassReflection $classReflection): bool { - $classLike = $classConst->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return false; + if (! $classReflection->isClass() && ! $classReflection->isEnum()) { + return true; } + $className = $classReflection->getName(); + $objectType = new ObjectType($className); foreach ($classReflection->getAncestors() as $ancestorClassReflection) { - $ancestorClass = $this->astResolver->resolveClassFromClassReflection( - $ancestorClassReflection, - $ancestorClassReflection->getName() - ); + $ancestorClass = $this->astResolver->resolveClassFromClassReflection($ancestorClassReflection); if (! $ancestorClass instanceof ClassLike) { continue; } // has in class? - $isClassConstFetchFound = (bool) $this->betterNodeFinder->find($ancestorClass, function (Node $node) use ( - $classConst + $isClassConstFetchFound = (bool) $this->betterNodeFinder->findFirst($ancestorClass, function (Node $node) use ( + $classConst, + $className, + $objectType ): bool { // property + static fetch if (! $node instanceof ClassConstFetch) { return false; } - return $this->isNameMatch($node, $classConst); + return $this->isNameMatch($node, $classConst, $className, $objectType); }); if ($isClassConstFetchFound) { @@ -61,11 +63,32 @@ public function hasClassConstFetch(ClassConst $classConst, ClassReflection $clas return false; } - private function isNameMatch(ClassConstFetch $classConstFetch, ClassConst $classConst): bool - { - $selfConstantName = 'self::' . $this->nodeNameResolver->getName($classConst); - $staticConstantName = 'static::' . $this->nodeNameResolver->getName($classConst); + private function isNameMatch( + ClassConstFetch $classConstFetch, + ClassConst $classConst, + string $className, + ObjectType $objectType + ): bool { + $classConstName = (string) $this->nodeNameResolver->getName($classConst); + $selfConstantName = 'self::' . $classConstName; + $staticConstantName = 'static::' . $classConstName; + $classNameConstantName = $className . '::' . $classConstName; + + if ($this->nodeNameResolver->isNames( + $classConstFetch, + [$selfConstantName, $staticConstantName, $classNameConstantName] + )) { + return true; + } - return $this->nodeNameResolver->isNames($classConstFetch, [$selfConstantName, $staticConstantName]); + if ($this->nodeTypeResolver->isObjectType($classConstFetch->class, $objectType)) { + if (! $classConstFetch->name instanceof Identifier) { + return true; + } + + return $this->nodeNameResolver->isName($classConstFetch->name, $classConstName); + } + + return false; } } diff --git a/src/NodeManipulator/ClassDependencyManipulator.php b/src/NodeManipulator/ClassDependencyManipulator.php index a86a41dfa40..61e43fb1730 100644 --- a/src/NodeManipulator/ClassDependencyManipulator.php +++ b/src/NodeManipulator/ClassDependencyManipulator.php @@ -2,32 +2,37 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; use PHPStan\Type\Type; -use Rector\Core\NodeAnalyzer\PropertyPresenceChecker; -use Rector\Core\NodeManipulator\Dependency\DependencyClassMethodDecorator; -use Rector\Core\Php\PhpVersionProvider; -use Rector\Core\PhpParser\Node\NodeFactory; -use Rector\Core\ValueObject\MethodName; -use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Enum\ObjectReference; +use Rector\NodeAnalyzer\PropertyPresenceChecker; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\PostRector\Collector\NodesToRemoveCollector; +use Rector\Php\PhpVersionProvider; +use Rector\PhpParser\AstResolver; +use Rector\PhpParser\Node\NodeFactory; use Rector\PostRector\ValueObject\PropertyMetadata; +use Rector\Reflection\ReflectionResolver; use Rector\TypeDeclaration\NodeAnalyzer\AutowiredClassMethodOrPropertyAnalyzer; +use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; -final class ClassDependencyManipulator +/** + * @see \Rector\Tests\NodeManipulator\ClassDependencyManipulatorTest + */ +final readonly class ClassDependencyManipulator { public function __construct( private ClassInsertManipulator $classInsertManipulator, @@ -37,19 +42,25 @@ public function __construct( private PhpVersionProvider $phpVersionProvider, private PropertyPresenceChecker $propertyPresenceChecker, private NodeNameResolver $nodeNameResolver, - private NodesToRemoveCollector $nodesToRemoveCollector, private AutowiredClassMethodOrPropertyAnalyzer $autowiredClassMethodOrPropertyAnalyzer, - private DependencyClassMethodDecorator $dependencyClassMethodDecorator + private ReflectionResolver $reflectionResolver, + private AstResolver $astResolver ) { } public function addConstructorDependency(Class_ $class, PropertyMetadata $propertyMetadata): void { + // already has property as dependency? skip it if ($this->hasClassPropertyAndDependency($class, $propertyMetadata)) { return; } - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) { + // special case for Symfony @required + $autowireClassMethod = $this->autowiredClassMethodOrPropertyAnalyzer->matchAutowiredMethodInClass($class); + + if (! $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::PROPERTY_PROMOTION + ) || $autowireClassMethod instanceof ClassMethod) { $this->classInsertManipulator->addPropertyToClass( $class, $propertyMetadata->getName(), @@ -57,55 +68,95 @@ public function addConstructorDependency(Class_ $class, PropertyMetadata $proper ); } - if ($this->shouldAddPromotedProperty($class, $propertyMetadata)) { - $this->addPromotedProperty($class, $propertyMetadata); - } else { + // in case of existing autowire method, re-use it + if ($autowireClassMethod instanceof ClassMethod) { $assign = $this->nodeFactory->createPropertyAssignment($propertyMetadata->getName()); - $this->addConstructorDependencyWithCustomAssign( - $class, + $this->classMethodAssignManipulator->addParameterAndAssignToMethod( + $autowireClassMethod, $propertyMetadata->getName(), $propertyMetadata->getType(), $assign ); + return; + + } + + $constructClassMethod = $this->resolveConstruct($class); + + // add PHP 8.0 promoted property + if ($this->shouldAddPromotedProperty($class, $propertyMetadata)) { + $this->addPromotedProperty($class, $propertyMetadata, $constructClassMethod); + return; } + + $assign = $this->nodeFactory->createPropertyAssignment($propertyMetadata->getName()); + + $this->addConstructorDependencyWithCustomAssign( + $class, + $propertyMetadata->getName(), + $propertyMetadata->getType(), + $assign + ); } + /** + * @api doctrine + */ public function addConstructorDependencyWithCustomAssign( Class_ $class, string $name, ?Type $type, Assign $assign ): void { - /** @var ClassMethod|null $constructorMethod */ - $constructorMethod = $class->getMethod(MethodName::CONSTRUCT); + $constructClassMethod = $this->resolveConstruct($class); + + if ($constructClassMethod instanceof ClassMethod) { + if (! $class->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) { + $parentArgs = []; + + foreach ($constructClassMethod->params as $originalParam) { + $parentArgs[] = new Arg(new Variable((string) $this->nodeNameResolver->getName( + $originalParam->var + ))); + } + + $constructClassMethod->stmts = [new Expression( + new StaticCall(new Name(ObjectReference::PARENT), MethodName::CONSTRUCT, $parentArgs) + )]; + $this->classInsertManipulator->addAsFirstMethod($class, $constructClassMethod); + + $this->classMethodAssignManipulator->addParameterAndAssignToMethod( + $constructClassMethod, + $name, + $type, + $assign + ); + } else { + $this->classMethodAssignManipulator->addParameterAndAssignToMethod( + $constructClassMethod, + $name, + $type, + $assign + ); + } - if ($constructorMethod !== null) { - $this->classMethodAssignManipulator->addParameterAndAssignToMethod( - $constructorMethod, - $name, - $type, - $assign - ); return; } - $constructorMethod = $this->nodeFactory->createPublicMethod(MethodName::CONSTRUCT); - - $this->classMethodAssignManipulator->addParameterAndAssignToMethod($constructorMethod, $name, $type, $assign); - $this->classInsertManipulator->addAsFirstMethod($class, $constructorMethod); + $constructClassMethod = $this->nodeFactory->createPublicMethod(MethodName::CONSTRUCT); - /** @var Scope $scope */ - $scope = $class->getAttribute(AttributeKey::SCOPE); - - $this->dependencyClassMethodDecorator->decorateConstructorWithParentDependencies( - $class, - $constructorMethod, - $scope + $this->classMethodAssignManipulator->addParameterAndAssignToMethod( + $constructClassMethod, + $name, + $type, + $assign ); + $this->classInsertManipulator->addAsFirstMethod($class, $constructClassMethod); } /** + * @api doctrine * @param Stmt[] $stmts */ public function addStmtsToConstructorIfNotThereYet(Class_ $class, array $stmts): void @@ -120,7 +171,7 @@ public function addStmtsToConstructorIfNotThereYet(Class_ $class, array $stmts): $classMethod->stmts[] = $this->createParentClassMethodCall(MethodName::CONSTRUCT); } - $classMethod->stmts = array_merge((array) $classMethod->stmts, $stmts); + $classMethod->stmts = [...(array) $classMethod->stmts, ...$stmts]; $class->stmts = array_merge($class->stmts, [$classMethod]); return; @@ -136,18 +187,64 @@ public function addStmtsToConstructorIfNotThereYet(Class_ $class, array $stmts): $classMethod->stmts = array_merge($stmts, (array) $classMethod->stmts); } - public function addInjectProperty(Class_ $class, PropertyMetadata $propertyMetadata): void + private function resolveConstruct(Class_ $class): ?ClassMethod { - if ($this->propertyPresenceChecker->hasClassContextProperty($class, $propertyMetadata)) { - return; + $constructorMethod = $class->getMethod(MethodName::CONSTRUCT); + + // exists in current class + if ($constructorMethod instanceof ClassMethod) { + return $constructorMethod; + } + + // lookup parent, found first found (nearest parent constructor to follow) + $classReflection = $this->reflectionResolver->resolveClassReflection($class); + if (! $classReflection instanceof ClassReflection) { + return null; } - $this->classInsertManipulator->addInjectPropertyToClass($class, $propertyMetadata); + $ancestors = array_filter( + $classReflection->getAncestors(), + static fn (ClassReflection $ancestor): bool => $ancestor->getName() !== $classReflection->getName() + ); + + foreach ($ancestors as $ancestor) { + if (! $ancestor->hasNativeMethod(MethodName::CONSTRUCT)) { + continue; + } + + $parentClass = $this->astResolver->resolveClassFromClassReflection($ancestor); + if (! $parentClass instanceof ClassLike) { + continue; + } + + $parentConstructorMethod = $parentClass->getMethod(MethodName::CONSTRUCT); + + if (! $parentConstructorMethod instanceof ClassMethod) { + continue; + } + + if ($parentConstructorMethod->isPrivate()) { + // stop, nearest __construct() uses private visibility + // which parent::__construct() will cause error + break; + } + + $constructorMethod = clone $parentConstructorMethod; + + // reprint parent method node to avoid invalid tokens + $this->nodeFactory->createReprintedNode($constructorMethod); + + return $constructorMethod; + } + + return null; } - private function addPromotedProperty(Class_ $class, PropertyMetadata $propertyMetadata): void - { - $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); + private function addPromotedProperty( + Class_ $class, + PropertyMetadata $propertyMetadata, + ?ClassMethod $constructClassMethod + ): void { $param = $this->nodeFactory->createPromotedPropertyParam($propertyMetadata); if ($constructClassMethod instanceof ClassMethod) { @@ -156,31 +253,36 @@ private function addPromotedProperty(Class_ $class, PropertyMetadata $propertyMe return; } - $constructClassMethod->params[] = $param; + // found construct, but only on parent, add to current class + if (! $class->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) { + $parentArgs = []; + + foreach ($constructClassMethod->params as $originalParam) { + $parentArgs[] = new Arg(new Variable((string) $this->nodeNameResolver->getName( + $originalParam->var + ))); + } + + $constructClassMethod->params[] = $param; + + $constructClassMethod->stmts = [new Expression( + new StaticCall(new Name(ObjectReference::PARENT), MethodName::CONSTRUCT, $parentArgs) + )]; + $this->classInsertManipulator->addAsFirstMethod($class, $constructClassMethod); + } else { + $constructClassMethod->params[] = $param; + } } else { $constructClassMethod = $this->nodeFactory->createPublicMethod(MethodName::CONSTRUCT); $constructClassMethod->params[] = $param; + $this->classInsertManipulator->addAsFirstMethod($class, $constructClassMethod); } - - /** @var Scope $scope */ - $scope = $class->getAttribute(AttributeKey::SCOPE); - - $this->dependencyClassMethodDecorator->decorateConstructorWithParentDependencies( - $class, - $constructClassMethod, - $scope - ); } private function hasClassParentClassMethod(Class_ $class, string $methodName): bool { - $scope = $class->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); + $classReflection = $this->reflectionResolver->resolveClassReflection($class); if (! $classReflection instanceof ClassReflection) { return false; } @@ -196,7 +298,7 @@ private function hasClassParentClassMethod(Class_ $class, string $methodName): b private function createParentClassMethodCall(string $methodName): Expression { - $staticCall = new StaticCall(new Name('parent'), $methodName); + $staticCall = new StaticCall(new Name(ObjectReference::PARENT), $methodName); return new Expression($staticCall); } @@ -229,7 +331,7 @@ private function hasClassPropertyAndDependency(Class_ $class, PropertyMetadata $ } // is inject/autowired property? - return $property instanceof Property && $this->autowiredClassMethodOrPropertyAnalyzer->detect($property); + return $property instanceof Property; } private function hasMethodParameter(ClassMethod $classMethod, string $name): bool @@ -248,12 +350,9 @@ private function shouldAddPromotedProperty(Class_ $class, PropertyMetadata $prop if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) { return false; } + // only if the property does not exist yet $existingProperty = $class->getProperty($propertyMetadata->getName()); - if (! $existingProperty instanceof Property) { - return true; - } - - return $this->nodesToRemoveCollector->isNodeRemoved($existingProperty); + return ! $existingProperty instanceof Property; } } diff --git a/src/NodeManipulator/ClassInsertManipulator.php b/src/NodeManipulator/ClassInsertManipulator.php index 4e2c4a01140..21e665ec86a 100644 --- a/src/NodeManipulator/ClassInsertManipulator.php +++ b/src/NodeManipulator/ClassInsertManipulator.php @@ -2,62 +2,74 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; -use PhpParser\Node\Name\FullyQualified; -use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use PhpParser\Node\Stmt\TraitUse; use PHPStan\Type\Type; -use Rector\Core\PhpParser\Node\NodeFactory; -use Rector\NodeNameResolver\NodeNameResolver; -use Rector\PostRector\ValueObject\PropertyMetadata; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\NodeFactory; -final class ClassInsertManipulator +final readonly class ClassInsertManipulator { - /** - * @var array> - */ - private const BEFORE_TRAIT_TYPES = [TraitUse::class, Property::class, ClassMethod::class]; - public function __construct( private NodeFactory $nodeFactory, - private NodeNameResolver $nodeNameResolver ) { } - public function addAsFirstMethod(Class_ $class, Property | ClassConst | ClassMethod $stmt): void + /** + * @api + */ + public function addAsFirstMethod(Class_ $class, Property | ClassConst | ClassMethod $addedStmt): void { - if ($this->isSuccessToInsertBeforeFirstMethod($class, $stmt)) { - return; - } - if ($this->isSuccessToInsertAfterLastProperty($class, $stmt)) { + $scope = $class->getAttribute(AttributeKey::SCOPE); + $addedStmt->setAttribute(AttributeKey::SCOPE, $scope); + + // no stmts? add this one + if ($class->stmts === []) { + $class->stmts[] = $addedStmt; return; } - $class->stmts[] = $stmt; - } + $newClassStmts = []; + $isAdded = false; - public function addConstantToClass(Class_ $class, string $constantName, ClassConst $classConst): void - { - if ($this->hasClassConstant($class, $constantName)) { - return; + foreach ($class->stmts as $key => $classStmt) { + $nextStmt = $class->stmts[$key + 1] ?? null; + + if ($isAdded === false) { + // first class method + if ($classStmt instanceof ClassMethod) { + $newClassStmts[] = $addedStmt; + $newClassStmts[] = $classStmt; + $isAdded = true; + continue; + } + + // after last property + if ($classStmt instanceof Property && ! $nextStmt instanceof Property) { + $newClassStmts[] = $classStmt; + $newClassStmts[] = $addedStmt; + $isAdded = true; + continue; + } + } + + $newClassStmts[] = $classStmt; } - $this->addAsFirstMethod($class, $classConst); - } + // still not added? try after last trait + // @todo - /** - * @param Property[] $properties - */ - public function addPropertiesToClass(Class_ $class, array $properties): void - { - foreach ($properties as $property) { - $this->addAsFirstMethod($class, $property); + if ($isAdded) { + $class->stmts = $newClassStmts; + return; } + + // keep added at least as first stmt + $class->stmts = array_merge([$addedStmt], $class->stmts); } /** @@ -73,94 +85,4 @@ public function addPropertyToClass(Class_ $class, string $name, ?Type $type): vo $property = $this->nodeFactory->createPrivatePropertyFromNameAndType($name, $type); $this->addAsFirstMethod($class, $property); } - - public function addInjectPropertyToClass(Class_ $class, PropertyMetadata $propertyMetadata): void - { - $existingProperty = $class->getProperty($propertyMetadata->getName()); - if ($existingProperty instanceof Property) { - return; - } - - $property = $this->nodeFactory->createPublicInjectPropertyFromNameAndType( - $propertyMetadata->getName(), - $propertyMetadata->getType() - ); - $this->addAsFirstMethod($class, $property); - } - - public function addAsFirstTrait(Class_ $class, string $traitName): void - { - $traitUse = new TraitUse([new FullyQualified($traitName)]); - $this->addTraitUse($class, $traitUse); - } - - /** - * @param Stmt[] $nodes - * @return Stmt[] - */ - public function insertBefore(array $nodes, Stmt $stmt, int $key): array - { - array_splice($nodes, $key, 0, [$stmt]); - - return $nodes; - } - - private function isSuccessToInsertBeforeFirstMethod(Class_ $class, Stmt $stmt): bool - { - foreach ($class->stmts as $key => $classStmt) { - if (! $classStmt instanceof ClassMethod) { - continue; - } - - $class->stmts = $this->insertBefore($class->stmts, $stmt, $key); - - return true; - } - - return false; - } - - private function isSuccessToInsertAfterLastProperty(Class_ $class, Stmt $stmt): bool - { - $previousElement = null; - foreach ($class->stmts as $key => $classStmt) { - if ($previousElement instanceof Property && ! $classStmt instanceof Property) { - $class->stmts = $this->insertBefore($class->stmts, $stmt, $key); - - return true; - } - - $previousElement = $classStmt; - } - - return false; - } - - private function hasClassConstant(Class_ $class, string $constantName): bool - { - foreach ($class->getConstants() as $classConst) { - if ($this->nodeNameResolver->isName($classConst, $constantName)) { - return true; - } - } - - return false; - } - - private function addTraitUse(Class_ $class, TraitUse $traitUse): void - { - foreach (self::BEFORE_TRAIT_TYPES as $type) { - foreach ($class->stmts as $key => $classStmt) { - if (! $classStmt instanceof $type) { - continue; - } - - $class->stmts = $this->insertBefore($class->stmts, $traitUse, $key); - - return; - } - } - - $class->stmts[] = $traitUse; - } } diff --git a/src/NodeManipulator/ClassManipulator.php b/src/NodeManipulator/ClassManipulator.php index 088c2f62caf..6c8f680975c 100644 --- a/src/NodeManipulator/ClassManipulator.php +++ b/src/NodeManipulator/ClassManipulator.php @@ -2,59 +2,43 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; -use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\Interface_; -use PhpParser\Node\Stmt\Property; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\ObjectType; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\PostRector\Collector\NodesToRemoveCollector; -final class ClassManipulator +final readonly class ClassManipulator { public function __construct( private NodeNameResolver $nodeNameResolver, - private NodesToRemoveCollector $nodesToRemoveCollector, - private ReflectionProvider $reflectionProvider + private ReflectionProvider $reflectionProvider, ) { } - public function hasParentMethodOrInterface(ObjectType $objectType, string $methodName): bool + public function hasParentMethodOrInterface(ObjectType $objectType, string $oldMethod): bool { if (! $this->reflectionProvider->hasClass($objectType->getClassName())) { return false; } $classReflection = $this->reflectionProvider->getClass($objectType->getClassName()); - foreach ($classReflection->getAncestors() as $ancestorClassReflection) { - if ($classReflection === $ancestorClassReflection) { + $ancestorClassReflections = [...$classReflection->getParents(), ...$classReflection->getInterfaces()]; + foreach ($ancestorClassReflections as $ancestorClassReflection) { + if (! $ancestorClassReflection->hasMethod($oldMethod)) { continue; } - if ($ancestorClassReflection->hasMethod($methodName)) { - return true; - } + return true; } return false; } /** - * @return string[] + * @api phpunit */ - public function getPrivatePropertyNames(Class_ $class): array - { - $privateProperties = array_filter( - $class->getProperties(), - fn (Property $property): bool => $property->isPrivate() - ); - - return $this->nodeNameResolver->getNames($privateProperties); - } - public function hasTrait(Class_ $class, string $desiredTrait): bool { foreach ($class->getTraitUses() as $traitUse) { @@ -69,41 +53,4 @@ public function hasTrait(Class_ $class, string $desiredTrait): bool return false; } - - public function replaceTrait(Class_ $class, string $oldTrait, string $newTrait): void - { - foreach ($class->getTraitUses() as $traitUse) { - foreach ($traitUse->traits as $key => $traitTrait) { - if (! $this->nodeNameResolver->isName($traitTrait, $oldTrait)) { - continue; - } - - $traitUse->traits[$key] = new FullyQualified($newTrait); - break; - } - } - } - - /** - * @return string[] - */ - public function getClassLikeNodeParentInterfaceNames(Class_ | Interface_ $classLike): array - { - if ($classLike instanceof Class_) { - return $this->nodeNameResolver->getNames($classLike->implements); - } - - return $this->nodeNameResolver->getNames($classLike->extends); - } - - public function removeInterface(Class_ $class, string $desiredInterface): void - { - foreach ($class->implements as $implement) { - if (! $this->nodeNameResolver->isName($implement, $desiredInterface)) { - continue; - } - - $this->nodesToRemoveCollector->addNodeToRemove($implement); - } - } } diff --git a/src/NodeManipulator/ClassMethodAssignManipulator.php b/src/NodeManipulator/ClassMethodAssignManipulator.php index 704aac2ea85..0870ed85fb3 100644 --- a/src/NodeManipulator/ClassMethodAssignManipulator.php +++ b/src/NodeManipulator/ClassMethodAssignManipulator.php @@ -2,73 +2,28 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; -use PhpParser\Node; -use PhpParser\Node\Arg; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\ClosureUse; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\New_; -use PhpParser\Node\Expr\Variable; -use PhpParser\Node\FunctionLike; -use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\Foreach_; -use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParameterReflection; use PHPStan\Type\Type; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\Node\NodeFactory; -use Rector\Core\Reflection\ReflectionResolver; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Node\NodeFactory; final class ClassMethodAssignManipulator { /** - * @var array + * @var array */ private array $alreadyAddedClassMethodNames = []; public function __construct( - private BetterNodeFinder $betterNodeFinder, - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private NodeFactory $nodeFactory, - private NodeNameResolver $nodeNameResolver, - private VariableManipulator $variableManipulator, - private NodeComparator $nodeComparator, - private ReflectionResolver $reflectionResolver, - private ArrayDestructVariableFilter $arrayDestructVariableFilter + private readonly NodeFactory $nodeFactory, + private readonly NodeNameResolver $nodeNameResolver, ) { } - /** - * @return Assign[] - */ - public function collectReadyOnlyAssignScalarVariables(ClassMethod $classMethod): array - { - $assignsOfScalarOrArrayToVariable = $this->variableManipulator->collectScalarOrArrayAssignsOfVariable( - $classMethod - ); - - // filter out [$value] = $array, array destructing - $readOnlyVariableAssigns = $this->arrayDestructVariableFilter->filterOut( - $assignsOfScalarOrArrayToVariable, - $classMethod - ); - - $readOnlyVariableAssigns = $this->filterOutReferencedVariables($readOnlyVariableAssigns, $classMethod); - $readOnlyVariableAssigns = $this->filterOutMultiAssigns($readOnlyVariableAssigns); - $readOnlyVariableAssigns = $this->filterOutForeachVariables($readOnlyVariableAssigns); - - return $this->variableManipulator->filterOutChangedVariables($readOnlyVariableAssigns, $classMethod); - } - public function addParameterAndAssignToMethod( ClassMethod $classMethod, string $name, @@ -82,61 +37,8 @@ public function addParameterAndAssignToMethod( $classMethod->params[] = $this->nodeFactory->createParamFromNameAndType($name, $type); $classMethod->stmts[] = new Expression($assign); - $classMethodHash = spl_object_hash($classMethod); - $this->alreadyAddedClassMethodNames[$classMethodHash][] = $name; - } - - /** - * @param Assign[] $variableAssigns - * @return Assign[] - */ - private function filterOutReferencedVariables(array $variableAssigns, ClassMethod $classMethod): array - { - $referencedVariables = $this->collectReferenceVariableNames($classMethod); - - return array_filter( - $variableAssigns, - fn (Assign $assign): bool => ! $this->nodeNameResolver->isNames($assign->var, $referencedVariables) - ); - } - - /** - * E.g. $a = $b = $c = '...'; - * - * @param Assign[] $readOnlyVariableAssigns - * @return Assign[] - */ - private function filterOutMultiAssigns(array $readOnlyVariableAssigns): array - { - return array_filter($readOnlyVariableAssigns, function (Assign $assign): bool { - $parent = $assign->getAttribute(AttributeKey::PARENT_NODE); - return ! $parent instanceof Assign; - }); - } - - /** - * @param Assign[] $variableAssigns - * @return Assign[] - */ - private function filterOutForeachVariables(array $variableAssigns): array - { - foreach ($variableAssigns as $key => $variableAssign) { - $foreach = $this->findParentForeach($variableAssign); - if (! $foreach instanceof Foreach_) { - continue; - } - - if ($this->nodeComparator->areNodesEqual($foreach->valueVar, $variableAssign->var)) { - unset($variableAssigns[$key]); - continue; - } - - if ($this->nodeComparator->areNodesEqual($foreach->keyVar, $variableAssign->var)) { - unset($variableAssigns[$key]); - } - } - - return $variableAssigns; + $classMethodId = spl_object_id($classMethod); + $this->alreadyAddedClassMethodNames[$classMethodId][] = $name; } private function hasMethodParameter(ClassMethod $classMethod, string $name): bool @@ -147,174 +49,11 @@ private function hasMethodParameter(ClassMethod $classMethod, string $name): boo } } - $classMethodHash = spl_object_hash($classMethod); - if (! isset($this->alreadyAddedClassMethodNames[$classMethodHash])) { + $classMethodId = spl_object_id($classMethod); + if (! isset($this->alreadyAddedClassMethodNames[$classMethodId])) { return false; } - return in_array($name, $this->alreadyAddedClassMethodNames[$classMethodHash], true); - } - - /** - * @return string[] - */ - private function collectReferenceVariableNames(ClassMethod $classMethod): array - { - $referencedVariables = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod, function (Node $node) use ( - &$referencedVariables - ) { - if (! $node instanceof Variable) { - return null; - } - - if ($this->nodeNameResolver->isName($node, 'this')) { - return null; - } - - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode !== null && $this->isExplicitlyReferenced($parentNode)) { - /** @var string $variableName */ - $variableName = $this->nodeNameResolver->getName($node); - $referencedVariables[] = $variableName; - return null; - } - - $argumentPosition = null; - if ($parentNode instanceof Arg) { - $argumentPosition = $parentNode->getAttribute(AttributeKey::ARGUMENT_POSITION); - $parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE); - } - - if (! $parentNode instanceof Node) { - return null; - } - - if ($argumentPosition === null) { - return null; - } - - /** @var string $variableName */ - $variableName = $this->nodeNameResolver->getName($node); - - if ($this->isCallOrConstructorWithReference($parentNode, $node, $argumentPosition)) { - $referencedVariables[] = $variableName; - } - }); - - return $referencedVariables; - } - - private function findParentForeach(Assign $assign): ?Foreach_ - { - /** @var Foreach_|FunctionLike|null $foundNode */ - $foundNode = $this->betterNodeFinder->findFirstPreviousOfTypes($assign, [Foreach_::class, FunctionLike::class]); - if (! $foundNode instanceof Foreach_) { - return null; - } - - return $foundNode; - } - - private function isExplicitlyReferenced(Node $node): bool - { - if (! property_exists($node, 'byRef')) { - return false; - } - - if ($node instanceof Arg || $node instanceof ClosureUse || $node instanceof Param) { - return $node->byRef; - } - - return false; - } - - private function isCallOrConstructorWithReference(Node $node, Variable $variable, int $argumentPosition): bool - { - if ($this->isMethodCallWithReferencedArgument($node, $variable)) { - return true; - } - - if ($this->isFuncCallWithReferencedArgument($node, $variable)) { - return true; - } - return $this->isConstructorWithReference($node, $argumentPosition); - } - - private function isMethodCallWithReferencedArgument(Node $node, Variable $variable): bool - { - if (! $node instanceof MethodCall) { - return false; - } - - $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromMethodCall($node); - if (! $methodReflection instanceof MethodReflection) { - return false; - } - - $variableName = $this->nodeNameResolver->getName($variable); - - foreach ($methodReflection->getVariants() as $parametersAcceptor) { - foreach ($parametersAcceptor->getParameters() as $parameterReflection) { - if ($parameterReflection->getName() !== $variableName) { - continue; - } - - return $parameterReflection->passedByReference() - ->yes(); - } - } - - return false; - } - - /** - * Matches e.g: - * - array_shift($value) - * - sort($values) - */ - private function isFuncCallWithReferencedArgument(Node $node, Variable $variable): bool - { - if (! $node instanceof FuncCall) { - return false; - } - - if (! $this->nodeNameResolver->isNames($node, ['array_shift', '*sort'])) { - return false; - } - - // is 1t argument - return $node->args[0]->value !== $variable; - } - - private function isConstructorWithReference(Node $node, int $argumentPosition): bool - { - if (! $node instanceof New_) { - return false; - } - - return $this->isParameterReferencedInMethodReflection($node, $argumentPosition); - } - - private function isParameterReferencedInMethodReflection(New_ $new, int $argumentPosition): bool - { - $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromNew($new); - if (! $methodReflection instanceof MethodReflection) { - return false; - } - - foreach ($methodReflection->getVariants() as $parametersAcceptor) { - /** @var ParameterReflection $parameterReflection */ - foreach ($parametersAcceptor->getParameters() as $parameterPosition => $parameterReflection) { - if ($parameterPosition !== $argumentPosition) { - continue; - } - return $parameterReflection->passedByReference() - ->yes(); - } - } - - return false; + return in_array($name, $this->alreadyAddedClassMethodNames[$classMethodId], true); } } diff --git a/src/NodeManipulator/ClassMethodManipulator.php b/src/NodeManipulator/ClassMethodManipulator.php index 3c74ebe2655..f7a452f9b96 100644 --- a/src/NodeManipulator/ClassMethodManipulator.php +++ b/src/NodeManipulator/ClassMethodManipulator.php @@ -2,94 +2,48 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; -use PhpParser\Node; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Name\FullyQualified; -use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use PHPStan\Type\ObjectType; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\ValueObject\MethodName; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\Reflection\ReflectionResolver; +use Rector\ValueObject\MethodName; -final class ClassMethodManipulator +final readonly class ClassMethodManipulator { public function __construct( - private BetterNodeFinder $betterNodeFinder, private NodeNameResolver $nodeNameResolver, - private NodeTypeResolver $nodeTypeResolver, - private NodeComparator $nodeComparator, - private FuncCallManipulator $funcCallManipulator + private ReflectionResolver $reflectionResolver, ) { } - public function isParameterUsedInClassMethod(Param $param, ClassMethod $classMethod): bool - { - $isUsedDirectly = (bool) $this->betterNodeFinder->findFirst( - (array) $classMethod->stmts, - fn (Node $node): bool => $this->nodeComparator->areNodesEqual($node, $param->var) - ); - - if ($isUsedDirectly) { - return true; - } - - /** @var FuncCall[] $compactFuncCalls */ - $compactFuncCalls = $this->betterNodeFinder->find((array) $classMethod->stmts, function (Node $node): bool { - if (! $node instanceof FuncCall) { - return false; - } - - return $this->nodeNameResolver->isName($node, 'compact'); - }); - - $arguments = $this->funcCallManipulator->extractArgumentsFromCompactFuncCalls($compactFuncCalls); - - return $this->nodeNameResolver->isNames($param, $arguments); - } - public function isNamedConstructor(ClassMethod $classMethod): bool { if (! $this->nodeNameResolver->isName($classMethod, MethodName::CONSTRUCT)) { return false; } - $classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if (! $classReflection instanceof ClassReflection) { return false; } + if ($classMethod->isPrivate()) { return true; } - if ($classLike->isFinal()) { + + if ($classReflection->isFinalByKeyword()) { return false; } + return $classMethod->isProtected(); } - public function hasParentMethodOrInterfaceMethod(ClassMethod $classMethod, ?string $methodName = null): bool + public function hasParentMethodOrInterfaceMethod(Class_ $class, string $methodName): bool { - $methodName ??= $this->nodeNameResolver->getName($classMethod->name); - if ($methodName === null) { - return false; - } - - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return false; - } - - $classReflection = $scope->getClassReflection(); + $classReflection = $this->reflectionResolver->resolveClassReflection($class); if (! $classReflection instanceof ClassReflection) { return false; } @@ -98,76 +52,18 @@ public function hasParentMethodOrInterfaceMethod(ClassMethod $classMethod, ?stri if ($parentClassReflection->hasMethod($methodName)) { return true; } - } - foreach ($classReflection->getInterfaces() as $interfaceReflection) { - if ($interfaceReflection->hasMethod($methodName)) { + if ($parentClassReflection->hasMethod(MethodName::CALL)) { return true; } } - return false; - } - - /** - * @param string[] $possibleNames - */ - public function addMethodParameterIfMissing(Node $node, ObjectType $objectType, array $possibleNames): string - { - $classMethodNode = $node->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethodNode instanceof ClassMethod) { - // or null? - throw new ShouldNotHappenException(); - } - - foreach ($classMethodNode->params as $paramNode) { - if (! $this->nodeTypeResolver->isObjectType($paramNode, $objectType)) { - continue; - } - - $paramName = $this->nodeNameResolver->getName($paramNode); - if (! is_string($paramName)) { - throw new ShouldNotHappenException(); - } - - return $paramName; - } - - $paramName = $this->resolveName($classMethodNode, $possibleNames); - $classMethodNode->params[] = new Param(new Variable($paramName), null, new FullyQualified( - $objectType->getClassName() - )); - - return $paramName; - } - - public function isPropertyPromotion(ClassMethod $classMethod): bool - { - foreach ($classMethod->params as $param) { - /** @var Param $param */ - if ($param->flags !== 0) { + foreach ($classReflection->getInterfaces() as $interfaceReflection) { + if ($interfaceReflection->hasMethod($methodName)) { return true; } } return false; } - - /** - * @param string[] $possibleNames - */ - private function resolveName(ClassMethod $classMethod, array $possibleNames): string - { - foreach ($possibleNames as $possibleName) { - foreach ($classMethod->params as $paramNode) { - if ($this->nodeNameResolver->isName($paramNode, $possibleName)) { - continue 2; - } - } - - return $possibleName; - } - - throw new ShouldNotHappenException(); - } } diff --git a/src/NodeManipulator/ClassMethodPropertyFetchManipulator.php b/src/NodeManipulator/ClassMethodPropertyFetchManipulator.php index 395af95f616..c45d19421b5 100644 --- a/src/NodeManipulator/ClassMethodPropertyFetchManipulator.php +++ b/src/NodeManipulator/ClassMethodPropertyFetchManipulator.php @@ -2,23 +2,28 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor; use Rector\NodeNameResolver\NodeNameResolver; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; -final class ClassMethodPropertyFetchManipulator +final readonly class ClassMethodPropertyFetchManipulator { public function __construct( private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, + private FunctionLikeManipulator $functionLikeManipulator ) { } @@ -31,17 +36,25 @@ public function __construct( * ↓ * (SomeType $anotherValue) */ - public function resolveParamForPropertyFetch(ClassMethod $classMethod, string $propertyName): ?Param + public function findParamAssignToPropertyName(ClassMethod $classMethod, string $propertyName): ?Param { $assignedParamName = null; $this->simpleCallableNodeTraverser->traverseNodesWithCallable( (array) $classMethod->stmts, function (Node $node) use ($propertyName, &$assignedParamName): ?int { + if ($node instanceof Class_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + if (! $node instanceof Assign) { return null; } + if (! $node->var instanceof PropertyFetch && ! $node->var instanceof StaticPropertyFetch) { + return null; + } + if (! $this->nodeNameResolver->isName($node->var, $propertyName)) { return null; } @@ -52,7 +65,7 @@ function (Node $node) use ($propertyName, &$assignedParamName): ?int { $assignedParamName = $this->nodeNameResolver->getName($node->expr); - return NodeTraverser::STOP_TRAVERSAL; + return NodeVisitor::STOP_TRAVERSAL; } ); @@ -72,4 +85,50 @@ function (Node $node) use ($propertyName, &$assignedParamName): ?int { return null; } + + /** + * E.g.: + * $this->value = 1000; + * ↓ + * (int $value) + * + * @return Expr[] + */ + public function findAssignsToPropertyName(ClassMethod $classMethod, string $propertyName): array + { + $assignExprs = []; + + $paramNames = $this->functionLikeManipulator->resolveParamNames($classMethod); + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + (array) $classMethod->stmts, + function (Node $node) use ($propertyName, &$assignExprs, $paramNames): ?int { + if ($node instanceof Class_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $node instanceof Assign) { + return null; + } + + if (! $node->var instanceof PropertyFetch && ! $node->var instanceof StaticPropertyFetch) { + return null; + } + + if (! $this->nodeNameResolver->isName($node->var, $propertyName)) { + return null; + } + + // skip param assigns + if ($this->nodeNameResolver->isNames($node->expr, $paramNames)) { + return null; + } + + $assignExprs[] = $node->expr; + return null; + } + ); + + return $assignExprs; + } } diff --git a/src/NodeManipulator/Dependency/DependencyClassMethodDecorator.php b/src/NodeManipulator/Dependency/DependencyClassMethodDecorator.php deleted file mode 100644 index b15a597fbca..00000000000 --- a/src/NodeManipulator/Dependency/DependencyClassMethodDecorator.php +++ /dev/null @@ -1,111 +0,0 @@ -nodeNameResolver->getName($class); - if ($className === null) { - return; - } - - if (! $this->reflectionProvider->hasClass($className)) { - return; - } - - $classReflection = $this->reflectionProvider->getClass($className); - - foreach ($classReflection->getParents() as $parentClassReflection) { - if (! $parentClassReflection->hasMethod(MethodName::CONSTRUCT)) { - continue; - } - - $constructorMethodReflection = $parentClassReflection->getMethod(MethodName::CONSTRUCT, $scope); - $parentConstructorClassMethod = $this->astResolver->resolveClassMethodFromMethodReflection( - $constructorMethodReflection - ); - - if (! $parentConstructorClassMethod instanceof ClassMethod) { - continue; - } - - $this->completeParentConstructorBasedOnParentNode($classMethod, $parentConstructorClassMethod); - - break; - } - } - - private function completeParentConstructorBasedOnParentNode( - ClassMethod $classMethod, - ClassMethod $parentClassMethod - ): void { - $paramsWithoutDefaultValue = []; - foreach ($parentClassMethod->params as $param) { - if ($param->default !== null) { - break; - } - - $paramsWithoutDefaultValue[] = $param; - } - - $cleanParams = $this->cleanParamsFromVisibilityAndAttributes($paramsWithoutDefaultValue); - - // replicate parent parameters - if ($cleanParams !== []) { - $classMethod->params = array_merge($cleanParams, $classMethod->params); - } - - $staticCall = $this->nodeFactory->createParentConstructWithParams($cleanParams); - $classMethod->stmts[] = new Expression($staticCall); - } - - /** - * @param Param[] $params - * @return Param[] - */ - private function cleanParamsFromVisibilityAndAttributes(array $params): array - { - $cleanParams = $this->promotedPropertyParamCleaner->cleanFromFlags($params); - - // remove deep attributes to avoid bugs with nested tokens re-print - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($cleanParams, function (Node $node): void { - $node->setAttributes([]); - }); - - return $cleanParams; - } -} diff --git a/src/NodeManipulator/ForeachManipulator.php b/src/NodeManipulator/ForeachManipulator.php deleted file mode 100644 index e09b766782d..00000000000 --- a/src/NodeManipulator/ForeachManipulator.php +++ /dev/null @@ -1,26 +0,0 @@ -stmts; - - if (count($stmts) !== 1) { - return null; - } - - $innerNode = $stmts[0]; - $innerNode = $innerNode instanceof Expression ? $innerNode->expr : $innerNode; - - return $callable($innerNode, $foreach); - } -} diff --git a/src/NodeManipulator/FuncCallManipulator.php b/src/NodeManipulator/FuncCallManipulator.php index 111f6240fb0..2f2f92eae00 100644 --- a/src/NodeManipulator/FuncCallManipulator.php +++ b/src/NodeManipulator/FuncCallManipulator.php @@ -2,12 +2,13 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; -use Rector\Core\PhpParser\Node\Value\ValueResolver; +use Rector\PhpParser\Node\Value\ValueResolver; -final class FuncCallManipulator +final readonly class FuncCallManipulator { public function __construct( private ValueResolver $valueResolver @@ -23,6 +24,10 @@ public function extractArgumentsFromCompactFuncCalls(array $compactFuncCalls): a $arguments = []; foreach ($compactFuncCalls as $compactFuncCall) { foreach ($compactFuncCall->args as $arg) { + if (! $arg instanceof Arg) { + continue; + } + $value = $this->valueResolver->getValue($arg->value); if ($value === null) { continue; diff --git a/src/NodeManipulator/FunctionLikeManipulator.php b/src/NodeManipulator/FunctionLikeManipulator.php index 440913b07d9..ff075585715 100644 --- a/src/NodeManipulator/FunctionLikeManipulator.php +++ b/src/NodeManipulator/FunctionLikeManipulator.php @@ -2,57 +2,29 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; -use PhpParser\Node; use PhpParser\Node\FunctionLike; -use PhpParser\Node\Stmt\Function_; -use PhpParser\Node\Stmt\Return_; -use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; -final class FunctionLikeManipulator +final readonly class FunctionLikeManipulator { public function __construct( - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, private NodeNameResolver $nodeNameResolver, - private PropertyFetchAnalyzer $propertyFetchAnalyzer ) { } /** * @return string[] */ - public function getReturnedLocalPropertyNames(FunctionLike $functionLike): array + public function resolveParamNames(FunctionLike $functionLike): array { - // process only class methods - if ($functionLike instanceof Function_) { - return []; - } - - $returnedLocalPropertyNames = []; - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($functionLike, function (Node $node) use ( - &$returnedLocalPropertyNames - ) { - if (! $node instanceof Return_) { - return null; - } - if ($node->expr === null) { - return null; - } - if (! $this->propertyFetchAnalyzer->isLocalPropertyFetch($node->expr)) { - return null; - } + $paramNames = []; - $propertyName = $this->nodeNameResolver->getName($node->expr); - if ($propertyName === null) { - return null; - } - - $returnedLocalPropertyNames[] = $propertyName; - }); + foreach ($functionLike->getParams() as $param) { + $paramNames[] = $this->nodeNameResolver->getName($param); + } - return $returnedLocalPropertyNames; + return $paramNames; } } diff --git a/src/NodeManipulator/IfManipulator.php b/src/NodeManipulator/IfManipulator.php index c860c833af4..92f239ff3ed 100644 --- a/src/NodeManipulator/IfManipulator.php +++ b/src/NodeManipulator/IfManipulator.php @@ -2,37 +2,29 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; -use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\BinaryOp\Identical; +use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\Exit_; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\Node\Value\ValueResolver; -use Rector\EarlyReturn\NodeTransformer\ConditionInverter; -use Rector\NodeNameResolver\NodeNameResolver; +use Rector\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Node\Value\ValueResolver; -final class IfManipulator +final readonly class IfManipulator { public function __construct( private BetterNodeFinder $betterNodeFinder, - private NodeNameResolver $nodeNameResolver, private StmtsManipulator $stmtsManipulator, private ValueResolver $valueResolver, - private ConditionInverter $conditionInverter, private NodeComparator $nodeComparator ) { } @@ -46,12 +38,11 @@ public function __construct( */ public function matchIfNotNullReturnValue(If_ $if): ?Expr { - $stmts = $if->stmts; - if (count($stmts) !== 1) { + if (count($if->stmts) !== 1) { return null; } - $insideIfNode = $stmts[0]; + $insideIfNode = $if->stmts[0]; if (! $insideIfNode instanceof Return_) { return null; } @@ -64,80 +55,7 @@ public function matchIfNotNullReturnValue(If_ $if): ?Expr } /** - * Matches: - * - * if (<$value> !== null) { - * $anotherValue = $value; - * } - */ - public function matchIfNotNullNextAssignment(If_ $if): ?Assign - { - if ($if->stmts === []) { - return null; - } - - if (! $if->cond instanceof NotIdentical) { - return null; - } - - if (! $this->isNotIdenticalNullCompare($if->cond)) { - return null; - } - - $insideIfNode = $if->stmts[0]; - if (! $insideIfNode instanceof Expression) { - return null; - } - - $assignedExpr = $insideIfNode->expr; - if (! $assignedExpr instanceof Assign) { - return null; - } - - return $assignedExpr; - } - - /** - * Matches: - * - * if (<$value> === null) { - * return null; - * } - * - * if (<$value> === 53;) { - * return 53; - * } - */ - public function matchIfValueReturnValue(If_ $if): ?Expr - { - $stmts = $if->stmts; - - if (count($stmts) !== 1) { - return null; - } - - $insideIfNode = $stmts[0]; - if (! $insideIfNode instanceof Return_) { - return null; - } - - if (! $if->cond instanceof Identical) { - return null; - } - - if ($this->nodeComparator->areNodesEqual($if->cond->left, $insideIfNode->expr)) { - return $if->cond->right; - } - - if ($this->nodeComparator->areNodesEqual($if->cond->right, $insideIfNode->expr)) { - return $if->cond->left; - } - - return null; - } - - /** - * @return mixed[] + * @return If_[] */ public function collectNestedIfsWithOnlyReturn(If_ $if): array { @@ -147,6 +65,7 @@ public function collectNestedIfsWithOnlyReturn(If_ $if): array while ($this->isIfWithOnlyStmtIf($currentIf)) { $ifs[] = $currentIf; + /** @var If_ $currentIf */ $currentIf = $currentIf->stmts[0]; } @@ -158,7 +77,7 @@ public function collectNestedIfsWithOnlyReturn(If_ $if): array return []; } - // last node is with the return value + // last if is with the return value $ifs[] = $currentIf; return $ifs; @@ -166,7 +85,7 @@ public function collectNestedIfsWithOnlyReturn(If_ $if): array public function isIfAndElseWithSameVariableAssignAsLastStmts(If_ $if, Expr $desiredExpr): bool { - if ($if->else === null) { + if (! $if->else instanceof Else_) { return false; } @@ -174,42 +93,25 @@ public function isIfAndElseWithSameVariableAssignAsLastStmts(If_ $if, Expr $desi return false; } - $lastIfStmt = $this->stmtsManipulator->getUnwrappedLastStmt($if->stmts); - if (! $lastIfStmt instanceof Assign) { + $lastIfNode = $this->stmtsManipulator->getUnwrappedLastStmt($if->stmts); + if (! $lastIfNode instanceof Assign) { return false; } - $lastElseStmt = $this->stmtsManipulator->getUnwrappedLastStmt($if->else->stmts); - if (! $lastElseStmt instanceof Assign) { + $lastElseNode = $this->stmtsManipulator->getUnwrappedLastStmt($if->else->stmts); + if (! $lastElseNode instanceof Assign) { return false; } - if (! $lastIfStmt->var instanceof Variable) { + if (! $lastIfNode->var instanceof Variable) { return false; } - if (! $this->nodeComparator->areNodesEqual($lastIfStmt->var, $lastElseStmt->var)) { + if (! $this->nodeComparator->areNodesEqual($lastIfNode->var, $lastElseNode->var)) { return false; } - return $this->nodeComparator->areNodesEqual($desiredExpr, $lastElseStmt->var); - } - /** - * Matches: - * if () { - * } else { - * } - */ - public function isIfOrIfElseWithFunctionCondition(If_ $if, string $functionName): bool - { - if ((bool) $if->elseifs) { - return false; - } - - if (! $if->cond instanceof FuncCall) { - return false; - } - return $this->nodeNameResolver->isName($if->cond, $functionName); + return $this->nodeComparator->areNodesEqual($desiredExpr, $lastElseNode->var); } /** @@ -226,12 +128,17 @@ public function collectNestedIfsWithNonBreaking(Foreach_ $foreach): array return []; } + if ($onlyForeachStmt->cond instanceof BooleanOr) { + return []; + } + $ifs = []; $currentIf = $onlyForeachStmt; while ($this->isIfWithOnlyStmtIf($currentIf)) { $ifs[] = $currentIf; + /** @var If_ $currentIf */ $currentIf = $currentIf->stmts[0]; } @@ -239,19 +146,12 @@ public function collectNestedIfsWithNonBreaking(Foreach_ $foreach): array if (! $this->isIfWithoutElseAndElseIfs($currentIf)) { return []; } - $betterNodeFinderFindInstanceOf = $this->betterNodeFinder->findInstanceOf($currentIf->stmts, Return_::class); - if ($betterNodeFinderFindInstanceOf !== []) { + if ($this->betterNodeFinder->hasInstancesOf($currentIf->stmts, [Return_::class, Exit_::class])) { return []; } - /** @var Exit_[] $exits */ - $exits = $this->betterNodeFinder->findInstanceOf($currentIf->stmts, Exit_::class); - if ($exits !== []) { - return []; - } - - // last node is with the expression + // last if is with the expression $ifs[] = $currentIf; @@ -259,79 +159,24 @@ public function collectNestedIfsWithNonBreaking(Foreach_ $foreach): array } /** - * @param class-string $className + * @param class-string $stmtClass */ - public function isIfWithOnly(Node $node, string $className): bool - { - if (! $node instanceof If_) { - return false; - } - - if (! $this->isIfWithoutElseAndElseIfs($node)) { - return false; - } - - return $this->hasOnlyStmtOfType($node, $className); - } - - public function isIfWithOnlyOneStmt(If_ $if): bool - { - return count($if->stmts) === 1; - } - - public function isIfCondUsingAssignIdenticalVariable(Node $if, Node $assign): bool + public function isIfWithOnly(If_ $if, string $stmtClass): bool { - if (! ($if instanceof If_ && $assign instanceof Assign)) { - return false; - } - - if (! $if->cond instanceof Identical) { + if (! $this->isIfWithoutElseAndElseIfs($if)) { return false; } - return $this->nodeComparator->areNodesEqual($this->getIfCondVar($if), $assign->var); - } - - public function isIfCondUsingAssignNotIdenticalVariable(If_ $if, Node $node): bool - { - if (! $node instanceof MethodCall && ! $node instanceof PropertyFetch) { - return false; - } - if (! $if->cond instanceof NotIdentical) { - return false; - } - return ! $this->nodeComparator->areNodesEqual($this->getIfCondVar($if), $node->var); + return $this->hasOnlyStmtOfType($if, $stmtClass); } public function isIfWithoutElseAndElseIfs(If_ $if): bool { - if ($if->else !== null) { + if ($if->else instanceof Else_) { return false; } - return ! (bool) $if->elseifs; - } - - public function createIfNegation(Expr $expr, Stmt $stmt): If_ - { - $expr = $this->conditionInverter->createInvertedCondition($expr); - - return new If_( - $expr, - [ - 'stmts' => [$stmt], - ] - ); - } - - public function createIfExpr(Expr $expr, Stmt $stmt): If_ - { - return new If_( - $expr, - [ - 'stmts' => [$stmt], - ] - ); + return $if->elseifs === []; } private function matchComparedAndReturnedNode(NotIdentical $notIdentical, Return_ $return): ?Expr @@ -346,6 +191,7 @@ private function matchComparedAndReturnedNode(NotIdentical $notIdentical, Return if (! $this->nodeComparator->areNodesEqual($notIdentical->right, $return->expr)) { return null; } + if ($this->valueResolver->isNull($notIdentical->left)) { return $notIdentical->right; } @@ -353,17 +199,6 @@ private function matchComparedAndReturnedNode(NotIdentical $notIdentical, Return return null; } - private function isNotIdenticalNullCompare(NotIdentical $notIdentical): bool - { - if ($this->nodeComparator->areNodesEqual($notIdentical->left, $notIdentical->right)) { - return false; - } - if ($this->valueResolver->isNull($notIdentical->right)) { - return true; - } - return $this->valueResolver->isNull($notIdentical->left); - } - private function isIfWithOnlyStmtIf(If_ $if): bool { if (! $this->isIfWithoutElseAndElseIfs($if)) { @@ -374,23 +209,15 @@ private function isIfWithOnlyStmtIf(If_ $if): bool } /** - * @param class-string $desiredType + * @param class-string $stmtClass */ - private function hasOnlyStmtOfType(If_ $if, string $desiredType): bool + private function hasOnlyStmtOfType(If_ $if, string $stmtClass): bool { $stmts = $if->stmts; if (count($stmts) !== 1) { return false; } - return is_a($stmts[0], $desiredType); - } - - private function getIfCondVar(If_ $if): Expr - { - /** @var Identical|NotIdentical $ifCond */ - $ifCond = $if->cond; - - return $this->valueResolver->isNull($ifCond->left) ? $ifCond->right : $ifCond->left; + return $stmts[0] instanceof $stmtClass; } } diff --git a/src/NodeManipulator/MagicPropertyFetchAnalyzer.php b/src/NodeManipulator/MagicPropertyFetchAnalyzer.php deleted file mode 100644 index b5f5a10883c..00000000000 --- a/src/NodeManipulator/MagicPropertyFetchAnalyzer.php +++ /dev/null @@ -1,88 +0,0 @@ -property" - */ -final class MagicPropertyFetchAnalyzer -{ - public function __construct( - private NodeNameResolver $nodeNameResolver, - private NodeTypeResolver $nodeTypeResolver, - private ReflectionProvider $reflectionProvider - ) { - } - - public function isMagicOnType(PropertyFetch | StaticPropertyFetch $expr, Type $type): bool - { - $varNodeType = $this->nodeTypeResolver->resolve($expr); - - if ($varNodeType instanceof ErrorType) { - return true; - } - - if ($varNodeType instanceof MixedType) { - return false; - } - - if ($varNodeType->isSuperTypeOf($type)->yes()) { - return false; - } - - $nodeName = $this->nodeNameResolver->getName($expr->name); - if ($nodeName === null) { - return false; - } - - return ! $this->hasPublicProperty($expr, $nodeName); - } - - private function hasPublicProperty(PropertyFetch | StaticPropertyFetch $expr, string $propertyName): bool - { - $scope = $expr->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - throw new ShouldNotHappenException(); - } - - if ($expr instanceof PropertyFetch) { - $propertyFetchType = $scope->getType($expr->var); - } else { - $propertyFetchType = $this->nodeTypeResolver->resolve($expr->class); - } - - if (! $propertyFetchType instanceof TypeWithClassName) { - return false; - } - - $propertyFetchType = $propertyFetchType->getClassName(); - if (! $this->reflectionProvider->hasClass($propertyFetchType)) { - return false; - } - - $classReflection = $this->reflectionProvider->getClass($propertyFetchType); - if (! $classReflection->hasProperty($propertyName)) { - return false; - } - - $propertyReflection = $classReflection->getProperty($propertyName, $scope); - return $propertyReflection->isPublic(); - } -} diff --git a/src/NodeManipulator/MethodCallManipulator.php b/src/NodeManipulator/MethodCallManipulator.php deleted file mode 100644 index cce9aebb5a3..00000000000 --- a/src/NodeManipulator/MethodCallManipulator.php +++ /dev/null @@ -1,184 +0,0 @@ -findMethodCallsOnVariable($variable); - - $methodCallNamesOnVariable = []; - foreach ($methodCallsOnVariable as $methodCallOnVariable) { - $methodName = $this->nodeNameResolver->getName($methodCallOnVariable->name); - if ($methodName === null) { - continue; - } - - $methodCallNamesOnVariable[] = $methodName; - } - - return array_unique($methodCallNamesOnVariable); - } - - /** - * @return MethodCall[] - */ - public function findMethodCallsIncludingChain(MethodCall $methodCall): array - { - $chainMethodCalls = []; - - // 1. collect method chain call - $currentMethodCallee = $methodCall->var; - while ($currentMethodCallee instanceof MethodCall) { - $chainMethodCalls[] = $currentMethodCallee; - $currentMethodCallee = $currentMethodCallee->var; - } - - // 2. collect on-same-variable calls - $onVariableMethodCalls = []; - if ($currentMethodCallee instanceof Variable) { - $onVariableMethodCalls = $this->findMethodCallsOnVariable($currentMethodCallee); - } - - $methodCalls = array_merge($chainMethodCalls, $onVariableMethodCalls); - - return $this->uniquateObjects($methodCalls); - } - - public function findAssignToVariable(Variable $variable): ?Assign - { - $parentNode = $variable->getAttribute(AttributeKey::PARENT_NODE); - if (! $parentNode instanceof Node) { - return null; - } - - $variableName = $this->nodeNameResolver->getName($variable); - if ($variableName === null) { - return null; - } - - do { - $assign = $this->findAssignToVariableName($parentNode, $variableName); - if ($assign instanceof Assign) { - return $assign; - } - - $parentNode = $this->resolvePreviousNodeInSameScope($parentNode); - } while ($parentNode instanceof Node && ! $parentNode instanceof FunctionLike); - - return null; - } - - /** - * @return MethodCall[] - */ - public function findMethodCallsOnVariable(Variable $variable): array - { - // get scope node, e.g. parent function call, method call or anonymous function - $classMethod = $variable->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return []; - } - - $variableName = $this->nodeNameResolver->getName($variable); - if ($variableName === null) { - return []; - } - - return $this->betterNodeFinder->find((array) $classMethod->stmts, function (Node $node) use ( - $variableName - ): bool { - if (! $node instanceof MethodCall) { - return false; - } - - // cover fluent interfaces too - $callerNode = $this->fluentChainMethodCallNodeAnalyzer->resolveRootExpr($node); - if (! $callerNode instanceof Variable) { - return false; - } - - return $this->nodeNameResolver->isName($callerNode, $variableName); - }); - } - - /** - * @see https://stackoverflow.com/a/4507991/1348344 - * @param object[] $objects - * @return object[] - * - * @template T - * @phpstan-param array|T[] $objects - * @phpstan-return array|T[] - */ - private function uniquateObjects(array $objects): array - { - $uniqueObjects = []; - foreach ($objects as $object) { - if (in_array($object, $uniqueObjects, true)) { - continue; - } - - $uniqueObjects[] = $object; - } - - // re-index - return array_values($uniqueObjects); - } - - private function findAssignToVariableName(Node $node, string $variableName): ?Node - { - return $this->betterNodeFinder->findFirst($node, function (Node $node) use ($variableName): bool { - if (! $node instanceof Assign) { - return false; - } - - if (! $node->var instanceof Variable) { - return false; - } - - return $this->nodeNameResolver->isName($node->var, $variableName); - }); - } - - private function resolvePreviousNodeInSameScope(Node $parentNode): ?Node - { - $previousParentNode = $parentNode; - $parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE); - - if (! $parentNode instanceof FunctionLike) { - // is about to leave → try previous expression - $previousStatement = $previousParentNode->getAttribute(AttributeKey::PREVIOUS_STATEMENT); - if ($previousStatement instanceof Expression) { - return $previousStatement->expr; - } - } - - return $parentNode; - } -} diff --git a/src/NodeManipulator/NullsafeManipulator.php b/src/NodeManipulator/NullsafeManipulator.php deleted file mode 100644 index 66411e85553..00000000000 --- a/src/NodeManipulator/NullsafeManipulator.php +++ /dev/null @@ -1,44 +0,0 @@ -var, $expr->name, $expr->args); - } - - if ($expr instanceof PropertyFetch) { - return new NullsafePropertyFetch($expr->var, $expr->name); - } - - return null; - } - - public function processNullSafeExprResult(?Expr $expr, Identifier $nextExprIdentifier): ?Expr - { - if ($expr === null) { - return null; - } - - $parentIdentifier = $nextExprIdentifier->getAttribute(AttributeKey::PARENT_NODE); - - if ($parentIdentifier instanceof MethodCall || $parentIdentifier instanceof NullsafeMethodCall) { - return new NullsafeMethodCall($expr, $nextExprIdentifier, $parentIdentifier->args); - } - - return new NullsafePropertyFetch($expr, $nextExprIdentifier); - } -} diff --git a/src/NodeManipulator/PropertyFetchAssignManipulator.php b/src/NodeManipulator/PropertyFetchAssignManipulator.php index 1f61c4bc8de..a8227767b40 100644 --- a/src/NodeManipulator/PropertyFetchAssignManipulator.php +++ b/src/NodeManipulator/PropertyFetchAssignManipulator.php @@ -2,75 +2,66 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; use PhpParser\Node; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\AssignOp; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Property; +use PhpParser\NodeVisitor; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\ValueObject\MethodName; -final class PropertyFetchAssignManipulator +final readonly class PropertyFetchAssignManipulator { public function __construct( private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, + private PropertyFetchAnalyzer $propertyFetchAnalyzer ) { } - /** - * @return string[] - */ - public function getPropertyNamesOfAssignOfVariable(Node $node, string $paramName): array + public function isAssignedMultipleTimesInConstructor(Class_ $class, Property $property): bool { - $propertyNames = []; + $classMethod = $class->getMethod(MethodName::CONSTRUCT); + if (! $classMethod instanceof ClassMethod) { + return false; + } - $this->simpleCallableNodeTraverser->traverseNodesWithCallable($node, function (Node $node) use ( - $paramName, - &$propertyNames - ) { - if (! $node instanceof Assign) { - return null; - } + $count = 0; + $propertyName = $this->nodeNameResolver->getName($property); - if (! $this->isVariableAssignToThisPropertyFetch($node, $paramName)) { - return null; - } + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + (array) $classMethod->getStmts(), + function (Node $node) use ($propertyName, &$count): ?int { + // skip anonymous classes and inner function + if ($node instanceof Class_ || $node instanceof Function_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } - /** @var Assign $node */ - $propertyName = $this->nodeNameResolver->getName($node->expr); - if ($propertyName) { - $propertyNames[] = $propertyName; - } + if (! $node instanceof Assign && ! $node instanceof AssignOp) { + return null; + } - return null; - }); + if (! $this->propertyFetchAnalyzer->isLocalPropertyFetchName($node->var, $propertyName)) { + return null; + } - return $propertyNames; - } + ++$count; - /** - * Matches: - * "$this->someValue = $;" - */ - private function isVariableAssignToThisPropertyFetch(Assign $assign, string $variableName): bool - { - if (! $assign->expr instanceof Variable) { - return false; - } + if ($count === 2) { + return NodeVisitor::STOP_TRAVERSAL; + } - if (! $this->nodeNameResolver->isName($assign->expr, $variableName)) { - return false; - } - - if (! $assign->var instanceof PropertyFetch) { - return false; - } - - $propertyFetch = $assign->var; + return null; + } + ); - // must be local property - return $this->nodeNameResolver->isName($propertyFetch->var, 'this'); + return $count === 2; } } diff --git a/src/NodeManipulator/PropertyManipulator.php b/src/NodeManipulator/PropertyManipulator.php index 9f99779ae9e..50f27addc7e 100644 --- a/src/NodeManipulator/PropertyManipulator.php +++ b/src/NodeManipulator/PropertyManipulator.php @@ -2,116 +2,153 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; +use Doctrine\ORM\Mapping\Table; use PhpParser\Node; -use PhpParser\Node\Arg; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\PostDec; -use PhpParser\Node\Expr\PostInc; -use PhpParser\Node\Expr\PreDec; -use PhpParser\Node\Expr\PreInc; use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Param; -use PhpParser\Node\Stmt\ClassLike; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use PHPStan\Reflection\ParametersAcceptorSelector; +use PhpParser\Node\Stmt\Trait_; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Type\ObjectType; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder; -use Rector\Core\Reflection\ReflectionResolver; -use Rector\Core\ValueObject\MethodName; +use Rector\Enum\ClassName; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\NodeNestingScope\ContextAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\ReadWrite\Guard\VariableToConstantGuard; -use Rector\ReadWrite\NodeAnalyzer\ReadWritePropertyAnalyzer; -use Symplify\PackageBuilder\Php\TypeChecker; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; +use Rector\Php80\NodeAnalyzer\PromotedPropertyResolver; +use Rector\PhpParser\AstResolver; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\NodeFinder\PropertyFetchFinder; +use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector; +use Rector\ValueObject\MethodName; /** * For inspiration to improve this service, * @see examples of variable modifications in https://wiki.php.net/rfc/readonly_properties_v2#proposal */ -final class PropertyManipulator +final readonly class PropertyManipulator { + /** + * @var string[]|class-string[] + */ + private const array ALLOWED_NOT_READONLY_CLASS_ANNOTATIONS = [ + 'ApiPlatform\Core\Annotation\ApiResource', + 'ApiPlatform\Metadata\ApiResource', + 'Doctrine\ORM\Mapping\Entity', + 'Doctrine\ORM\Mapping\Table', + 'Doctrine\ORM\Mapping\MappedSuperclass', + 'Doctrine\ORM\Mapping\Embeddable', + // Deprecated in ODM 2.16 + 'Doctrine\ODM\MongoDB\Mapping\Annotations\Document', + 'Doctrine\ODM\MongoDB\Mapping\Annotations\EmbeddedDocument', + // New in ODM 2.16 + 'Doctrine\ODM\MongoDB\Mapping\Attribute\Document', + 'Doctrine\ODM\MongoDB\Mapping\Attribute\EmbeddedDocument', + ]; + public function __construct( - private AssignManipulator $assignManipulator, private BetterNodeFinder $betterNodeFinder, - private VariableToConstantGuard $variableToConstantGuard, - private ReadWritePropertyAnalyzer $readWritePropertyAnalyzer, private PhpDocInfoFactory $phpDocInfoFactory, - private TypeChecker $typeChecker, private PropertyFetchFinder $propertyFetchFinder, - private ReflectionResolver $reflectionResolver, - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, + private PhpAttributeAnalyzer $phpAttributeAnalyzer, + private NodeTypeResolver $nodeTypeResolver, + private PromotedPropertyResolver $promotedPropertyResolver, + private ConstructorAssignDetector $constructorAssignDetector, + private AstResolver $astResolver, + private PropertyFetchAnalyzer $propertyFetchAnalyzer, + private ContextAnalyzer $contextAnalyzer ) { } - public function isPropertyUsedInReadContext(Property | Param $propertyOrPromotedParam): bool - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($propertyOrPromotedParam); - - // @todo attributes too - if ($phpDocInfo->hasByAnnotationClasses([ - 'Doctrine\ORM\Mapping\Id', - 'Doctrine\ORM\Mapping\Column', - 'Doctrine\ORM\Mapping\OneToMany', - 'Doctrine\ORM\Mapping\ManyToMany', - 'Doctrine\ORM\Mapping\ManyToOne', - 'Doctrine\ORM\Mapping\OneToOne', - 'JMS\Serializer\Annotation\Type', - ])) { + public function isPropertyChangeableExceptConstructor( + Class_ $class, + Property | Param $propertyOrParam, + Scope $scope + ): bool { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($class); + + if ($this->hasAllowedNotReadonlyAnnotationOrAttribute($phpDocInfo, $class)) { return true; } - // $propertyOrPromotedParam->attrGroups + if ($this->phpAttributeAnalyzer->hasPhpAttribute($propertyOrParam, ClassName::JMS_TYPE)) { + return true; + } + + $propertyFetches = $this->propertyFetchFinder->findPrivatePropertyFetches($class, $propertyOrParam, $scope); + $classMethod = $class->getMethod(MethodName::CONSTRUCT); - $privatePropertyFetches = $this->propertyFetchFinder->findPrivatePropertyFetches($propertyOrPromotedParam); - foreach ($privatePropertyFetches as $privatePropertyFetch) { - if ($this->readWritePropertyAnalyzer->isRead($privatePropertyFetch)) { + foreach ($propertyFetches as $propertyFetch) { + if ($this->contextAnalyzer->isChangeableContext($propertyFetch)) { return true; } - } - // has classLike $this->$variable call? - /** @var ClassLike $classLike */ - $classLike = $propertyOrPromotedParam->getAttribute(AttributeKey::CLASS_NODE); + // skip for constructor? it is allowed to set value in constructor method + $propertyName = (string) $this->nodeNameResolver->getName($propertyFetch); + + if ($this->isPropertyAssignedOnlyInConstructor($class, $propertyName, $propertyFetch, $classMethod)) { + continue; + } - return (bool) $this->betterNodeFinder->findFirst($classLike->stmts, function (Node $node): bool { - if (! $node instanceof PropertyFetch) { - return false; + if ($this->contextAnalyzer->isLeftPartOfAssign($propertyFetch)) { + return true; } - if (! $this->readWritePropertyAnalyzer->isRead($node)) { - return false; + if ($propertyFetch->getAttribute(AttributeKey::IS_UNSET_VAR) === true) { + return true; } + } - return $node->name instanceof Expr; - }); + return false; } - public function isPropertyChangeableExceptConstructor(Property | Param $propertyOrParam): bool + /** + * @api Used in rector-symfony + */ + public function resolveExistingClassPropertyNameByType(Class_ $class, ObjectType $objectType): ?string { - $propertyFetches = $this->propertyFetchFinder->findPrivatePropertyFetches($propertyOrParam); + foreach ($class->getProperties() as $property) { + $propertyType = $this->nodeTypeResolver->getType($property); + if (! $propertyType->equals($objectType)) { + continue; + } - foreach ($propertyFetches as $propertyFetch) { - if ($this->isChangeableContext($propertyFetch)) { - return true; + return $this->nodeNameResolver->getName($property); + } + + $promotedPropertyParams = $this->promotedPropertyResolver->resolveFromClass($class); + foreach ($promotedPropertyParams as $promotedPropertyParam) { + $paramType = $this->nodeTypeResolver->getType($promotedPropertyParam); + if (! $paramType->equals($objectType)) { + continue; } - // skip for constructor? it is allowed to set value in constructor method - $classMethod = $propertyFetch->getAttribute(AttributeKey::METHOD_NODE); - if ($classMethod instanceof ClassMethod && $this->nodeNameResolver->isName( - $classMethod->name, - MethodName::CONSTRUCT - )) { + return $this->nodeNameResolver->getName($promotedPropertyParam); + } + + return null; + } + + public function isUsedByTrait(ClassReflection $classReflection, string $propertyName): bool + { + foreach ($classReflection->getTraits() as $traitUse) { + $trait = $this->astResolver->resolveClassFromClassReflection($traitUse); + if (! $trait instanceof Trait_) { continue; } - if ($this->assignManipulator->isLeftPartOfAssign($propertyFetch)) { + if ($this->propertyFetchAnalyzer->containsLocalPropertyFetchName($trait, $propertyName)) { return true; } } @@ -119,67 +156,57 @@ public function isPropertyChangeableExceptConstructor(Property | Param $property return false; } - public function isPropertyChangeable(Property $property): bool + public function hasTraitWithSamePropertyOrWritten(ClassReflection $classReflection, string $propertyName): bool { - $propertyFetches = $this->propertyFetchFinder->findPrivatePropertyFetches($property); - - foreach ($propertyFetches as $propertyFetch) { - if ($this->isChangeableContext($propertyFetch)) { + foreach ($classReflection->getTraits() as $traitUse) { + if ($traitUse->hasInstanceProperty($propertyName) || $traitUse->hasStaticProperty($propertyName)) { return true; } - if ($this->assignManipulator->isLeftPartOfAssign($propertyFetch)) { + $trait = $this->astResolver->resolveClassFromClassReflection($traitUse); + if (! $trait instanceof Trait_) { + continue; + } + + // is property written to + if ($this->propertyFetchAnalyzer->containsWrittenPropertyFetchName($trait, $propertyName)) { return true; } + } return false; } - private function isChangeableContext(PropertyFetch | StaticPropertyFetch $propertyFetch): bool - { - $parent = $propertyFetch->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { + private function isPropertyAssignedOnlyInConstructor( + Class_ $class, + string $propertyName, + StaticPropertyFetch|PropertyFetch $propertyFetch, + ?ClassMethod $classMethod + ): bool { + if (! $classMethod instanceof ClassMethod) { return false; } - if ($this->typeChecker->isInstanceOf($parent, [PreInc::class, PreDec::class, PostInc::class, PostDec::class])) { - $parent = $parent->getAttribute(AttributeKey::PARENT_NODE); - } + $node = $this->betterNodeFinder->findFirst( + (array) $classMethod->stmts, + static fn (Node $subNode): bool => ($subNode instanceof PropertyFetch || $subNode instanceof StaticPropertyFetch) && $subNode === $propertyFetch + ); - if (! $parent instanceof Node) { + // there is property unset in Test class, so only check on __construct + if (! $node instanceof Node) { return false; } - if ($parent instanceof Arg) { - $readArg = $this->variableToConstantGuard->isReadArg($parent); - if (! $readArg) { - return true; - } - - $caller = $parent->getAttribute(AttributeKey::PARENT_NODE); - if ($caller instanceof MethodCall || $caller instanceof StaticCall) { - return $this->isFoundByRefParam($caller); - } - } - - return false; + return $this->constructorAssignDetector->isPropertyAssigned($class, $propertyName); } - private function isFoundByRefParam(MethodCall | StaticCall $node): bool + private function hasAllowedNotReadonlyAnnotationOrAttribute(PhpDocInfo $phpDocInfo, Class_ $class): bool { - $functionLikeReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); - if ($functionLikeReflection === null) { - return false; - } - - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($functionLikeReflection->getVariants()); - foreach ($parametersAcceptor->getParameters() as $parameterReflection) { - if ($parameterReflection->passedByReference()->yes()) { - return true; - } + if ($phpDocInfo->hasByAnnotationClasses(self::ALLOWED_NOT_READONLY_CLASS_ANNOTATIONS)) { + return true; } - return false; + return $this->phpAttributeAnalyzer->hasPhpAttributes($class, self::ALLOWED_NOT_READONLY_CLASS_ANNOTATIONS); } } diff --git a/src/NodeManipulator/StmtsManipulator.php b/src/NodeManipulator/StmtsManipulator.php index 836974e6cfa..f8890983350 100644 --- a/src/NodeManipulator/StmtsManipulator.php +++ b/src/NodeManipulator/StmtsManipulator.php @@ -2,32 +2,46 @@ declare(strict_types=1); -namespace Rector\Core\NodeManipulator; +namespace Rector\NodeManipulator; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; +use PhpParser\Node\Stmt\Finally_; +use PhpParser\Node\Stmt\TryCatch; +use Rector\DeadCode\NodeAnalyzer\ExprUsedInNodeAnalyzer; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Node\BetterNodeFinder; -final class StmtsManipulator +final readonly class StmtsManipulator { public function __construct( private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - private NodeComparator $nodeComparator + private BetterNodeFinder $betterNodeFinder, + private NodeComparator $nodeComparator, + private ExprUsedInNodeAnalyzer $exprUsedInNodeAnalyzer ) { } /** * @param Stmt[] $stmts */ - public function getUnwrappedLastStmt(array $stmts): ?Node + public function getUnwrappedLastStmt(array $stmts): null|Expr|Stmt { + if ($stmts === []) { + return null; + } + $lastStmtKey = array_key_last($stmts); $lastStmt = $stmts[$lastStmtKey]; if ($lastStmt instanceof Expression) { + $lastStmt->expr->setAttribute(AttributeKey::COMMENTS, $lastStmt->getAttribute(AttributeKey::COMMENTS)); return $lastStmt->expr; } @@ -42,7 +56,7 @@ public function filterOutExistingStmts(ClassMethod $classMethod, array $stmts): { $this->simpleCallableNodeTraverser->traverseNodesWithCallable( (array) $classMethod->stmts, - function (Node $node) use (&$stmts) { + function (Node $node) use (&$stmts): null { foreach ($stmts as $key => $assign) { if (! $this->nodeComparator->areNodesEqual($node, $assign)) { continue; @@ -57,4 +71,40 @@ function (Node $node) use (&$stmts) { return $stmts; } + + /** + * @param StmtsAware $stmtsAware + */ + public function isVariableUsedInNextStmt(Node $stmtsAware, int $jumpToKey, string $variableName): bool + { + if ($stmtsAware->stmts === null) { + return false; + } + + $lastKey = array_key_last($stmtsAware->stmts); + $stmts = []; + + for ($key = $jumpToKey; $key <= $lastKey; ++$key) { + if (! isset($stmtsAware->stmts[$key])) { + // can be just removed + continue; + } + + $stmts[] = $stmtsAware->stmts[$key]; + } + + if ($stmtsAware instanceof TryCatch) { + $stmts = array_merge($stmts, $stmtsAware->catches); + + if ($stmtsAware->finally instanceof Finally_) { + $stmts = array_merge($stmts, $stmtsAware->finally->stmts); + } + } + + $variable = new Variable($variableName); + return (bool) $this->betterNodeFinder->findFirst( + $stmts, + fn (Node $subNode): bool => $this->exprUsedInNodeAnalyzer->isUsed($subNode, $variable) + ); + } } diff --git a/src/NodeManipulator/VariableManipulator.php b/src/NodeManipulator/VariableManipulator.php deleted file mode 100644 index 6371b9621a9..00000000000 --- a/src/NodeManipulator/VariableManipulator.php +++ /dev/null @@ -1,150 +0,0 @@ -simpleCallableNodeTraverser->traverseNodesWithCallable( - (array) $classMethod->getStmts(), - function (Node $node) use (&$assignsOfArrayToVariable) { - if (! $node instanceof Assign) { - return null; - } - - if (! $node->var instanceof Variable) { - return null; - } - - if (! $node->expr instanceof Array_ && ! $node->expr instanceof Scalar) { - return null; - } - - if ($node->expr instanceof Encapsed) { - return null; - } - - if ($node->expr instanceof Array_ && ! $this->arrayManipulator->isArrayOnlyScalarValues($node->expr)) { - return null; - } - - if ($this->isTestCaseExpectedVariable($node->var)) { - return null; - } - - $assignsOfArrayToVariable[] = $node; - } - ); - - return $assignsOfArrayToVariable; - } - - /** - * @param Assign[] $assignsOfArrayToVariable - * @return Assign[] - */ - public function filterOutChangedVariables(array $assignsOfArrayToVariable, ClassMethod $classMethod): array - { - return array_filter( - $assignsOfArrayToVariable, - fn (Assign $assign): bool => $this->isReadOnlyVariable($classMethod, $assign) - ); - } - - private function isTestCaseExpectedVariable(Variable $variable): bool - { - /** @var string $className */ - $className = $variable->getAttribute(AttributeKey::CLASS_NAME); - if (! \str_ends_with($className, 'Test')) { - return false; - } - - return $this->nodeNameResolver->isName($variable, 'expect*'); - } - - /** - * Inspiration - * @see \Rector\Core\NodeManipulator\PropertyManipulator::isPropertyUsedInReadContext() - */ - private function isReadOnlyVariable(ClassMethod $classMethod, Assign $assign): bool - { - if (! $assign->var instanceof Variable) { - return false; - } - - $variable = $assign->var; - $variableUsages = $this->collectVariableUsages($classMethod, $variable, $assign); - - foreach ($variableUsages as $variableUsage) { - $parent = $variableUsage->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Arg && ! $this->variableToConstantGuard->isReadArg($parent)) { - return false; - } - - if (! $this->assignManipulator->isLeftPartOfAssign($variableUsage)) { - continue; - } - - return false; - } - - return true; - } - - /** - * @return Variable[] - */ - private function collectVariableUsages(ClassMethod $classMethod, Variable $variable, Assign $assign): array - { - return $this->betterNodeFinder->find((array) $classMethod->getStmts(), function (Node $node) use ( - $variable, - $assign - ): bool { - if (! $node instanceof Variable) { - return false; - } - - // skip initialization - $parentNode = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parentNode === $assign) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node, $variable); - }); - } -} diff --git a/src/NodeNameResolver/Contract/NodeNameResolverInterface.php b/src/NodeNameResolver/Contract/NodeNameResolverInterface.php new file mode 100644 index 00000000000..025f2205536 --- /dev/null +++ b/src/NodeNameResolver/Contract/NodeNameResolverInterface.php @@ -0,0 +1,24 @@ + + */ + public function getNode(): string; + + /** + * @param TNode $node + */ + public function resolve(Node $node, ?Scope $scope): ?string; +} diff --git a/src/NodeNameResolver/NodeNameResolver.php b/src/NodeNameResolver/NodeNameResolver.php new file mode 100644 index 00000000000..51d8c62ba52 --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver.php @@ -0,0 +1,259 @@ + + */ + private array $nodeNameResolversByClass = []; + + /** + * @param NodeNameResolverInterface[] $nodeNameResolvers + */ + public function __construct( + private readonly ClassNaming $classNaming, + private readonly CallAnalyzer $callAnalyzer, + private readonly iterable $nodeNameResolvers = [] + ) { + } + + /** + * @param string[] $names + */ + public function isNames(Node $node, array $names): bool + { + $nodeName = $this->getName($node); + if ($nodeName === null) { + return false; + } + + foreach ($names as $name) { + if ($this->isStringName($nodeName, $name)) { + return true; + } + } + + return false; + } + + /** + * @param Node|Node[] $node + * @param MethodName::*|string $name + */ + public function isName(Node | array $node, string $name): bool + { + $nodes = is_array($node) ? $node : [$node]; + + foreach ($nodes as $node) { + if ($this->isSingleName($node, $name)) { + return true; + } + } + + return false; + } + + /** + * Some nodes have always-known string name. This makes PHPStan smarter. + * @see https://phpstan.org/writing-php-code/phpdoc-types#conditional-return-types + * + * @return ($node is Param ? string : + * ($node is ClassMethod ? string : + * ($node is Property ? string : + * ($node is PropertyItem ? string : + * ($node is Trait_ ? string : + * ($node is Interface_ ? string : + * ($node is Const_ ? string : + * ($node is Node\Const_ ? string : + * ($node is Name ? string : + * string|null ))))))))) + */ + public function getName(Node | string $node): ?string + { + if (is_string($node)) { + return $node; + } + + // useful for looped imported names + $namespacedName = $node->getAttribute(AttributeKey::NAMESPACED_NAME); + if (is_string($namespacedName)) { + return $namespacedName; + } + + if ( + ($node instanceof MethodCall || $node instanceof StaticCall || $node instanceof NullsafeMethodCall) + && $this->isCallOrIdentifier($node->name) + ) { + return null; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + $resolvedName = $this->resolveNodeName($node, $scope); + if ($resolvedName !== null) { + return $resolvedName; + } + + // more complex + if (! property_exists($node, 'name')) { + return null; + } + + // unable to resolve + if ($node->name instanceof Expr) { + return null; + } + + return (string) $node->name; + } + + /** + * @api + */ + public function areNamesEqual(Node $firstNode, Node $secondNode): bool + { + $secondResolvedName = $this->getName($secondNode); + if ($secondResolvedName === null) { + return false; + } + + return $this->isName($firstNode, $secondResolvedName); + } + + /** + * @api + * + * @param Name[]|Node[] $nodes + * @return string[] + */ + public function getNames(array $nodes): array + { + $names = []; + foreach ($nodes as $node) { + $name = $this->getName($node); + if (! is_string($name)) { + throw new ShouldNotHappenException(); + } + + $names[] = $name; + } + + return $names; + } + + public function getShortName(string | Name | Identifier | ClassLike $name): string + { + return $this->classNaming->getShortName($name); + } + + public function isStringName(string $resolvedName, string $desiredName): bool + { + if ($desiredName === '') { + return false; + } + + // special case + if ($desiredName === 'Object') { + return $desiredName === $resolvedName; + } + + if (strcasecmp($resolvedName, $desiredName) === 0) { + return true; + } + + foreach (self::REGEX_WILDCARD_CHARS as $char) { + if (str_contains($desiredName, $char)) { + throw new ShouldNotHappenException( + 'Matching of regular expressions is no longer supported. Use $this->getName() and compare with e.g. str_ends_with() or str_starts_with() instead.' + ); + } + } + + return false; + } + + private function isCallOrIdentifier(Expr|Identifier $node): bool + { + if ($node instanceof Expr) { + return $this->callAnalyzer->isObjectCall($node); + } + + return true; + } + + private function isSingleName(Node $node, string $desiredName): bool + { + if ($node instanceof CallLike && ! $node instanceof FuncCall) { + // method call cannot have a name, only the variable or method name + return false; + } + + $resolvedName = $this->getName($node); + if ($resolvedName === null) { + return false; + } + + return $this->isStringName($resolvedName, $desiredName); + } + + private function resolveNodeName(Node $node, ?Scope $scope): ?string + { + $nodeClass = $node::class; + if (array_key_exists($nodeClass, $this->nodeNameResolversByClass)) { + $resolver = $this->nodeNameResolversByClass[$nodeClass]; + + if ($resolver instanceof NodeNameResolverInterface) { + return $resolver->resolve($node, $scope); + } + + return null; + } + + foreach ($this->nodeNameResolvers as $nodeNameResolver) { + if (! \is_a($node, $nodeNameResolver->getNode(), \true)) { + continue; + } + + $this->nodeNameResolversByClass[$nodeClass] = $nodeNameResolver; + + return $nodeNameResolver->resolve($node, $scope); + } + + $this->nodeNameResolversByClass[$nodeClass] = null; + + return null; + } +} diff --git a/src/NodeNameResolver/NodeNameResolver/ClassConstFetchNameResolver.php b/src/NodeNameResolver/NodeNameResolver/ClassConstFetchNameResolver.php new file mode 100644 index 00000000000..48e0ca91a26 --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/ClassConstFetchNameResolver.php @@ -0,0 +1,42 @@ + + */ +final class ClassConstFetchNameResolver implements NodeNameResolverInterface +{ + public function getNode(): string + { + return ClassConstFetch::class; + } + + /** + * @param ClassConstFetch $node + */ + public function resolve(Node $node, ?Scope $scope): ?string + { + if ($node->class instanceof Expr) { + return null; + } + + if (! $node->name instanceof Identifier) { + return null; + } + + $class = $node->class->toString(); + $name = $node->name->toString(); + + return $class . '::' . $name; + } +} diff --git a/src/NodeNameResolver/NodeNameResolver/ClassConstNameResolver.php b/src/NodeNameResolver/NodeNameResolver/ClassConstNameResolver.php new file mode 100644 index 00000000000..ac84c4d3545 --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/ClassConstNameResolver.php @@ -0,0 +1,34 @@ + + */ +final class ClassConstNameResolver implements NodeNameResolverInterface +{ + public function getNode(): string + { + return ClassConst::class; + } + + /** + * @param ClassConst $node + */ + public function resolve(Node $node, ?Scope $scope): ?string + { + if ($node->consts === []) { + return null; + } + + $onlyConstant = $node->consts[0]; + return $onlyConstant->name->toString(); + } +} diff --git a/src/NodeNameResolver/NodeNameResolver/ClassNameResolver.php b/src/NodeNameResolver/NodeNameResolver/ClassNameResolver.php new file mode 100644 index 00000000000..cbc1a7dde6e --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/ClassNameResolver.php @@ -0,0 +1,39 @@ + + */ +final class ClassNameResolver implements NodeNameResolverInterface +{ + public function getNode(): string + { + return ClassLike::class; + } + + /** + * @param ClassLike $node + */ + public function resolve(Node $node, ?Scope $scope): ?string + { + if ($node->namespacedName instanceof Name) { + return $node->namespacedName->toString(); + } + + if (! $node->name instanceof Identifier) { + return null; + } + + return $node->name->toString(); + } +} diff --git a/src/NodeNameResolver/NodeNameResolver/FuncCallNameResolver.php b/src/NodeNameResolver/NodeNameResolver/FuncCallNameResolver.php new file mode 100644 index 00000000000..19b95dac056 --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/FuncCallNameResolver.php @@ -0,0 +1,58 @@ + + */ +final readonly class FuncCallNameResolver implements NodeNameResolverInterface +{ + public function __construct( + private ReflectionProvider $reflectionProvider + ) { + } + + public function getNode(): string + { + return FuncCall::class; + } + + /** + * If some function is namespaced, it will be used over global one. + * But only if it really exists. + * + * @param FuncCall $node + */ + public function resolve(Node $node, ?Scope $scope): ?string + { + if ($node->name instanceof Expr) { + return null; + } + + $namespaceName = $node->name->getAttribute(AttributeKey::NAMESPACED_NAME); + if ($namespaceName instanceof FullyQualified) { + $functionFqnName = $namespaceName->toString(); + + if ($this->reflectionProvider->hasFunction($namespaceName, null)) { + return $functionFqnName; + } + } + + if (is_string($namespaceName)) { + return $namespaceName; + } + + return (string) $node->name; + } +} diff --git a/packages/NodeNameResolver/NodeNameResolver/FunctionNameResolver.php b/src/NodeNameResolver/NodeNameResolver/FunctionNameResolver.php similarity index 75% rename from packages/NodeNameResolver/NodeNameResolver/FunctionNameResolver.php rename to src/NodeNameResolver/NodeNameResolver/FunctionNameResolver.php index 96fe1695dab..db12b62de46 100644 --- a/packages/NodeNameResolver/NodeNameResolver/FunctionNameResolver.php +++ b/src/NodeNameResolver/NodeNameResolver/FunctionNameResolver.php @@ -8,13 +8,12 @@ use PhpParser\Node\Stmt\Function_; use PHPStan\Analyser\Scope; use Rector\NodeNameResolver\Contract\NodeNameResolverInterface; -use Rector\NodeTypeResolver\Node\AttributeKey; +/** + * @implements NodeNameResolverInterface + */ final class FunctionNameResolver implements NodeNameResolverInterface { - /** - * @return class-string - */ public function getNode(): string { return Function_::class; @@ -23,17 +22,16 @@ public function getNode(): string /** * @param Function_ $node */ - public function resolve(Node $node): ?string + public function resolve(Node $node, ?Scope $scope): string { $bareName = (string) $node->name; - $scope = $node->getAttribute(AttributeKey::SCOPE); if (! $scope instanceof Scope) { return $bareName; } $namespaceName = $scope->getNamespace(); - if ($namespaceName) { + if ($namespaceName !== null) { return $namespaceName . '\\' . $bareName; } diff --git a/src/NodeNameResolver/NodeNameResolver/NameNameResolver.php b/src/NodeNameResolver/NodeNameResolver/NameNameResolver.php new file mode 100644 index 00000000000..41c93c43227 --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/NameNameResolver.php @@ -0,0 +1,29 @@ + + */ +final class NameNameResolver implements NodeNameResolverInterface +{ + public function getNode(): string + { + return Name::class; + } + + /** + * @param Name $node + */ + public function resolve(Node $node, ?Scope $scope): string + { + return $node->toString(); + } +} diff --git a/src/NodeNameResolver/NodeNameResolver/ParamNameResolver.php b/src/NodeNameResolver/NodeNameResolver/ParamNameResolver.php new file mode 100644 index 00000000000..1f425bd2eb1 --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/ParamNameResolver.php @@ -0,0 +1,39 @@ + + */ +final class ParamNameResolver implements NodeNameResolverInterface +{ + public function getNode(): string + { + return Param::class; + } + + /** + * @param Param $node + */ + public function resolve(Node $node, ?Scope $scope): ?string + { + if ($node->var instanceof Error) { + return null; + } + + if ($node->var->name instanceof Expr) { + return null; + } + + return $node->var->name; + } +} diff --git a/src/NodeNameResolver/NodeNameResolver/PropertyNameResolver.php b/src/NodeNameResolver/NodeNameResolver/PropertyNameResolver.php new file mode 100644 index 00000000000..6fb270be23f --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/PropertyNameResolver.php @@ -0,0 +1,34 @@ + + */ +final class PropertyNameResolver implements NodeNameResolverInterface +{ + public function getNode(): string + { + return Property::class; + } + + /** + * @param Property $node + */ + public function resolve(Node $node, ?Scope $scope): ?string + { + if ($node->props === []) { + return null; + } + + $onlyProperty = $node->props[0]; + return $onlyProperty->name->toString(); + } +} diff --git a/src/NodeNameResolver/NodeNameResolver/UseNameResolver.php b/src/NodeNameResolver/NodeNameResolver/UseNameResolver.php new file mode 100644 index 00000000000..32191044722 --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/UseNameResolver.php @@ -0,0 +1,34 @@ + + */ +final class UseNameResolver implements NodeNameResolverInterface +{ + public function getNode(): string + { + return Use_::class; + } + + /** + * @param Use_ $node + */ + public function resolve(Node $node, ?Scope $scope): ?string + { + if ($node->uses === []) { + return null; + } + + $onlyUse = $node->uses[0]; + return $onlyUse->name->toString(); + } +} diff --git a/src/NodeNameResolver/NodeNameResolver/VariableNameResolver.php b/src/NodeNameResolver/NodeNameResolver/VariableNameResolver.php new file mode 100644 index 00000000000..b42c381b127 --- /dev/null +++ b/src/NodeNameResolver/NodeNameResolver/VariableNameResolver.php @@ -0,0 +1,34 @@ + + */ +final class VariableNameResolver implements NodeNameResolverInterface +{ + public function getNode(): string + { + return Variable::class; + } + + /** + * @param Variable $node + */ + public function resolve(Node $node, ?Scope $scope): ?string + { + if ($node->name instanceof Expr) { + return null; + } + + return $node->name; + } +} diff --git a/src/NodeNameResolver/Regex/RegexPatternDetector.php b/src/NodeNameResolver/Regex/RegexPatternDetector.php new file mode 100644 index 00000000000..4a9e87dd5a8 --- /dev/null +++ b/src/NodeNameResolver/Regex/RegexPatternDetector.php @@ -0,0 +1,52 @@ + + */ + private const array START_AND_END_DELIMITERS = [ + '(' => ')', + '{' => '}', + '[' => ']', + '<' => '>', + ]; + + public function isRegexPattern(string $name): bool + { + if (strlen($name) <= 2) { + return false; + } + + $firstChar = $name[0]; + $lastChar = $name[strlen($name) - 1]; + if ($firstChar !== $lastChar) { + foreach (self::START_AND_END_DELIMITERS as $start => $end) { + if ($firstChar !== $start) { + continue; + } + + if ($lastChar !== $end) { + continue; + } + + return true; + } + + return false; + } + + return in_array($firstChar, self::POSSIBLE_DELIMITERS, true); + } +} diff --git a/src/NodeNestingScope/ContextAnalyzer.php b/src/NodeNestingScope/ContextAnalyzer.php new file mode 100644 index 00000000000..ece022ee036 --- /dev/null +++ b/src/NodeNestingScope/ContextAnalyzer.php @@ -0,0 +1,61 @@ +getAttribute(AttributeKey::IS_IN_LOOP_OR_SWITCH) === true; + } + + /** + * @api + */ + public function isInIf(Node $node): bool + { + return $node->getAttribute(AttributeKey::IS_IN_IF) === true; + } + + public function isChangeableContext( + PropertyFetch | StaticPropertyFetch | NullsafePropertyFetch $propertyFetch + ): bool { + if ($propertyFetch->getAttribute(AttributeKey::IS_UNSET_VAR, false)) { + return true; + } + + if ($propertyFetch->getAttribute(AttributeKey::INSIDE_ARRAY_DIM_FETCH, false)) { + return true; + } + + if ($propertyFetch->getAttribute(AttributeKey::IS_USED_AS_ARG_BY_REF_VALUE, false) === true) { + return true; + } + + return $propertyFetch->getAttribute(AttributeKey::IS_INCREMENT_OR_DECREMENT, false) === true; + } + + public function isLeftPartOfAssign(Node $node): bool + { + if ($node->getAttribute(AttributeKey::IS_BEING_ASSIGNED) === true) { + return true; + } + + if ($node->getAttribute(AttributeKey::IS_ASSIGN_REF_EXPR) === true) { + return true; + } + + return $node->getAttribute(AttributeKey::IS_ASSIGN_OP_VAR) === true; + } +} diff --git a/src/NodeNestingScope/ValueObject/ControlStructure.php b/src/NodeNestingScope/ValueObject/ControlStructure.php new file mode 100644 index 00000000000..187a5103fd6 --- /dev/null +++ b/src/NodeNestingScope/ValueObject/ControlStructure.php @@ -0,0 +1,37 @@ +> + */ + public const array CONDITIONAL_NODE_SCOPE_TYPES = [ + If_::class, + While_::class, + Do_::class, + Else_::class, + ElseIf_::class, + Catch_::class, + Case_::class, + Match_::class, + Switch_::class, + Foreach_::class, + ]; +} diff --git a/src/NodeTypeResolver/Contract/NodeTypeResolverAwareInterface.php b/src/NodeTypeResolver/Contract/NodeTypeResolverAwareInterface.php new file mode 100644 index 00000000000..9457b505677 --- /dev/null +++ b/src/NodeTypeResolver/Contract/NodeTypeResolverAwareInterface.php @@ -0,0 +1,12 @@ +resolveAdditionalConfigFiles(); + + try { + $this->container = $containerFactory->create( + SimpleParameterProvider::provideStringParameter(Option::CONTAINER_CACHE_DIRECTORY), + $additionalConfigFiles, + [] + ); + } catch (Throwable $throwable) { + if ($throwable->getMessage() === "File 'phar://phpstan.phar/conf/bleedingEdge.neon' is missing or is not readable.") { + $symfonyStyle = new SymfonyStyle(new ArrayInput([]), new ConsoleOutput()); + $symfonyStyle->error( + str_replace("\r\n", "\n", sprintf( + self::INVALID_BLEEDING_EDGE_PATH_MESSAGE, + $throwable->getMessage() + )) + ); + + exit(-1); + } + + throw $throwable; + } + } + + /** + * @api + */ + public function createReflectionProvider(): ReflectionProvider + { + return $this->container->getByType(ReflectionProvider::class); + } + + /** + * @api + */ + public function createEmulativeLexer(): Lexer + { + return $this->container->getService('currentPhpVersionLexer'); + } + + /** + * @api + */ + public function createPHPStanParser(): Parser + { + return $this->container->getService('currentPhpVersionRichParser'); + } + + /** + * @api + */ + public function createNodeScopeResolver(): NodeScopeResolver + { + return $this->container->getByType(NodeScopeResolver::class); + } + + /** + * @api + */ + public function createScopeFactory(): ScopeFactory + { + return $this->container->getByType(ScopeFactory::class); + } + + /** + * @template TObject as Object + * + * @param class-string $type + * @return TObject + */ + public function getByType(string $type): object + { + return $this->container->getByType($type); + } + + /** + * @api + */ + public function createFileHelper(): FileHelper + { + return $this->container->getByType(FileHelper::class); + } + + /** + * @api + */ + public function createTypeNodeResolver(): TypeNodeResolver + { + return $this->container->getByType(TypeNodeResolver::class); + } + + /** + * @api + */ + public function createDynamicSourceLocatorProvider(): DynamicSourceLocatorProvider + { + return $this->container->getByType(DynamicSourceLocatorProvider::class); + } + + /** + * @return string[] + */ + private function resolveAdditionalConfigFiles(): array + { + $additionalConfigFiles = []; + + if (SimpleParameterProvider::hasParameter(Option::PHPSTAN_FOR_RECTOR_PATHS)) { + $paths = SimpleParameterProvider::provideArrayParameter(Option::PHPSTAN_FOR_RECTOR_PATHS); + foreach ($paths as $path) { + Assert::string($path); + $additionalConfigFiles[] = $path; + } + } + + $additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/static-reflection.neon'; + $additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/better-infer.neon'; + $additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/parser.neon'; + + return array_filter($additionalConfigFiles, file_exists(...)); + } +} diff --git a/src/NodeTypeResolver/Node/AttributeKey.php b/src/NodeTypeResolver/Node/AttributeKey.php new file mode 100644 index 00000000000..1df957cdfac --- /dev/null +++ b/src/NodeTypeResolver/Node/AttributeKey.php @@ -0,0 +1,171 @@ +nodeTraverser = new NodeTraverser($cloningVisitor); + } + + /** + * @param Stmt[] $stmts + * @return Stmt[] + */ + public function decorateNodesFromFile(string $filePath, array $stmts): array + { + $stmts = $this->phpStanNodeScopeResolver->processNodes($stmts, $filePath); + return $this->nodeTraverser->traverse($stmts); + } +} diff --git a/src/NodeTypeResolver/NodeTypeCorrector.php b/src/NodeTypeResolver/NodeTypeCorrector.php new file mode 100644 index 00000000000..72a388b46bc --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeCorrector.php @@ -0,0 +1,59 @@ +accessoryNonEmptyStringTypeCorrector->correct($type); + $type = $this->genericClassStringTypeCorrector->correct($type); + + $type = $this->removeAccessoryArrayListType($type); + + return $this->accessoryNonEmptyArrayTypeCorrector->correct($type); + } + + private function removeAccessoryArrayListType(Type $type): Type + { + if (! $type instanceof IntersectionType) { + return $type; + } + + $cleanTypes = []; + foreach ($type->getTypes() as $intersectionType) { + if ($intersectionType instanceof AccessoryArrayListType) { + continue; + } + + $cleanTypes[] = $intersectionType; + } + + if (count($cleanTypes) === 1) { + return $cleanTypes[0]; + } + + return new IntersectionType($cleanTypes); + } +} diff --git a/src/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyArrayTypeCorrector.php b/src/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyArrayTypeCorrector.php new file mode 100644 index 00000000000..f8489bfd867 --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyArrayTypeCorrector.php @@ -0,0 +1,42 @@ +isArray()->yes()) { + return $mainType; + } + + foreach ($mainType->getTypes() as $type) { + if ($type instanceof NonEmptyArrayType) { + return new ArrayType(new MixedType(), new MixedType()); + } + + if ($type instanceof ArrayType + && $type->getIterableValueType() instanceof IntersectionType + && $type->getIterableValueType() + ->isString() + ->yes()) { + return new ArrayType(new MixedType(), new StringType()); + } + } + + return $mainType; + } +} diff --git a/src/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyStringTypeCorrector.php b/src/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyStringTypeCorrector.php new file mode 100644 index 00000000000..5c293bd464e --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyStringTypeCorrector.php @@ -0,0 +1,25 @@ +isNonEmptyString()->yes()) { + return $mainType; + } + + return new StringType(); + } +} diff --git a/src/NodeTypeResolver/NodeTypeCorrector/GenericClassStringTypeCorrector.php b/src/NodeTypeResolver/NodeTypeCorrector/GenericClassStringTypeCorrector.php new file mode 100644 index 00000000000..a05a1a4bf1c --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeCorrector/GenericClassStringTypeCorrector.php @@ -0,0 +1,42 @@ +getValue(); + if (! $this->reflectionProvider->hasClass($value)) { + return $traverseCallback($traversedType); + } + + $classReflection = $this->reflectionProvider->getClass($value); + if ($classReflection->getName() !== $value) { + return $traverseCallback($traversedType); + } + + return new GenericClassStringType(new ObjectType($value)); + }); + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver.php new file mode 100644 index 00000000000..a2cf353e419 --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver.php @@ -0,0 +1,662 @@ +, NodeTypeResolverInterface> + */ + private array $nodeTypeResolvers = []; + + /** + * @param NodeTypeResolverInterface[] $nodeTypeResolvers + */ + public function __construct( + private readonly ObjectTypeSpecifier $objectTypeSpecifier, + private readonly ClassAnalyzer $classAnalyzer, + private readonly NodeTypeCorrector $nodeTypeCorrector, + private readonly ReflectionProvider $reflectionProvider, + private readonly RenamedClassesDataCollector $renamedClassesDataCollector, + private readonly NodeNameResolver $nodeNameResolver, + private readonly PhpVersionProvider $phpVersionProvider, + iterable $nodeTypeResolvers + ) { + foreach ($nodeTypeResolvers as $nodeTypeResolver) { + if ($nodeTypeResolver instanceof NodeTypeResolverAwareInterface) { + $nodeTypeResolver->autowire($this); + } + + foreach ($nodeTypeResolver->getNodeClasses() as $nodeClass) { + $this->nodeTypeResolvers[$nodeClass] = $nodeTypeResolver; + } + } + } + + /** + * @api doctrine symfony + * @param ObjectType[] $requiredTypes + */ + public function isObjectTypes(Node $node, array $requiredTypes): bool + { + foreach ($requiredTypes as $requiredType) { + if ($this->isObjectType($node, $requiredType)) { + return true; + } + } + + return false; + } + + public function isObjectType(Node $node, ObjectType $requiredObjectType): bool + { + if ($node instanceof ClassConstFetch) { + return false; + } + + // warn about invalid use of this method + if ($node instanceof ClassMethod || $node instanceof ClassConst) { + throw new ShouldNotHappenException(sprintf(self::ERROR_MESSAGE, $node::class, ClassLike::class)); + } + + $resolvedType = $this->getType($node); + + // cover call $this on trait + if ($resolvedType instanceof ErrorType && ($node instanceof Variable && $this->nodeNameResolver->isName( + $node, + 'this' + ))) { + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return false; + } + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + if ($classReflection->isTrait()) { + $resolvedType = new ObjectType($classReflection->getName()); + } + } + + if ($resolvedType instanceof MixedType) { + return false; + } + + if ($resolvedType instanceof ThisType) { + $resolvedType = $resolvedType->getStaticObjectType(); + } + + if ($resolvedType instanceof ObjectType) { + try { + return $this->resolveObjectType($resolvedType, $requiredObjectType); + } catch (ClassNotFoundException) { + // in some type checks, the provided type in rector.php configuration does not have to exists + return false; + } + } + + if ($resolvedType instanceof ObjectWithoutClassType) { + return $this->isMatchObjectWithoutClassType($resolvedType, $requiredObjectType); + } + + return $this->isMatchingUnionType($resolvedType, $requiredObjectType); + } + + public function getType(Node $node): Type + { + if ($node instanceof NullableType) { + $type = $this->getType($node->type); + + if (! $type instanceof MixedType) { + return new UnionType([$type, new NullType()]); + } + } + + if ($node instanceof Ternary) { + $ternaryType = $this->resolveTernaryType($node); + if (! $ternaryType instanceof MixedType) { + return $ternaryType; + } + } + + if ($node instanceof Coalesce) { + $first = $this->getType($node->left); + $second = $this->getType($node->right); + + if ($this->isUnionTypeable($first, $second)) { + return new UnionType([$first, $second]); + } + } + + $type = $this->resolveByNodeTypeResolvers($node); + + if ($type instanceof Type) { + $type = $this->nodeTypeCorrector->correctType($type); + + if ($type instanceof ObjectType) { + $scope = $node->getAttribute(AttributeKey::SCOPE); + $type = $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType( + $node, + $type, + $scope, + true + ); + } + + return $type; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + + if (! $scope instanceof Scope) { + return new MixedType(); + } + + if ($node instanceof NodeUnionType) { + $types = []; + + foreach ($node->types as $type) { + $types[] = $this->getType($type); + } + + return new UnionType($types); + } + + if (! $node instanceof Expr) { + return new MixedType(); + } + + $type = $this->nodeTypeCorrector->correctType($scope->getType($node)); + + // hot fix for phpstan not resolving chain method calls + if (! $node instanceof MethodCall) { + return $type; + } + + if (! $type instanceof MixedType) { + return $type; + } + + return $this->getType($node->var); + } + + /** + * e.g. string|null, ObjectNull|null + */ + public function isNullableType(Node $node): bool + { + $nodeType = $this->getType($node); + return TypeCombinator::containsNull($nodeType); + } + + public function getNativeType(Expr $expr): Type + { + $scope = $expr->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return new MixedType(); + } + + // cover direct New_ class + if ($this->classAnalyzer->isAnonymousClass($expr)) { + $type = $this->nodeTypeResolvers[New_::class]->resolve($expr); + if ($type instanceof ObjectWithoutClassType) { + return $type; + } + } + + $type = $this->resolveNativeTypeWithBuiltinMethodCallFallback($expr, $scope); + if ($expr instanceof ArrayDimFetch) { + $type = $this->resolveArrayDimFetchType($expr, $scope, $type); + } + + if (! $type instanceof UnionType) { + if ($this->isAnonymousObjectType($type)) { + return new ObjectWithoutClassType(); + } + + return $this->nodeTypeCorrector->correctType($type); + } + + return $this->resolveNativeUnionType($type); + } + + public function isNumberType(Expr $expr): bool + { + $nodeType = $this->getNativeType($expr); + if ($nodeType->isInteger()->yes()) { + return true; + } + + return $nodeType->isFloat() + ->yes(); + } + + /** + * @template TType as Type + * + * @param class-string $desiredType + * @return TType|null + */ + public function matchNullableTypeOfSpecificType(Expr $expr, string $desiredType): ?Type + { + $nodeType = $this->getType($expr); + if (! $nodeType instanceof UnionType) { + return null; + } + + $bareType = TypeCombinator::removeNull($nodeType); + if (! $bareType instanceof $desiredType) { + return null; + } + + return $bareType; + } + + public function getFullyQualifiedClassName(TypeWithClassName $typeWithClassName): string + { + if ($typeWithClassName instanceof ShortenedObjectType) { + return $typeWithClassName->getFullyQualifiedName(); + } + + if ($typeWithClassName instanceof AliasedObjectType) { + return $typeWithClassName->getFullyQualifiedName(); + } + + return $typeWithClassName->getClassName(); + } + + public function isMethodStaticCallOrClassMethodObjectType(Node $node, ObjectType $objectType): bool + { + if ($node instanceof MethodCall || $node instanceof NullsafeMethodCall) { + if ($this->isEnumTypeMatch($node, $objectType)) { + return true; + } + + // method call is variable return + return $this->isObjectType($node->var, $objectType); + } + + if ($node instanceof StaticCall) { + return $this->isObjectType($node->class, $objectType); + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return false; + } + + $classReflection = $scope->getClassReflection(); + + if (! $classReflection instanceof ClassReflection) { + return false; + } + + if ($classReflection->getName() === $objectType->getClassName()) { + return true; + } + + if ($classReflection->is($objectType->getClassName())) { + return true; + } + + return $classReflection->hasTraitUse($objectType->getClassName()); + } + + /** + * Allow pull type from + * + * - native function + * - always defined by assignment + * + * eg: + * + * $parts = parse_url($url); + * if (!empty($parts['host'])) { } + * + * or + * + * $parts = ['host' => 'foo']; + * if (!empty($parts['host'])) { } + */ + private function resolveArrayDimFetchType( + ArrayDimFetch $arrayDimFetch, + Scope $scope, + Type $originalNativeType + ): Type { + $nativeVariableType = $scope->getNativeType($arrayDimFetch->var); + if ($nativeVariableType instanceof MixedType || ($nativeVariableType instanceof ArrayType && $nativeVariableType->getIterableValueType() instanceof MixedType)) { + return $originalNativeType; + } + + $type = $scope->getType($arrayDimFetch); + + if (! $arrayDimFetch->dim instanceof String_) { + return $type; + } + + $variableType = $scope->getType($arrayDimFetch->var); + if (! $variableType instanceof ConstantArrayType) { + return $type; + } + + $optionalKeys = $variableType->getOptionalKeys(); + foreach ($variableType->getKeyTypes() as $key => $keyType) { + if (! $keyType instanceof ConstantStringType) { + continue; + } + + if ($keyType->getValue() !== $arrayDimFetch->dim->value) { + continue; + } + + if (! in_array($key, $optionalKeys, true)) { + continue; + } + + return $originalNativeType; + } + + return $type; + } + + private function resolveNativeUnionType(UnionType $unionType): UnionType + { + $hasChanged = false; + $types = $unionType->getTypes(); + foreach ($types as $key => $childType) { + if ($this->isAnonymousObjectType($childType)) { + $types[$key] = new ObjectWithoutClassType(); + $hasChanged = true; + } + } + + if ($hasChanged) { + return new UnionType($types); + } + + return $unionType; + } + + private function isMatchObjectWithoutClassType( + ObjectWithoutClassType $objectWithoutClassType, + ObjectType $requiredObjectType + ): bool { + if ($objectWithoutClassType instanceof ObjectWithoutClassTypeWithParentTypes) { + foreach ($objectWithoutClassType->getParentTypes() as $typeWithClassName) { + if ($requiredObjectType->isSuperTypeOf($typeWithClassName)->yes()) { + return true; + } + } + } + + return false; + } + + private function isAnonymousObjectType(Type $type): bool + { + if (! $type instanceof ObjectType) { + return false; + } + + $classReflection = $type->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + return $classReflection->isAnonymous(); + } + + private function isUnionTypeable(Type $first, Type $second): bool + { + return ! $first instanceof UnionType && ! $second instanceof UnionType && ! $second->isNull() + ->yes(); + } + + private function isMatchingUnionType(Type $resolvedType, ObjectType $requiredObjectType): bool + { + $type = TypeCombinator::removeNull($resolvedType); + + if ($type instanceof NeverType) { + return false; + } + + // for falsy nullables + $type = TypeCombinator::remove($type, new ConstantBooleanType(false)); + + if ($type instanceof ObjectWithoutClassType) { + return $this->isMatchObjectWithoutClassType($type, $requiredObjectType); + } + + return $requiredObjectType->isSuperTypeOf($type) + ->yes(); + } + + private function resolveByNodeTypeResolvers(Node $node): ?Type + { + foreach ($this->nodeTypeResolvers as $nodeClass => $nodeTypeResolver) { + if (! $node instanceof $nodeClass) { + continue; + } + + return $nodeTypeResolver->resolve($node); + } + + return null; + } + + private function isObjectTypeOfObjectType(ObjectType $resolvedObjectType, ObjectType $requiredObjectType): bool + { + $requiredClassName = $requiredObjectType->getClassName(); + $resolvedClassName = $resolvedObjectType->getClassName(); + + if ($resolvedClassName === $requiredClassName) { + return true; + } + + if ($resolvedObjectType->isInstanceOf($requiredClassName)->yes()) { + return true; + } + + if (! $this->reflectionProvider->hasClass($requiredClassName)) { + return false; + } + + $requiredClassReflection = $this->reflectionProvider->getClass($requiredClassName); + + if ($requiredClassReflection->isTrait()) { + if (! $this->reflectionProvider->hasClass($resolvedClassName)) { + return false; + } + + $resolvedClassReflection = $this->reflectionProvider->getClass($resolvedClassName); + + foreach ($resolvedClassReflection->getAncestors() as $ancestorClassReflection) { + if ($ancestorClassReflection->hasTraitUse($requiredClassName)) { + return true; + } + } + } + + return false; + } + + private function resolveObjectType(ObjectType $resolvedObjectType, ObjectType $requiredObjectType): bool + { + $renamedObjectType = $this->renamedClassesDataCollector->matchClassName($resolvedObjectType); + if (! $renamedObjectType instanceof ObjectType) { + return $this->isObjectTypeOfObjectType($resolvedObjectType, $requiredObjectType); + } + + if (! $this->isObjectTypeOfObjectType($renamedObjectType, $requiredObjectType)) { + return $this->isObjectTypeOfObjectType($resolvedObjectType, $requiredObjectType); + } + + return true; + } + + private function resolveTernaryType(Ternary $ternary): MixedType|UnionType + { + if ($ternary->if instanceof Expr) { + $first = $this->getType($ternary->if); + $second = $this->getType($ternary->else); + + if ($this->isUnionTypeable($first, $second)) { + return new UnionType([$first, $second]); + } + } + + $condType = $this->getType($ternary->cond); + if ($this->isNullableType($ternary->cond) && $condType instanceof UnionType) { + $first = $condType->getTypes()[0]; + $second = $this->getType($ternary->else); + + if ($this->isUnionTypeable($first, $second)) { + return new UnionType([$first, $second]); + } + } + + return new MixedType(); + } + + /** + * Method calls on native PHP classes report mixed, + * even on strict known type; this fallbacks to getType() that provides correct type + */ + private function resolveNativeTypeWithBuiltinMethodCallFallback(Expr $expr, Scope $scope): Type + { + if ($expr instanceof MethodCall) { + $callerType = $scope->getType($expr->var); + if ($callerType instanceof ObjectType && $callerType->getClassReflection() instanceof ClassReflection && $callerType->getClassReflection()->isBuiltin()) { + return $scope->getType($expr); + } + } + + if ($expr instanceof FuncCall) { + if (! $expr->name instanceof Name) { + return $scope->getNativeType($expr); + } + + $functionName = new Name((string) $this->nodeNameResolver->getName($expr)); + if (! $this->reflectionProvider->hasFunction($functionName, null)) { + return $scope->getNativeType($expr); + } + + $functionReflection = $this->reflectionProvider->getFunction($functionName, null); + if (! $functionReflection instanceof NativeFunctionReflection) { + return $scope->getNativeType($expr); + } + + if ($this->isSubstrOnPHP74($expr)) { + return new UnionType([new StringType(), new ConstantBooleanType(false)]); + } + + return $scope->getType($expr); + } + + return $scope->getNativeType($expr); + } + + private function isEnumTypeMatch(MethodCall|NullsafeMethodCall $call, ObjectType $objectType): bool + { + if (! $call->var instanceof ClassConstFetch) { + return false; + } + + // possibly enum + $classConstFetch = $call->var; + + if (! $classConstFetch->class instanceof FullyQualified) { + return false; + } + + $className = $classConstFetch->class->toString(); + if (! $this->reflectionProvider->hasClass($className)) { + return false; + } + + $classReflection = $this->reflectionProvider->getClass($className); + if (! $classReflection->isEnum()) { + return false; + } + + return $classReflection->getName() === $objectType->getClassName(); + } + + /** + * substr can return false on php 7.x and bellow + */ + private function isSubstrOnPHP74(FuncCall $funcCall): bool + { + if ($funcCall->isFirstClassCallable()) { + return false; + } + + if (! $this->nodeNameResolver->isName($funcCall, 'substr')) { + return false; + } + + return ! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_80); + } +} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/CastTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/CastTypeResolver.php similarity index 89% rename from packages/NodeTypeResolver/NodeTypeResolver/CastTypeResolver.php rename to src/NodeTypeResolver/NodeTypeResolver/CastTypeResolver.php index e84ffae54a9..aa0fbb2812c 100644 --- a/packages/NodeTypeResolver/NodeTypeResolver/CastTypeResolver.php +++ b/src/NodeTypeResolver/NodeTypeResolver/CastTypeResolver.php @@ -20,15 +20,18 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\StringType; use PHPStan\Type\Type; -use Rector\Core\Exception\NotImplementedYetException; +use Rector\Exception\NotImplementedYetException; use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface; +/** + * @implements NodeTypeResolverInterface + */ final class CastTypeResolver implements NodeTypeResolverInterface { /** * @var array, class-string> */ - private const CAST_CLASS_TO_TYPE_MAP = [ + private const array CAST_CLASS_TO_TYPE_MAP = [ Bool_::class => BooleanType::class, String_::class => StringType::class, Int_::class => IntegerType::class, @@ -49,7 +52,7 @@ public function getNodeClasses(): array public function resolve(Node $node): Type { foreach (self::CAST_CLASS_TO_TYPE_MAP as $castClass => $typeClass) { - if (is_a($node, $castClass, true)) { + if ($node instanceof $castClass) { return new $typeClass(); } } diff --git a/packages/NodeTypeResolver/NodeTypeResolver/ClassAndInterfaceTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/ClassAndInterfaceTypeResolver.php similarity index 77% rename from packages/NodeTypeResolver/NodeTypeResolver/ClassAndInterfaceTypeResolver.php rename to src/NodeTypeResolver/NodeTypeResolver/ClassAndInterfaceTypeResolver.php index 58a2f537d7f..545c6884ec5 100644 --- a/packages/NodeTypeResolver/NodeTypeResolver/ClassAndInterfaceTypeResolver.php +++ b/src/NodeTypeResolver/NodeTypeResolver/ClassAndInterfaceTypeResolver.php @@ -12,15 +12,23 @@ use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface; use Rector\NodeTypeResolver\Node\AttributeKey; /** * @see \Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\ClassTypeResolverTest * @see \Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\InterfaceTypeResolverTest + * + * @implements NodeTypeResolverInterface */ -final class ClassAndInterfaceTypeResolver implements NodeTypeResolverInterface +final readonly class ClassAndInterfaceTypeResolver implements NodeTypeResolverInterface { + public function __construct( + private NodeNameResolver $nodeNameResolver + ) { + } + /** * @return array> */ @@ -43,7 +51,7 @@ public function resolve(Node $node): Type $classReflection = $scope->getClassReflection(); if (! $classReflection instanceof ClassReflection) { - return new MixedType(); + return new ObjectType((string) $this->nodeNameResolver->getName($node)); } return new ObjectType($classReflection->getName(), null, $classReflection); diff --git a/src/NodeTypeResolver/NodeTypeResolver/ClassConstFetchTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/ClassConstFetchTypeResolver.php new file mode 100644 index 00000000000..3486841aa8a --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/ClassConstFetchTypeResolver.php @@ -0,0 +1,54 @@ + + */ +final class ClassConstFetchTypeResolver implements NodeTypeResolverInterface +{ + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [ClassConstFetch::class]; + } + + /** + * @param ClassConstFetch $node + */ + public function resolve(Node $node): Type + { + $scope = $node->getAttribute(AttributeKey::SCOPE); + + if (! $scope instanceof Scope) { + return new MixedType(); + } + + if ($node->class instanceof FullyQualified) { + return $scope->getType($node); + } + + if ($node->class instanceof Name && $node->class->hasAttribute(AttributeKey::NAMESPACED_NAME)) { + $newNode = clone $node; + $newNode->class = new FullyQualified($node->class->getAttribute(AttributeKey::NAMESPACED_NAME)); + + return $scope->getType($newNode); + } + + return $scope->getType($node); + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php new file mode 100644 index 00000000000..e6b8a64afaf --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php @@ -0,0 +1,85 @@ + + */ +final class IdentifierTypeResolver implements NodeTypeResolverInterface +{ + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [Identifier::class]; + } + + /** + * @param Identifier $node + * @return StringType|BooleanType|ConstantBooleanType|NullType|ObjectWithoutClassType|ArrayType|IterableType|IntegerType|FloatType|MixedType + */ + public function resolve(Node $node): Type + { + $lowerString = $node->toLowerString(); + + if ($lowerString === 'string') { + return new StringType(); + } + + if ($lowerString === 'bool') { + return new BooleanType(); + } + + if ($lowerString === 'false') { + return new ConstantBooleanType(false); + } + + if ($lowerString === 'true') { + return new ConstantBooleanType(true); + } + + if ($lowerString === 'null') { + return new NullType(); + } + + if ($lowerString === 'object') { + return new ObjectWithoutClassType(); + } + + if ($lowerString === 'array') { + return new ArrayType(new MixedType(), new MixedType()); + } + + if ($lowerString === 'int') { + return new IntegerType(); + } + + if ($lowerString === 'iterable') { + return new IterableType(new MixedType(), new MixedType()); + } + + if ($lowerString === 'float') { + return new FloatType(); + } + + return new MixedType(); + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver/NameTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/NameTypeResolver.php new file mode 100644 index 00000000000..94a90f36b59 --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/NameTypeResolver.php @@ -0,0 +1,105 @@ + + */ +final class NameTypeResolver implements NodeTypeResolverInterface +{ + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [Name::class, FullyQualified::class]; + } + + /** + * @param Name $node + */ + public function resolve(Node $node): Type + { + // not instanceof FullyQualified means it is a Name + if (! $node instanceof FullyQualified && $node->hasAttribute(AttributeKey::NAMESPACED_NAME)) { + return $this->resolve(new FullyQualified($node->getAttribute(AttributeKey::NAMESPACED_NAME))); + } + + if ($node->toString() === ObjectReference::PARENT) { + return $this->resolveParent($node); + } + + $fullyQualifiedName = $this->resolveFullyQualifiedName($node); + return new ObjectType($fullyQualifiedName); + } + + private function resolveClassReflection(Name|FullyQualified $node): ?ClassReflection + { + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + return $scope->getClassReflection(); + } + + private function resolveParent(Name $name): MixedType | ObjectType | UnionType + { + $classReflection = $this->resolveClassReflection($name); + if (! $classReflection instanceof ClassReflection || ! $classReflection->isClass()) { + return new MixedType(); + } + + if ($classReflection->isAnonymous()) { + return new MixedType(); + } + + $parentClassObjectTypes = []; + foreach ($classReflection->getParents() as $parentClassReflection) { + $parentClassObjectTypes[] = new ObjectType($parentClassReflection->getName()); + } + + if ($parentClassObjectTypes === []) { + return new MixedType(); + } + + if (count($parentClassObjectTypes) === 1) { + return $parentClassObjectTypes[0]; + } + + return new UnionType($parentClassObjectTypes); + } + + private function resolveFullyQualifiedName(Name $name): string + { + $nameValue = $name->toString(); + + if (in_array($nameValue, [ObjectReference::SELF, ObjectReference::STATIC], true)) { + $classReflection = $this->resolveClassReflection($name); + if (! $classReflection instanceof ClassReflection || $classReflection->isAnonymous()) { + return $name->toString(); + } + + return $classReflection->getName(); + } + + return $nameValue; + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php new file mode 100644 index 00000000000..a0d7640d64e --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php @@ -0,0 +1,94 @@ + + */ +final readonly class NewTypeResolver implements NodeTypeResolverInterface +{ + public function __construct( + private NodeNameResolver $nodeNameResolver, + private ClassAnalyzer $classAnalyzer, + ) { + } + + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [New_::class]; + } + + /** + * @param New_ $node + */ + public function resolve(Node $node): Type + { + if ($node->class instanceof Name) { + $className = $this->nodeNameResolver->getName($node->class); + if (! in_array($className, [ObjectReference::SELF, ObjectReference::PARENT], true)) { + return new ObjectType($className); + } + } + + $isAnonymousClass = $this->classAnalyzer->isAnonymousClass($node->class); + if ($isAnonymousClass) { + return $this->resolveAnonymousClassType($node); + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + // new node probably + return new MixedType(); + } + + return $scope->getType($node); + } + + private function resolveAnonymousClassType(New_ $new): ObjectWithoutClassType + { + if (! $new->class instanceof Class_) { + return new ObjectWithoutClassType(); + } + + $directParentTypes = []; + + $class = $new->class; + if ($class->extends instanceof Name) { + $parentClass = (string) $class->extends; + $directParentTypes[] = new FullyQualifiedObjectType($parentClass); + } + + foreach ($class->implements as $implement) { + $parentClass = (string) $implement; + $directParentTypes[] = new FullyQualifiedObjectType($parentClass); + } + + if ($directParentTypes !== []) { + return new ObjectWithoutClassTypeWithParentTypes($directParentTypes); + } + + return new ObjectWithoutClassType(); + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver/ParamTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/ParamTypeResolver.php new file mode 100644 index 00000000000..a44d633fa23 --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/ParamTypeResolver.php @@ -0,0 +1,48 @@ + + */ +final class ParamTypeResolver implements NodeTypeResolverInterface, NodeTypeResolverAwareInterface +{ + private NodeTypeResolver $nodeTypeResolver; + + public function autowire(NodeTypeResolver $nodeTypeResolver): void + { + $this->nodeTypeResolver = $nodeTypeResolver; + } + + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [Param::class]; + } + + /** + * @param Param $node + */ + public function resolve(Node $node): Type + { + if ($node->type === null) { + return new MixedType(); + } + + return $this->nodeTypeResolver->getType($node->type); + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver/PropertyFetchTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/PropertyFetchTypeResolver.php new file mode 100644 index 00000000000..cfcb2b15e5f --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/PropertyFetchTypeResolver.php @@ -0,0 +1,98 @@ + + */ +final class PropertyFetchTypeResolver implements NodeTypeResolverInterface, NodeTypeResolverAwareInterface +{ + private NodeTypeResolver $nodeTypeResolver; + + public function __construct( + private readonly NodeNameResolver $nodeNameResolver, + private readonly ReflectionProvider $reflectionProvider + ) { + } + + public function autowire(NodeTypeResolver $nodeTypeResolver): void + { + $this->nodeTypeResolver = $nodeTypeResolver; + } + + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [PropertyFetch::class]; + } + + /** + * @param PropertyFetch $node + */ + public function resolve(Node $node): Type + { + // compensate 3rd party non-analysed property reflection + $vendorPropertyType = $this->getVendorPropertyFetchType($node); + if (! $vendorPropertyType instanceof MixedType) { + return $vendorPropertyType; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return new MixedType(); + } + + return $scope->getType($node); + } + + private function getVendorPropertyFetchType(PropertyFetch $propertyFetch): Type + { + // 3rd party code + $propertyName = $this->nodeNameResolver->getName($propertyFetch->name); + if ($propertyName === null) { + return new MixedType(); + } + + $varType = $this->nodeTypeResolver->getType($propertyFetch->var); + if (! $varType instanceof ObjectType) { + return new MixedType(); + } + + if (! $this->reflectionProvider->hasClass($varType->getClassName())) { + return new MixedType(); + } + + $classReflection = $this->reflectionProvider->getClass($varType->getClassName()); + if (! $classReflection->hasInstanceProperty($propertyName)) { + return new MixedType(); + } + + $propertyFetchScope = $propertyFetch->getAttribute(AttributeKey::SCOPE); + if (! $propertyFetchScope instanceof Scope) { + return new MixedType(); + } + + $extendedPropertyReflection = $classReflection->getInstanceProperty($propertyName, $propertyFetchScope); + + return $extendedPropertyReflection->getReadableType(); + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver/PropertyTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/PropertyTypeResolver.php new file mode 100644 index 00000000000..83df1ca42c2 --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/PropertyTypeResolver.php @@ -0,0 +1,46 @@ + + */ +final readonly class PropertyTypeResolver implements NodeTypeResolverInterface +{ + public function __construct( + private PropertyFetchTypeResolver $propertyFetchTypeResolver + ) { + } + + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [Property::class]; + } + + /** + * @param Property $node + */ + public function resolve(Node $node): Type + { + // fake property to local PropertyFetch → PHPStan understands that + $propertyFetch = new PropertyFetch(new Variable('this'), (string) $node->props[0]->name); + $propertyFetch->setAttribute(AttributeKey::SCOPE, $node->getAttribute(AttributeKey::SCOPE)); + + return $this->propertyFetchTypeResolver->resolve($propertyFetch); + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver/ScalarTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/ScalarTypeResolver.php new file mode 100644 index 00000000000..dc877fe5cc2 --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/ScalarTypeResolver.php @@ -0,0 +1,64 @@ + + */ +final class ScalarTypeResolver implements NodeTypeResolverInterface +{ + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [Scalar::class]; + } + + public function resolve(Node $node): Type + { + if ($node instanceof Float_) { + return new ConstantFloatType($node->value); + } + + if ($node instanceof String_) { + return new ConstantStringType($node->value); + } + + if ($node instanceof Int_) { + return new ConstantIntegerType($node->value); + } + + if ($node instanceof MagicConst) { + return new ConstantStringType($node->getName()); + } + + if ($node instanceof InterpolatedString) { + return new StringType(); + } + + if ($node instanceof InterpolatedStringPart) { + return new ConstantStringType($node->value); + } + + throw new NotImplementedYetException(); + } +} diff --git a/src/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php new file mode 100644 index 00000000000..7b7f8839224 --- /dev/null +++ b/src/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php @@ -0,0 +1,114 @@ + + */ +final class StaticCallMethodCallTypeResolver implements NodeTypeResolverInterface, NodeTypeResolverAwareInterface +{ + private NodeTypeResolver $nodeTypeResolver; + + public function __construct( + private readonly NodeNameResolver $nodeNameResolver + ) { + } + + public function autowire(NodeTypeResolver $nodeTypeResolver): void + { + $this->nodeTypeResolver = $nodeTypeResolver; + } + + /** + * @return array> + */ + public function getNodeClasses(): array + { + return [StaticCall::class, MethodCall::class]; + } + + /** + * @param StaticCall|MethodCall $node + */ + public function resolve(Node $node): Type + { + $methodName = $this->nodeNameResolver->getName($node->name); + + // no specific method found, return class types, e.g. ::$method() + if (! is_string($methodName)) { + return new MixedType(); + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return new MixedType(); + } + + $nodeReturnType = $scope->getType($node); + if (! $nodeReturnType instanceof MixedType) { + return $nodeReturnType; + } + + if ($node instanceof MethodCall) { + $callerType = $this->nodeTypeResolver->getType($node->var); + } else { + $callerType = $this->nodeTypeResolver->getType($node->class); + } + + foreach ($callerType->getObjectClassReflections() as $objectClassReflection) { + $classMethodReturnType = $this->resolveClassMethodReturnType( + $objectClassReflection, + $node, + $methodName, + $scope + ); + if (! $classMethodReturnType instanceof MixedType) { + return $classMethodReturnType; + } + } + + return new MixedType(); + } + + private function resolveClassMethodReturnType( + ClassReflection $classReflection, + StaticCall|MethodCall $node, + string $methodName, + Scope $scope + ): Type { + foreach ($classReflection->getAncestors() as $ancestorClassReflection) { + if (! $ancestorClassReflection->hasMethod($methodName)) { + continue; + } + + $methodReflection = $ancestorClassReflection->getMethod($methodName, $scope); + if ($methodReflection instanceof PhpMethodReflection) { + $parametersAcceptorWithPhpDocs = ParametersAcceptorSelectorVariantsWrapper::select( + $methodReflection, + $node, + $scope + ); + return $parametersAcceptorWithPhpDocs->getReturnType(); + } + } + + return new MixedType(); + } +} diff --git a/packages/NodeTypeResolver/NodeTypeResolver/TraitTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver/TraitTypeResolver.php similarity index 88% rename from packages/NodeTypeResolver/NodeTypeResolver/TraitTypeResolver.php rename to src/NodeTypeResolver/NodeTypeResolver/TraitTypeResolver.php index 499d1dee100..edd8c2334c1 100644 --- a/packages/NodeTypeResolver/NodeTypeResolver/TraitTypeResolver.php +++ b/src/NodeTypeResolver/NodeTypeResolver/TraitTypeResolver.php @@ -15,8 +15,10 @@ /** * @see \Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\TraitTypeResolver\TraitTypeResolverTest + * + * @implements NodeTypeResolverInterface */ -final class TraitTypeResolver implements NodeTypeResolverInterface +final readonly class TraitTypeResolver implements NodeTypeResolverInterface { public function __construct( private ReflectionProvider $reflectionProvider @@ -54,10 +56,6 @@ public function resolve(Node $node): Type return $types[0]; } - if (count($types) > 1) { - return new UnionType($types); - } - - return new MixedType(); + return new UnionType($types); } } diff --git a/src/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php b/src/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php new file mode 100644 index 00000000000..4b89d93898d --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php @@ -0,0 +1,30 @@ +parentTypes; + } +} diff --git a/src/NodeTypeResolver/PHPStan/ParametersAcceptorSelectorVariantsWrapper.php b/src/NodeTypeResolver/PHPStan/ParametersAcceptorSelectorVariantsWrapper.php new file mode 100644 index 00000000000..551dc39c959 --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/ParametersAcceptorSelectorVariantsWrapper.php @@ -0,0 +1,33 @@ +getVariants(); + if ($node instanceof FunctionLike) { + return ParametersAcceptorSelector::combineAcceptors($variants); + } + + if ($node->isFirstClassCallable()) { + return ParametersAcceptorSelector::combineAcceptors($variants); + } + + return ParametersAcceptorSelector::selectFromArgs($scope, $node->getArgs(), $variants); + } +} diff --git a/src/NodeTypeResolver/PHPStan/Scope/Contract/NodeVisitor/ScopeResolverNodeVisitorInterface.php b/src/NodeTypeResolver/PHPStan/Scope/Contract/NodeVisitor/ScopeResolverNodeVisitorInterface.php new file mode 100644 index 00000000000..df374f9627e --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/Scope/Contract/NodeVisitor/ScopeResolverNodeVisitorInterface.php @@ -0,0 +1,14 @@ +nodeTraverser = new NodeTraverser(...$decoratingNodeVisitors); + } + + /** + * @param Stmt[] $stmts + * @return Stmt[] + */ + public function processNodes( + array $stmts, + string $filePath, + ?MutatingScope $formerMutatingScope = null + ): array { + /** + * The stmts must be array of Stmt, or it will be silently skipped by PHPStan + * @see vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php:282 + */ + + Assert::allIsInstanceOf($stmts, Stmt::class); + + $scope = $formerMutatingScope ?? $this->scopeFactory->createFromFile($filePath); + + $nodeCallback = function (Node $node, MutatingScope $mutatingScope) use ( + &$nodeCallback, + $filePath, + ): void { + if ($mutatingScope instanceof FiberScope) { + $mutatingScope = $mutatingScope->toMutatingScope(); + } + + // the class reflection is resolved AFTER entering to class node + // so we need to get it from the first after this one + if ($node instanceof Class_ || $node instanceof Interface_ || $node instanceof Enum_) { + $mutatingScope = $this->resolveClassOrInterfaceScope($node, $mutatingScope); + $node->setAttribute(AttributeKey::SCOPE, $mutatingScope); + + if ($node instanceof Class_) { + if ($node->extends instanceof FullyQualified) { + $node->extends->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + foreach ($node->implements as $implement) { + $implement->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + return; + } + + if ($node instanceof Trait_) { + $this->processTrait($node, $mutatingScope, $nodeCallback); + + return; + } + + // special case for unreachable nodes + // early check here as UnreachableStatementNode is special VirtualNode + // so node to be checked inside + if ($node instanceof UnreachableStatementNode) { + $this->processUnreachableStatementNode($node, $mutatingScope, $nodeCallback); + return; + } + + // init current Node set Attribute + // not a VirtualNode, then set scope attribute + // do not return early, as its properties will be checked next + if (! $node instanceof VirtualNode) { + $node->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + // handle unwrapped stmts + if ($node instanceof FileNode) { + $this->nodeScopeResolverProcessNodes($node->stmts, $mutatingScope, $nodeCallback); + return; + } + + $this->decorateNodeAttrGroups($node, $mutatingScope, $nodeCallback); + + if (( + $node instanceof Expression || + $node instanceof Return_ || + $node instanceof EnumCase || + $node instanceof Cast || + $node instanceof YieldFrom || + $node instanceof UnaryMinus || + $node instanceof UnaryPlus || + $node instanceof Throw_ || + $node instanceof Empty_ || + $node instanceof BooleanNot || + $node instanceof Clone_ || + $node instanceof ErrorSuppress || + $node instanceof BitwiseNot || + $node instanceof Eval_ || + $node instanceof Print_ || + $node instanceof Exit_ || + $node instanceof ArrowFunction || + $node instanceof Include_ || + $node instanceof Instanceof_ + ) && $node->expr instanceof Expr) { + $node->expr->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return; + } + + if ($node instanceof PostInc || + $node instanceof PostDec || + $node instanceof PreInc || + $node instanceof PreDec) { + $node->var->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return; + } + + if ($node instanceof ArrayDimFetch) { + $node->var->setAttribute(AttributeKey::SCOPE, $mutatingScope); + if ($node->dim instanceof Expr) { + $node->dim->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + return; + } + + if ($node instanceof Assign || $node instanceof AssignOp || $node instanceof AssignRef) { + $this->processAssign($node, $mutatingScope); + + if ($node->var instanceof Variable && $node->var->name instanceof Expr) { + $this->nodeScopeResolverProcessNodes( + [new Expression($node->var), new Expression($node->expr)], + $mutatingScope, + $nodeCallback + ); + } + + return; + } + + if ($node instanceof Ternary) { + $this->processTernary($node, $mutatingScope); + return; + } + + if ($node instanceof BinaryOp) { + $this->processBinaryOp($node, $mutatingScope); + return; + } + + if ($node instanceof Arg) { + $node->value->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return; + } + + if ($node instanceof Foreach_) { + // decorate value as well + $node->valueVar->setAttribute(AttributeKey::SCOPE, $mutatingScope); + if ($node->valueVar instanceof List_) { + $this->processArray($node->valueVar, $mutatingScope); + } + + return; + } + + if ($node instanceof For_) { + foreach (array_merge($node->init, $node->cond, $node->loop) as $expr) { + $expr->setAttribute(AttributeKey::SCOPE, $mutatingScope); + if ($expr instanceof BinaryOp) { + $this->processBinaryOp($expr, $mutatingScope); + } + + if ($expr instanceof Assign) { + $this->processAssign($expr, $mutatingScope); + } + } + + return; + } + + if ($node instanceof Array_) { + $this->processArray($node, $mutatingScope); + return; + } + + if ($node instanceof Property) { + $this->processProperty($node, $mutatingScope, $nodeCallback); + return; + } + + if ($node instanceof Switch_) { + $this->processSwitch($node, $mutatingScope); + return; + } + + if ($node instanceof TryCatch) { + $this->processTryCatch($node, $mutatingScope); + return; + } + + if ($node instanceof Catch_) { + $this->processCatch($node, $filePath, $mutatingScope); + return; + } + + if ($node instanceof NullableType) { + $node->type->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return; + } + + if ($node instanceof UnionType || $node instanceof IntersectionType) { + foreach ($node->types as $type) { + $type->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + return; + } + + if ($node instanceof StaticPropertyFetch || $node instanceof ClassConstFetch) { + $node->class->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $node->name->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return; + } + + if ($node instanceof PropertyFetch) { + $node->var->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $node->name->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return; + } + + if ($node instanceof ConstFetch) { + $node->name->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return; + } + + if ($node instanceof CallLike) { + $this->processCallLike($node, $mutatingScope); + return; + } + + if ($node instanceof Match_) { + $this->processMatch($node, $mutatingScope); + return; + } + + if ($node instanceof Yield_) { + $this->processYield($node, $mutatingScope); + return; + } + + if ($node instanceof Isset_ || $node instanceof Unset_) { + $this->processIssetOrUnset($node, $mutatingScope); + return; + } + + if ($node instanceof Echo_) { + $this->processEcho($node, $mutatingScope); + return; + } + + if ($node instanceof If_ || $node instanceof ElseIf_ || $node instanceof Do_ || $node instanceof While_) { + $node->cond->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return; + } + + if ($node instanceof MethodCallableNode || $node instanceof FunctionCallableNode || $node instanceof StaticMethodCallableNode || $node instanceof InstantiationCallableNode) { + $node->getOriginalNode() + ->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $this->processCallLike($node->getOriginalNode(), $mutatingScope); + + return; + } + }; + + try { + $this->nodeScopeResolverProcessNodes($stmts, $scope, $nodeCallback); + } catch (Error $error) { + if (! str_starts_with($error->getMessage(), 'Call to undefined method ' . Printer::class . '::pPHPStan_')) { + throw $error; + } + + // nothing we can do more precise here as error printing from deep internal PHPStan Printer service with service injection we cannot reset + // in the middle of process + // fallback to fill by found scope + RectorNodeScopeResolver::processNodes($stmts, $scope); + } + + // use after scope filling so DecoratingNodeVisitorInterface instance can fetch the scope of target node + // @see https://github.com/rectorphp/rector-src/pull/7721#discussion_r2595932460 + $this->nodeTraverser->traverse($stmts); + + return $stmts; + } + + private function processYield(Yield_ $yield, MutatingScope $mutatingScope): void + { + if ($yield->key instanceof Expr) { + $yield->key->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + if ($yield->value instanceof Expr) { + $yield->value->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + private function processIssetOrUnset(Isset_|Unset_ $node, MutatingScope $mutatingScope): void + { + foreach ($node->vars as $var) { + $var->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + private function processEcho(Echo_ $echo, MutatingScope $mutatingScope): void + { + foreach ($echo->exprs as $expr) { + $expr->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + private function processMatch(Match_ $match, MutatingScope $mutatingScope): void + { + $match->cond->setAttribute(AttributeKey::SCOPE, $mutatingScope); + foreach ($match->arms as $arm) { + if ($arm->conds !== null) { + foreach ($arm->conds as $cond) { + $cond->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + $arm->body->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + /** + * @param Stmt[] $stmts + * @param callable(Node $node, MutatingScope $scope): void $nodeCallback + */ + private function nodeScopeResolverProcessNodes( + array $stmts, + MutatingScope $mutatingScope, + callable $nodeCallback + ): void { + try { + $this->nodeScopeResolver->processNodes($stmts, $mutatingScope, $nodeCallback); + } catch (ParserErrorsException|ParserException|ShouldNotHappenException|UndefinedVariableException) { + // nothing we can do more precise here as error parsing from deep internal PHPStan service with service injection we cannot reset + // in the middle of process + // fallback to fill by found scope + RectorNodeScopeResolver::processNodes($stmts, $mutatingScope); + } + } + + private function processCallLike(CallLike $callLike, MutatingScope $mutatingScope): void + { + if ($callLike instanceof StaticCall) { + $callLike->class->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $callLike->name->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } elseif ($callLike instanceof MethodCall || $callLike instanceof NullsafeMethodCall) { + $callLike->var->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $callLike->name->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } elseif ($callLike instanceof FuncCall) { + $callLike->name->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } elseif ($callLike instanceof New_ && ! $callLike->class instanceof Class_) { + $callLike->class->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + private function processAssign(Assign|AssignOp|AssignRef $assign, MutatingScope $mutatingScope): void + { + $assign->var->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $assign->expr->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + private function processArray(List_|Array_ $array, MutatingScope $mutatingScope): void + { + foreach ($array->items as $arrayItem) { + if (! $arrayItem instanceof ArrayItem) { + continue; + } + + $this->processArrayItem($arrayItem, $mutatingScope); + } + } + + private function processArrayItem(ArrayItem $arrayItem, MutatingScope $mutatingScope): void + { + $arrayItem->setAttribute(AttributeKey::SCOPE, $mutatingScope); + + if ($arrayItem->key instanceof Expr) { + $arrayItem->key->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + $arrayItem->value->setAttribute(AttributeKey::SCOPE, $mutatingScope); + + if ($arrayItem->value instanceof List_) { + $this->processArray($arrayItem->value, $mutatingScope); + } + } + + /** + * @param callable(Node $trait, MutatingScope $scope): void $nodeCallback + */ + private function decorateNodeAttrGroups(Node $node, MutatingScope $mutatingScope, callable $nodeCallback): void + { + // better to have AttrGroupsAwareInterface for all Node definition with attrGroups property + // but because may conflict with StmtsAwareInterface patch, this needs to be here + if ( + ! $node instanceof Param && + ! $node instanceof ArrowFunction && + ! $node instanceof Closure && + ! $node instanceof ClassConst && + ! $node instanceof ClassLike && + ! $node instanceof ClassMethod && + ! $node instanceof EnumCase && + ! $node instanceof Function_ && + ! $node instanceof Property + ) { + return; + } + + foreach ($node->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + foreach ($attr->args as $arg) { + $this->nodeScopeResolverProcessNodes( + [new Expression($arg->value)], + $mutatingScope, + $nodeCallback + ); + } + } + } + } + + private function processSwitch(Switch_ $switch, MutatingScope $mutatingScope): void + { + $switch->cond->setAttribute(AttributeKey::SCOPE, $mutatingScope); + + // decorate value as well + foreach ($switch->cases as $case) { + $case->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + private function processCatch(Catch_ $catch, string $filePath, MutatingScope $mutatingScope): void + { + $varName = $catch->var instanceof Variable + ? $this->nodeNameResolver->getName($catch->var) + : null; + + $type = TypeCombinator::union( + ...array_map(static fn (Name $name): ObjectType => new ObjectType((string) $name), $catch->types) + ); + + $catchMutatingScope = $mutatingScope->enterCatchType($type, $varName); + $this->processNodes($catch->stmts, $filePath, $catchMutatingScope); + } + + private function processTryCatch(TryCatch $tryCatch, MutatingScope $mutatingScope): void + { + if ($tryCatch->finally instanceof Finally_) { + $tryCatch->finally->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + /** + * @param callable(Node $node, MutatingScope $scope): void $nodeCallback + */ + private function processUnreachableStatementNode( + UnreachableStatementNode $unreachableStatementNode, + MutatingScope $mutatingScope, + callable $nodeCallback + ): void { + $originalStmt = $unreachableStatementNode->getOriginalStatement(); + + $this->nodeScopeResolverProcessNodes( + array_merge([$originalStmt], $unreachableStatementNode->getNextStatements()), + $mutatingScope, + $nodeCallback + ); + } + + /** + * @param callable(Node $node, MutatingScope $scope): void $nodeCallback + */ + private function processProperty(Property $property, MutatingScope $mutatingScope, callable $nodeCallback): void + { + foreach ($property->props as $propertyProperty) { + $propertyProperty->setAttribute(AttributeKey::SCOPE, $mutatingScope); + + if ($propertyProperty->default instanceof Expr) { + $propertyProperty->default->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + } + + foreach ($property->hooks as $hook) { + if ($hook->body === null) { + continue; + } + + /** @var Stmt[] $stmts */ + $stmts = $hook->body instanceof Expr + ? [new Expression($hook->body)] + : [$hook->body]; + $this->nodeScopeResolverProcessNodes($stmts, $mutatingScope, $nodeCallback); + } + } + + private function processBinaryOp(BinaryOp $binaryOp, MutatingScope $mutatingScope): void + { + $binaryOp->left->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $binaryOp->right->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + private function processTernary(Ternary $ternary, MutatingScope $mutatingScope): void + { + if ($ternary->if instanceof Expr) { + $ternary->if->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + $ternary->else->setAttribute(AttributeKey::SCOPE, $mutatingScope); + } + + private function resolveClassOrInterfaceScope( + Class_ | Interface_ | Enum_ $classLike, + MutatingScope $mutatingScope + ): MutatingScope { + $isAnonymous = $this->classAnalyzer->isAnonymousClass($classLike); + + // is anonymous class? - not possible to enter it since PHPStan 0.12.33, see https://github.com/phpstan/phpstan-src/commit/e87fb0ec26f9c8552bbeef26a868b1e5d8185e91 + if ($classLike instanceof Class_ && $isAnonymous) { + $classReflection = $this->reflectionProvider->getAnonymousClassReflection($classLike, $mutatingScope); + } else { + $className = $this->resolveClassName($classLike); + if (! $this->reflectionProvider->hasClass($className)) { + return $mutatingScope; + } + + $classReflection = $this->reflectionProvider->getClass($className); + } + + try { + return $mutatingScope->enterClass($classReflection); + } catch (ShouldNotHappenException) { + } + + $context = $this->privatesAccessor->getPrivateProperty($mutatingScope, 'context'); + $this->privatesAccessor->setPrivateProperty($context, 'classReflection', null); + + try { + return $mutatingScope->enterClass($classReflection); + } catch (ShouldNotHappenException) { + } + + return $mutatingScope; + } + + private function resolveClassName(Class_ | Interface_ | Trait_| Enum_ $classLike): string + { + if ($classLike->namespacedName instanceof Name) { + return (string) $classLike->namespacedName; + } + + if (! $classLike->name instanceof Identifier) { + return ''; + } + + return $classLike->name->toString(); + } + + /** + * @param callable(Node $trait, MutatingScope $scope): void $nodeCallback + */ + private function processTrait(Trait_ $trait, MutatingScope $mutatingScope, callable $nodeCallback): void + { + $traitName = $this->resolveClassName($trait); + + if (! $this->reflectionProvider->hasClass($traitName)) { + $trait->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $this->nodeScopeResolverProcessNodes($trait->stmts, $mutatingScope, $nodeCallback); + $this->decorateNodeAttrGroups($trait, $mutatingScope, $nodeCallback); + + return; + } + + $traitClassReflection = $this->reflectionProvider->getClass($traitName); + + $traitScope = clone $mutatingScope; + + /** @var ScopeContext $scopeContext */ + $scopeContext = $this->privatesAccessor->getPrivateProperty($traitScope, self::CONTEXT); + $traitContext = clone $scopeContext; + + // before entering the class/trait again, we have to tell scope no class was set, otherwise it crashes + $this->privatesAccessor->setPrivateProperty($traitContext, 'classReflection', $traitClassReflection); + + $this->privatesAccessor->setPrivateProperty($traitScope, self::CONTEXT, $traitContext); + + $trait->setAttribute(AttributeKey::SCOPE, $traitScope); + $this->nodeScopeResolverProcessNodes($trait->stmts, $traitScope, $nodeCallback); + $this->decorateNodeAttrGroups($trait, $traitScope, $nodeCallback); + } +} diff --git a/src/NodeTypeResolver/PHPStan/Scope/RectorNodeScopeResolver.php b/src/NodeTypeResolver/PHPStan/Scope/RectorNodeScopeResolver.php new file mode 100644 index 00000000000..72107d9d2a1 --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/Scope/RectorNodeScopeResolver.php @@ -0,0 +1,34 @@ +traverseNodesWithCallable( + $stmts, + function (Node $node) use ($mutatingScope): null { + $node->setAttribute(AttributeKey::SCOPE, $mutatingScope); + return null; + } + ); + } +} diff --git a/src/NodeTypeResolver/PHPStan/Scope/ScopeFactory.php b/src/NodeTypeResolver/PHPStan/Scope/ScopeFactory.php new file mode 100644 index 00000000000..8dd3aa29142 --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/Scope/ScopeFactory.php @@ -0,0 +1,23 @@ +phpStanScopeFactory->create($scopeContext); + } +} diff --git a/src/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php b/src/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php new file mode 100644 index 00000000000..d988a153561 --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php @@ -0,0 +1,76 @@ +isAlwaysTruableArrayType($type); + } + + if ($type instanceof UnionType && TypeCombinator::containsNull($type)) { + return false; + } + + // always trueish + if ($type instanceof ObjectType) { + return true; + } + + if ($type instanceof ConstantScalarType && ! $type->isNull()->yes()) { + return (bool) $type->getValue(); + } + + if ($type->isScalar()->yes()) { + return false; + } + + return $this->isAlwaysTruableUnionType($type); + } + + private function isAlwaysTruableUnionType(Type $type): bool + { + if (! $type instanceof UnionType) { + return false; + } + + foreach ($type->getTypes() as $unionedType) { + if (! $this->isAlwaysTruableType($unionedType)) { + return false; + } + } + + return true; + } + + private function isAlwaysTruableArrayType(ArrayType $arrayType): bool + { + $itemType = $arrayType->getIterableValueType(); + if (! $itemType instanceof ConstantScalarType) { + return false; + } + + return (bool) $itemType->getValue(); + } +} diff --git a/src/NodeTypeResolver/PHPStan/Type/TypeFactory.php b/src/NodeTypeResolver/PHPStan/Type/TypeFactory.php new file mode 100644 index 00000000000..49d431e8d37 --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/Type/TypeFactory.php @@ -0,0 +1,229 @@ +unwrapUnionedTypes($types); + $types = $this->uniquateTypes($types, true); + + return $this->createUnionOrSingleType($types); + } + + /** + * @param Type[] $types + */ + public function createMixedPassedOrUnionType(array $types, bool $keepConstantTypes = false): Type + { + $types = $this->unwrapUnionedTypes($types); + $types = $this->uniquateTypes($types, $keepConstantTypes); + + return $this->createUnionOrSingleType($types); + } + + /** + * @template TType as Type + * @param array $types + * @return array + */ + public function uniquateTypes(array $types, bool $keepConstant = false): array + { + $constantTypeHashes = []; + $uniqueTypes = []; + $totalTypes = count($types); + + $hasFalse = false; + $hasTrue = false; + foreach ($types as $type) { + $type = $this->normalizeObjectType($totalTypes, $type); + $type = $this->normalizeBooleanType($hasFalse, $hasTrue, $type); + + $removedConstantType = $this->removeValueFromConstantType($type); + $removedConstantTypeHash = $this->typeHasher->createTypeHash($removedConstantType); + + if ($keepConstant && $type !== $removedConstantType) { + $typeHash = $this->typeHasher->createTypeHash($type); + $constantTypeHashes[$typeHash] = $removedConstantTypeHash; + } else { + $type = $removedConstantType; + $typeHash = $removedConstantTypeHash; + } + + $uniqueTypes[$typeHash] = $type; + } + + foreach ($constantTypeHashes as $constantTypeHash => $removedConstantTypeHash) { + if (array_key_exists($removedConstantTypeHash, $uniqueTypes)) { + unset($uniqueTypes[$constantTypeHash]); + } + } + + // re-index + return array_values($uniqueTypes); + } + + private function normalizeObjectType(int $totalTypes, Type $type): Type + { + if ($totalTypes > 1 && $type instanceof ObjectWithoutClassTypeWithParentTypes) { + $parents = $type->getParentTypes(); + return new ObjectType($parents[0]->getClassName()); + } + + return $type; + } + + private function normalizeBooleanType(bool &$hasFalse, bool &$hasTrue, Type $type): Type + { + if ($type->isTrue()->yes()) { + $hasTrue = true; + } + + if ($type->isFalse()->yes()) { + $hasFalse = true; + } + + if ($hasFalse && $hasTrue && ($type->isTrue()->yes() || $type->isFalse()->yes())) { + return new BooleanType(); + } + + return $type; + } + + /** + * @param Type[] $types + * @return Type[] + */ + private function unwrapUnionedTypes(array $types): array + { + // unwrap union types + $unwrappedTypes = []; + foreach ($types as $type) { + $flattenTypes = TypeUtils::flattenTypes($type); + + foreach ($flattenTypes as $flattenType) { + if ($flattenType instanceof ConstantArrayType) { + $unwrappedTypes = [...$unwrappedTypes, ...$this->unwrapConstantArrayTypes($flattenType)]; + } else { + $unwrappedTypes = $this->resolveNonConstantArrayType($flattenType, $unwrappedTypes); + } + } + } + + return $unwrappedTypes; + } + + /** + * @param Type[] $unwrappedTypes + * @return Type[] + */ + private function resolveNonConstantArrayType(Type $type, array $unwrappedTypes): array + { + $unwrappedTypes[] = $type; + return $unwrappedTypes; + } + + /** + * @param Type[] $types + */ + private function createUnionOrSingleType(array $types): Type + { + if ($types === []) { + return new MixedType(); + } + + if (count($types) === 1) { + return $types[0]; + } + + foreach ($types as $type) { + if ($type instanceof MixedType) { + return new MixedType(); + } + } + + return new UnionType($types); + } + + private function removeValueFromConstantType(Type $type): Type + { + // remove values from constant types + if ($type instanceof ConstantFloatType) { + return new FloatType(); + } + + if ($type instanceof ConstantStringType) { + return new StringType(); + } + + if ($type instanceof ConstantIntegerType) { + return new IntegerType(); + } + + if ($type->isTrue()->yes() || $type->isFalse()->yes()) { + return new BooleanType(); + } + + return $type; + } + + /** + * @return Type[] + */ + private function unwrapConstantArrayTypes(ConstantArrayType $constantArrayType): array + { + $unwrappedTypes = []; + + $flattenKeyTypes = TypeUtils::flattenTypes($constantArrayType->getIterableKeyType()); + $flattenItemTypes = TypeUtils::flattenTypes($constantArrayType->getIterableValueType()); + + foreach ($flattenItemTypes as $position => $nestedFlattenItemType) { + $nestedFlattenKeyType = $flattenKeyTypes[$position] ?? null; + if (! $nestedFlattenKeyType instanceof Type) { + $nestedFlattenKeyType = new MixedType(); + } + + if ($nestedFlattenItemType instanceof ConstantArrayType) { + $innerArrayTypes = $this->unwrapConstantArrayTypes($nestedFlattenItemType); + foreach ($innerArrayTypes as $innerArrayType) { + // preserve outer array -> inner array structure: array + $unwrappedTypes[] = new ArrayType($nestedFlattenKeyType, $innerArrayType); + } + + continue; + } + + $unwrappedTypes[] = new ArrayType($nestedFlattenKeyType, $nestedFlattenItemType); + } + + return $unwrappedTypes; + } +} diff --git a/src/NodeTypeResolver/PHPStan/TypeHasher.php b/src/NodeTypeResolver/PHPStan/TypeHasher.php new file mode 100644 index 00000000000..1caa97a9f3b --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/TypeHasher.php @@ -0,0 +1,85 @@ +createTypeHash($firstType) === $this->createTypeHash($secondType); + } + + public function createTypeHash(Type $type): string + { + if ($type instanceof MixedType) { + return $type->describe(VerbosityLevel::precise()) . $type->isExplicitMixed(); + } + + if ($type instanceof ArrayType) { + return $this->createTypeHash($type->getIterableValueType()) . $this->createTypeHash( + $type->getIterableKeyType() + ) . $type->getItemType()->describe(VerbosityLevel::precise()) . '[]'; + } + + if ($type instanceof GenericObjectType) { + return $type->describe(VerbosityLevel::precise()); + } + + if ($type instanceof TypeWithClassName) { + return $this->resolveUniqueTypeWithClassNameHash($type); + } + + if ($type->isConstantValue()->yes()) { + return $type::class; + } + + $type = $this->normalizeObjectType($type); + return $type->describe(VerbosityLevel::value()); + } + + private function resolveUniqueTypeWithClassNameHash(TypeWithClassName $typeWithClassName): string + { + if ($typeWithClassName instanceof ShortenedObjectType) { + return $typeWithClassName->getFullyQualifiedName(); + } + + if ($typeWithClassName instanceof AliasedObjectType) { + return $typeWithClassName->getFullyQualifiedName(); + } + + return $typeWithClassName->getClassName(); + } + + private function normalizeObjectType(Type $type): Type + { + return TypeTraverser::map($type, static function (Type $currentType, callable $traverseCallback): Type { + if ($currentType instanceof ShortenedObjectType) { + return new FullyQualifiedObjectType($currentType->getFullyQualifiedName()); + } + + if ($currentType instanceof AliasedObjectType) { + return new FullyQualifiedObjectType($currentType->getFullyQualifiedName()); + } + + if ($currentType instanceof ObjectType && ! $currentType instanceof GenericObjectType && $currentType->getClassName() !== 'Iterator' && $currentType->getClassName() !== 'iterable') { + return new FullyQualifiedObjectType($currentType->getClassName()); + } + + return $traverseCallback($currentType); + }); + } +} diff --git a/src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockClassRenamer.php b/src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockClassRenamer.php new file mode 100644 index 00000000000..3f1432aa119 --- /dev/null +++ b/src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockClassRenamer.php @@ -0,0 +1,40 @@ +addPhpDocNodeVisitor($this->classRenamePhpDocNodeVisitor); + + $this->classRenamePhpDocNodeVisitor->setCurrentPhpNode($currentPhpNode); + + $this->classRenamePhpDocNodeVisitor->setOldToNewTypes($oldToNewTypes); + + $phpDocNodeTraverser->traverse($phpDocInfo->getPhpDocNode()); + + return $this->classRenamePhpDocNodeVisitor->hasChanged(); + } +} diff --git a/src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockNameImporter.php b/src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockNameImporter.php new file mode 100644 index 00000000000..aa644f35056 --- /dev/null +++ b/src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockNameImporter.php @@ -0,0 +1,33 @@ +children === []) { + return false; + } + + $this->nameImportingPhpDocNodeVisitor->setCurrentNode($node); + + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + $phpDocNodeTraverser->addPhpDocNodeVisitor($this->nameImportingPhpDocNodeVisitor); + $phpDocNodeTraverser->traverse($phpDocNode); + + return $this->nameImportingPhpDocNodeVisitor->hasChanged(); + } +} diff --git a/packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockTagReplacer.php b/src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockTagReplacer.php similarity index 86% rename from packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockTagReplacer.php rename to src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockTagReplacer.php index d523305d64b..61d923780ea 100644 --- a/packages/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockTagReplacer.php +++ b/src/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockTagReplacer.php @@ -9,15 +9,17 @@ use Rector\BetterPhpDocParser\Annotation\AnnotationNaming; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; -final class DocBlockTagReplacer +final readonly class DocBlockTagReplacer { public function __construct( private AnnotationNaming $annotationNaming ) { } - public function replaceTagByAnother(PhpDocInfo $phpDocInfo, string $oldTag, string $newTag): void + public function replaceTagByAnother(PhpDocInfo $phpDocInfo, string $oldTag, string $newTag): bool { + $hasChanged = false; + $oldTag = $this->annotationNaming->normalizeName($oldTag); $newTag = $this->annotationNaming->normalizeName($newTag); @@ -33,6 +35,9 @@ public function replaceTagByAnother(PhpDocInfo $phpDocInfo, string $oldTag, stri unset($phpDocNode->children[$key]); $phpDocNode->children[] = new PhpDocTagNode($newTag, new GenericTagValueNode('')); + $hasChanged = true; } + + return $hasChanged; } } diff --git a/src/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php b/src/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php new file mode 100644 index 00000000000..8d9db4329b2 --- /dev/null +++ b/src/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php @@ -0,0 +1,127 @@ +currentPhpNode = $phpNode; + } + + public function beforeTraverse(Node $node): void + { + if ($this->oldToNewTypes === []) { + throw new ShouldNotHappenException('Configure "$oldToNewClasses" first'); + } + + if (! $this->currentPhpNode instanceof PhpNode) { + throw new ShouldNotHappenException('Configure "$currentPhpNode" first'); + } + + $this->hasChanged = false; + } + + public function enterNode(Node $node): ?Node + { + if (! $node instanceof IdentifierTypeNode) { + return null; + } + + /** @var \PhpParser\Node $currentPhpNode */ + $currentPhpNode = $this->currentPhpNode; + $staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($node, $currentPhpNode); + + // non object type and @template is to not be renamed + if (! $staticType instanceof ObjectType || $staticType instanceof TemplateObjectType) { + return null; + } + + // make sure to compare FQNs + $objectType = $this->ensureFQCNObject($staticType, $node->name); + + foreach ($this->oldToNewTypes as $oldToNewType) { + $oldType = $oldToNewType->getOldType(); + if (! $oldType instanceof ObjectType) { + continue; + } + + if (! $objectType->equals($oldType)) { + continue; + } + + $newTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($oldToNewType->getNewType()); + + $parentType = $node->getAttribute(PhpDocAttributeKey::PARENT); + if ($parentType instanceof TypeNode) { + // mirror attributes + $newTypeNode->setAttribute(PhpDocAttributeKey::PARENT, $parentType); + } + + $this->hasChanged = true; + + $this->renamedNameCollector->add($oldType->getClassName()); + return $newTypeNode; + } + + return null; + } + + /** + * @param OldToNewType[] $oldToNewTypes + */ + public function setOldToNewTypes(array $oldToNewTypes): void + { + $this->oldToNewTypes = $oldToNewTypes; + } + + public function hasChanged(): bool + { + return $this->hasChanged; + } + + private function ensureFQCNObject(ObjectType $objectType, string $identifierName): ObjectType + { + if ($objectType instanceof ShortenedObjectType && str_starts_with($identifierName, '\\')) { + return new ObjectType(ltrim($identifierName, '\\')); + } + + if ($objectType instanceof ShortenedObjectType || $objectType instanceof AliasedObjectType) { + return new ObjectType($objectType->getFullyQualifiedName()); + } + + return $objectType; + } +} diff --git a/src/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php b/src/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php new file mode 100644 index 00000000000..dc1006472ee --- /dev/null +++ b/src/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php @@ -0,0 +1,283 @@ +currentPhpParserNode instanceof PhpParserNode) { + throw new ShouldNotHappenException('Set "$currentPhpParserNode" first'); + } + } + + public function enterNode(Node $node): ?Node + { + if ($node instanceof SpacelessPhpDocTagNode) { + return $this->enterSpacelessPhpDocTagNode($node); + } + + if ($node instanceof DoctrineAnnotationTagValueNode) { + $this->processDoctrineAnnotationTagValueNode($node); + return $node; + } + + if (! $node instanceof IdentifierTypeNode) { + return null; + } + + if (! $this->currentPhpParserNode instanceof PhpParserNode) { + throw new ShouldNotHappenException(); + } + + // no \, skip early + if (! str_contains($node->name, '\\')) { + return null; + } + + $staticType = $this->identifierPhpDocTypeMapper->mapIdentifierTypeNode($node, $this->currentPhpParserNode); + $staticType = $this->resolveFullyQualified($staticType); + if (! $staticType instanceof FullyQualifiedObjectType) { + return null; + } + + $file = $this->currentFileProvider->getFile(); + if (! $file instanceof File) { + return null; + } + + return $this->processFqnNameImport($this->currentPhpParserNode, $node, $staticType, $file); + } + + public function setCurrentNode(PhpParserNode $phpParserNode): void + { + $this->hasChanged = false; + $this->currentPhpParserNode = $phpParserNode; + } + + public function hasChanged(): bool + { + return $this->hasChanged; + } + + private function resolveFullyQualified(Type $type): ?FullyQualifiedObjectType + { + if ($type instanceof ShortenedObjectType || $type instanceof AliasedObjectType) { + return new FullyQualifiedObjectType($type->getFullyQualifiedName()); + } + + if ($type instanceof FullyQualifiedObjectType) { + return $type; + } + + return null; + } + + private function processFqnNameImport( + PhpParserNode $phpParserNode, + IdentifierTypeNode $identifierTypeNode, + FullyQualifiedObjectType $fullyQualifiedObjectType, + File $file + ): ?IdentifierTypeNode { + $parentNode = $identifierTypeNode->getAttribute(PhpDocAttributeKey::PARENT); + if ($parentNode instanceof TemplateTagValueNode) { + // might break + return null; + } + + // standardize to FQN + if (str_starts_with($fullyQualifiedObjectType->getClassName(), '@')) { + $fullyQualifiedObjectType = new FullyQualifiedObjectType(ltrim( + $fullyQualifiedObjectType->getClassName(), + '@' + )); + } + + if ($this->classNameImportSkipper->shouldSkipNameForFullyQualifiedObjectType( + $file, + $phpParserNode, + $fullyQualifiedObjectType + )) { + return null; + } + + $newNode = new IdentifierTypeNode($fullyQualifiedObjectType->getShortName()); + + // should skip because its already used + if ($this->useNodesToAddCollector->isShortImported($file, $fullyQualifiedObjectType) + && ! $this->useNodesToAddCollector->isImportShortable($file, $fullyQualifiedObjectType)) { + return null; + } + + if ($this->shouldImport($file, $newNode, $identifierTypeNode, $fullyQualifiedObjectType)) { + $this->useNodesToAddCollector->addUseImport($fullyQualifiedObjectType); + $this->hasChanged = true; + + return $newNode; + } + + return null; + } + + private function shouldImport( + File $file, + IdentifierTypeNode $newNode, + IdentifierTypeNode $identifierTypeNode, + FullyQualifiedObjectType $fullyQualifiedObjectType + ): bool { + if ($newNode->name === $identifierTypeNode->name) { + return false; + } + + if (str_starts_with($identifierTypeNode->name, '\\')) { + if ($fullyQualifiedObjectType->getShortName() !== $fullyQualifiedObjectType->getClassName()) { + return $fullyQualifiedObjectType->getShortName() !== ltrim($identifierTypeNode->name, '\\'); + } + + return true; + } + + $className = $fullyQualifiedObjectType->getClassName(); + + if (! $this->reflectionProvider->hasClass($className)) { + return false; + } + + $firstPath = Strings::before($identifierTypeNode->name, '\\' . $newNode->name); + if ($firstPath === null) { + return ! $this->useNodesToAddCollector->hasImport($file, $fullyQualifiedObjectType); + } + + if ($firstPath === '') { + return true; + } + + $namespaceParts = explode('\\', ltrim($firstPath, '\\')); + return count($namespaceParts) > 1; + } + + private function processDoctrineAnnotationTagValueNode( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode + ): void { + $currentPhpParserNode = $this->currentPhpParserNode; + if (! $currentPhpParserNode instanceof PhpParserNode) { + throw new ShouldNotHappenException(); + } + + $identifierTypeNode = $doctrineAnnotationTagValueNode->identifierTypeNode; + $staticType = $this->identifierPhpDocTypeMapper->mapIdentifierTypeNode( + $identifierTypeNode, + $currentPhpParserNode + ); + $staticType = $this->resolveFullyQualified($staticType); + + if (! $staticType instanceof FullyQualifiedObjectType) { + return; + } + + $file = $this->currentFileProvider->getFile(); + if (! $file instanceof File) { + return; + } + + $shortentedIdentifierTypeNode = $this->processFqnNameImport( + $currentPhpParserNode, + $identifierTypeNode, + $staticType, + $file + ); + + if (! $shortentedIdentifierTypeNode instanceof IdentifierTypeNode) { + return; + } + + $doctrineAnnotationTagValueNode->identifierTypeNode = $shortentedIdentifierTypeNode; + $doctrineAnnotationTagValueNode->markAsChanged(); + } + + private function enterSpacelessPhpDocTagNode( + SpacelessPhpDocTagNode $spacelessPhpDocTagNode + ): SpacelessPhpDocTagNode | null { + if (! $spacelessPhpDocTagNode->value instanceof DoctrineAnnotationTagValueNode) { + return null; + } + + // special case for doctrine annotation + if (! str_starts_with($spacelessPhpDocTagNode->name, '@')) { + return null; + } + + $attributeClass = ltrim($spacelessPhpDocTagNode->name, '@\\'); + $identifierTypeNode = new IdentifierTypeNode($attributeClass); + + $currentPhpParserNode = $this->currentPhpParserNode; + if (! $currentPhpParserNode instanceof PhpParserNode) { + throw new ShouldNotHappenException(); + } + + $staticType = $this->identifierPhpDocTypeMapper->mapIdentifierTypeNode( + new IdentifierTypeNode($attributeClass), + $currentPhpParserNode + ); + $staticType = $this->resolveFullyQualified($staticType); + + if (! $staticType instanceof FullyQualifiedObjectType) { + return null; + } + + $file = $this->currentFileProvider->getFile(); + if (! $file instanceof File) { + return null; + } + + $importedName = $this->processFqnNameImport( + $currentPhpParserNode, + $identifierTypeNode, + $staticType, + $file + ); + + if ($importedName instanceof IdentifierTypeNode) { + $spacelessPhpDocTagNode->name = '@' . $importedName->name; + return $spacelessPhpDocTagNode; + } + + return null; + } +} diff --git a/packages/NodeTypeResolver/Reflection/BetterReflection/RectorBetterReflectionSourceLocatorFactory.php b/src/NodeTypeResolver/Reflection/BetterReflection/RectorBetterReflectionSourceLocatorFactory.php similarity index 86% rename from packages/NodeTypeResolver/Reflection/BetterReflection/RectorBetterReflectionSourceLocatorFactory.php rename to src/NodeTypeResolver/Reflection/BetterReflection/RectorBetterReflectionSourceLocatorFactory.php index 724a62d9f20..0c74e8f7c5e 100644 --- a/packages/NodeTypeResolver/Reflection/BetterReflection/RectorBetterReflectionSourceLocatorFactory.php +++ b/src/NodeTypeResolver/Reflection/BetterReflection/RectorBetterReflectionSourceLocatorFactory.php @@ -9,7 +9,10 @@ use PHPStan\Reflection\BetterReflection\BetterReflectionSourceLocatorFactory; use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocator\IntermediateSourceLocator; -final class RectorBetterReflectionSourceLocatorFactory +/** + * @api used on phpstan config factory + */ +final readonly class RectorBetterReflectionSourceLocatorFactory { public function __construct( private BetterReflectionSourceLocatorFactory $betterReflectionSourceLocatorFactory, @@ -24,7 +27,7 @@ public function create(): MemoizingSourceLocator // make PHPStan first source locator, so we avoid parsing every single file - huge performance hit! $aggregateSourceLocator = new AggregateSourceLocator([$phpStanSourceLocator, $this->intermediateSourceLocator]); - // important for cache + // important for cache, but should rebuild for tests return new MemoizingSourceLocator($aggregateSourceLocator); } } diff --git a/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocator/IntermediateSourceLocator.php b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocator/IntermediateSourceLocator.php new file mode 100644 index 00000000000..aa75a9841ea --- /dev/null +++ b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocator/IntermediateSourceLocator.php @@ -0,0 +1,59 @@ +dynamicSourceLocatorProvider->provide(); + + try { + $reflection = $sourceLocator->locateIdentifier($reflector, $identifier); + } catch (CouldNotReadFileException) { + return null; + } + + if ($reflection instanceof Reflection) { + return $reflection; + } + + return null; + } + + /** + * Find all identifiers of a type + * @return array + */ + public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array + { + $sourceLocator = $this->dynamicSourceLocatorProvider->provide(); + + try { + $reflections = $sourceLocator->locateIdentifiersByType($reflector, $identifierType); + } catch (CouldNotReadFileException) { + return []; + } + + if ($reflections !== []) { + return $reflections; + } + + return []; + } +} diff --git a/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php new file mode 100644 index 00000000000..f5fc2e9dd12 --- /dev/null +++ b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php @@ -0,0 +1,94 @@ +filePaths = [$filePath]; + } + + /** + * @param string[] $files + */ + public function addFiles(array $files): void + { + $this->filePaths = array_unique(array_merge($this->filePaths, $files)); + } + + /** + * @param string[] $directories + */ + public function addDirectories(array $directories): void + { + $this->directories = array_unique(array_merge($this->directories, $directories)); + } + + public function provide(): SourceLocator + { + // do not cache for PHPUnit, as in test every fixture is different + $isPHPUnitRun = StaticPHPUnitEnvironment::isPHPUnitRun(); + + if ($this->aggregateSourceLocator instanceof AggregateSourceLocator && ! $isPHPUnitRun) { + return $this->aggregateSourceLocator; + } + + $sourceLocators = []; + + foreach ($this->filePaths as $file) { + $sourceLocators[] = $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($file); + } + + foreach ($this->directories as $directory) { + $sourceLocators[] = $this->optimizedDirectorySourceLocatorFactory->createByDirectory($directory); + } + + return $this->aggregateSourceLocator = new AggregateSourceLocator($sourceLocators); + } + + public function arePathsEmpty(): bool + { + return $this->filePaths === [] && $this->directories === []; + } + + /** + * @api to allow fast single-container tests + */ + public function reset(): void + { + $this->filePaths = []; + $this->directories = []; + $this->aggregateSourceLocator = null; + } +} diff --git a/src/NodeTypeResolver/TypeAnalyzer/ArrayTypeAnalyzer.php b/src/NodeTypeResolver/TypeAnalyzer/ArrayTypeAnalyzer.php new file mode 100644 index 00000000000..f427a6fd2a5 --- /dev/null +++ b/src/NodeTypeResolver/TypeAnalyzer/ArrayTypeAnalyzer.php @@ -0,0 +1,23 @@ +nodeTypeResolver->getNativeType($expr); + return $nodeType->isArray() + ->yes(); + } +} diff --git a/src/NodeTypeResolver/TypeAnalyzer/StringTypeAnalyzer.php b/src/NodeTypeResolver/TypeAnalyzer/StringTypeAnalyzer.php new file mode 100644 index 00000000000..e640fe29046 --- /dev/null +++ b/src/NodeTypeResolver/TypeAnalyzer/StringTypeAnalyzer.php @@ -0,0 +1,23 @@ +nodeTypeResolver->getType($expr); + return $nodeType->isString() + ->yes(); + } +} diff --git a/src/NodeTypeResolver/TypeComparator/ArrayTypeComparator.php b/src/NodeTypeResolver/TypeComparator/ArrayTypeComparator.php new file mode 100644 index 00000000000..62a96ae36b7 --- /dev/null +++ b/src/NodeTypeResolver/TypeComparator/ArrayTypeComparator.php @@ -0,0 +1,36 @@ +isSuperTypeOf($checkedType) + ->yes(); + } + + $checkedKeyType = $checkedType->getIterableKeyType(); + $mainKeyType = $mainType->getIterableKeyType(); + + if (! $mainKeyType instanceof MixedType && $mainKeyType->isSuperTypeOf($checkedKeyType)->yes()) { + return true; + } + + $checkedItemType = $checkedType->getIterableValueType(); + $mainItemType = $mainType->getIterableValueType(); + + return $checkedItemType->isSuperTypeOf($mainItemType) + ->yes(); + } +} diff --git a/src/NodeTypeResolver/TypeComparator/ScalarTypeComparator.php b/src/NodeTypeResolver/TypeComparator/ScalarTypeComparator.php new file mode 100644 index 00000000000..8298871bc4a --- /dev/null +++ b/src/NodeTypeResolver/TypeComparator/ScalarTypeComparator.php @@ -0,0 +1,76 @@ +isString()->yes() && $secondType->isString()->yes()) { + // prevents "class-string" vs "string" + $firstTypeClass = $firstType::class; + $secondTypeClass = $secondType::class; + + return $firstTypeClass === $secondTypeClass; + } + + if ($firstType->isInteger()->yes() && $secondType->isInteger()->yes()) { + // prevents "int" vs "int" + $firstTypeClass = $firstType::class; + $secondTypeClass = $secondType::class; + + return $firstTypeClass === $secondTypeClass; + } + + if ($firstType->isFloat()->yes() && $secondType->isFloat()->yes()) { + return true; + } + + if (! $firstType->isBoolean()->yes()) { + return false; + } + + return $secondType->isBoolean() + ->yes(); + } + + /** + * E.g. first is string, second is bool + */ + public function areDifferentScalarTypes(Type $firstType, Type $secondType): bool + { + if (! $firstType->isScalar()->yes()) { + return false; + } + + if (! $secondType->isScalar()->yes()) { + return false; + } + + // treat class-string and string the same + if ($firstType->isString()->yes() && $secondType->isString()->yes()) { + return false; + } + + if ($firstType->isInteger()->yes() && $secondType->isInteger()->yes()) { + return false; + } + + if (! $firstType->isString()->yes()) { + return $firstType::class !== $secondType::class; + } + + if (! $secondType->isClassString()->yes()) { + return $firstType::class !== $secondType::class; + } + + return false; + } +} diff --git a/src/NodeTypeResolver/TypeComparator/TypeComparator.php b/src/NodeTypeResolver/TypeComparator/TypeComparator.php new file mode 100644 index 00000000000..7470a2fc94b --- /dev/null +++ b/src/NodeTypeResolver/TypeComparator/TypeComparator.php @@ -0,0 +1,250 @@ +normalizeTemplateType($firstType); + $secondType = $this->normalizeTemplateType($secondType); + + $firstTypeHash = $this->typeHasher->createTypeHash($firstType); + $secondTypeHash = $this->typeHasher->createTypeHash($secondType); + + if ($firstTypeHash === $secondTypeHash) { + return true; + } + + if ($this->scalarTypeComparator->areEqualScalar($firstType, $secondType)) { + return true; + } + + // aliases and types + if ($this->areAliasedObjectMatchingFqnObject($firstType, $secondType)) { + return true; + } + + if ($this->typeHasher->areTypesEqual($firstType, $secondType)) { + return true; + } + + // is template of + return $this->areArrayTypeWithSingleObjectChildToParent($firstType, $secondType); + } + + public function arePhpParserAndPhpStanPhpDocTypesEqual( + Node $phpParserNode, + TypeNode $phpStanDocTypeNode, + Node $node + ): bool { + $phpParserNodeType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($phpParserNode); + $phpStanDocType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( + $phpStanDocTypeNode, + $node + ); + + if (! $this->areTypesEqual($phpParserNodeType, $phpStanDocType) && $this->isSubtype( + $phpStanDocType, + $phpParserNodeType + )) { + return false; + } + + // normalize bool union types + $phpParserNodeType = $this->normalizeConstantBooleanType($phpParserNodeType); + $phpStanDocType = $this->normalizeConstantBooleanType($phpStanDocType); + + // is scalar replace by another - remove it? + $areDifferentScalarTypes = $this->scalarTypeComparator->areDifferentScalarTypes( + $phpParserNodeType, + $phpStanDocType + ); + + if (! $areDifferentScalarTypes && ! $this->areTypesEqual($phpParserNodeType, $phpStanDocType)) { + return false; + } + + if ($this->areTypesSameWithLiteralTypeInPhpDoc($areDifferentScalarTypes, $phpStanDocType, $phpParserNodeType)) { + return false; + } + + if ($phpStanDocType instanceof UnionType || $phpStanDocType instanceof IntersectionType) { + foreach ($phpStanDocType->getTypes() as $type) { + if ($type instanceof TemplateObjectType) { + return false; + } + } + } + + return $this->isThisTypeInFinalClass($phpStanDocType, $phpParserNodeType, $phpParserNode); + } + + public function isSubtype(Type $checkedType, Type $mainType): bool + { + $checkedType = $this->normalizeTemplateType($checkedType); + $mainType = $this->normalizeTemplateType($mainType); + + if ($mainType instanceof MixedType) { + return false; + } + + if (! $mainType instanceof ArrayType) { + return $mainType->isSuperTypeOf($checkedType) + ->yes(); + } + + if (! $checkedType instanceof ArrayType) { + return $mainType->isSuperTypeOf($checkedType) + ->yes(); + } + + return $this->arrayTypeComparator->isSubtype($checkedType, $mainType); + } + + /** + * unless it by ref, object param has its own life vs redefined variable + * see https://3v4l.org/dI5Pe vs https://3v4l.org/S8i71 + */ + private function normalizeTemplateType(Type $type): Type + { + return $type instanceof TemplateType + ? $type->getBound() + : $type; + } + + private function areAliasedObjectMatchingFqnObject(Type $firstType, Type $secondType): bool + { + if ($firstType instanceof AliasedObjectType && $secondType instanceof ObjectType) { + return $firstType->getFullyQualifiedName() === $secondType->getClassName(); + } + + if (! $firstType instanceof ObjectType) { + return false; + } + + if (! $secondType instanceof AliasedObjectType) { + return false; + } + + return $secondType->getFullyQualifiedName() === $firstType->getClassName(); + } + + /** + * E.g. class A extends B, class B → A[] is subtype of B[] → keep A[] + */ + private function areArrayTypeWithSingleObjectChildToParent(Type $firstType, Type $secondType): bool + { + if (! $firstType instanceof ArrayType) { + return false; + } + + if (! $secondType instanceof ArrayType) { + return false; + } + + $firstArrayItemType = $firstType->getIterableValueType(); + $secondArrayItemType = $secondType->getIterableValueType(); + + return $this->isMutualObjectSubtypes($firstArrayItemType, $secondArrayItemType); + } + + private function isMutualObjectSubtypes(Type $firstArrayItemType, Type $secondArrayItemType): bool + { + if (! $firstArrayItemType instanceof ObjectType) { + return false; + } + + if (! $secondArrayItemType instanceof ObjectType) { + return false; + } + + if ($firstArrayItemType->isSuperTypeOf($secondArrayItemType)->yes()) { + return true; + } + + return $secondArrayItemType->isSuperTypeOf($firstArrayItemType) + ->yes(); + } + + private function normalizeConstantBooleanType(Type $type): Type + { + return TypeTraverser::map($type, static function (Type $type, callable $callable): Type { + if ($type->isTrue()->yes() || $type->isFalse()->yes()) { + return new BooleanType(); + } + + return $callable($type); + }); + } + + private function areTypesSameWithLiteralTypeInPhpDoc( + bool $areDifferentScalarTypes, + Type $phpStanDocType, + Type $phpParserNodeType + ): bool { + return $areDifferentScalarTypes + && $phpStanDocType instanceof ConstantScalarType + && $phpParserNodeType->isSuperTypeOf($phpStanDocType) + ->yes(); + } + + private function isThisTypeInFinalClass(Type $phpStanDocType, Type $phpParserNodeType, Node $node): bool + { + /** + * Special case for $this/(self|static) compare + * + * $this refers to the exact object identity, not just the same type. Therefore, it's valid and should not be removed + * @see https://wiki.php.net/rfc/this_return_type for more context + */ + if ($phpStanDocType instanceof ThisType && $phpParserNodeType instanceof StaticType) { + return false; + } + + $isStaticReturnDocTypeWithThisType = $phpStanDocType instanceof StaticType && $phpParserNodeType instanceof ThisType; + + if (! $isStaticReturnDocTypeWithThisType) { + return true; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + + if (! $classReflection instanceof ClassReflection || ! $classReflection->isClass()) { + return false; + } + + return $classReflection->isFinalByKeyword(); + } +} diff --git a/packages/NodeTypeResolver/ValueObject/OldToNewType.php b/src/NodeTypeResolver/ValueObject/OldToNewType.php similarity index 91% rename from packages/NodeTypeResolver/ValueObject/OldToNewType.php rename to src/NodeTypeResolver/ValueObject/OldToNewType.php index 5d9e1870893..2477106e301 100644 --- a/packages/NodeTypeResolver/ValueObject/OldToNewType.php +++ b/src/NodeTypeResolver/ValueObject/OldToNewType.php @@ -6,7 +6,7 @@ use PHPStan\Type\Type; -final class OldToNewType +final readonly class OldToNewType { public function __construct( private Type $oldType, diff --git a/src/NonPhpFile/NonPhpFileProcessor.php b/src/NonPhpFile/NonPhpFileProcessor.php deleted file mode 100644 index b7e7c1d5f6f..00000000000 --- a/src/NonPhpFile/NonPhpFileProcessor.php +++ /dev/null @@ -1,55 +0,0 @@ -nonPhpRectors as $nonPhpRector) { - $newFileContent = $nonPhpRector->refactorFileContent($file->getFileContent()); - $file->changeFileContent($newFileContent); - } - } - - public function supports(File $file, Configuration $configuration): bool - { - $smartFileInfo = $file->getSmartFileInfo(); - - // early assign to variable for increase performance - // @see https://3v4l.org/FM3vY#focus=8.0.7 vs https://3v4l.org/JZW7b#focus=8.0.7 - $pathname = $smartFileInfo->getPathname(); - // bug in path extension - foreach ($this->getSupportedFileExtensions() as $fileExtension) { - if (\str_ends_with($pathname, '.' . $fileExtension)) { - return true; - } - } - - return false; - } - - /** - * @return string[] - */ - public function getSupportedFileExtensions(): array - { - return StaticNonPhpFileSuffixes::SUFFIXES; - } -} diff --git a/src/NonPhpFile/Rector/RenameClassNonPhpRector.php b/src/NonPhpFile/Rector/RenameClassNonPhpRector.php deleted file mode 100644 index aea92490791..00000000000 --- a/src/NonPhpFile/Rector/RenameClassNonPhpRector.php +++ /dev/null @@ -1,148 +0,0 @@ -|\.|\'))|(?\s+\\\\))'; - - /** - * @see https://regex101.com/r/HKUFJD/5 - * @see https://stackoverflow.com/a/3926546/1348344 - * @var string - */ - private const STANDALONE_CLASS_SUFFIX_REGEX = '(?=::)#'; - - /** - * @var array - */ - private array $renameClasses = []; - - public function __construct( - private RenamedClassesDataCollector $renamedClassesDataCollector, - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Change class names and just renamed classes in non-PHP files, NEON, YAML, TWIG, LATTE, blade etc. mostly with regular expressions', - [ - new ConfiguredCodeSample( - <<<'CODE_SAMPLE' -services: - - SomeOldClass -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -services: - - SomeNewClass -CODE_SAMPLE - , - [ - self::RENAME_CLASSES => [ - 'SomeOldClass' => 'SomeNewClass', - ], - ] - ), - ] - ); - } - - public function refactorFileContent(string $fileContent): string - { - $classRenames = $this->getRenameClasses(); - return $this->renameClasses($fileContent, $classRenames); - } - - /** - * @param array> $configuration - */ - public function configure(array $configuration): void - { - $this->renameClasses = $configuration[self::RENAME_CLASSES] ?? []; - } - - /** - * @param array $classRenames - */ - private function renameClasses(string $newContent, array $classRenames): string - { - $classRenames = $this->addDoubleSlahed($classRenames); - - foreach ($classRenames as $oldClass => $newClass) { - // the old class is without slashes, it can make mess as similar to a word in the text, so we have to be more strict about it - $oldClassRegex = $this->createOldClassRegex($oldClass); - $newContent = Strings::replace( - $newContent, - $oldClassRegex, - fn (array $match): string => ($match['extra_space'] ?? '') . $newClass - ); - } - - return $newContent; - } - - /** - * Process with double quotes too, e.g. in twig - * - * @param array $classRenames - * @return array - */ - private function addDoubleSlahed(array $classRenames): array - { - foreach ($classRenames as $oldClass => $newClass) { - // to prevent no slash override - if (! \str_contains($oldClass, '\\')) { - continue; - } - - $doubleSlashOldClass = str_replace('\\', '\\\\', $oldClass); - $doubleSlashNewClass = str_replace('\\', '\\\\', $newClass); - - $classRenames[$doubleSlashOldClass] = $doubleSlashNewClass; - } - - return $classRenames; - } - - /** - * @return array - */ - private function getRenameClasses(): array - { - return array_merge($this->renameClasses, $this->renamedClassesDataCollector->getOldToNewClasses()); - } - - private function createOldClassRegex(string $oldClass): string - { - if (! \str_contains($oldClass, '\\')) { - return self::STANDALONE_CLASS_PREFIX_REGEX - . preg_quote($oldClass, '#') - . self::STANDALONE_CLASS_SUFFIX_REGEX; - } - - return '#' . preg_quote($oldClass, '#') . '#'; - } -} diff --git a/src/PHPStan/Reflection/TypeToCallReflectionResolver/ClosureTypeToCallReflectionResolver.php b/src/PHPStan/Reflection/TypeToCallReflectionResolver/ClosureTypeToCallReflectionResolver.php deleted file mode 100644 index f9459c148fc..00000000000 --- a/src/PHPStan/Reflection/TypeToCallReflectionResolver/ClosureTypeToCallReflectionResolver.php +++ /dev/null @@ -1,33 +0,0 @@ -getCallableParametersAcceptors($scope), - null, - TrinaryLogic::createMaybe() - ); - } -} diff --git a/src/PHPStan/Reflection/TypeToCallReflectionResolver/ConstantArrayTypeToCallReflectionResolver.php b/src/PHPStan/Reflection/TypeToCallReflectionResolver/ConstantArrayTypeToCallReflectionResolver.php deleted file mode 100644 index b7b15337787..00000000000 --- a/src/PHPStan/Reflection/TypeToCallReflectionResolver/ConstantArrayTypeToCallReflectionResolver.php +++ /dev/null @@ -1,119 +0,0 @@ -findTypeAndMethodName($type); - if (! $constantArrayTypeAndMethod instanceof ConstantArrayTypeAndMethod) { - return null; - } - if ($constantArrayTypeAndMethod->isUnknown()) { - return null; - } - if (! $constantArrayTypeAndMethod->getCertainty()->yes()) { - return null; - } - - $constantArrayType = $constantArrayTypeAndMethod->getType(); - - $methodReflection = $constantArrayType->getMethod($constantArrayTypeAndMethod->getMethod(), $scope); - if (! $scope->canCallMethod($methodReflection)) { - return null; - } - - return $methodReflection; - } - - /** - * @see https://github.com/phpstan/phpstan-src/blob/b1fd47bda2a7a7d25091197b125c0adf82af6757/src/Type/Constant/ConstantArrayType.php#L209 - */ - private function findTypeAndMethodName(ConstantArrayType $constantArrayType): ?ConstantArrayTypeAndMethod - { - if (! $this->areKeyTypesValid($constantArrayType)) { - return null; - } - - if (count($constantArrayType->getValueTypes()) !== 2) { - return null; - } - - $classOrObjectType = $constantArrayType->getValueTypes()[0]; - $methodType = $constantArrayType->getValueTypes()[1]; - - if (! $methodType instanceof ConstantStringType) { - return ConstantArrayTypeAndMethod::createUnknown(); - } - - $objectWithoutClassType = new ObjectWithoutClassType(); - - if ($classOrObjectType instanceof ConstantStringType) { - $value = $classOrObjectType->getValue(); - if (! $this->reflectionProvider->hasClass($value)) { - return ConstantArrayTypeAndMethod::createUnknown(); - } - - $classReflection = $this->reflectionProvider->getClass($value); - $type = new ObjectType($classReflection->getName()); - } elseif ($objectWithoutClassType->isSuperTypeOf($classOrObjectType)->yes()) { - $type = $classOrObjectType; - } else { - return ConstantArrayTypeAndMethod::createUnknown(); - } - - $trinaryLogic = $type->hasMethod($methodType->getValue()); - if (! $trinaryLogic->no()) { - return ConstantArrayTypeAndMethod::createConcrete($type, $methodType->getValue(), $trinaryLogic); - } - - return null; - } - - private function areKeyTypesValid(ConstantArrayType $constantArrayType): bool - { - $keyTypes = $constantArrayType->getKeyTypes(); - - if (count($keyTypes) !== 2) { - return false; - } - - if ($keyTypes[0]->isSuperTypeOf(new ConstantIntegerType(0))->no()) { - return false; - } - - return ! $keyTypes[1]->isSuperTypeOf(new ConstantIntegerType(1)) - ->no(); - } -} diff --git a/src/PHPStan/Reflection/TypeToCallReflectionResolver/ConstantStringTypeToCallReflectionResolver.php b/src/PHPStan/Reflection/TypeToCallReflectionResolver/ConstantStringTypeToCallReflectionResolver.php deleted file mode 100644 index 1466e98d16e..00000000000 --- a/src/PHPStan/Reflection/TypeToCallReflectionResolver/ConstantStringTypeToCallReflectionResolver.php +++ /dev/null @@ -1,85 +0,0 @@ -[a-zA-Z_\\x7f-\\xff\\\\][a-zA-Z0-9_\\x7f-\\xff\\\\]*)::(?<' . self::METHOD_KEY . '>[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)\\z#'; - - /** - * @var string - */ - private const CLASS_KEY = 'class'; - - /** - * @var string - */ - private const METHOD_KEY = 'method'; - - public function __construct( - private ReflectionProvider $reflectionProvider, - ) { - } - - public function supports(Type $type): bool - { - return $type instanceof ConstantStringType; - } - - /** - * @param ConstantStringType $type - * @return FunctionReflection|MethodReflection|null - */ - public function resolve(Type $type, Scope $scope) - { - $value = $type->getValue(); - - // 'my_function' - $name = new Name($value); - if ($this->reflectionProvider->hasFunction($name, null)) { - return $this->reflectionProvider->getFunction($name, null); - } - - // 'MyClass::myStaticFunction' - $matches = Strings::match($value, self::STATIC_METHOD_REGEX); - if ($matches === null) { - return null; - } - - $class = $matches[self::CLASS_KEY]; - if (! $this->reflectionProvider->hasClass($class)) { - return null; - } - - $classReflection = $this->reflectionProvider->getClass($class); - - $method = $matches[self::METHOD_KEY]; - if (! $classReflection->hasMethod($method)) { - return null; - } - - return $classReflection->getMethod($method, $scope); - } -} diff --git a/src/PHPStan/Reflection/TypeToCallReflectionResolver/ObjectTypeToCallReflectionResolver.php b/src/PHPStan/Reflection/TypeToCallReflectionResolver/ObjectTypeToCallReflectionResolver.php deleted file mode 100644 index 50a67a985c4..00000000000 --- a/src/PHPStan/Reflection/TypeToCallReflectionResolver/ObjectTypeToCallReflectionResolver.php +++ /dev/null @@ -1,46 +0,0 @@ -getClassName(); - if (! $this->reflectionProvider->hasClass($className)) { - return null; - } - - $classReflection = $this->reflectionProvider->getClass($className); - if (! $classReflection->hasNativeMethod('__invoke')) { - return null; - } - - return $classReflection->getNativeMethod('__invoke'); - } -} diff --git a/src/PHPStan/Reflection/TypeToCallReflectionResolver/TypeToCallReflectionResolverRegistry.php b/src/PHPStan/Reflection/TypeToCallReflectionResolver/TypeToCallReflectionResolverRegistry.php deleted file mode 100644 index 6df0bc16b09..00000000000 --- a/src/PHPStan/Reflection/TypeToCallReflectionResolver/TypeToCallReflectionResolverRegistry.php +++ /dev/null @@ -1,38 +0,0 @@ -resolvers as $resolver) { - if (! $resolver->supports($type)) { - continue; - } - - return $resolver->resolve($type, $scope); - } - - return null; - } -} diff --git a/src/PHPStan/ScopeFetcher.php b/src/PHPStan/ScopeFetcher.php new file mode 100644 index 00000000000..1c1338f0353 --- /dev/null +++ b/src/PHPStan/ScopeFetcher.php @@ -0,0 +1,31 @@ +getAttribute(AttributeKey::SCOPE); + + if (! $currentScope instanceof Scope) { + $errorMessage = sprintf( + 'Scope not available on "%s" node. Fix scope refresh on changed nodes first', + $node::class, + ); + + throw new ShouldNotHappenException($errorMessage); + } + + return $currentScope; + } +} diff --git a/src/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php b/src/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php new file mode 100644 index 00000000000..f9b93c09569 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php @@ -0,0 +1,36 @@ + + */ + public function getNodeClass(): string; + + /** + * @param TType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode; + + /** + * @param TType $type + * @param TypeKind::* $typeKind + * @return Name|ComplexType|Identifier|null + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node; +} diff --git a/src/PHPStanStaticTypeMapper/DoctrineTypeAnalyzer.php b/src/PHPStanStaticTypeMapper/DoctrineTypeAnalyzer.php new file mode 100644 index 00000000000..05478716588 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/DoctrineTypeAnalyzer.php @@ -0,0 +1,47 @@ +getTypes() as $unionedType) { + if ($this->isInstanceOfCollectionType($unionedType)) { + $hasDoctrineCollectionType = true; + } + + if ($unionedType->isArray()->yes()) { + $isArrayType = true; + } + } + + if (! $hasDoctrineCollectionType) { + return false; + } + + return $isArrayType; + } + + public function isInstanceOfCollectionType(Type $type): bool + { + if (! $type instanceof ObjectType) { + return false; + } + + return $type->isInstanceOf('Doctrine\Common\Collections\Collection') + ->yes(); + } +} diff --git a/src/PHPStanStaticTypeMapper/Enum/TypeKind.php b/src/PHPStanStaticTypeMapper/Enum/TypeKind.php new file mode 100644 index 00000000000..49d6de14834 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/Enum/TypeKind.php @@ -0,0 +1,16 @@ +typeMappers as $typeMapper) { + if (! is_a($type, $typeMapper->getNodeClass(), true)) { + continue; + } + + return $typeMapper->mapToPHPStanPhpDocTypeNode($type); + } + + throw new NotImplementedYetException(__METHOD__ . ' for ' . $type::class); + } + + /** + * @param TypeKind::* $typeKind + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Name | ComplexType | Identifier | null + { + foreach ($this->typeMappers as $typeMapper) { + if (! is_a($type, $typeMapper->getNodeClass(), true)) { + continue; + } + + return $typeMapper->mapToPhpParserNode($type, $typeKind); + } + + throw new NotImplementedYetException(__METHOD__ . ' for ' . $type::class); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryLiteralStringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryLiteralStringTypeMapper.php new file mode 100644 index 00000000000..3de93d94342 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryLiteralStringTypeMapper.php @@ -0,0 +1,50 @@ + + */ +final readonly class AccessoryLiteralStringTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return AccessoryLiteralStringType::class; + } + + /** + * @param AccessoryLiteralStringType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param AccessoryLiteralStringType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('string'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonEmptyStringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonEmptyStringTypeMapper.php new file mode 100644 index 00000000000..cf29203d425 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonEmptyStringTypeMapper.php @@ -0,0 +1,50 @@ + + */ +final readonly class AccessoryNonEmptyStringTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return AccessoryNonEmptyStringType::class; + } + + /** + * @param AccessoryNonEmptyStringType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param AccessoryNonEmptyStringType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('string'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonFalsyStringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonFalsyStringTypeMapper.php new file mode 100644 index 00000000000..0b64e264277 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNonFalsyStringTypeMapper.php @@ -0,0 +1,50 @@ + + */ +final readonly class AccessoryNonFalsyStringTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return AccessoryNonFalsyStringType::class; + } + + /** + * @param AccessoryNonFalsyStringType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param AccessoryNonFalsyStringType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('string'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNumericStringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNumericStringTypeMapper.php new file mode 100644 index 00000000000..f79e8508a7c --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryNumericStringTypeMapper.php @@ -0,0 +1,50 @@ + + */ +final readonly class AccessoryNumericStringTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return AccessoryNumericStringType::class; + } + + /** + * @param AccessoryNumericStringType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param AccessoryNumericStringType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('string'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php new file mode 100644 index 00000000000..43e6b4931b2 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php @@ -0,0 +1,234 @@ + + */ +final class ArrayTypeMapper implements TypeMapperInterface +{ + public const string HAS_GENERIC_TYPE_PARENT = 'has_generic_type_parent'; + + private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; + + public function __construct( + private readonly GenericClassStringTypeNormalizer $genericClassStringTypeNormalizer, + private readonly DetailedTypeAnalyzer $detailedTypeAnalyzer + ) { + } + + // To avoid circular dependency + + public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void + { + $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; + } + + public function getNodeClass(): string + { + return ArrayType::class; + } + + /** + * @param ArrayType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + // this cannot be handled by PHPStan $type->toPhpDocNode() as requires space removal around "|" in union type + // then e.g. "int" instead of explicit number, and nice arrays + $itemType = $type->getIterableValueType(); + + $isGenericArray = $this->isGenericArrayCandidate($type); + + if ($itemType instanceof UnionType + && ! $type instanceof ConstantArrayType + && ! $isGenericArray + ) { + return $this->createArrayTypeNodeFromUnionType($itemType); + } + + if ($itemType instanceof ArrayType && $this->isGenericArrayCandidate($itemType)) { + return $this->createGenericArrayType($type, true); + } + + if ($isGenericArray) { + return $this->createGenericArrayType($type, true); + } + + // keep "int" key in arary + if ($type->getKeyType() instanceof IntegerType) { + $keyTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($type->getKeyType()); + + if (! $type->isList()->maybe()) { + $nestedTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($type->getItemType()); + + return new GenericTypeNode(new IdentifierTypeNode('array'), [$keyTypeNode, $nestedTypeNode]); + } + } + + $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($itemType); + return new SpacingAwareArrayTypeNode($itemTypeNode); + } + + /** + * @param ArrayType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Identifier + { + return new Identifier('array'); + } + + private function createArrayTypeNodeFromUnionType(UnionType $unionType): SpacingAwareArrayTypeNode + { + $unionedArrayType = []; + foreach ($unionType->getTypes() as $unionedType) { + $typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($unionedType); + $unionedArrayType[(string) $typeNode] = $typeNode; + } + + if (count($unionedArrayType) > 1) { + return new SpacingAwareArrayTypeNode(new BracketsAwareUnionTypeNode($unionedArrayType)); + } + + /** @var TypeNode $arrayType */ + $arrayType = array_shift($unionedArrayType); + return new SpacingAwareArrayTypeNode($arrayType); + } + + private function isGenericArrayCandidate(ArrayType $arrayType): bool + { + if ($arrayType->getKeyType() instanceof MixedType) { + return false; + } + + if ($this->isClassStringArrayType($arrayType)) { + return true; + } + + // skip simple arrays, like "string[]", from converting to obvious "array" + if ($this->isIntegerKeyAndNonNestedArray($arrayType)) { + return false; + } + + if ($arrayType->getKeyType() instanceof NeverType) { + return false; + } + + // make sure the integer key type is not natural/implicit array int keys + $keysArrayType = $arrayType->getKeysArray(); + if (! $keysArrayType instanceof ConstantArrayType) { + return true; + } + + foreach ($keysArrayType->getValueTypes() as $key => $keyType) { + if (! $keyType instanceof ConstantIntegerType) { + return true; + } + + if ($key !== $keyType->getValue()) { + return true; + } + } + + return false; + } + + private function createGenericArrayType(ArrayType $arrayType, bool $withKey = false): GenericTypeNode + { + $itemType = $arrayType->getIterableValueType(); + $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($itemType); + $identifierTypeNode = new IdentifierTypeNode('array'); + + // is class-string[] list only + if ($this->isClassStringArrayType($arrayType)) { + $withKey = false; + } + + if ($withKey) { + $keyTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType->getKeyType()); + + if ($itemTypeNode instanceof BracketsAwareUnionTypeNode && $this->isPairClassTooDetailed($itemType)) { + $genericTypes = [$keyTypeNode, $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode( + new ClassStringType() + )]; + } else { + $genericTypes = [$keyTypeNode, $itemTypeNode]; + } + } else { + $genericTypes = [$itemTypeNode]; + } + + // @see https://github.com/phpstan/phpdoc-parser/blob/98a088b17966bdf6ee25c8a4b634df313d8aa531/tests/PHPStan/Parser/PhpDocParserTest.php#L2692-L2696 + + foreach ($genericTypes as $genericType) { + /** @var TypeNode $genericType */ + $genericType->setAttribute(self::HAS_GENERIC_TYPE_PARENT, $withKey); + } + + $identifierTypeNode->setAttribute(self::HAS_GENERIC_TYPE_PARENT, $withKey); + + return new GenericTypeNode($identifierTypeNode, $genericTypes); + } + + private function isPairClassTooDetailed(Type $itemType): bool + { + if (! $itemType instanceof UnionType) { + return false; + } + + if (! $this->genericClassStringTypeNormalizer->isAllGenericClassStringType($itemType)) { + return false; + } + + return $this->detailedTypeAnalyzer->isTooDetailed($itemType); + } + + private function isIntegerKeyAndNonNestedArray(ArrayType $arrayType): bool + { + if (! $arrayType->getKeyType()->isInteger()->yes()) { + return false; + } + + return ! $arrayType->getIterableValueType() + ->isArray() + ->yes(); + } + + private function isClassStringArrayType(ArrayType $arrayType): bool + { + if ($arrayType->getKeyType() instanceof MixedType) { + return $arrayType->getIterableValueType() instanceof GenericClassStringType; + } + + if ($arrayType->getKeyType() instanceof ConstantIntegerType) { + return $arrayType->getIterableValueType() instanceof GenericClassStringType; + } + + return false; + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php new file mode 100644 index 00000000000..2c380e04ab4 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php @@ -0,0 +1,71 @@ + + */ +final readonly class BooleanTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return BooleanType::class; + } + + /** + * @param BooleanType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param BooleanType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + if ($typeKind === TypeKind::PROPERTY) { + return new Identifier('bool'); + } + + if ($typeKind === TypeKind::UNION && $type->isFalse()->yes()) { + return new Identifier('false'); + } + + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::NULL_FALSE_TRUE_STANDALONE_TYPE)) { + return new Identifier('bool'); + } + + if ($type->isTrue()->yes()) { + return new Identifier('true'); + } + + if ($type->isFalse()->yes()) { + return new Identifier('false'); + } + + return new Identifier('bool'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php new file mode 100644 index 00000000000..ddb6b35e5f8 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php @@ -0,0 +1,46 @@ + + */ +final class CallableTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return CallableType::class; + } + + /** + * @param CallableType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param TypeKind::* $typeKind + * @param CallableType|ClosureType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if ($typeKind === TypeKind::PROPERTY) { + return null; + } + + return new Identifier('callable'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php new file mode 100644 index 00000000000..2249fbe94e0 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php @@ -0,0 +1,66 @@ + + */ +final readonly class ClassStringTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return ClassStringType::class; + } + + /** + * @param ClassStringType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { + if (! $type instanceof ObjectType) { + return $traverse($type); + } + + $typeClass = $type::class; + + if ($typeClass === 'PHPStan\Type\ObjectType') { + return new ObjectType('\\' . $type->getClassName()); + } + + return $traverse($type); + }); + + return $type->toPhpDocNode(); + } + + /** + * @param ClassStringType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('string'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php new file mode 100644 index 00000000000..a5f08c244ec --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php @@ -0,0 +1,99 @@ + + */ +final readonly class ClosureTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return ClosureType::class; + } + + /** + * @param ClosureType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + $typeNode = $type->toPhpDocNode(); + + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + $phpDocNodeTraverser->traverseWithCallable( + $typeNode, + '', + static function (AstNode $astNode): ?FullyQualifiedIdentifierTypeNode { + if (! $astNode instanceof IdentifierTypeNode) { + return null; + } + + if ($astNode->name !== 'Closure') { + return null; + } + + return new FullyQualifiedIdentifierTypeNode('Closure'); + } + ); + + return $typeNode; + } + + /** + * @param TypeKind::* $typeKind + * @param ClosureType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + // ref https://3v4l.org/iKMK6#v5.3.29 + if ($typeKind === TypeKind::PARAM && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::ANONYMOUS_FUNCTION_PARAM_TYPE + )) { + return new FullyQualified('Closure'); + } + + // ref https://3v4l.org/g8WvW#v7.4.0 + if ($typeKind === TypeKind::PROPERTY && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::TYPED_PROPERTIES + )) { + return new FullyQualified('Closure'); + } + + // ref https://3v4l.org/nUreN#v7.0.0 + if ($typeKind === TypeKind::RETURN && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::ANONYMOUS_FUNCTION_RETURN_TYPE + )) { + return new FullyQualified('Closure'); + } + + // ref https://3v4l.org/ruh5g#v8.0.0 + if ($typeKind === TypeKind::UNION && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::UNION_TYPES + )) { + return new FullyQualified('Closure'); + } + + return null; + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeForParameterMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeForParameterMapper.php new file mode 100644 index 00000000000..bd7c0480cd9 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeForParameterMapper.php @@ -0,0 +1,50 @@ + + */ +final class ConditionalTypeForParameterMapper implements TypeMapperInterface +{ + private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; + + public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void + { + $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; + } + + public function getNodeClass(): string + { + return ConditionalTypeForParameter::class; + } + + /** + * @param ConditionalTypeForParameter $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param ConditionalTypeForParameter $type + * @param TypeKind::* $typeKind + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + $type = TypeCombinator::union($type->getIf(), $type->getElse()); + return $this->phpStanStaticTypeMapper->mapToPhpParserNode($type, $typeKind); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php new file mode 100644 index 00000000000..27f9aeb08d3 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php @@ -0,0 +1,63 @@ + + */ +final class ConditionalTypeMapper implements TypeMapperInterface +{ + private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; + + public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void + { + $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; + } + + public function getNodeClass(): string + { + return ConditionalType::class; + } + + /** + * @param ConditionalType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { + if ($type instanceof ObjectType && ! $type->getClassReflection() instanceof ClassReflection) { + $newClassName = (string) Strings::after($type->getClassName(), '\\', -1); + return $traverse(new ObjectType($newClassName)); + } + + return $traverse($type); + }); + + return $type->toPhpDocNode(); + } + + /** + * @param ConditionalType $type + * @param TypeKind::* $typeKind + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + $type = TypeCombinator::union($type->getIf(), $type->getElse()); + return $this->phpStanStaticTypeMapper->mapToPhpParserNode($type, $typeKind); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ConstantArrayTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ConstantArrayTypeMapper.php new file mode 100644 index 00000000000..0d993ed34ea --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ConstantArrayTypeMapper.php @@ -0,0 +1,47 @@ + + */ +final readonly class ConstantArrayTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return ConstantArrayType::class; + } + + /** + * @param ConstantArrayType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('array'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php new file mode 100644 index 00000000000..a61fa0fd531 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php @@ -0,0 +1,50 @@ + + */ +final readonly class FloatTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return FloatType::class; + } + + /** + * @param FloatType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param FloatType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('float'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/GenericClassStringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/GenericClassStringTypeMapper.php new file mode 100644 index 00000000000..b85df380011 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/GenericClassStringTypeMapper.php @@ -0,0 +1,50 @@ + + */ +final readonly class GenericClassStringTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return GenericClassStringType::class; + } + + /** + * @param GenericClassStringType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param GenericClassStringType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('string'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/HasMethodTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/HasMethodTypeMapper.php new file mode 100644 index 00000000000..eb707476bc7 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/HasMethodTypeMapper.php @@ -0,0 +1,43 @@ + + */ +final readonly class HasMethodTypeMapper implements TypeMapperInterface +{ + public function __construct( + private ObjectWithoutClassTypeMapper $objectWithoutClassTypeMapper + ) { + } + + public function getNodeClass(): string + { + return HasMethodType::class; + } + + /** + * @param HasMethodType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param HasMethodType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + return $this->objectWithoutClassTypeMapper->mapToPhpParserNode($type, $typeKind); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/HasOffsetTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/HasOffsetTypeMapper.php new file mode 100644 index 00000000000..c9a72e2a9ac --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/HasOffsetTypeMapper.php @@ -0,0 +1,38 @@ + + */ +final class HasOffsetTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return HasOffsetType::class; + } + + /** + * @param HasOffsetType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param HasOffsetType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Identifier + { + return new Identifier('array'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/HasOffsetValueTypeTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/HasOffsetValueTypeTypeMapper.php new file mode 100644 index 00000000000..500ccca6690 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/HasOffsetValueTypeTypeMapper.php @@ -0,0 +1,38 @@ + + */ +final class HasOffsetValueTypeTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return HasOffsetValueType::class; + } + + /** + * @param HasOffsetValueType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param HasOffsetValueType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Identifier + { + return new Identifier('array'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/HasPropertyTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/HasPropertyTypeMapper.php new file mode 100644 index 00000000000..c22cb412218 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/HasPropertyTypeMapper.php @@ -0,0 +1,43 @@ + + */ +final readonly class HasPropertyTypeMapper implements TypeMapperInterface +{ + public function __construct( + private ObjectWithoutClassTypeMapper $objectWithoutClassTypeMapper + ) { + } + + public function getNodeClass(): string + { + return HasPropertyType::class; + } + + /** + * @param HasPropertyType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param HasPropertyType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + return $this->objectWithoutClassTypeMapper->mapToPhpParserNode($type, $typeKind); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php new file mode 100644 index 00000000000..4762a6e4f41 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php @@ -0,0 +1,51 @@ + + */ +final readonly class IntegerTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return IntegerType::class; + } + + /** + * @param IntegerType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): IdentifierTypeNode + { + // note: cannot be handled by PHPStan as uses explicit values + return new IdentifierTypeNode('int'); + } + + /** + * @param IntegerType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('int'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php new file mode 100644 index 00000000000..e64fb9cb1aa --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php @@ -0,0 +1,133 @@ + + */ +final readonly class IntersectionTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider, + private ObjectWithoutClassTypeMapper $objectWithoutClassTypeMapper, + private ObjectTypeMapper $objectTypeMapper, + private ScalarStringToTypeMapper $scalarStringToTypeMapper, + ) { + } + + public function getNodeClass(): string + { + return IntersectionType::class; + } + + /** + * @param IntersectionType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + $typeNode = $type->toPhpDocNode(); + + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + $phpDocNodeTraverser->traverseWithCallable( + $typeNode, + '', + function (AstNode $astNode): int|IdentifierTypeNode { + if ($astNode instanceof UnionTypeNode) { + return PhpDocNodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($astNode instanceof ArrayShapeItemNode) { + return PhpDocNodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $astNode instanceof IdentifierTypeNode) { + return PhpDocNodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + $type = $this->scalarStringToTypeMapper->mapScalarStringToType($astNode->name); + if ($type->isScalar()->yes()) { + return PhpDocNodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($type->isArray()->yes()) { + return PhpDocNodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($type instanceof MixedType && $type->isExplicitMixed()) { + return PhpDocNodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + $astNode->name = '\\' . ltrim($astNode->name, '\\'); + return $astNode; + } + ); + + return $typeNode; + } + + /** + * @param IntersectionType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::INTERSECTION_TYPES)) { + return null; + } + + $intersectionedTypeNodes = []; + foreach ($type->getTypes() as $type) { + if ($type instanceof ObjectWithoutClassType) { + return $this->objectWithoutClassTypeMapper->mapToPhpParserNode($type, $typeKind); + } + + if (! $type instanceof ObjectType) { + return null; + } + + $resolvedType = $this->objectTypeMapper->mapToPhpParserNode($type, $typeKind); + + if (! $resolvedType instanceof FullyQualified) { + return null; + } + + $intersectionedTypeNodes[] = $resolvedType; + } + + if ($intersectionedTypeNodes === []) { + return null; + } + + if (count($intersectionedTypeNodes) === 1) { + return current($intersectionedTypeNodes); + } + + if ($typeKind === TypeKind::UNION && ! $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::UNION_INTERSECTION_TYPES + )) { + return null; + } + + return new Node\IntersectionType($intersectionedTypeNodes); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php new file mode 100644 index 00000000000..9e810fd8067 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php @@ -0,0 +1,38 @@ + + */ +final class IterableTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return IterableType::class; + } + + /** + * @param IterableType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param IterableType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Identifier + { + return new Identifier('iterable'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php new file mode 100644 index 00000000000..9093fb84268 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php @@ -0,0 +1,59 @@ + + */ +final readonly class MixedTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return MixedType::class; + } + + /** + * @param MixedType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param MixedType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::MIXED_TYPE)) { + return null; + } + + if (! $type->isExplicitMixed()) { + return null; + } + + if ($typeKind === TypeKind::UNION) { + return null; + } + + return new Identifier('mixed'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php new file mode 100644 index 00000000000..3d825a4e25f --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php @@ -0,0 +1,38 @@ + + */ +final class NeverTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return NeverType::class; + } + + /** + * @param NeverType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param NeverType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + return null; + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/NonEmptyArrayTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/NonEmptyArrayTypeMapper.php new file mode 100644 index 00000000000..fd1ed0793d9 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/NonEmptyArrayTypeMapper.php @@ -0,0 +1,38 @@ + + */ +final class NonEmptyArrayTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return NonEmptyArrayType::class; + } + + /** + * @param NonEmptyArrayType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param NonEmptyArrayType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Identifier + { + return new Identifier('array'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php new file mode 100644 index 00000000000..7687a94ff4a --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php @@ -0,0 +1,62 @@ + + */ +final readonly class NullTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return NullType::class; + } + + /** + * @param NullType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param TypeKind::* $typeKind + * @param NullType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + // can be a standalone type, only case where null makes sense + if ($this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::NULL_FALSE_TRUE_STANDALONE_TYPE + ) && $typeKind === TypeKind::RETURN) { + return new Identifier('null'); + } + + // if part of union, can be added even in PHP 8.0 + if ($typeKind === TypeKind::UNION && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::NULLABLE_TYPE + )) { + return new Identifier('null'); + } + + return null; + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php new file mode 100644 index 00000000000..78cecd0feb4 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php @@ -0,0 +1,100 @@ + + */ +final class ObjectTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return ObjectType::class; + } + + /** + * @param ObjectType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { + if ($type instanceof ArrayType && ($type->getItemType() instanceof MixedType && $type->getKeyType() instanceof MixedType)) { + return new ArrayType(new MixedType(), new MixedType(true)); + } + + if (! $type instanceof ObjectType) { + return $traverse($type); + } + + $typeClass = $type::class; + + // early native ObjectType check + if ($typeClass === 'PHPStan\Type\ObjectType') { + return new ObjectType('\\' . $type->getClassName()); + } + + if ($type instanceof FullyQualifiedObjectType) { + return new ObjectType('\\' . $type->getClassName()); + } + + if ($type instanceof GenericObjectType) { + return $traverse(new GenericObjectType('\\' . $type->getClassName(), $type->getTypes()), $traverse); + } + + return $traverse($type, $traverse); + }); + + return $type->toPhpDocNode(); + } + + /** + * @param ObjectType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if ($type instanceof SelfObjectType) { + return new Name('self'); + } + + if ($type instanceof ShortenedObjectType || $type instanceof AliasedObjectType) { + return new FullyQualified($type->getFullyQualifiedName()); + } + + if ($type instanceof FullyQualifiedObjectType) { + $className = $type->getClassName(); + + if (str_starts_with($className, '\\')) { + // skip leading \ + return new FullyQualified(Strings::substring($className, 1)); + } + + return new FullyQualified($className); + } + + if ($type instanceof NonExistingObjectType) { + return null; + } + + return new FullyQualified($type->getClassName()); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php new file mode 100644 index 00000000000..479e70fd9be --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php @@ -0,0 +1,63 @@ + + */ +final readonly class ObjectWithoutClassTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return ObjectWithoutClassType::class; + } + + /** + * @param ObjectWithoutClassType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param ObjectWithoutClassType|HasMethodType|HasPropertyType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + // special case for anonymous classes that implement another type + if ($type instanceof ObjectWithoutClassTypeWithParentTypes) { + $parentTypes = $type->getParentTypes(); + if (count($parentTypes) === 1) { + $parentType = $parentTypes[0]; + return new FullyQualified($parentType->getClassName()); + } + } + + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::OBJECT_TYPE)) { + return null; + } + + return new Identifier('object'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/OversizedArrayTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/OversizedArrayTypeMapper.php new file mode 100644 index 00000000000..38b95b500ef --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/OversizedArrayTypeMapper.php @@ -0,0 +1,40 @@ + + */ +final class OversizedArrayTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return OversizedArrayType::class; + } + + /** + * @param OversizedArrayType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param TypeKind::* $typeKind + * @param OversizedArrayType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Identifier + { + return new Identifier('array'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php new file mode 100644 index 00000000000..9ef8b7020f0 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php @@ -0,0 +1,39 @@ + + */ +final class ParentStaticTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return ParentStaticType::class; + } + + /** + * @param ParentStaticType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param ParentStaticType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Name + { + return new Name(ObjectReference::PARENT); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php new file mode 100644 index 00000000000..0b9620c4b4e --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php @@ -0,0 +1,38 @@ + + */ +final class ResourceTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return ResourceType::class; + } + + /** + * @param ResourceType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param ResourceType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + return null; + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/SelfObjectTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/SelfObjectTypeMapper.php new file mode 100644 index 00000000000..738cd579b59 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/SelfObjectTypeMapper.php @@ -0,0 +1,38 @@ + + */ +final class SelfObjectTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return SelfObjectType::class; + } + + /** + * @param SelfObjectType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param SelfObjectType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Name + { + return new Name('self'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php new file mode 100644 index 00000000000..d59ee36e44a --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php @@ -0,0 +1,63 @@ + + */ +final readonly class StaticTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return StaticType::class; + } + + /** + * @param StaticType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param SimpleStaticType|StaticType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Name + { + if ($type instanceof SelfStaticType) { + return new Name(ObjectReference::SELF); + } + + if ($typeKind !== TypeKind::RETURN) { + return new Name(ObjectReference::SELF); + } + + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::STATIC_RETURN_TYPE)) { + return new Name(ObjectReference::SELF); + } + + return new Name(ObjectReference::STATIC); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php new file mode 100644 index 00000000000..71fc70c5bd5 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php @@ -0,0 +1,57 @@ + + */ +final readonly class StrictMixedTypeMapper implements TypeMapperInterface +{ + private const string MIXED = 'mixed'; + + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return StrictMixedType::class; + } + + /** + * @param StrictMixedType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param StrictMixedType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::MIXED_TYPE)) { + return null; + } + + if ($typeKind === TypeKind::UNION) { + return null; + } + + return new Identifier(self::MIXED); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php new file mode 100644 index 00000000000..fb67d91c126 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php @@ -0,0 +1,47 @@ + + */ +final readonly class StringTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return StringType::class; + } + + /** + * @param StringType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('string'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ThisTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ThisTypeMapper.php new file mode 100644 index 00000000000..fb2a26b3938 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ThisTypeMapper.php @@ -0,0 +1,38 @@ + + */ +final class ThisTypeMapper implements TypeMapperInterface +{ + public function getNodeClass(): string + { + return ThisType::class; + } + + /** + * @param ThisType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param ThisType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): Name + { + return new Name('self'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php new file mode 100644 index 00000000000..73c9ff95e8f --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php @@ -0,0 +1,50 @@ + + */ +final readonly class TypeWithClassNameTypeMapper implements TypeMapperInterface +{ + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return TypeWithClassName::class; + } + + /** + * @param TypeWithClassName $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param TypeWithClassName $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) { + return null; + } + + return new Identifier('string'); + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php new file mode 100644 index 00000000000..3e40a981f7f --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php @@ -0,0 +1,247 @@ + + */ +final class UnionTypeMapper implements TypeMapperInterface +{ + private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; + + public function __construct( + private readonly PhpVersionProvider $phpVersionProvider, + private readonly PropertyAnalyzer $propertyAnalyzer + ) { + } + + public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void + { + $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; + } + + public function getNodeClass(): string + { + return UnionType::class; + } + + /** + * @param UnionType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): BracketsAwareUnionTypeNode + { + $unionTypesNodes = []; + $existingTypes = []; + + foreach ($type->getTypes() as $unionedType) { + if ($unionedType instanceof ArrayType && $unionedType->getItemType() instanceof NeverType) { + $unionedType = new ArrayType($unionedType->getKeyType(), new MixedType()); + } + + $unionedType = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($unionedType); + + if ($unionedType instanceof SpacingAwareArrayTypeNode && $unionedType->type instanceof BracketsAwareUnionTypeNode) { + foreach ($unionedType->type->types as $key => $innerTypeNode) { + $printedInnerType = (string) $innerTypeNode; + if (in_array($printedInnerType, $existingTypes, true)) { + unset($unionedType->type->types[$key]); + continue; + } + + $existingTypes[] = $printedInnerType; + } + + if ($unionedType->type->types === []) { + continue; + } + } + + $unionTypesNodes[] = $unionedType; + } + + return new BracketsAwareUnionTypeNode($unionTypesNodes); + } + + /** + * @param UnionType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + $phpParserUnionType = $this->matchPhpParserUnionType($type, $typeKind); + if ($phpParserUnionType instanceof PhpParserUnionType) { + return $this->resolveUnionTypeNode($phpParserUnionType); + } + + return $phpParserUnionType; + } + + /** + * If type is nullable, and has only one other value, + * this creates at least "?Type" in case of PHP 7.1-7.4 + */ + private function resolveTypeWithNullablePHPParserUnionType( + PhpParserUnionType $phpParserUnionType + ): PhpParserUnionType|NullableType|null { + $totalTypes = count($phpParserUnionType->types); + if ($totalTypes === 2) { + $phpParserUnionType->types = array_values($phpParserUnionType->types); + $firstType = $phpParserUnionType->types[0]; + $secondType = $phpParserUnionType->types[1]; + + try { + Assert::isAnyOf($firstType, [Name::class, Identifier::class]); + Assert::isAnyOf($secondType, [Name::class, Identifier::class]); + } catch (InvalidArgumentException) { + return $this->resolveUnionTypes($phpParserUnionType); + } + + $firstTypeValue = $firstType->toString(); + $secondTypeValue = $secondType->toString(); + + if ($firstTypeValue === $secondTypeValue) { + return $this->resolveUnionTypes($phpParserUnionType); + } + + if ($firstTypeValue === 'null') { + return $this->resolveNullableType(new NullableType($secondType)); + } + + if ($secondTypeValue === 'null') { + return $this->resolveNullableType(new NullableType($firstType)); + } + } + + return $this->resolveUnionTypes($phpParserUnionType); + } + + private function resolveNullableType(NullableType $nullableType): null|NullableType|PhpParserUnionType + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::NULLABLE_TYPE)) { + return null; + } + + /** @var PHPParserNodeIntersectionType|Identifier|Name $type */ + $type = $nullableType->type; + if (! $type instanceof PHPParserNodeIntersectionType) { + // ?false is allowed only since PHP 8.2+, lets fallback to bool instead + if ($type->toString() === 'false' && ! $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::NULL_FALSE_TRUE_STANDALONE_TYPE + )) { + return new NullableType(new Identifier('bool')); + } + + return $nullableType; + } + + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES)) { + return null; + } + + $types = [$type]; + $types[] = new Identifier('null'); + + return new PhpParserUnionType($types); + } + + private function resolveUnionTypes(PhpParserUnionType $phpParserUnionType): ?PhpParserUnionType + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES)) { + return null; + } + + return $phpParserUnionType; + } + + private function hasObjectAndStaticType(PhpParserUnionType $phpParserUnionType): bool + { + $hasAnonymousObjectType = false; + $hasObjectType = false; + foreach ($phpParserUnionType->types as $type) { + if ($type instanceof Identifier && $type->toString() === 'object') { + $hasAnonymousObjectType = true; + continue; + } + + if ($type instanceof FullyQualified || ($type instanceof Name && $type->isSpecialClassName())) { + $hasObjectType = true; + continue; + } + } + + return $hasObjectType && $hasAnonymousObjectType; + } + + /** + * @return Name|FullyQualified|ComplexType|Identifier|null + */ + private function matchPhpParserUnionType(UnionType $unionType, string $typeKind): ?Node + { + $phpParserUnionedTypes = []; + + foreach ($unionType->getTypes() as $unionedType) { + // NullType or ConstantBooleanType with false value inside UnionType is allowed + // void type and mixed type are not allowed in union + $phpParserNode = $this->phpStanStaticTypeMapper->mapToPhpParserNode($unionedType, TypeKind::UNION); + if ($phpParserNode === null) { + return null; + } + + // special callable type only not allowed on property + if ($typeKind === TypeKind::PROPERTY && $this->propertyAnalyzer->isForbiddenType($unionedType)) { + return null; + } + + $phpParserUnionedTypes[] = $phpParserNode; + } + + /** @var Identifier[]|Name[] $phpParserUnionedTypes */ + $phpParserUnionedTypes = array_unique($phpParserUnionedTypes, SORT_REGULAR); + + $countPhpParserUnionedTypes = count($phpParserUnionedTypes); + if ($countPhpParserUnionedTypes === 1) { + return $phpParserUnionedTypes[0]; + } + + return $this->resolveTypeWithNullablePHPParserUnionType(new PhpParserUnionType($phpParserUnionedTypes)); + } + + private function resolveUnionTypeNode(PhpParserUnionType $phpParserUnionType): ?PhpParserUnionType + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::UNION_TYPES)) { + return null; + } + + // special case that would crash, when stdClass and object is used, + if ($this->hasObjectAndStaticType($phpParserUnionType)) { + return null; + } + + return $phpParserUnionType; + } +} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php new file mode 100644 index 00000000000..649d40025b2 --- /dev/null +++ b/src/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php @@ -0,0 +1,58 @@ + + */ +final readonly class VoidTypeMapper implements TypeMapperInterface +{ + private const string VOID = 'void'; + + public function __construct( + private PhpVersionProvider $phpVersionProvider + ) { + } + + public function getNodeClass(): string + { + return VoidType::class; + } + + /** + * @param VoidType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + return $type->toPhpDocNode(); + } + + /** + * @param TypeKind::* $typeKind + * @param VoidType $type + */ + public function mapToPhpParserNode(Type $type, string $typeKind): ?Node + { + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::VOID_TYPE)) { + return null; + } + + if (in_array($typeKind, [TypeKind::PARAM, TypeKind::PROPERTY, TypeKind::UNION], true)) { + return null; + } + + return new Identifier(self::VOID); + } +} diff --git a/src/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php b/src/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php new file mode 100644 index 00000000000..88d74822e6c --- /dev/null +++ b/src/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php @@ -0,0 +1,92 @@ +getTypes() as $unionedType) { + $className = ClassNameFromObjectTypeResolver::resolve($unionedType); + if ($className === null) { + continue; + } + + return $unionedType; + } + + return $type; + } + + public function unwrapFirstCallableTypeFromUnionType(Type $type): Type + { + if (! $type instanceof UnionType) { + return $type; + } + + foreach ($type->getTypes() as $unionedType) { + if (! $unionedType->isCallable()->yes()) { + continue; + } + + return $unionedType; + } + + return $type; + } + + public function isIterableTypeValue(string $className, Type $type): bool + { + $typeClassName = ClassNameFromObjectTypeResolver::resolve($type); + if ($typeClassName === null) { + return false; + } + + // get the namespace from $className + $classNamespace = $this->namespace($className); + + // get the namespace from $parameterReflection + $reflectionNamespace = $this->namespace($typeClassName); + + // then match with + return $reflectionNamespace === $classNamespace && str_ends_with($typeClassName, '\TValue'); + } + + public function isIterableTypeKey(string $className, Type $type): bool + { + $typeClassName = ClassNameFromObjectTypeResolver::resolve($type); + if ($typeClassName === null) { + return false; + } + + // get the namespace from $className + $classNamespace = $this->namespace($className); + + // get the namespace from $parameterReflection + $reflectionNamespace = $this->namespace($typeClassName); + + // then match with + return $reflectionNamespace === $classNamespace && str_ends_with($typeClassName, '\TKey'); + } + + public function removeNullTypeFromUnionType(UnionType $unionType): Type + { + return TypeCombinator::removeNull($unionType); + } + + private function namespace(string $class): string + { + return implode('\\', array_slice(explode('\\', $class), 0, -1)); + } +} diff --git a/src/Parallel/Application/ParallelFileProcessor.php b/src/Parallel/Application/ParallelFileProcessor.php new file mode 100644 index 00000000000..a1c8d3e0696 --- /dev/null +++ b/src/Parallel/Application/ParallelFileProcessor.php @@ -0,0 +1,281 @@ +getJobs()); + $streamSelectLoop = new StreamSelectLoop(); + + // basic properties setup + $numberOfProcesses = $schedule->getNumberOfProcesses(); + + // initial counters + + /** @var FileDiff[] $fileDiffs */ + $fileDiffs = []; + + /** @var SystemError[] $systemErrors */ + $systemErrors = []; + + $tcpServer = new TcpServer('127.0.0.1:0', $streamSelectLoop); + $this->processPool = new ProcessPool($tcpServer); + + $tcpServer->on(ReactEvent::CONNECTION, function (ConnectionInterface $connection) use (&$jobs): void { + $inDecoder = new Decoder($connection, true, 512, 0, 4 * 1024 * 1024); + $outEncoder = new Encoder($connection); + + $inDecoder->on(ReactEvent::DATA, function (array $data) use (&$jobs, $inDecoder, $outEncoder): void { + $action = $data[ReactCommand::ACTION]; + if ($action !== Action::HELLO) { + return; + } + + $processIdentifier = $data[Option::PARALLEL_IDENTIFIER]; + $parallelProcess = $this->processPool->getProcess($processIdentifier); + $parallelProcess->bindConnection($inDecoder, $outEncoder); + + if ($jobs === []) { + $this->processPool->quitProcess($processIdentifier); + return; + } + + $jobsChunk = array_pop($jobs); + + $parallelProcess->request([ + ReactCommand::ACTION => Action::MAIN, + Content::FILES => $jobsChunk, + ]); + }); + }); + + /** @var string $serverAddress */ + $serverAddress = $tcpServer->getAddress(); + + /** @var int $serverPort */ + $serverPort = parse_url($serverAddress, PHP_URL_PORT); + + $systemErrorsCount = 0; + $reachedSystemErrorsCountLimit = false; + $totalChanged = 0; + + $handleErrorCallable = function (Throwable $throwable) use ( + &$systemErrors, + &$systemErrorsCount, + &$reachedSystemErrorsCountLimit + ): void { + $systemErrors[] = new SystemError( + $throwable->getMessage(), + $throwable->getFile(), + $throwable->getLine(), + ); + + ++$systemErrorsCount; + $reachedSystemErrorsCountLimit = true; + $this->processPool->quitAll(); + + // This sleep has to be here, because event though we have called $this->processPool->quitAll(), + // it takes some time for the child processes to actually die, during which they can still write to cache + // @see https://github.com/rectorphp/rector-src/pull/3834/files#r1231696531 + sleep(1); + }; + + $timeoutInSeconds = SimpleParameterProvider::provideIntParameter(Option::PARALLEL_JOB_TIMEOUT_IN_SECONDS); + $fileChunksBudgetPerProcess = []; + + $processSpawner = function () use ( + &$systemErrors, + &$fileDiffs, + &$jobs, + $postFileCallback, + &$systemErrorsCount, + &$reachedInternalErrorsCountLimit, + $mainScript, + $input, + $serverPort, + $streamSelectLoop, + $timeoutInSeconds, + $handleErrorCallable, + &$fileChunksBudgetPerProcess, + &$processSpawner, + &$totalChanged + ): void { + $processIdentifier = Random::generate(); + $workerCommandLine = $this->workerCommandLineFactory->create( + $mainScript, + ProcessCommand::class, + 'worker', + $input, + $processIdentifier, + $serverPort, + ); + $fileChunksBudgetPerProcess[$processIdentifier] = self::MAX_CHUNKS_PER_WORKER; + + $parallelProcess = new ParallelProcess($workerCommandLine, $streamSelectLoop, $timeoutInSeconds); + + $parallelProcess->start( + // 1. callable on data + function (array $json) use ( + $parallelProcess, + &$systemErrors, + &$fileDiffs, + &$jobs, + $postFileCallback, + &$systemErrorsCount, + &$reachedInternalErrorsCountLimit, + $processIdentifier, + &$fileChunksBudgetPerProcess, + &$processSpawner, + &$totalChanged + ): void { + /** @var array{ + * total_changed: int, + * system_errors: mixed[], + * file_diffs: array, + * files_count: int, + * system_errors_count: int + * } $json */ + $totalChanged += $json[Bridge::TOTAL_CHANGED]; + + // decode arrays to objects + foreach ($json[Bridge::SYSTEM_ERRORS] as $jsonError) { + if (is_string($jsonError)) { + $systemErrors[] = new SystemError('System error: ' . $jsonError); + continue; + } + + $systemErrors[] = SystemError::decode($jsonError); + } + + foreach ($json[Bridge::FILE_DIFFS] as $jsonFileDiff) { + $fileDiffs[] = FileDiff::decode($jsonFileDiff); + } + + $postFileCallback($json[Bridge::FILES_COUNT]); + + $systemErrorsCount += $json[Bridge::SYSTEM_ERRORS_COUNT]; + if ($systemErrorsCount >= self::SYSTEM_ERROR_LIMIT) { + $reachedInternalErrorsCountLimit = true; + $this->processPool->quitAll(); + } + + if ($fileChunksBudgetPerProcess[$processIdentifier] <= 0) { + // kill the current worker, and spawn a fresh one to free memory + $this->processPool->quitProcess($processIdentifier); + + ($processSpawner)(); + return; + } + + if ($jobs === []) { + $this->processPool->quitProcess($processIdentifier); + return; + } + + $jobsChunk = array_pop($jobs); + $parallelProcess->request([ + ReactCommand::ACTION => Action::MAIN, + Content::FILES => $jobsChunk, + ]); + --$fileChunksBudgetPerProcess[$processIdentifier]; + }, + + // 2. callable on error + $handleErrorCallable, + + // 3. callable on exit + function ($exitCode, string $stdErr) use (&$systemErrors, $processIdentifier): void { + $this->processPool->tryQuitProcess($processIdentifier); + if ($exitCode === Command::SUCCESS) { + return; + } + + if ($exitCode === null) { + return; + } + + $systemErrors[] = new SystemError('Child process error: ' . $stdErr); + } + ); + + $this->processPool->attachProcess($processIdentifier, $parallelProcess); + }; + + for ($i = 0; $i < $numberOfProcesses; ++$i) { + // nothing else to process, stop now + if ($jobs === []) { + break; + } + + ($processSpawner)(); + } + + $streamSelectLoop->run(); + + if ($reachedSystemErrorsCountLimit) { + $systemErrors[] = new SystemError(sprintf( + 'Reached system errors count limit of %d, exiting...', + self::SYSTEM_ERROR_LIMIT + )); + } + + return new ProcessResult($systemErrors, $fileDiffs, $totalChanged); + } +} diff --git a/src/Parallel/Command/WorkerCommandLineFactory.php b/src/Parallel/Command/WorkerCommandLineFactory.php new file mode 100644 index 00000000000..34dc91e5010 --- /dev/null +++ b/src/Parallel/Command/WorkerCommandLineFactory.php @@ -0,0 +1,203 @@ + $mainCommandClass + */ + public function create( + string $mainScript, + string $mainCommandClass, + string $workerCommandName, + InputInterface $input, + string $identifier, + int $port + ): string { + $commandArguments = array_slice($_SERVER['argv'], 1); + + // add implicit "process" command name if missing + if ($commandArguments !== [] && ($commandArguments[0] !== 'process' && $commandArguments[0] !== 'p') && ! defined( + 'PHPUNIT_COMPOSER_INSTALL' + )) { + $commandArguments = array_merge(['process'], $commandArguments); + } + + $args = [PHP_BINARY, $mainScript, ...$commandArguments]; + $workerCommandArray = []; + + $mainCommand = $this->commandFromReflectionFactory->create($mainCommandClass); + + if ($mainCommand->getName() === null) { + $errorMessage = sprintf('The command name for "%s" is missing', $mainCommand::class); + throw new ParallelShouldNotHappenException($errorMessage); + } + + $mainCommandName = $mainCommand->getName(); + $mainCommandNames = [$mainCommandName, $mainCommandName[0]]; + + foreach ($args as $arg) { + // skip command name + if (in_array($arg, $mainCommandNames, true)) { + break; + } + + $workerCommandArray[] = escapeshellarg((string) $arg); + } + + $workerCommandArray[] = $workerCommandName; + + $mainCommandOptionNames = $this->getCommandOptionNames($mainCommand); + $workerCommandOptions = $this->mirrorCommandOptions($input, $mainCommandOptionNames); + $workerCommandArray = array_merge($workerCommandArray, $workerCommandOptions); + + // for TCP local server + $workerCommandArray[] = '--port'; + $workerCommandArray[] = $port; + + $workerCommandArray[] = '--identifier'; + $workerCommandArray[] = escapeshellarg($identifier); + + /** @var string[] $paths */ + $paths = $input->getArgument(Option::SOURCE); + foreach ($paths as $path) { + $workerCommandArray[] = escapeshellarg($path); + } + + // set json output + $workerCommandArray[] = self::OPTION_DASHES . Option::OUTPUT_FORMAT; + $workerCommandArray[] = escapeshellarg(JsonOutputFormatter::NAME); + + // disable colors, breaks json_decode() otherwise + // @see https://github.com/symfony/symfony/issues/1238 + $workerCommandArray[] = '--no-ansi'; + + // Only pass --config if explicitly set via command line + // If not set, the worker will resolve config using RectorConfigsResolver fallback mechanism + if ($input->hasOption(Option::CONFIG)) { + $configValue = $input->getOption(Option::CONFIG); + if (is_string($configValue) && $configValue !== '') { + $workerCommandArray[] = '--config'; + /** + * On parallel, the command is generated with `--config` addition + * Using escapeshellarg() to ensure the --config path escaped, even when it has a space. + * + * eg: + * --config /path/e2e/parallel with space/rector.php + * + * that can cause error: + * + * File /rector-src/e2e/parallel\" was not found + * + * the escaped result is: + * + * --config '/path/e2e/parallel with space/rector.php' + * + * tested in macOS and Ubuntu (github action) + */ + $config = $configValue; + $workerCommandArray[] = escapeshellarg($this->filePathHelper->relativePath($config)); + } + } + + if ($input->getOption(Option::ONLY) !== null) { + $workerCommandArray[] = self::OPTION_DASHES . Option::ONLY; + $workerCommandArray[] = escapeshellarg((string) $input->getOption(Option::ONLY)); + } + + return implode(' ', $workerCommandArray); + } + + private function shouldSkipOption(InputInterface $input, string $optionName): bool + { + if (! $input->hasOption($optionName)) { + return true; + } + + // skip output format and config, handled separately in create() + return $optionName === Option::OUTPUT_FORMAT || $optionName === Option::CONFIG; + } + + /** + * @return string[] + */ + private function getCommandOptionNames(Command $command): array + { + $inputDefinition = $command->getDefinition(); + + $optionNames = []; + foreach ($inputDefinition->getOptions() as $inputOption) { + $optionNames[] = $inputOption->getName(); + } + + return $optionNames; + } + + /** + * Keeps all options that are allowed in check command options + * + * @param string[] $mainCommandOptionNames + * @return string[] + */ + private function mirrorCommandOptions(InputInterface $input, array $mainCommandOptionNames): array + { + $workerCommandOptions = []; + + foreach ($mainCommandOptionNames as $mainCommandOptionName) { + if ($this->shouldSkipOption($input, $mainCommandOptionName)) { + continue; + } + + /** @var bool|string|null $optionValue */ + $optionValue = $input->getOption($mainCommandOptionName); + + // skip clutter + if ($optionValue === null) { + continue; + } + + if (is_bool($optionValue)) { + if ($optionValue) { + $workerCommandOptions[] = self::OPTION_DASHES . $mainCommandOptionName; + } + + continue; + } + + if ($mainCommandOptionName === 'memory-limit') { + // symfony/console does not accept -1 as value without assign + $workerCommandOptions[] = self::OPTION_DASHES . $mainCommandOptionName . '=' . \escapeshellarg( + $optionValue + ); + } else { + $workerCommandOptions[] = self::OPTION_DASHES . $mainCommandOptionName; + $workerCommandOptions[] = \escapeshellarg($optionValue); + } + } + + return $workerCommandOptions; + } +} diff --git a/src/Parallel/ValueObject/Bridge.php b/src/Parallel/ValueObject/Bridge.php new file mode 100644 index 00000000000..0d581733025 --- /dev/null +++ b/src/Parallel/ValueObject/Bridge.php @@ -0,0 +1,23 @@ +parameterProvider->provideIntParameter(Option::PHP_VERSION_FEATURES); - if ($phpVersionFeatures > 0) { - return $phpVersionFeatures; + if (SimpleParameterProvider::hasParameter(Option::PHP_VERSION_FEATURES)) { + $this->phpVersionFeatures = SimpleParameterProvider::provideIntParameter(Option::PHP_VERSION_FEATURES); + $this->validatePhpVersionFeaturesParameter($this->phpVersionFeatures); + } + + if ($this->phpVersionFeatures > 0) { + return $this->phpVersionFeatures; } // for tests if (StaticPHPUnitEnvironment::isPHPUnitRun()) { - // so we don't have to up - return 100000; + // so we don't have to keep with up with newest version + return PhpVersion::PHP_10; } $projectComposerJson = getcwd() . '/composer.json'; if (file_exists($projectComposerJson)) { - $phpVersion = $this->projectComposerJsonPhpVersionResolver->resolve($projectComposerJson); + $phpVersion = ComposerJsonPhpVersionResolver::resolve($projectComposerJson); if ($phpVersion !== null) { - return $phpVersion; + return $this->phpVersionFeatures = $phpVersion; } } - return PHP_VERSION_ID; + // fallback to current PHP runtime version + return $this->phpVersionFeatures = PHP_VERSION_ID; } public function isAtLeastPhpVersion(int $phpVersion): bool { return $phpVersion <= $this->provide(); } + + private function validatePhpVersionFeaturesParameter(mixed $phpVersionFeatures): void + { + if ($phpVersionFeatures === null) { + return; + } + + // get all constants + $phpVersionReflectionClass = new ReflectionClass(PhpVersion::class); + // @todo check + if (in_array($phpVersionFeatures, $phpVersionReflectionClass->getConstants(), true)) { + return; + } + + if (! is_int($phpVersionFeatures)) { + $this->throwInvalidTypeException($phpVersionFeatures); + } + + if (StringUtils::isMatch( + (string) $phpVersionFeatures, + self::VALID_PHP_VERSION_REGEX + ) && $phpVersionFeatures >= (PhpVersion::PHP_53 - 1)) { + return; + } + + $this->throwInvalidTypeException($phpVersionFeatures); + } + + private function throwInvalidTypeException(mixed $phpVersionFeatures): never + { + $errorMessage = sprintf( + 'Parameter "%s::%s" must be int, "%s" given.%sUse constant from "%s" to provide it, e.g. "%s::%s"', + Option::class, + 'PHP_VERSION_FEATURES', + (string) $phpVersionFeatures, + PHP_EOL, + PhpVersion::class, + PhpVersion::class, + 'PHP_80' + ); + throw new InvalidConfigurationException($errorMessage); + } } diff --git a/src/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver.php b/src/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver.php new file mode 100644 index 00000000000..13c77128181 --- /dev/null +++ b/src/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver.php @@ -0,0 +1,84 @@ + + */ + private static array $cachedPhpVersions = []; + + /** + * @return PhpVersion::* + */ + public static function resolveFromCwdOrFail(): int + { + // use composer.json PHP version + $projectComposerJsonFilePath = getcwd() . '/composer.json'; + if (file_exists($projectComposerJsonFilePath)) { + $projectPhpVersion = self::resolve($projectComposerJsonFilePath); + if (is_int($projectPhpVersion)) { + return $projectPhpVersion; + } + } + + throw new InvalidConfigurationException(sprintf( + 'We could not find local "composer.json" to determine your PHP version.%sPlease, fill the PHP version set in withPhpSets() manually.', + PHP_EOL + )); + } + + /** + * @return PhpVersion::*|null + */ + public static function resolve(string $composerJson): ?int + { + if (array_key_exists($composerJson, self::$cachedPhpVersions)) { + return self::$cachedPhpVersions[$composerJson]; + } + + $projectComposerJson = JsonFileSystem::readFilePath($composerJson); + + // give this one a priority, as more generic one. see https://github.com/composer/composer/issues/7914 + $requirePhpVersion = $projectComposerJson['require']['php'] ?? $projectComposerJson['require']['php-64bit'] ?? null; + if ($requirePhpVersion !== null) { + self::$cachedPhpVersions[$composerJson] = self::createIntVersionFromComposerVersion($requirePhpVersion); + return self::$cachedPhpVersions[$composerJson]; + } + + // see https://getcomposer.org/doc/06-config.md#platform + $platformPhp = $projectComposerJson['config']['platform']['php'] ?? null; + if ($platformPhp !== null) { + self::$cachedPhpVersions[$composerJson] = PhpVersionFactory::createIntVersion($platformPhp); + return self::$cachedPhpVersions[$composerJson]; + } + + return self::$cachedPhpVersions[$composerJson] = null; + } + + /** + * @return PhpVersion::* + */ + private static function createIntVersionFromComposerVersion(string $projectPhpVersion): int + { + $versionParser = new VersionParser(); + $constraint = $versionParser->parseConstraints($projectPhpVersion); + + $lowerBound = $constraint->getLowerBound(); + $lowerBoundVersion = $lowerBound->getVersion(); + + return PhpVersionFactory::createIntVersion($lowerBoundVersion); + } +} diff --git a/src/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver.php b/src/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver.php deleted file mode 100644 index e0e05a1a008..00000000000 --- a/src/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver.php +++ /dev/null @@ -1,50 +0,0 @@ -composerJsonFactory->createFromFilePath($composerJson); - - // see https://getcomposer.org/doc/06-config.md#platform - $platformPhp = $projectComposerJson->getConfig()['platform']['php'] ?? null; - if ($platformPhp !== null) { - return $this->phpVersionFactory->createIntVersion($platformPhp); - } - - $requirePhpVersion = $projectComposerJson->getRequirePhpVersion(); - if ($requirePhpVersion === null) { - return null; - } - - return $this->createIntVersionFromComposerVersion($requirePhpVersion); - } - - private function createIntVersionFromComposerVersion(string $projectPhpVersion): int - { - $constraint = $this->versionParser->parseConstraints($projectPhpVersion); - - $lowerBound = $constraint->getLowerBound(); - $lowerBoundVersion = $lowerBound->getVersion(); - - return $this->phpVersionFactory->createIntVersion($lowerBoundVersion); - } -} diff --git a/src/Php/PolyfillPackagesProvider.php b/src/Php/PolyfillPackagesProvider.php new file mode 100644 index 00000000000..597901d00a6 --- /dev/null +++ b/src/Php/PolyfillPackagesProvider.php @@ -0,0 +1,60 @@ + + */ + private null|array $cachedPolyfillPackages = null; + + /** + * @return array + */ + public function provide(): array + { + // disable cache in tests + if (SimpleParameterProvider::hasParameter(Option::POLYFILL_PACKAGES)) { + return SimpleParameterProvider::provideArrayParameter(Option::POLYFILL_PACKAGES); + } + + // already cached, even only empty array + if ($this->cachedPolyfillPackages !== null) { + return $this->cachedPolyfillPackages; + } + + $projectComposerJson = getcwd() . '/composer.json'; + if (! file_exists($projectComposerJson)) { + $this->cachedPolyfillPackages = []; + return $this->cachedPolyfillPackages; + } + + $composerContents = FileSystem::read($projectComposerJson); + $composerJson = Json::decode($composerContents, forceArrays: true); + + $this->cachedPolyfillPackages = $this->filterPolyfillPackages($composerJson['require'] ?? []); + + return $this->cachedPolyfillPackages; + } + + /** + * @param array $require + * @return array + */ + private function filterPolyfillPackages(array $require): array + { + return array_filter(array_keys($require), static fn (string $packageName): bool => str_starts_with( + $packageName, + 'symfony/polyfill-' + )); + } +} diff --git a/src/Php/Regex/RegexPatternArgumentManipulator.php b/src/Php/Regex/RegexPatternArgumentManipulator.php deleted file mode 100644 index e085e65ccd6..00000000000 --- a/src/Php/Regex/RegexPatternArgumentManipulator.php +++ /dev/null @@ -1,191 +0,0 @@ - - */ - private const FUNCTIONS_WITH_PATTERNS_TO_ARGUMENT_POSITION = [ - 'preg_match' => 0, - 'preg_replace_callback_array' => 0, - 'preg_replace_callback' => 0, - 'preg_replace' => 0, - 'preg_match_all' => 0, - 'preg_split' => 0, - 'preg_grep' => 0, - ]; - - /** - * @var array> - */ - private const STATIC_METHODS_WITH_PATTERNS_TO_ARGUMENT_POSITION = [ - Strings::class => [ - 'match' => 1, - 'matchAll' => 1, - 'replace' => 1, - 'split' => 1, - ], - ]; - - public function __construct( - private BetterNodeFinder $betterNodeFinder, - private NodeNameResolver $nodeNameResolver, - private NodeTypeResolver $nodeTypeResolver, - private LocalConstantFinder $localConstantFinder, - private NodeComparator $nodeComparator - ) { - } - - /** - * @return String_[] - */ - public function matchCallArgumentWithRegexPattern(Expr $expr): array - { - if ($expr instanceof FuncCall) { - return $this->processFuncCall($expr); - } - - if ($expr instanceof StaticCall) { - return $this->processStaticCall($expr); - } - - return []; - } - - /** - * @return String_[] - */ - private function processFuncCall(FuncCall $funcCall): array - { - foreach (self::FUNCTIONS_WITH_PATTERNS_TO_ARGUMENT_POSITION as $functionName => $argumentPosition) { - if (! $this->nodeNameResolver->isName($funcCall, $functionName)) { - continue; - } - - if (! isset($funcCall->args[$argumentPosition])) { - return []; - } - - return $this->resolveArgumentValues($funcCall->args[$argumentPosition]->value); - } - - return []; - } - - /** - * @return String_[] - */ - private function processStaticCall(StaticCall $staticCall): array - { - foreach (self::STATIC_METHODS_WITH_PATTERNS_TO_ARGUMENT_POSITION as $type => $methodNamesToArgumentPosition) { - if (! $this->nodeTypeResolver->isObjectType($staticCall->class, new ObjectType($type))) { - continue; - } - - foreach ($methodNamesToArgumentPosition as $methodName => $argumentPosition) { - if (! $this->nodeNameResolver->isName($staticCall->name, $methodName)) { - continue; - } - - if (! isset($staticCall->args[$argumentPosition])) { - return []; - } - - return $this->resolveArgumentValues($staticCall->args[$argumentPosition]->value); - } - } - - return []; - } - - /** - * @return String_[] - */ - private function resolveArgumentValues(Expr $expr): array - { - if ($expr instanceof String_) { - return [$expr]; - } - - if ($expr instanceof Variable) { - $strings = []; - $assignNodes = $this->findAssignerForVariable($expr); - foreach ($assignNodes as $assignNode) { - if ($assignNode->expr instanceof String_) { - $strings[] = $assignNode->expr; - } - } - - return $strings; - } - - if ($expr instanceof ClassConstFetch) { - return $this->matchClassConstFetchStringValue($expr); - } - - return []; - } - - /** - * @return Assign[] - */ - private function findAssignerForVariable(Variable $variable): array - { - $classMethod = $variable->getAttribute(AttributeKey::METHOD_NODE); - if (! $classMethod instanceof ClassMethod) { - return []; - } - - return $this->betterNodeFinder->find([$classMethod], function (Node $node) use ($variable): ?Assign { - if (! $node instanceof Assign) { - return null; - } - - if (! $this->nodeComparator->areNodesEqual($node->var, $variable)) { - return null; - } - - return $node; - }); - } - - /** - * @return String_[] - */ - private function matchClassConstFetchStringValue(ClassConstFetch $classConstFetch): array - { - $classConst = $this->localConstantFinder->match($classConstFetch); - if (! $classConst instanceof Const_) { - return []; - } - - if ($classConst->value instanceof String_) { - return [$classConst->value]; - } - - return []; - } -} diff --git a/src/Php/ReservedKeywordAnalyzer.php b/src/Php/ReservedKeywordAnalyzer.php index adc92a91c24..109daa66e49 100644 --- a/src/Php/ReservedKeywordAnalyzer.php +++ b/src/Php/ReservedKeywordAnalyzer.php @@ -2,108 +2,14 @@ declare(strict_types=1); -namespace Rector\Core\Php; +namespace Rector\Php; + +use PHPStan\Analyser\Scope; final class ReservedKeywordAnalyzer { - /** - * @see https://www.php.net/manual/en/reserved.keywords.php - * @var string[] - */ - private const RESERVED_KEYWORDS = [ - '__halt_compiler', - 'abstract', - 'and', - 'array', - 'as', - 'break', - 'callable', - 'case', - 'catch', - 'class', - 'clone', - 'const', - 'continue', - 'declare', - 'default', - 'die', - 'do', - 'echo', - 'else', - 'elseif', - 'empty', - 'enddeclare', - 'endfor', - 'endforeach', - 'endif', - 'endswitch', - 'endwhile', - 'eval', - 'exit', - 'extends', - 'final', - 'finally', - 'fn', - 'for', - 'foreach', - 'function', - 'global', - 'goto', - 'if', - 'implements', - 'include', - 'include_once', - 'instanceof', - 'insteadof', - 'interface', - 'isset', - 'list', - 'namespace', - 'new', - 'or', - 'print', - 'private', - 'protected', - 'public', - 'require', - 'require_once', - 'return', - 'static', - 'switch', - 'throw', - 'trait', - 'try', - 'unset', - 'use', - 'var', - 'while', - 'xor', - 'yield', - ]; - - /** - * @var string[] - */ - private const NATIVE_VARIABLE_NAMES = [ - '_ENV', - '_POST', - '_GET', - '_COOKIE', - '_SERVER', - '_FILES', - '_REQUEST', - '_SESSION', - 'GLOBALS', - ]; - public function isNativeVariable(string $name): bool { - return in_array($name, self::NATIVE_VARIABLE_NAMES, true); - } - - public function isReserved(string $keyword): bool - { - $keyword = strtolower($keyword); - return in_array($keyword, self::RESERVED_KEYWORDS, true); + return in_array($name, Scope::SUPERGLOBAL_VARIABLES, true); } } diff --git a/src/Php/TypeAnalyzer.php b/src/Php/TypeAnalyzer.php deleted file mode 100644 index 50bb789dbff..00000000000 --- a/src/Php/TypeAnalyzer.php +++ /dev/null @@ -1,101 +0,0 @@ -#'; - - /** - * @var string - * @see https://regex101.com/r/57HGpC/1 - */ - private const SQUARE_BRACKET_REGEX = '#(\[\])+$#'; - - /** - * @var string[] - */ - private array $phpSupportedTypes = [ - 'string', - 'bool', - 'int', - 'null', - 'array', - 'false', - 'true', - 'mixed', - 'iterable', - 'float', - 'self', - 'parent', - 'callable', - 'void', - ]; - - public function __construct(PhpVersionProvider $phpVersionProvider) - { - if ($phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::OBJECT_TYPE)) { - $this->phpSupportedTypes[] = 'object'; - } - } - - public function isPhpReservedType(string $type): bool - { - $types = explode('|', $type); - - $reservedTypes = array_merge($this->phpSupportedTypes, self::EXTRA_TYPES); - - foreach ($types as $type) { - $type = strtolower($type); - - // remove [] from arrays - $type = Strings::replace($type, self::SQUARE_BRACKET_REGEX, ''); - - if (in_array($type, $reservedTypes, true)) { - return true; - } - } - - return false; - } - - public function normalizeType(string $type): string - { - $loweredType = strtolower($type); - if ($loweredType === 'boolean') { - return 'bool'; - } - - if (in_array($loweredType, ['double', 'real'], true)) { - return 'float'; - } - - if ($loweredType === 'integer') { - return 'int'; - } - - if ($loweredType === 'callback') { - return 'callable'; - } - - if (Strings::match($loweredType, self::ARRAY_TYPE_REGEX)) { - return 'array'; - } - - return $type; - } -} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper.php new file mode 100644 index 00000000000..ac8cbad06e1 --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper.php @@ -0,0 +1,65 @@ +annotationToAttributeMappers as $annotationToAttributeMapper) { + if ($annotationToAttributeMapper->isCandidate($value)) { + return $annotationToAttributeMapper->map($value); + } + } + + if ($value instanceof Expr) { + return $value; + } + + // remove node, as handled elsewhere + if ($value instanceof DoctrineAnnotationTagValueNode) { + return DocTagNodeState::REMOVE_ARRAY; + } + + if ($value instanceof ArrayItemNode) { + return BuilderHelpers::normalizeValue((string) $value); + } + + if ($value instanceof StringNode) { + return new String_($value->value, [ + AttributeKey::KIND => $value->getAttribute(AttributeKey::KIND), + ]); + } + + // fallback + return BuilderHelpers::normalizeValue($value); + } +} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper/ArrayAnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper/ArrayAnnotationToAttributeMapper.php new file mode 100644 index 00000000000..f2c9660129d --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper/ArrayAnnotationToAttributeMapper.php @@ -0,0 +1,99 @@ + + */ +final class ArrayAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface +{ + private AnnotationToAttributeMapper $annotationToAttributeMapper; + + public function __construct( + private readonly ValueResolver $valueResolver + ) { + } + + public function autowire(AnnotationToAttributeMapper $annotationToAttributeMapper): void + { + $this->annotationToAttributeMapper = $annotationToAttributeMapper; + } + + public function isCandidate(mixed $value): bool + { + return is_array($value); + } + + /** + * @param mixed[] $value + */ + public function map($value): Array_ + { + $arrayItems = []; + + foreach ($value as $key => $singleValue) { + $valueExpr = $this->annotationToAttributeMapper->map($singleValue); + + // remove node + if ($valueExpr === DocTagNodeState::REMOVE_ARRAY) { + continue; + } + + // remove value + if ($this->isRemoveArrayPlaceholder($singleValue)) { + continue; + } + + if ($valueExpr instanceof ArrayItem) { + $valueExpr = $this->resolveValueExprWithSingleQuoteHandling($valueExpr); + $arrayItems[] = $this->resolveValueExprWithSingleQuoteHandling($valueExpr); + } else { + $keyExpr = null; + if (! is_int($key)) { + $keyExpr = $this->annotationToAttributeMapper->map($key); + Assert::isInstanceOf($keyExpr, Expr::class); + } + + $arrayItems[] = new ArrayItem($valueExpr, $keyExpr); + } + } + + return new Array_($arrayItems); + } + + private function resolveValueExprWithSingleQuoteHandling(ArrayItem $arrayItem): ArrayItem + { + if (! $arrayItem->key instanceof Expr && $arrayItem->value instanceof ClassConstFetch && $arrayItem->value->class instanceof Name && str_contains( + (string) $arrayItem->value->class, + "'" + )) { + $arrayItem->value = new String_($this->valueResolver->getValue($arrayItem->value)); + return $arrayItem; + } + + return $arrayItem; + } + + private function isRemoveArrayPlaceholder(mixed $value): bool + { + if (! is_array($value)) { + return false; + } + + return in_array(DocTagNodeState::REMOVE_ARRAY, $value, true); + } +} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper/ArrayItemNodeAnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper/ArrayItemNodeAnnotationToAttributeMapper.php new file mode 100644 index 00000000000..0ed80be0dff --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper/ArrayItemNodeAnnotationToAttributeMapper.php @@ -0,0 +1,91 @@ + + */ +final class ArrayItemNodeAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface +{ + private AnnotationToAttributeMapper $annotationToAttributeMapper; + + /** + * Avoid circular reference + */ + public function autowire(AnnotationToAttributeMapper $annotationToAttributeMapper): void + { + $this->annotationToAttributeMapper = $annotationToAttributeMapper; + } + + public function isCandidate(mixed $value): bool + { + return $value instanceof ArrayItemNode; + } + + /** + * @param ArrayItemNode $arrayItemNode + */ + public function map($arrayItemNode): ArrayItem + { + $valueExpr = $this->annotationToAttributeMapper->map($arrayItemNode->value); + + if ($valueExpr === DocTagNodeState::REMOVE_ARRAY) { + return new ArrayItem(new String_($valueExpr)); + } + + if ($arrayItemNode->key !== null) { + /** @var Expr $keyExpr */ + $keyExpr = $this->annotationToAttributeMapper->map($arrayItemNode->key); + } else { + if ($this->hasNoParenthesesAnnotation($arrayItemNode)) { + try { + RectorAssert::className(ltrim((string) $arrayItemNode->value, '@')); + + $identifierTypeNode = new IdentifierTypeNode($arrayItemNode->value); + $arrayItemNode->value = new DoctrineAnnotationTagValueNode($identifierTypeNode); + + return $this->map($arrayItemNode); + } catch (InvalidArgumentException) { + } + } + + $keyExpr = null; + } + + // @todo how to skip natural integer keys? + + return new ArrayItem($valueExpr, $keyExpr); + } + + private function hasNoParenthesesAnnotation(ArrayItemNode $arrayItemNode): bool + { + if ($arrayItemNode->value instanceof StringNode) { + return false; + } + + if (! is_string($arrayItemNode->value)) { + return false; + } + + if (! str_starts_with($arrayItemNode->value, '@')) { + return false; + } + + return ! str_ends_with($arrayItemNode->value, ')'); + } +} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper/ClassConstFetchAnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper/ClassConstFetchAnnotationToAttributeMapper.php new file mode 100644 index 00000000000..ccf552e8beb --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper/ClassConstFetchAnnotationToAttributeMapper.php @@ -0,0 +1,60 @@ + + */ +final class ClassConstFetchAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface +{ + public function isCandidate(mixed $value): bool + { + if (! is_string($value)) { + return false; + } + + if (! str_contains($value, '::')) { + return false; + } + + // is quoted? skip it + return ! str_starts_with($value, '"'); + } + + /** + * @param string $value + * @return String_|ClassConstFetch + */ + public function map($value): Node + { + $values = explode('::', $value); + + if (count($values) !== 2) { + return new String_($value); + } + + [$class, $constant] = $values; + if ($class === '') { + return new String_($value); + } + + try { + RectorAssert::className(ltrim($class, '\\')); + RectorAssert::constantName($constant); + } catch (InvalidArgumentException) { + return new String_($value); + } + + return new ClassConstFetch(new Name($class), $constant); + } +} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper/ConstExprNodeAnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper/ConstExprNodeAnnotationToAttributeMapper.php new file mode 100644 index 00000000000..f44e56e223e --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper/ConstExprNodeAnnotationToAttributeMapper.php @@ -0,0 +1,51 @@ + + */ +final class ConstExprNodeAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface +{ + public function isCandidate(mixed $value): bool + { + return $value instanceof ConstExprNode; + } + + /** + * @param ConstExprNode $value + */ + public function map($value): Expr + { + if ($value instanceof ConstExprIntegerNode) { + return BuilderHelpers::normalizeValue((int) $value->value); + } + + if ($value instanceof ConstantFloatType || $value instanceof ConstantBooleanType) { + return BuilderHelpers::normalizeValue($value->getValue()); + } + + if ($value instanceof ConstExprTrueNode) { + return BuilderHelpers::normalizeValue(true); + } + + if ($value instanceof ConstExprFalseNode) { + return BuilderHelpers::normalizeValue(false); + } + + throw new NotImplementedYetException(); + } +} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper/CurlyListNodeAnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper/CurlyListNodeAnnotationToAttributeMapper.php new file mode 100644 index 00000000000..52bc0999bf2 --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper/CurlyListNodeAnnotationToAttributeMapper.php @@ -0,0 +1,74 @@ + + */ +final class CurlyListNodeAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface +{ + private AnnotationToAttributeMapper $annotationToAttributeMapper; + + /** + * Avoid circular reference + */ + public function autowire(AnnotationToAttributeMapper $annotationToAttributeMapper): void + { + $this->annotationToAttributeMapper = $annotationToAttributeMapper; + } + + public function isCandidate(mixed $value): bool + { + return $value instanceof CurlyListNode; + } + + /** + * @param CurlyListNode $value + */ + public function map($value): Array_ + { + $arrayItems = []; + $arrayItemNodes = $value->getValues(); + $loop = -1; + + foreach ($arrayItemNodes as $arrayItemNode) { + $valueExpr = $this->annotationToAttributeMapper->map($arrayItemNode); + + // remove node + if ($valueExpr === DocTagNodeState::REMOVE_ARRAY) { + continue; + } + + Assert::isInstanceOf($valueExpr, ArrayItem::class); + + if (! is_numeric($arrayItemNode->key)) { + $arrayItems[] = $valueExpr; + continue; + } + + ++$loop; + + $arrayItemNodeKey = (int) $arrayItemNode->key; + if ($loop === $arrayItemNodeKey) { + $arrayItems[] = $valueExpr; + continue; + } + + $valueExpr->key = new Int_($arrayItemNodeKey); + $arrayItems[] = $valueExpr; + } + + return new Array_($arrayItems); + } +} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper/DoctrineAnnotationAnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper/DoctrineAnnotationAnnotationToAttributeMapper.php new file mode 100644 index 00000000000..999cf53f8e8 --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper/DoctrineAnnotationAnnotationToAttributeMapper.php @@ -0,0 +1,76 @@ + + */ +final class DoctrineAnnotationAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface +{ + private AnnotationToAttributeMapper $annotationToAttributeMapper; + + public function __construct( + private readonly PhpVersionProvider $phpVersionProvider, + private readonly AttributeArrayNameInliner $attributeArrayNameInliner, + ) { + } + + /** + * Avoid circular reference + */ + public function autowire(AnnotationToAttributeMapper $annotationToAttributeMapper): void + { + $this->annotationToAttributeMapper = $annotationToAttributeMapper; + } + + public function isCandidate(mixed $value): bool + { + if (! $value instanceof DoctrineAnnotationTagValueNode) { + return false; + } + + return $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::NEW_INITIALIZERS); + } + + /** + * @param DoctrineAnnotationTagValueNode $value + */ + public function map($value): New_ + { + $annotationShortName = $this->resolveAnnotationName($value); + $values = $value->getValues(); + if ($values !== []) { + $argValues = $this->annotationToAttributeMapper->map($value->getValues()); + + if ($argValues instanceof Array_) { + // create named args + $args = $this->attributeArrayNameInliner->inlineArrayToArgs($argValues); + } else { + throw new ShouldNotHappenException(); + } + } else { + $args = []; + } + + return new New_(new Name($annotationShortName), $args); + } + + private function resolveAnnotationName(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode): string + { + $annotationShortName = $doctrineAnnotationTagValueNode->identifierTypeNode->name; + return ltrim($annotationShortName, '@'); + } +} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper/StringAnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper/StringAnnotationToAttributeMapper.php new file mode 100644 index 00000000000..726a66eaf46 --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper/StringAnnotationToAttributeMapper.php @@ -0,0 +1,40 @@ + + */ +final class StringAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface +{ + public function isCandidate(mixed $value): bool + { + return is_string($value); + } + + /** + * @param string $value + */ + public function map($value): String_ + { + if (str_contains($value, "'") && ! str_contains($value, "\n")) { + $kind = String_::KIND_DOUBLE_QUOTED; + } else { + $kind = String_::KIND_SINGLE_QUOTED; + } + + if (str_starts_with($value, '"') && str_ends_with($value, '"')) { + $value = trim($value, '"'); + } + + return new String_($value, [ + AttributeKey::KIND => $kind, + ]); + } +} diff --git a/src/PhpAttribute/AnnotationToAttributeMapper/StringNodeAnnotationToAttributeMapper.php b/src/PhpAttribute/AnnotationToAttributeMapper/StringNodeAnnotationToAttributeMapper.php new file mode 100644 index 00000000000..05f7298c610 --- /dev/null +++ b/src/PhpAttribute/AnnotationToAttributeMapper/StringNodeAnnotationToAttributeMapper.php @@ -0,0 +1,31 @@ + + */ +final class StringNodeAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface +{ + public function isCandidate(mixed $value): bool + { + return $value instanceof StringNode; + } + + /** + * @param StringNode $value + */ + public function map($value): String_ + { + return new String_($value->value, [ + AttributeKey::KIND => $value->getAttribute(AttributeKey::KIND), + ]); + } +} diff --git a/src/PhpAttribute/AttributeArrayNameInliner.php b/src/PhpAttribute/AttributeArrayNameInliner.php new file mode 100644 index 00000000000..6ee6a72b284 --- /dev/null +++ b/src/PhpAttribute/AttributeArrayNameInliner.php @@ -0,0 +1,86 @@ + $array + * @return list + */ + public function inlineArrayToArgs(Array_|array $array, ?string $attributeClass = null): array + { + if (is_array($array)) { + return $this->inlineArray($array, $attributeClass); + } + + return $this->inlineArrayNode($array); + } + + /** + * @return list + */ + private function inlineArrayNode(Array_ $array): array + { + $args = []; + + foreach ($array->items as $arrayItem) { + if (! $arrayItem instanceof ArrayItem) { + continue; + } + + if ($arrayItem->key instanceof String_) { + $string = $arrayItem->key; + $argumentName = new Identifier($string->value); + $args[] = new Arg($arrayItem->value, false, false, [], $argumentName); + } else { + $args[] = new Arg($arrayItem->value); + } + } + + return $args; + } + + /** + * @param list $args + * @return list + */ + private function inlineArray(array $args, ?string $attributeClass = null): array + { + Assert::allIsAOf($args, Arg::class); + foreach ($args as $arg) { + if ($attributeClass === self::OPEN_API_PROPERTY_ATTRIBUTE + && $arg->name instanceof Identifier + && $arg->name->toString() === 'example') { + continue; + } + + if ($arg->value instanceof String_ && is_numeric($arg->value->value)) { + // use equal over identical on purpose to verify if it is an integer + if ((float) $arg->value->value == (int) $arg->value->value) { + $arg->value = new Int_((int) $arg->value->value); + } else { + $arg->value = new Float_((float) $arg->value->value); + } + } + } + + return $args; + } +} diff --git a/src/PhpAttribute/Contract/AnnotationToAttributeMapperInterface.php b/src/PhpAttribute/Contract/AnnotationToAttributeMapperInterface.php new file mode 100644 index 00000000000..feb40065a3f --- /dev/null +++ b/src/PhpAttribute/Contract/AnnotationToAttributeMapperInterface.php @@ -0,0 +1,20 @@ +phpDocInfoFactory->createFromNode($node); + if ($phpDocInfo instanceof PhpDocInfo) { + $deprecatedAttributeGroup = $this->handleDeprecated($phpDocInfo); + if ($deprecatedAttributeGroup instanceof AttributeGroup) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + $node->attrGroups = array_merge($node->attrGroups, [$deprecatedAttributeGroup]); + $this->removeDeprecatedAnnotations($phpDocInfo); + $hasChanged = true; + } + } + + return $hasChanged ? $node : null; + } + + private function handleDeprecated(PhpDocInfo $phpDocInfo): ?AttributeGroup + { + $attributeGroup = null; + $desiredTagValueNodes = $phpDocInfo->getTagsByName('deprecated'); + foreach ($desiredTagValueNodes as $desiredTagValueNode) { + if (! $desiredTagValueNode->value instanceof DeprecatedTagValueNode) { + continue; + } + + $attributeGroup = $this->createAttributeGroup($desiredTagValueNode->value->description); + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $desiredTagValueNode); + + break; + } + + return $attributeGroup; + } + + private function createAttributeGroup(string $annotationValue): AttributeGroup + { + $matches = Strings::match($annotationValue, self::VERSION_MATCH_REGEX); + + if ($matches === null) { + $annotationValue = Strings::replace($annotationValue, self::START_STAR_SPACED_REGEX, ''); + + return new AttributeGroup([ + new Attribute( + new FullyQualified('Deprecated'), + [new Arg( + value: new String_($annotationValue, [ + AttributeKey::KIND => String_::KIND_NOWDOC, + AttributeKey::DOC_LABEL => 'TXT', + ]), + name: new Identifier('message') + )] + ), + ]); + } + + $since = $matches[1] ?? null; + $message = $matches[2] ?? null; + + return $this->phpAttributeGroupFactory->createFromClassWithItems('Deprecated', array_filter([ + 'message' => $message, + 'since' => $since, + ])); + } + + private function removeDeprecatedAnnotations(PhpDocInfo $phpDocInfo): bool + { + $hasChanged = false; + + $desiredTagValueNodes = $phpDocInfo->getTagsByName('deprecated'); + foreach ($desiredTagValueNodes as $desiredTagValueNode) { + if (! $desiredTagValueNode->value instanceof GenericTagValueNode) { + continue; + } + + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $desiredTagValueNode); + $hasChanged = true; + } + + return $hasChanged; + } +} diff --git a/src/PhpAttribute/Enum/DocTagNodeState.php b/src/PhpAttribute/Enum/DocTagNodeState.php new file mode 100644 index 00000000000..26fae37fcd0 --- /dev/null +++ b/src/PhpAttribute/Enum/DocTagNodeState.php @@ -0,0 +1,10 @@ +isExistingAttributeClass($annotationToAttribute)) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $phpDocInfo instanceof PhpDocInfo) { + return null; + } + + $uses = $this->useImportsResolver->resolveBareUses(); + + return $this->processDoctrineAnnotationClass($phpDocInfo, $uses, $annotationToAttribute); + } + + /** + * @param Use_[] $uses + */ + private function processDoctrineAnnotationClass( + PhpDocInfo $phpDocInfo, + array $uses, + AnnotationToAttribute $annotationToAttribute + ): ?AttributeGroup { + if ($phpDocInfo->getPhpDocNode()->children === []) { + return null; + } + + $doctrineTagAndAnnotationToAttributes = []; + $doctrineTagValueNodes = []; + + foreach ($phpDocInfo->getPhpDocNode()->children as $phpDocChildNode) { + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if (! $phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) { + continue; + } + + $doctrineTagValueNode = $phpDocChildNode->value; + if (! $doctrineTagValueNode->hasClassName($annotationToAttribute->getTag())) { + continue; + } + + $doctrineTagAndAnnotationToAttributes[] = new DoctrineTagAndAnnotationToAttribute( + $doctrineTagValueNode, + $annotationToAttribute + ); + + $doctrineTagValueNodes[] = $doctrineTagValueNode; + } + + $attributeGroups = $this->attrGroupsFactory->create($doctrineTagAndAnnotationToAttributes, $uses); + + foreach ($doctrineTagValueNodes as $doctrineTagValueNode) { + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineTagValueNode); + } + + return $attributeGroups[0] ?? null; + } + + private function isExistingAttributeClass(AnnotationToAttribute $annotationToAttribute): bool + { + // make sure the attribute class really exists to avoid error on early upgrade + if (! $this->reflectionProvider->hasClass($annotationToAttribute->getAttributeClass())) { + return false; + } + + // make sure the class is marked as attribute + $classReflection = $this->reflectionProvider->getClass($annotationToAttribute->getAttributeClass()); + return $classReflection->isAttributeClass(); + } +} diff --git a/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php b/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php new file mode 100644 index 00000000000..003fe4129ea --- /dev/null +++ b/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php @@ -0,0 +1,117 @@ +reflectionProvider->hasClass($annotationToAttribute->getAttributeClass())) { + return; + } + + $attributeClassReflection = $this->reflectionProvider->getClass($annotationToAttribute->getAttributeClass()); + if (! $attributeClassReflection->hasConstructor()) { + return; + } + + $parameterReflections = $this->resolveConstructorParameterReflections($attributeClassReflection); + + foreach ($parameterReflections as $parameterReflection) { + foreach ($args as $arg) { + if (! $arg->value instanceof Array_) { + continue; + } + + $arrayItem = current($arg->value->items) ?: null; + if (! $arrayItem instanceof ArrayItem) { + continue; + } + + if (! $arrayItem->key instanceof String_) { + continue; + } + + $keyString = $arrayItem->key; + if ($keyString->value !== $parameterReflection->getName()) { + continue; + } + + // ensure type is casted to integer + if (! $arrayItem->value instanceof String_) { + continue; + } + + if (! $this->containsInteger($parameterReflection->getType())) { + continue; + } + + $valueString = $arrayItem->value; + if (! is_numeric($valueString->value)) { + continue; + } + + $arrayItem->value = new Int_((int) $valueString->value); + } + } + } + + private function containsInteger(Type $type): bool + { + if ($type->isInteger()->yes()) { + return true; + } + + if (! $type instanceof UnionType) { + return false; + } + + foreach ($type->getTypes() as $unionedType) { + if ($unionedType->isInteger()->yes()) { + return true; + } + } + + return false; + } + + /** + * @return ParameterReflection[] + */ + private function resolveConstructorParameterReflections(ClassReflection $classReflection): array + { + $extendedMethodReflection = $classReflection->getConstructor(); + + $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors( + $extendedMethodReflection->getVariants() + ); + + return $extendedParametersAcceptor->getParameters(); + } +} diff --git a/src/PhpAttribute/NodeFactory/AttributeNameFactory.php b/src/PhpAttribute/NodeFactory/AttributeNameFactory.php new file mode 100644 index 00000000000..ba630c66e7e --- /dev/null +++ b/src/PhpAttribute/NodeFactory/AttributeNameFactory.php @@ -0,0 +1,65 @@ +getAttributeClass() === $annotationToAttribute->getTag()) { + $attributeName = $doctrineAnnotationTagValueNode->identifierTypeNode->name; + $attributeName = ltrim($attributeName, '@'); + + if (str_starts_with($attributeName, '\\')) { + return new FullyQualified(ltrim($attributeName, '\\')); + } + + return new Name($attributeName); + } + + // B. different name + $useAliasMetadata = $this->useAliasNameMatcher->match( + $uses, + $doctrineAnnotationTagValueNode->identifierTypeNode->name, + $annotationToAttribute + ); + if ($useAliasMetadata instanceof UseAliasMetadata) { + $useUse = $useAliasMetadata->getUseUse(); + + // is same as name? + $useImportName = $useAliasMetadata->getUseImportName(); + if ($useUse->name->toString() !== $useImportName) { + // no? rename + $useUse->name = new Name($useImportName); + } + + return new Name($useAliasMetadata->getShortAttributeName()); + } + + // 3. the class is not aliased and is completely new... return the FQN version + return new FullyQualified($annotationToAttribute->getAttributeClass()); + } +} diff --git a/src/PhpAttribute/NodeFactory/NamedArgsFactory.php b/src/PhpAttribute/NodeFactory/NamedArgsFactory.php new file mode 100644 index 00000000000..be2275fd8a4 --- /dev/null +++ b/src/PhpAttribute/NodeFactory/NamedArgsFactory.php @@ -0,0 +1,68 @@ + $values + * @return list + */ + public function createFromValues(array $values): array + { + $args = []; + + foreach ($values as $key => $argValue) { + $name = null; + + if ($argValue instanceof ArrayItem) { + if ($argValue->key instanceof String_) { + $name = new Identifier($argValue->key->value); + } + + $argValue = $argValue->value; + } + + $expr = BuilderHelpers::normalizeValue($argValue); + + // for named arguments + if (! $name instanceof Identifier && is_string($key)) { + $name = new Identifier($key); + } + + $this->normalizeStringDoubleQuote($expr); + + $args[] = new Arg($expr, false, false, [], $name); + } + + return $args; + } + + private function normalizeStringDoubleQuote(Expr $expr): void + { + if (! $expr instanceof String_) { + return; + } + + // avoid escaping quotes + preserve newlines + if (! str_contains($expr->value, "'")) { + return; + } + + if (str_contains($expr->value, "\n")) { + return; + } + + $expr->setAttribute(AttributeKey::KIND, String_::KIND_DOUBLE_QUOTED); + } +} diff --git a/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php b/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php new file mode 100644 index 00000000000..88e9a6c0367 --- /dev/null +++ b/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php @@ -0,0 +1,164 @@ +createFromClass($annotationToAttribute->getAttributeClass(), $value); + } + + /** + * @param AttributeName::*|string $attributeClass + */ + public function createFromClass(string $attributeClass, ?string $value = null): AttributeGroup + { + $fullyQualified = new FullyQualified($attributeClass); + $attribute = new Attribute($fullyQualified); + if ($value !== null && $value !== '') { + $arg = new Arg(new String_($value)); + $attribute->args = [$arg]; + } + + return new AttributeGroup([$attribute]); + } + + /** + * @api tests + * @param mixed[] $items + */ + public function createFromClassWithItems(string $attributeClass, array $items): AttributeGroup + { + $fullyQualified = new FullyQualified($attributeClass); + + $args = $this->createArgsFromItems($items); + $attribute = new Attribute($fullyQualified, $args); + + return new AttributeGroup([$attribute]); + } + + /** + * @param Use_[] $uses + */ + public function create( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, + AnnotationToAttribute $annotationToAttribute, + array $uses + ): AttributeGroup { + $values = $doctrineAnnotationTagValueNode->getValuesWithSilentKey(); + $args = $this->createArgsFromItems($values, '', $annotationToAttribute->getClassReferenceFields()); + + $this->annotationToAttributeIntegerValueCaster->castAttributeTypes($annotationToAttribute, $args); + + $args = $this->attributeArrayNameInliner->inlineArrayToArgs($args, $annotationToAttribute->getAttributeClass()); + + $attributeName = $this->attributeNameFactory->create( + $annotationToAttribute, + $doctrineAnnotationTagValueNode, + $uses + ); + + // keep FQN in the attribute, so it can be easily detected later + $attributeName->setAttribute(AttributeKey::PHP_ATTRIBUTE_NAME, $annotationToAttribute->getAttributeClass()); + + $attribute = new Attribute($attributeName, $args); + return new AttributeGroup([$attribute]); + } + + /** + * @api tests + * + * @param ArrayItemNode[]|mixed[] $items + * @param string $attributeClass @deprecated + * @param string[] $classReferencedFields + * + * @return list + */ + public function createArgsFromItems( + array $items, + string $attributeClass = '', + array $classReferencedFields = [] + ): array { + $mappedItems = $this->annotationToAttributeMapper->map($items); + + $this->mapClassReferences($mappedItems, $classReferencedFields); + + $values = $mappedItems instanceof Array_ ? $mappedItems->items : $mappedItems; + + // the key here should contain the named argument + return $this->namedArgsFactory->createFromValues($values); + } + + /** + * @param string[] $classReferencedFields + */ + private function mapClassReferences(Expr|string $expr, array $classReferencedFields): void + { + if (! $expr instanceof Array_) { + return; + } + + foreach ($expr->items as $arrayItem) { + if (! $arrayItem instanceof ArrayItem) { + continue; + } + + if (! $arrayItem->key instanceof String_) { + continue; + } + + if (! in_array($arrayItem->key->value, $classReferencedFields)) { + continue; + } + + if ($arrayItem->value instanceof ClassConstFetch) { + continue; + } + + if (! $arrayItem->value instanceof String_) { + continue; + } + + if ($arrayItem->value->value === '') { + continue; + } + + $arrayItem->value = new ClassConstFetch(new FullyQualified($arrayItem->value->value), 'class'); + } + } +} diff --git a/src/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php b/src/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php new file mode 100644 index 00000000000..05c62bc3c72 --- /dev/null +++ b/src/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php @@ -0,0 +1,270 @@ +getValues(); + + $values = $this->removeItems($values, $nestedAnnotationToAttribute); + + $args = $this->createArgsFromItems($values); + + $args = $this->attributeArrayNameInliner->inlineArrayToArgs($args); + + $attributeName = $this->attributeNameFactory->create( + $nestedAnnotationToAttribute, + $doctrineAnnotationTagValueNode, + $uses + ); + + $attribute = new Attribute($attributeName, $args); + return new AttributeGroup([$attribute]); + } + + /** + * @return AttributeGroup[] + */ + public function createNested( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, + NestedAnnotationToAttribute $nestedAnnotationToAttribute, + ): array { + $attributeGroups = []; + + if ($nestedAnnotationToAttribute->hasExplicitParameters()) { + return $this->createFromExplicitProperties( + $nestedAnnotationToAttribute, + $doctrineAnnotationTagValueNode + ); + } + + $nestedAnnotationPropertyToAttributeClass = $nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses()[0]; + + foreach ($doctrineAnnotationTagValueNode->values as $arrayItemNode) { + $nestedDoctrineAnnotationTagValueNode = $arrayItemNode->value; + if (! $nestedDoctrineAnnotationTagValueNode instanceof CurlyListNode) { + continue; + } + + foreach ($nestedDoctrineAnnotationTagValueNode->values as $nestedArrayItemNode) { + if (! $nestedArrayItemNode->value instanceof DoctrineAnnotationTagValueNode) { + continue; + } + + $attributeArgs = $this->createAttributeArgs($nestedArrayItemNode->value); + + $originalIdentifier = $doctrineAnnotationTagValueNode->identifierTypeNode->name; + + $attributeName = $this->resolveAliasedAttributeName( + $originalIdentifier, + $nestedAnnotationPropertyToAttributeClass + ); + + $attribute = new Attribute($attributeName, $attributeArgs); + $attributeGroups[] = new AttributeGroup([$attribute]); + } + } + + return $attributeGroups; + } + + /** + * @return list + */ + private function createAttributeArgs( + DoctrineAnnotationTagValueNode $nestedDoctrineAnnotationTagValueNode + ): array { + $args = $this->createArgsFromItems($nestedDoctrineAnnotationTagValueNode->getValues()); + + return $this->attributeArrayNameInliner->inlineArrayToArgs($args); + } + + /** + * @param ArrayItemNode[] $arrayItemNodes + * @return list + */ + private function createArgsFromItems(array $arrayItemNodes): array + { + $arrayItemNodes = $this->annotationToAttributeMapper->map($arrayItemNodes); + + $values = $arrayItemNodes instanceof Array_ ? $arrayItemNodes->items : $arrayItemNodes; + + return $this->namedArgsFactory->createFromValues($values); + } + + /** + * @todo improve this hardcoded approach later + */ + private function resolveAliasedAttributeName( + string $originalIdentifier, + AnnotationPropertyToAttributeClass $annotationPropertyToAttributeClass + ): FullyQualified|Name { + /** @var string $shortDoctrineAttributeName */ + $shortDoctrineAttributeName = Strings::after( + $annotationPropertyToAttributeClass->getAttributeClass(), + '\\', + -1 + ); + + if (str_starts_with($originalIdentifier, '@ORM')) { + // or alias + return new Name('ORM\\' . $shortDoctrineAttributeName); + } + + // short alias + if (! str_contains($originalIdentifier, '\\')) { + return new Name($shortDoctrineAttributeName); + } + + return new FullyQualified($annotationPropertyToAttributeClass->getAttributeClass()); + } + + /** + * @param ArrayItemNode[] $arrayItemNodes + * @return ArrayItemNode[] + */ + private function removeItems( + array $arrayItemNodes, + NestedAnnotationToAttribute $nestedAnnotationToAttribute + ): array { + foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $annotationPropertyToAttributeClass) { + foreach ($arrayItemNodes as $key => $arrayItemNode) { + if ($arrayItemNode->key !== $annotationPropertyToAttributeClass->getAnnotationProperty()) { + continue; + } + + unset($arrayItemNodes[$key]); + } + } + + return $arrayItemNodes; + } + + /** + * @return AttributeGroup[] + */ + private function createFromExplicitProperties( + NestedAnnotationToAttribute $nestedAnnotationToAttribute, + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode + ): array { + $attributeGroups = []; + + foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $annotationPropertyToAttributeClass) { + /** @var string $annotationProperty */ + $annotationProperty = $annotationPropertyToAttributeClass->getAnnotationProperty(); + + $nestedArrayItemNode = $doctrineAnnotationTagValueNode->getValue($annotationProperty); + if (! $nestedArrayItemNode instanceof ArrayItemNode) { + continue; + } + + if (! $nestedArrayItemNode->value instanceof CurlyListNode) { + throw new ShouldNotHappenException(); + } + + foreach ($nestedArrayItemNode->value->getValues() as $arrayItemNode) { + $nestedDoctrineAnnotationTagValueNode = $arrayItemNode->value; + + if (! $nestedDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { + Assert::string($nestedDoctrineAnnotationTagValueNode); + + $match = Strings::match( + $nestedDoctrineAnnotationTagValueNode, + DoctrineAnnotationDecorator::LONG_ANNOTATION_REGEX + ); + + if (! isset($match['class_name'])) { + throw new ShouldNotHappenException(); + } + + $identifierTypeNode = new IdentifierTypeNode($match['class_name']); + $identifierTypeNode->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $match['class_name']); + + $annotationContent = $match['annotation_content'] ?? ''; + $nestedTokenIterator = $this->tokenIteratorFactory->create($annotationContent); + + // mimics doctrine behavior just in phpdoc-parser syntax :) + // https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L742 + $values = $this->staticDoctrineAnnotationParser->resolveAnnotationMethodCall( + $nestedTokenIterator, + new Nop() + ); + + $nestedDoctrineAnnotationTagValueNode = new DoctrineAnnotationTagValueNode( + $identifierTypeNode, + $match['annotation_content'] ?? '', + $values + ); + } + + $attributeArgs = $this->createAttributeArgs($nestedDoctrineAnnotationTagValueNode); + + $originalIdentifier = $nestedDoctrineAnnotationTagValueNode->identifierTypeNode->name; + + $attributeName = $this->resolveAliasedAttributeName( + $originalIdentifier, + $annotationPropertyToAttributeClass + ); + + if ($annotationPropertyToAttributeClass->doesNeedNewImport() && count( + $attributeName->getParts() + ) === 1) { + $attributeName->setAttribute( + AttributeKey::EXTRA_USE_IMPORT, + $annotationPropertyToAttributeClass->getAttributeClass() + ); + } + + $attribute = new Attribute($attributeName, $attributeArgs); + $attributeGroups[] = new AttributeGroup([$attribute]); + } + } + + return $attributeGroups; + } +} diff --git a/src/PhpAttribute/UseAliasNameMatcher.php b/src/PhpAttribute/UseAliasNameMatcher.php new file mode 100644 index 00000000000..2f87ed87197 --- /dev/null +++ b/src/PhpAttribute/UseAliasNameMatcher.php @@ -0,0 +1,79 @@ +uses as $useUse) { + // we need to use original use statement + $originalUseUseNode = $useUse->getAttribute(AttributeKey::ORIGINAL_NODE); + if (! $originalUseUseNode instanceof UseItem) { + continue; + } + + if (! $originalUseUseNode->alias instanceof Identifier) { + continue; + } + + $alias = $originalUseUseNode->alias->toString(); + if (! str_starts_with($shortAnnotationName, $alias)) { + continue; + } + + $fullyQualifiedAnnotationName = $originalUseUseNode->name->toString() . ltrim( + $shortAnnotationName, + $alias + ); + if ($fullyQualifiedAnnotationName !== $annotationToAttribute->getTag()) { + continue; + } + + $annotationParts = explode('\\', $fullyQualifiedAnnotationName); + $attributeParts = explode('\\', $annotationToAttribute->getAttributeClass()); + + // requirement for matching single part rename + if (count($annotationParts) !== count($attributeParts)) { + continue; + } + + // now we are matching correct contact and old and new have the same number of parts + $useImportPartCount = substr_count($originalUseUseNode->name->toString(), '\\') + 1; + $newAttributeImportPart = array_slice($attributeParts, 0, $useImportPartCount); + $newAttributeImport = implode('\\', $newAttributeImportPart); + + $shortNamePartCount = count($attributeParts) - $useImportPartCount; + + // +1, to remove the alias part + $attributeParts = array_slice($attributeParts, -$shortNamePartCount); + $shortAttributeName = $alias . '\\' . implode('\\', $attributeParts); + + return new UseAliasMetadata($shortAttributeName, $newAttributeImport, $useUse); + } + } + + return null; + } +} diff --git a/src/PhpAttribute/ValueObject/UseAliasMetadata.php b/src/PhpAttribute/ValueObject/UseAliasMetadata.php new file mode 100644 index 00000000000..74ae032466d --- /dev/null +++ b/src/PhpAttribute/ValueObject/UseAliasMetadata.php @@ -0,0 +1,32 @@ +shortAttributeName; + } + + public function getUseImportName(): string + { + return $this->useImportName; + } + + public function getUseUse(): UseItem + { + return $this->useItem; + } +} diff --git a/src/PhpDocParser/NodeTraverser/SimpleCallableNodeTraverser.php b/src/PhpDocParser/NodeTraverser/SimpleCallableNodeTraverser.php new file mode 100644 index 00000000000..1c19421e2e2 --- /dev/null +++ b/src/PhpDocParser/NodeTraverser/SimpleCallableNodeTraverser.php @@ -0,0 +1,43 @@ +traverse($nodes); + } +} diff --git a/src/PhpDocParser/NodeVisitor/CallableNodeVisitor.php b/src/PhpDocParser/NodeVisitor/CallableNodeVisitor.php new file mode 100644 index 00000000000..6a26408588d --- /dev/null +++ b/src/PhpDocParser/NodeVisitor/CallableNodeVisitor.php @@ -0,0 +1,47 @@ +callable = $callable; + } + + /** + * @return NodeVisitor::*|Node|null|Node[] + */ + public function enterNode(Node $node): int|Node|null|array + { + $originalNode = $node; + + $callable = $this->callable; + + /** @var NodeVisitor::*|Node|null|Node[] $newNode */ + $newNode = $callable($node); + + if ($originalNode instanceof Stmt && $newNode instanceof Expr) { + return new Expression($newNode); + } + + return $newNode; + } +} diff --git a/src/PhpDocParser/PhpDocParser/Contract/PhpDocNodeVisitorInterface.php b/src/PhpDocParser/PhpDocParser/Contract/PhpDocNodeVisitorInterface.php new file mode 100644 index 00000000000..c2b0defd061 --- /dev/null +++ b/src/PhpDocParser/PhpDocParser/Contract/PhpDocNodeVisitorInterface.php @@ -0,0 +1,24 @@ +phpDocNodeVisitors[] = $phpDocNodeVisitor; + } + + public function traverse(Node $node): void + { + foreach ($this->phpDocNodeVisitors as $phpDocNodeVisitor) { + $phpDocNodeVisitor->beforeTraverse($node); + } + + $node = $this->traverseNode($node); + + foreach ($this->phpDocNodeVisitors as $phpDocNodeVisitor) { + $phpDocNodeVisitor->afterTraverse($node); + } + } + + /** + * @param callable(Node $node): (int|null|Node) $callable + */ + public function traverseWithCallable(Node $node, string $docContent, callable $callable): void + { + $callablePhpDocNodeVisitor = new CallablePhpDocNodeVisitor($callable, $docContent); + $this->addPhpDocNodeVisitor($callablePhpDocNodeVisitor); + + $this->traverse($node); + } + + /** + * @template TNode of Node + * @param TNode $node + * @return TNode + */ + private function traverseNode(Node $node): Node + { + $objectPublicPropertiesToValues = get_object_vars($node); + $subNodeNames = array_keys($objectPublicPropertiesToValues); + + foreach ($subNodeNames as $subNodeName) { + $subNode = &$node->{$subNodeName}; + + if (\is_array($subNode)) { + $subNode = $this->traverseArray($subNode); + } elseif ($subNode instanceof Node) { + $breakVisitorIndex = null; + $traverseChildren = true; + + foreach ($this->phpDocNodeVisitors as $visitorIndex => $phpDocNodeVisitor) { + $return = $phpDocNodeVisitor->enterNode($subNode); + + if ($return !== null) { + if ($return instanceof Node) { + $subNode = $return; + } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { + $traverseChildren = false; + } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + $traverseChildren = false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = true; + } elseif ($return === self::NODE_REMOVE) { + unset($subNode); + continue 2; + } else { + throw new InvalidTraverseException( + 'enterNode() returned invalid value of type ' . gettype($return) + ); + } + } + } + + if ($traverseChildren) { + $subNode = $this->traverseNode($subNode); + if ($this->stopTraversal) { + break; + } + } + + foreach ($this->phpDocNodeVisitors as $visitorIndex => $phpDocNodeVisitor) { + $phpDocNodeVisitor->leaveNode($subNode); + + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } + } + + return $node; + } + + /** + * @param array $nodes + * @return array + */ + private function traverseArray(array $nodes): array + { + foreach ($nodes as $key => &$node) { + // can be string or something else + if (! $node instanceof Node) { + continue; + } + + $traverseChildren = true; + $breakVisitorIndex = null; + + foreach ($this->phpDocNodeVisitors as $visitorIndex => $phpDocNodeVisitor) { + $return = $phpDocNodeVisitor->enterNode($node); + + if ($return !== null) { + if ($return instanceof Node) { + $node = $return; + } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { + $traverseChildren = false; + } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + $traverseChildren = false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = true; + } elseif ($return === self::NODE_REMOVE) { + // remove node + unset($nodes[$key]); + continue 2; + } else { + throw new InvalidTraverseException('enterNode() returned invalid value of type ' . gettype( + $return + )); + } + } + } + + // should traverse node children's properties? + if ($traverseChildren) { + $node = $this->traverseNode($node); + if ($this->stopTraversal) { + break; + } + } + + foreach ($this->phpDocNodeVisitors as $visitorIndex => $phpDocNodeVisitor) { + $return = $phpDocNodeVisitor->leaveNode($node); + + if ($return !== null) { + if ($return instanceof Node) { + $node = $return; + } elseif (\is_array($return)) { + $doNodes[] = [$key, $return]; + break; + } elseif ($return === self::NODE_REMOVE) { + $doNodes[] = [$key, []]; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = true; + break 2; + } else { + throw new InvalidTraverseException( + 'leaveNode() returned invalid value of type ' . gettype($return) + ); + } + } + + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } + + return $nodes; + } +} diff --git a/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/AbstractPhpDocNodeVisitor.php b/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/AbstractPhpDocNodeVisitor.php new file mode 100644 index 00000000000..1f442f98baa --- /dev/null +++ b/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/AbstractPhpDocNodeVisitor.php @@ -0,0 +1,35 @@ +callable = $callable; + } + + public function enterNode(Node $node): int|Node|null + { + $callable = $this->callable; + return $callable($node, $this->docContent); + } +} diff --git a/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/CloningPhpDocNodeVisitor.php b/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/CloningPhpDocNodeVisitor.php new file mode 100644 index 00000000000..e51e7e528ec --- /dev/null +++ b/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/CloningPhpDocNodeVisitor.php @@ -0,0 +1,28 @@ +hasAttribute(PhpDocAttributeKey::ORIG_NODE)) { + $clonedNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, $node); + } + + return $clonedNode; + } +} diff --git a/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/ParentConnectingPhpDocNodeVisitor.php b/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/ParentConnectingPhpDocNodeVisitor.php new file mode 100644 index 00000000000..b12645fba2b --- /dev/null +++ b/src/PhpDocParser/PhpDocParser/PhpDocNodeVisitor/ParentConnectingPhpDocNodeVisitor.php @@ -0,0 +1,49 @@ +stack = [$node]; + } + + public function enterNode(Node $node): Node + { + if ($this->stack !== []) { + $parentNode = $this->stack[count($this->stack) - 1]; + $node->setAttribute(PhpDocAttributeKey::PARENT, $parentNode); + } + + $this->stack[] = $node; + + return $node; + } + + /** + * @return null|int|\PhpParser\Node|Node[] Replacement node (or special return + */ + public function leaveNode(Node $node): int|\PhpParser\Node|array|null + { + array_pop($this->stack); + return null; + } +} diff --git a/src/PhpDocParser/PhpDocParser/ValueObject/PhpDocAttributeKey.php b/src/PhpDocParser/PhpDocParser/ValueObject/PhpDocAttributeKey.php new file mode 100644 index 00000000000..4ab931c5325 --- /dev/null +++ b/src/PhpDocParser/PhpDocParser/ValueObject/PhpDocAttributeKey.php @@ -0,0 +1,12 @@ +> + * @var array */ - private array $classMethodsByClassAndMethod = []; - - /** - * Parsing files is very heavy performance, so this will help to leverage it - * The value can be also null, as the method might not exist in the class. - * - * @var array> - */ - private array $functionsByName = []; - - /** - * Parsing files is very heavy performance, so this will help to leverage it - * The value can be also null, as the method might not exist in the class. - * - * @var array - */ - private array $classLikesByName = []; + private array $parsedFileNodes = []; public function __construct( - private Parser $parser, - private SmartFileSystem $smartFileSystem, - private NodeFinder $nodeFinder, - private NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator, - private BetterNodeFinder $betterNodeFinder, - private NodeNameResolver $nodeNameResolver, - private ReflectionProvider $reflectionProvider, - private ReflectionResolver $reflectionResolver, - private NodeTypeResolver $nodeTypeResolver, + private readonly RectorParser $rectorParser, + private readonly NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator, + private readonly NodeNameResolver $nodeNameResolver, + private readonly ReflectionProvider $reflectionProvider, + private readonly NodeTypeResolver $nodeTypeResolver, + private readonly MethodReflectionResolver $methodReflectionResolver, + private readonly BetterNodeFinder $betterNodeFinder, ) { } - public function resolveClassFromName(string $className): Class_ | Trait_ | Interface_ | null + /** + * @api downgrade + */ + public function resolveClassFromName(string $className): Class_ | Trait_ | Interface_ | Enum_ | null { if (! $this->reflectionProvider->hasClass($className)) { return null; } $classReflection = $this->reflectionProvider->getClass($className); - return $this->resolveClassFromClassReflection($classReflection, $className); - } - - public function resolveClassFromObjectType( - TypeWithClassName $typeWithClassName - ): Class_ | Trait_ | Interface_ | null { - return $this->resolveClassFromName($typeWithClassName->getClassName()); + return $this->resolveClassFromClassReflection($classReflection); } public function resolveClassMethodFromMethodReflection(MethodReflection $methodReflection): ?ClassMethod { $classReflection = $methodReflection->getDeclaringClass(); - - if (isset($this->classMethodsByClassAndMethod[$classReflection->getName()][$methodReflection->getName()])) { - return $this->classMethodsByClassAndMethod[$classReflection->getName()][$methodReflection->getName()]; - } - $fileName = $classReflection->getFileName(); - // probably native PHP method → un-parseable - if ($fileName === false) { - return null; - } - - $fileContent = $this->smartFileSystem->readFile($fileName); - if (! is_string($fileContent)) { - // avoids parsing again falsy file - $this->classMethodsByClassAndMethod[$classReflection->getName()][$methodReflection->getName()] = null; - return null; - } - $nodes = $this->parseFileNameToDecoratedNodes($fileName); - if ($nodes === null) { - return null; - } + $classLikeName = $classReflection->getName(); + $methodName = $methodReflection->getName(); - $class = $this->nodeFinder->findFirstInstanceOf($nodes, Class_::class); - if (! $class instanceof Class_) { - // avoids looking for a class in a file where is not present - $this->classMethodsByClassAndMethod[$classReflection->getName()][$methodReflection->getName()] = null; - return null; - } - - $classMethod = $class->getMethod($methodReflection->getName()); - $this->classMethodsByClassAndMethod[$classReflection->getName()][$methodReflection->getName()] = $classMethod; + /** @var ClassMethod|null $classMethod */ + $classMethod = null; + $this->betterNodeFinder->findFirst( + $nodes, + function (Node $node) use ($classLikeName, $methodName, &$classMethod): bool { + if (! $node instanceof ClassLike) { + return false; + } + + if (! $this->nodeNameResolver->isName($node, $classLikeName)) { + return false; + } + + $method = $node->getMethod($methodName); + if ($method instanceof ClassMethod) { + $classMethod = $method; + return true; + } + + return false; + } + ); return $classMethod; } public function resolveClassMethodOrFunctionFromCall( - FuncCall | StaticCall | MethodCall $call, - Scope $scope + FuncCall | StaticCall | MethodCall | New_ | NullsafeMethodCall $call ): ClassMethod | Function_ | null { if ($call instanceof FuncCall) { - return $this->resolveFunctionFromFuncCall($call, $scope); + return $this->resolveFunctionFromFuncCall($call); } return $this->resolveClassMethodFromCall($call); } - public function resolveFunctionFromFunctionReflection(PhpFunctionReflection $phpFunctionReflection): ?Function_ + public function resolveFunctionFromFunctionReflection(FunctionReflection $functionReflection): ?Function_ { - if (isset($this->functionsByName[$phpFunctionReflection->getName()])) { - return $this->functionsByName[$phpFunctionReflection->getName()]; - } - - $fileName = $phpFunctionReflection->getFileName(); - if ($fileName === false) { - return null; - } - - $fileContent = $this->smartFileSystem->readFile($fileName); - if (! is_string($fileContent)) { - // to avoid parsing missing function again - $this->functionsByName[$phpFunctionReflection->getName()] = null; + if (! $functionReflection instanceof PhpFunctionReflection) { return null; } + $fileName = $functionReflection->getFileName(); $nodes = $this->parseFileNameToDecoratedNodes($fileName); - if ($nodes === null) { - return null; - } - /** @var Function_[] $functions */ - $functions = $this->nodeFinder->findInstanceOf($nodes, Function_::class); - foreach ($functions as $function) { - if (! $this->nodeNameResolver->isName($function, $phpFunctionReflection->getName())) { - continue; - } + $functionName = $functionReflection->getName(); - // to avoid parsing missing function again - $this->functionsByName[$phpFunctionReflection->getName()] = $function; - - return $function; - } + /** @var Function_|null $functionNode */ + $functionNode = $this->betterNodeFinder->findFirst( + $nodes, + function (Node $node) use ($functionName): bool { + if (! $node instanceof Function_) { + return false; + } - // to avoid parsing missing function again - $this->functionsByName[$phpFunctionReflection->getName()] = null; + return $this->nodeNameResolver->isName($node, $functionName); + } + ); - return null; + return $functionNode; } /** @@ -194,7 +152,7 @@ public function resolveFunctionFromFunctionReflection(PhpFunctionReflection $php */ public function resolveClassMethod(string $className, string $methodName): ?ClassMethod { - $methodReflection = $this->reflectionResolver->resolveMethodReflection($className, $methodName, null); + $methodReflection = $this->methodReflectionResolver->resolveMethodReflection($className, $methodName, null); if (! $methodReflection instanceof MethodReflection) { return null; } @@ -208,20 +166,27 @@ public function resolveClassMethod(string $className, string $methodName): ?Clas return $classMethod; } - public function resolveClassMethodFromMethodCall(MethodCall $methodCall): ?ClassMethod + public function resolveClassMethodFromCall(MethodCall | StaticCall | NullsafeMethodCall | New_ $call): ?ClassMethod { - return $this->resolveClassMethodFromCall($methodCall); - } + if ($call instanceof New_) { + if ($call->class instanceof Class_) { + return null; + } - public function resolveClassMethodFromCall(MethodCall | StaticCall $call): ?ClassMethod - { - if ($call instanceof MethodCall) { - $callerStaticType = $this->nodeTypeResolver->resolve($call->var); - } else { - $callerStaticType = $this->nodeTypeResolver->resolve($call->class); + $className = $this->nodeNameResolver->getName($call->class); + if ($className === null) { + return null; + } + + return $this->resolveClassMethod($className, MethodName::CONSTRUCT); } - if (! $callerStaticType instanceof TypeWithClassName) { + $callerStaticType = ($call instanceof MethodCall || $call instanceof NullsafeMethodCall) + ? $this->nodeTypeResolver->getType($call->var) + : $this->nodeTypeResolver->getType($call->class); + + $className = ClassNameFromObjectTypeResolver::resolve($callerStaticType); + if ($className === null) { return null; } @@ -230,54 +195,33 @@ public function resolveClassMethodFromCall(MethodCall | StaticCall $call): ?Clas return null; } - return $this->resolveClassMethod($callerStaticType->getClassName(), $methodName); + return $this->resolveClassMethod($className, $methodName); } public function resolveClassFromClassReflection( - ClassReflection $classReflection, - string $className - ): Trait_ | Class_ | Interface_ | null { + ClassReflection $classReflection + ): Trait_ | Class_ | Interface_ | Enum_ | null { if ($classReflection->isBuiltin()) { return null; } - if (isset($this->classLikesByName[$classReflection->getName()])) { - return $this->classLikesByName[$classReflection->getName()]; - } - $fileName = $classReflection->getFileName(); - - // probably internal class - if ($fileName === false) { - // avoid parsing falsy-file again - $this->classLikesByName[$classReflection->getName()] = null; - return null; - } - - $fileContent = $this->smartFileSystem->readFile($fileName); - - $nodes = $this->parser->parse($fileContent); - if ($nodes === null) { - // avoid parsing falsy-file again - $this->classLikesByName[$classReflection->getName()] = null; - return null; - } - - /** @var array $classLikes */ - $classLikes = $this->betterNodeFinder->findInstanceOf($nodes, ClassLike::class); - - $reflectionClassName = $classReflection->getName(); - foreach ($classLikes as $classLike) { - if ($reflectionClassName !== $className) { - continue; + $stmts = $this->parseFileNameToDecoratedNodes($fileName); + $className = $classReflection->getName(); + + /** @var Class_|Trait_|Interface_|Enum_|null $classLike */ + $classLike = $this->betterNodeFinder->findFirst( + $stmts, + function (Node $node) use ($className): bool { + if (! $node instanceof ClassLike) { + return false; + } + + return $this->nodeNameResolver->isName($node, $className); } + ); - $this->classLikesByName[$classReflection->getName()] = $classLike; - return $classLike; - } - - $this->classLikesByName[$classReflection->getName()] = null; - return null; + return $classLike; } /** @@ -285,65 +229,115 @@ public function resolveClassFromClassReflection( */ public function parseClassReflectionTraits(ClassReflection $classReflection): array { + /** @var ClassReflection[] $classLikes */ $classLikes = $classReflection->getTraits(true); $traits = []; foreach ($classLikes as $classLike) { $fileName = $classLike->getFileName(); - if (! $fileName) { - continue; - } - $nodes = $this->parseFileNameToDecoratedNodes($fileName); - if ($nodes === null) { - continue; - } + $traitName = $classLike->getName(); - /** @var Trait_|null $trait */ - $trait = $this->betterNodeFinder->findFirst( + $traitNode = $this->betterNodeFinder->findFirst( $nodes, - fn (Node $node): bool => $node instanceof Trait_ && $this->nodeNameResolver->isName( - $node, - $classLike->getName() - ) + function (Node $node) use ($traitName): bool { + if (! $node instanceof Trait_) { + return false; + } + + return $this->nodeNameResolver->isName($node, $traitName); + } ); - if (! $trait instanceof Trait_) { + if (! $traitNode instanceof Trait_) { continue; } - $traits[] = $trait; + $traits[] = $traitNode; } return $traits; } public function resolvePropertyFromPropertyReflection( - ReflectionProperty $reflectionProperty + PhpPropertyReflection $phpPropertyReflection ): Property | Param | null { - $reflectionClass = $reflectionProperty->getDeclaringClass(); - - $fileName = $reflectionClass->getFileName(); - if ($fileName === false) { - return null; - } + $classReflection = $phpPropertyReflection->getDeclaringClass(); + $fileName = $classReflection->getFileName(); $nodes = $this->parseFileNameToDecoratedNodes($fileName); - if ($nodes === null) { + if ($nodes === []) { return null; } - $desiredPropertyName = $reflectionProperty->name; + $nativeReflectionProperty = $phpPropertyReflection->getNativeReflection(); + $desiredClassName = $classReflection->getName(); + $desiredPropertyName = $nativeReflectionProperty->getName(); + + $propertyNode = null; + $this->betterNodeFinder->findFirst( + $nodes, + function (Node $node) use ($desiredClassName, $desiredPropertyName, &$propertyNode): bool { + if (! $node instanceof ClassLike) { + return false; + } + + if (! $this->nodeNameResolver->isName($node, $desiredClassName)) { + return false; + } - /** @var Property[] $properties */ - $properties = $this->betterNodeFinder->findInstanceOf($nodes, Property::class); - foreach ($properties as $property) { - if ($this->nodeNameResolver->isName($property, $desiredPropertyName)) { - return $property; + $property = $node->getProperty($desiredPropertyName); + if ($property instanceof Property) { + $propertyNode = $property; + return true; + } + + return false; } + ); + + if ($propertyNode instanceof Property) { + return $propertyNode; } // promoted property - return $this->findPromotedPropertyByName($nodes, $desiredPropertyName); + return $this->findPromotedPropertyByName($nodes, $desiredClassName, $desiredPropertyName); + } + + /** + * @return Stmt[] + */ + public function parseFileNameToDecoratedNodes(?string $fileName): array + { + // probably native PHP → un-parseable + if ($fileName === null) { + return []; + } + + if (isset($this->parsedFileNodes[$fileName])) { + return $this->parsedFileNodes[$fileName]; + } + + try { + $stmts = $this->rectorParser->parseFile($fileName); + } catch (Throwable $throwable) { + /** + * phpstan.phar contains jetbrains/phpstorm-stubs which the code is not downgraded + * that if read from lower php < 8.1 may cause crash + * + * @see https://github.com/rectorphp/rector/issues/8193 on php 8.0 + * @see https://github.com/rectorphp/rector/issues/8145 on php 7.4 + */ + if (str_contains($fileName, 'phpstan.phar')) { + return []; + } + + throw $throwable; + } + + return $this->parsedFileNodes[$fileName] = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile( + $fileName, + $stmts + ); } private function locateClassMethodInTrait(string $methodName, MethodReflection $methodReflection): ?ClassMethod @@ -354,73 +348,75 @@ private function locateClassMethodInTrait(string $methodName, MethodReflection $ /** @var ClassMethod|null $classMethod */ $classMethod = $this->betterNodeFinder->findFirst( $traits, - fn (Node $node): bool => $node instanceof ClassMethod && $this->nodeNameResolver->isName($node, $methodName) + function (Node $node) use ($methodName): bool { + if (! $node instanceof ClassMethod) { + return false; + } + + return $this->nodeNameResolver->isName($node, $methodName); + } ); - $this->classMethodsByClassAndMethod[$classReflection->getName()][$methodName] = $classMethod; return $classMethod; } /** - * @return Stmt[]|null + * @param Stmt[] $stmts */ - private function parseFileNameToDecoratedNodes(string $fileName): ?array - { - $fileContent = $this->smartFileSystem->readFile($fileName); - $nodes = $this->parser->parse($fileContent); - if ($nodes === null) { - return null; - } - - $smartFileInfo = new SmartFileInfo($fileName); - $file = new File($smartFileInfo, $smartFileInfo->getContents()); - - return $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($file, $nodes); - } - - /** - * @param Stmt[] $nodes - */ - private function findPromotedPropertyByName(array $nodes, string $desiredPropertyName): ?Param - { - $class = $this->betterNodeFinder->findFirstInstanceOf($nodes, Class_::class); - if (! $class instanceof Class_) { - return null; - } - - $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); - if (! $constructClassMethod instanceof ClassMethod) { - return null; - } - - foreach ($constructClassMethod->getParams() as $param) { - if ($param->flags === 0) { - continue; + private function findPromotedPropertyByName( + array $stmts, + string $desiredClassName, + string $desiredPropertyName + ): ?Param { + /** @var Param|null $paramNode */ + $paramNode = null; + + $this->betterNodeFinder->findFirst( + $stmts, + function (Node $node) use ($desiredClassName, $desiredPropertyName, &$paramNode): bool { + if (! $node instanceof Class_) { + return false; + } + + if (! $this->nodeNameResolver->isName($node, $desiredClassName)) { + return false; + } + + $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { + return false; + } + + foreach ($constructClassMethod->getParams() as $param) { + if (! $param->isPromoted()) { + continue; + } + + if ($this->nodeNameResolver->isName($param, $desiredPropertyName)) { + $paramNode = $param; + return true; + } + } + + return false; } + ); - if ($this->nodeNameResolver->isName($param, $desiredPropertyName)) { - return $param; - } - } - - return null; + return $paramNode; } - private function resolveFunctionFromFuncCall(FuncCall $funcCall, Scope $scope): ?Function_ + private function resolveFunctionFromFuncCall(FuncCall $funcCall): ?Function_ { if ($funcCall->name instanceof Expr) { return null; } - if (! $this->reflectionProvider->hasFunction($funcCall->name, $scope)) { - return null; - } - - $reflectionFunction = $this->reflectionProvider->getFunction($funcCall->name, $scope); - if (! $reflectionFunction instanceof PhpFunctionReflection) { + $functionName = new Name((string) $this->nodeNameResolver->getName($funcCall)); + if (! $this->reflectionProvider->hasFunction($functionName, null)) { return null; } - return $this->resolveFunctionFromFunctionReflection($reflectionFunction); + $functionReflection = $this->reflectionProvider->getFunction($functionName, null); + return $this->resolveFunctionFromFunctionReflection($functionReflection); } } diff --git a/src/PhpParser/Comparing/ConditionSearcher.php b/src/PhpParser/Comparing/ConditionSearcher.php deleted file mode 100644 index 15612674c2d..00000000000 --- a/src/PhpParser/Comparing/ConditionSearcher.php +++ /dev/null @@ -1,110 +0,0 @@ -else; - - if (! $elseNode instanceof Else_) { - return false; - } - - /** @var Variable $varNode */ - $varNode = $assign->var; - - if (! $this->hasVariableRedeclaration($varNode, $if->stmts)) { - return false; - } - - foreach ($if->elseifs as $elseifNode) { - if (! $this->hasVariableRedeclaration($varNode, $elseifNode->stmts)) { - return false; - } - } - - $isInCond = (bool) $this->betterNodeFinder->findFirst( - $if->cond, - fn (Node $subNode): bool => $this->nodeComparator->areNodesEqual($varNode, $subNode) - ); - - if ($isInCond) { - return false; - } - - return $this->hasVariableRedeclaration($varNode, $elseNode->stmts); - } - - /** - * @param Stmt[] $stmts - */ - private function hasVariableRedeclaration(Variable $variable, array $stmts): bool - { - foreach ($stmts as $stmt) { - if ($this->hasVariableUsedInExpression($variable, $stmt)) { - return false; - } - - if ($this->hasVariableDeclaration($variable, $stmt)) { - return true; - } - } - - return false; - } - - private function hasVariableUsedInExpression(Variable $variable, Stmt $stmt): bool - { - if ($stmt instanceof Expression) { - $node = $stmt->expr instanceof Assign ? $stmt->expr->expr : $stmt->expr; - } else { - $node = $stmt; - } - - return (bool) $this->betterNodeFinder->findFirst( - $node, - fn (Node $subNode): bool => $this->nodeComparator->areNodesEqual($variable, $subNode) - ); - } - - private function hasVariableDeclaration(Variable $variable, Stmt $stmt): bool - { - if (! $stmt instanceof Expression) { - return false; - } - - if (! $stmt->expr instanceof Assign) { - return false; - } - - $assignVar = $stmt->expr->var; - if (! $assignVar instanceof Variable) { - return false; - } - - if ($variable->name !== $assignVar->name) { - return false; - } - - return true; - } -} diff --git a/src/PhpParser/Comparing/NodeComparator.php b/src/PhpParser/Comparing/NodeComparator.php index 2b5206176ad..04ffe9e5006 100644 --- a/src/PhpParser/Comparing/NodeComparator.php +++ b/src/PhpParser/Comparing/NodeComparator.php @@ -2,14 +2,13 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Comparing; +namespace Rector\PhpParser\Comparing; use PhpParser\Node; use Rector\Comments\CommentRemover; -use Rector\Core\PhpParser\Printer\BetterStandardPrinter; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Printer\BetterStandardPrinter; -final class NodeComparator +final readonly class NodeComparator { public function __construct( private CommentRemover $commentRemover, @@ -35,23 +34,36 @@ public function printWithoutComments(Node | array | null $node): string */ public function areNodesEqual(Node | array | null $firstNode, Node | array | null $secondNode): bool { - return $this->printWithoutComments($firstNode) === $this->printWithoutComments($secondNode); + if ($firstNode instanceof Node && ! $secondNode instanceof Node) { + return false; + } + + if (! $firstNode instanceof Node && $secondNode instanceof Node) { + return false; + } + + if (is_array($firstNode) && ! is_array($secondNode)) { + return false; + } + + if (! is_array($secondNode)) { + return $this->printWithoutComments($firstNode) === $this->printWithoutComments($secondNode); + } + + if (is_array($firstNode)) { + return $this->printWithoutComments($firstNode) === $this->printWithoutComments($secondNode); + } + + return false; } /** + * @api * @param Node[] $availableNodes */ public function isNodeEqual(Node $singleNode, array $availableNodes): bool { - // remove comments, only content is relevant - $singleNode = clone $singleNode; - $singleNode->setAttribute(AttributeKey::COMMENTS, null); - foreach ($availableNodes as $availableNode) { - // remove comments, only content is relevant - $availableNode = clone $availableNode; - $availableNode->setAttribute(AttributeKey::COMMENTS, null); - if ($this->areNodesEqual($singleNode, $availableNode)) { return true; } @@ -69,6 +81,13 @@ public function areSameNode(Node $firstNode, Node $secondNode): bool return true; } + $firstClass = $firstNode::class; + $secondClass = $secondNode::class; + + if ($firstClass !== $secondClass) { + return false; + } + if ($firstNode->getStartTokenPos() !== $secondNode->getStartTokenPos()) { return false; } @@ -77,9 +96,9 @@ public function areSameNode(Node $firstNode, Node $secondNode): bool return false; } - $firstClass = $firstNode::class; - $secondClass = $secondNode::class; + $printFirstNode = $this->betterStandardPrinter->print($firstNode); + $printSecondNode = $this->betterStandardPrinter->print($secondNode); - return $firstClass === $secondClass; + return $printFirstNode === $printSecondNode; } } diff --git a/src/PhpParser/Enum/NodeGroup.php b/src/PhpParser/Enum/NodeGroup.php new file mode 100644 index 00000000000..fce2d1cec8f --- /dev/null +++ b/src/PhpParser/Enum/NodeGroup.php @@ -0,0 +1,93 @@ +> + */ + public const array STMTS_AWARE = [ + Block::class, + Closure::class, + Case_::class, + Catch_::class, + ClassMethod::class, + Do_::class, + Else_::class, + ElseIf_::class, + Finally_::class, + For_::class, + Foreach_::class, + Function_::class, + If_::class, + Namespace_::class, + TryCatch::class, + While_::class, + FileNode::class, + Declare_::class, + ]; + + /** + * @var array> + */ + public const array STMTS_TO_HAVE_NEXT_NEWLINE = [ + ClassMethod::class, + Function_::class, + Property::class, + If_::class, + Foreach_::class, + Do_::class, + While_::class, + For_::class, + ClassConst::class, + TryCatch::class, + Class_::class, + Trait_::class, + Interface_::class, + Switch_::class, + ]; + + public static function isStmtAwareNode(Node $node): bool + { + foreach (self::STMTS_AWARE as $stmtAwareClass) { + if ($node instanceof $stmtAwareClass) { + return true; + } + } + + return false; + } +} diff --git a/src/PhpParser/Node/AssignAndBinaryMap.php b/src/PhpParser/Node/AssignAndBinaryMap.php index f8c5a8b697f..4da7e629619 100644 --- a/src/PhpParser/Node/AssignAndBinaryMap.php +++ b/src/PhpParser/Node/AssignAndBinaryMap.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Node; +namespace Rector\PhpParser\Node; use PhpParser\Node; use PhpParser\Node\Expr; @@ -42,16 +42,14 @@ use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual; use PhpParser\Node\Expr\BooleanNot; use PhpParser\Node\Expr\Cast\Bool_; -use PHPStan\Analyser\Scope; -use PHPStan\Type\BooleanType; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\NodeTypeResolver; final class AssignAndBinaryMap { /** * @var array, class-string> */ - private const BINARY_OP_TO_INVERSE_CLASSES = [ + private const array BINARY_OP_TO_INVERSE_CLASSES = [ Identical::class => NotIdentical::class, NotIdentical::class => Identical::class, Equal::class => NotEqual::class, @@ -63,9 +61,9 @@ final class AssignAndBinaryMap ]; /** - * @var array, class-string> + * @var array, class-string> */ - private const ASSIGN_OP_TO_BINARY_OP_CLASSES = [ + private const array ASSIGN_OP_TO_BINARY_OP_CLASSES = [ AssignBitwiseOr::class => BitwiseOr::class, AssignBitwiseAnd::class => BitwiseAnd::class, AssignBitwiseXor::class => BitwiseXor::class, @@ -81,15 +79,21 @@ final class AssignAndBinaryMap ]; /** - * @var array, class-string> + * @var array, class-string> */ private array $binaryOpToAssignClasses = []; - public function __construct() - { - $this->binaryOpToAssignClasses = array_flip(self::ASSIGN_OP_TO_BINARY_OP_CLASSES); + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver + ) { + /** @var array, class-string> $binaryClassesToAssignOp */ + $binaryClassesToAssignOp = array_flip(self::ASSIGN_OP_TO_BINARY_OP_CLASSES); + $this->binaryOpToAssignClasses = $binaryClassesToAssignOp; } + /** + * @return class-string|null + */ public function getAlternative(Node $node): ?string { $nodeClass = $node::class; @@ -105,6 +109,9 @@ public function getAlternative(Node $node): ?string return null; } + /** + * @return class-string|null + */ public function getInversed(BinaryOp $binaryOp): ?string { $nodeClass = $binaryOp::class; @@ -121,13 +128,9 @@ public function getTruthyExpr(Expr $expr): Expr return $expr; } - $scope = $expr->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return new Bool_($expr); - } - - $type = $scope->getType($expr); - if ($type instanceof BooleanType) { + $exprType = $this->nodeTypeResolver->getType($expr); + // $type = $scope->getType($expr); + if ($exprType->isBoolean()->yes()) { return $expr; } diff --git a/src/PhpParser/Node/BetterNodeFinder.php b/src/PhpParser/Node/BetterNodeFinder.php index fad765049b6..714fee68d93 100644 --- a/src/PhpParser/Node/BetterNodeFinder.php +++ b/src/PhpParser/Node/BetterNodeFinder.php @@ -2,72 +2,38 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Node; +namespace Rector\PhpParser\Node; use PhpParser\Node; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\StaticPropertyFetch; +use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\Yield_; +use PhpParser\Node\Expr\YieldFrom; use PhpParser\Node\FunctionLike; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassLike; -use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\Property; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Return_; use PhpParser\NodeFinder; -use Rector\Core\NodeAnalyzer\ClassAnalyzer; -use Rector\Core\PhpParser\Comparing\NodeComparator; +use PhpParser\NodeVisitor; +use Rector\NodeAnalyzer\ClassAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeNestingScope\ParentScopeFinder; -use Rector\NodeTypeResolver\Node\AttributeKey; -use Symplify\PackageBuilder\Php\TypeChecker; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; use Webmozart\Assert\Assert; /** - * @see \Rector\Core\Tests\PhpParser\Node\BetterNodeFinder\BetterNodeFinderTest + * @see \Rector\Tests\PhpParser\Node\BetterNodeFinder\BetterNodeFinderTest */ -final class BetterNodeFinder +final readonly class BetterNodeFinder { public function __construct( private NodeFinder $nodeFinder, private NodeNameResolver $nodeNameResolver, - private TypeChecker $typeChecker, - private NodeComparator $nodeComparator, private ClassAnalyzer $classAnalyzer, - private ParentScopeFinder $parentScopeFinder + private SimpleCallableNodeTraverser $simpleCallableNodeTraverser ) { } - /** - * @template T of Node - * @param class-string $type - * @return T|null - */ - public function findParentType(Node $node, string $type): ?Node - { - Assert::isAOf($type, Node::class); - - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Node) { - return null; - } - - do { - if (is_a($parent, $type, true)) { - return $parent; - } - - if (! $parent instanceof Node) { - return null; - } - } while ($parent = $parent->getAttribute(AttributeKey::PARENT_NODE)); - - return null; - } - /** * @template T of Node * @param array> $types @@ -79,7 +45,7 @@ public function findInstancesOf(Node | array $nodes, array $types): array $foundInstances = []; foreach ($types as $type) { $currentFoundInstances = $this->findInstanceOf($nodes, $type); - $foundInstances = array_merge($foundInstances, $currentFoundInstances); + $foundInstances = [...$foundInstances, ...$currentFoundInstances]; } return $foundInstances; @@ -100,6 +66,8 @@ public function findInstanceOf(Node | array $nodes, string $type): array * @template T of Node * @param class-string $type * @param Node|Node[] $nodes + * + * @return T|null */ public function findFirstInstanceOf(Node | array $nodes, string $type): ?Node { @@ -109,23 +77,24 @@ public function findFirstInstanceOf(Node | array $nodes, string $type): ?Node /** * @param class-string $type - * @param Node|Node[] $nodes + * @param Node[] $nodes */ - public function hasInstanceOfName(Node | array $nodes, string $type, string $name): bool + public function hasInstanceOfName(array $nodes, string $type, string $name): bool { Assert::isAOf($type, Node::class); return (bool) $this->findInstanceOfName($nodes, $type, $name); } /** - * @param Node|Node[] $nodes + * @param Node[] $nodes */ - public function hasVariableOfName(Node | array $nodes, string $name): bool + public function hasVariableOfName(array $nodes, string $name): bool { - return (bool) $this->findVariableOfName($nodes, $name); + return $this->findVariableOfName($nodes, $name) instanceof Node; } /** + * @api * @param Node|Node[] $nodes * @return Variable|null */ @@ -142,38 +111,20 @@ public function hasInstancesOf(Node | array $nodes, array $types): bool { Assert::allIsAOf($types, Node::class); - foreach ($types as $type) { - $foundNode = $this->nodeFinder->findFirstInstanceOf($nodes, $type); - if (! $foundNode instanceof Node) { - continue; + return (bool) $this->nodeFinder->findFirst($nodes, static function (Node $node) use ($types): bool { + foreach ($types as $type) { + if ($node instanceof $type) { + return true; + } } - return true; - } - - return false; - } - - /** - * @template T of Node - * @param class-string $type - * @param Node|Node[] $nodes - */ - public function findLastInstanceOf(Node | array $nodes, string $type): ?Node - { - Assert::isAOf($type, Node::class); - - $foundInstances = $this->nodeFinder->findInstanceOf($nodes, $type); - if ($foundInstances === []) { - return null; - } - - $lastItemKey = array_key_last($foundInstances); - return $foundInstances[$lastItemKey]; + return false; + }); } /** * @param Node|Node[] $nodes + * @param callable(Node $node): bool $filter * @return Node[] */ public function find(Node | array $nodes, callable $filter): array @@ -182,40 +133,20 @@ public function find(Node | array $nodes, callable $filter): array } /** - * Excludes anonymous classes! - * - * @param Node[]|Node $nodes - * @return ClassLike[] - */ - public function findClassLikes(array | Node $nodes): array - { - return $this->find($nodes, function (Node $node): bool { - if (! $node instanceof ClassLike) { - return false; - } - // skip anonymous classes - return ! ($node instanceof Class_ && $this->classAnalyzer->isAnonymousClass($node)); - }); - } - - /** + * @api symfony * @param Node[] $nodes - * @return ClassLike|null + * @return Class_|null */ public function findFirstNonAnonymousClass(array $nodes): ?Node { - return $this->findFirst($nodes, function (Node $node): bool { - if (! $node instanceof ClassLike) { - return false; - } - - // skip anonymous classes - return ! ($node instanceof Class_ && $this->classAnalyzer->isAnonymousClass($node)); - }); + // skip anonymous classes + return $this->findFirst($nodes, fn (Node $node): bool => + $node instanceof Class_ && ! $this->classAnalyzer->isAnonymousClass($node)); } /** * @param Node|Node[] $nodes + * @param callable(Node $filter): bool $filter */ public function findFirst(Node | array $nodes, callable $filter): ?Node { @@ -223,157 +154,145 @@ public function findFirst(Node | array $nodes, callable $filter): ?Node } /** - * @return Assign|null + * @template T of Node + * @param array>|class-string $types */ - public function findPreviousAssignToExpr(Expr $expr): ?Node + public function hasInstancesOfInFunctionLikeScoped(FunctionLike $functionLike, string|array $types): bool { - return $this->findFirstPrevious($expr, function (Node $node) use ($expr): bool { - if (! $node instanceof Assign) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node->var, $expr); - }); - } - - public function findFirstPreviousOfNode(Node $node, callable $filter): ?Node - { - // move to previous expression - $previousStatement = $node->getAttribute(AttributeKey::PREVIOUS_NODE); - if ($previousStatement !== null) { - $foundNode = $this->findFirst([$previousStatement], $filter); - // we found what we need - if ($foundNode !== null) { - return $foundNode; - } - - return $this->findFirstPreviousOfNode($previousStatement, $filter); + if (is_string($types)) { + $types = [$types]; } - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof FunctionLike) { - return null; - } + $isFoundNode = false; + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + (array) $functionLike->getStmts(), + static function (Node $subNode) use ($types, &$isFoundNode): ?int { + if ($subNode instanceof Class_ || $subNode instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + foreach ($types as $type) { + if ($subNode instanceof $type) { + $isFoundNode = true; + return NodeVisitor::STOP_TRAVERSAL; + } + } - if ($parent instanceof Node) { - return $this->findFirstPreviousOfNode($parent, $filter); - } + return null; + } + ); - return null; + return $isFoundNode; } - public function findFirstPrevious(Node $node, callable $filter): ?Node + /** + * @return Return_[] + */ + public function findReturnsScoped(FunctionLike $functionLike): array { - $node = $node instanceof Expression ? $node : $node->getAttribute(AttributeKey::CURRENT_STATEMENT); - if ($node === null) { - return null; - } + $returns = []; - $foundNode = $this->findFirst([$node], $filter); - // we found what we need - if ($foundNode !== null) { - return $foundNode; - } + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + (array) $functionLike->getStmts(), + function (Node $subNode) use (&$returns): ?int { + if ($subNode instanceof Class_ || $subNode instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } - // move to previous expression - $previousStatement = $node->getAttribute(AttributeKey::PREVIOUS_STATEMENT); - if ($previousStatement !== null) { - return $this->findFirstPrevious($previousStatement, $filter); - } + if ($subNode instanceof Yield_ || $subNode instanceof YieldFrom) { + $returns = []; + return NodeVisitor::STOP_TRAVERSAL; + } - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent === null) { - return null; - } + if ($subNode instanceof Return_) { + $returns[] = $subNode; + } - return $this->findFirstPrevious($parent, $filter); + return null; + } + ); + + return $returns; } /** + * @api to be used + * * @template T of Node - * @param array> $types + * @param Node[] $nodes + * @param class-string|array> $types + * @return T[] */ - public function findFirstPreviousOfTypes(Node $mainNode, array $types): ?Node + public function findInstancesOfScoped(array $nodes, string|array $types): array { - return $this->findFirstPrevious( - $mainNode, - fn (Node $node): bool => $this->typeChecker->isInstanceOf($node, $types) - ); - } + // here verify only pass single nodes as FunctionLike + if (count($nodes) === 1 + && ($nodes[0] instanceof FunctionLike)) { + $nodes = (array) $nodes[0]->getStmts(); + } - public function findFirstNext(Node $node, callable $filter): ?Node - { - $next = $node->getAttribute(AttributeKey::NEXT_NODE); - if ($next instanceof Node) { - if ($next instanceof Return_ && $next->expr === null) { - return null; - } + if (is_string($types)) { + $types = [$types]; + } - $found = $this->findFirst($next, $filter); - if ($found instanceof Node) { - return $found; - } + /** @var T[] $foundNodes */ + $foundNodes = []; - return $this->findFirstNext($next, $filter); - } + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $nodes, + static function (Node $subNode) use ($types, &$foundNodes): ?int { + if ($subNode instanceof Class_ || ($subNode instanceof FunctionLike && ! $subNode instanceof ClassMethod && ! $subNode instanceof ArrowFunction)) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } - $parent = $node->getAttribute(AttributeKey::PARENT_NODE); - if ($parent instanceof Return_ || $parent instanceof FunctionLike) { - return null; - } + foreach ($types as $type) { + if ($subNode instanceof $type) { + $foundNodes[] = $subNode; + return null; + } + } - if ($parent instanceof Node) { - return $this->findFirstNext($parent, $filter); - } + return null; + } + ); - return null; + return $foundNodes; } /** - * @return Expr[] + * @template T of Node + * @param array>|class-string $types + * @return array */ - public function findSameNamedExprs(Expr | Variable | Property | PropertyFetch | StaticPropertyFetch $expr): array + public function findInstancesOfInFunctionLikeScoped(FunctionLike $functionLike, string|array $types): array { - // assign of empty string to something - $scopeNode = $this->parentScopeFinder->find($expr); - if ($scopeNode === null) { - return []; - } - - if ($expr instanceof Variable) { - $exprName = $this->nodeNameResolver->getName($expr); - if ($exprName === null) { - return []; - } - - $variables = $this->findInstancesOf($scopeNode, [Variable::class]); - - return array_filter( - $variables, - fn (Variable $variable): bool => $this->nodeNameResolver->isName($variable, $exprName) - ); - } + return $this->findInstancesOfScoped([$functionLike], $types); + } - if ($expr instanceof Property) { - $singleProperty = $expr->props[0]; - $exprName = $this->nodeNameResolver->getName($singleProperty->name); - } elseif ($expr instanceof StaticPropertyFetch || $expr instanceof PropertyFetch) { - $exprName = $this->nodeNameResolver->getName($expr->name); - } else { - return []; - } + /** + * @param callable(Node $node): bool $filter + */ + public function findFirstInFunctionLikeScoped(FunctionLike $functionLike, callable $filter): ?Node + { + $scopedNode = null; - if ($exprName === null) { - return []; - } + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + (array) $functionLike->getStmts(), + function (Node $subNode) use (&$scopedNode, $filter): ?int { + if (! $filter($subNode)) { + if ($subNode instanceof Class_ || $subNode instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } - $propertyFetches = $this->findInstancesOf($scopeNode, [PropertyFetch::class, StaticPropertyFetch::class]); + return null; + } - return array_filter( - $propertyFetches, - fn (PropertyFetch | StaticPropertyFetch $propertyFetch): bool => - $this->nodeNameResolver->isName($propertyFetch->name, $exprName) + $scopedNode = $subNode; + return NodeVisitor::STOP_TRAVERSAL; + } ); + + return $scopedNode; } /** @@ -385,15 +304,7 @@ private function findInstanceOfName(Node | array $nodes, string $type, string $n { Assert::isAOf($type, Node::class); - $foundInstances = $this->nodeFinder->findInstanceOf($nodes, $type); - foreach ($foundInstances as $foundInstance) { - if (! $this->nodeNameResolver->isName($foundInstance, $name)) { - continue; - } - - return $foundInstance; - } - - return null; + return $this->nodeFinder->findFirst($nodes, fn (Node $node): bool => + $node instanceof $type && $this->nodeNameResolver->isName($node, $name)); } } diff --git a/src/PhpParser/Node/CustomNode/FileWithoutNamespace.php b/src/PhpParser/Node/CustomNode/FileWithoutNamespace.php index 81e30154d00..da4c17f529f 100644 --- a/src/PhpParser/Node/CustomNode/FileWithoutNamespace.php +++ b/src/PhpParser/Node/CustomNode/FileWithoutNamespace.php @@ -2,25 +2,21 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Node\CustomNode; +namespace Rector\PhpParser\Node\CustomNode; -use PhpParser\Node\Stmt; -use PhpParser\NodeAbstract; +use Override; +use Rector\Contract\PhpParser\Node\StmtsAwareInterface; +use Rector\PhpParser\Node\FileNode; /** + * @deprecated Use @see \Rector\PhpParser\Node\FileNode instead + * @api + * * Inspired by https://github.com/phpstan/phpstan-src/commit/ed81c3ad0b9877e6122c79b4afda9d10f3994092 */ -final class FileWithoutNamespace extends NodeAbstract +final class FileWithoutNamespace extends FileNode implements StmtsAwareInterface { - /** - * @param Stmt[] $stmts - */ - public function __construct( - public array $stmts - ) { - parent::__construct(); - } - + #[Override] public function getType(): string { return 'FileWithoutNamespace'; @@ -29,6 +25,7 @@ public function getType(): string /** * @return string[] */ + #[Override] public function getSubNodeNames(): array { return ['stmts']; diff --git a/src/PhpParser/Node/FileNode.php b/src/PhpParser/Node/FileNode.php new file mode 100644 index 00000000000..be23ed4a3b5 --- /dev/null +++ b/src/PhpParser/Node/FileNode.php @@ -0,0 +1,98 @@ +getAttributes() : []; + + parent::__construct($attributes); + } + + /** + * This triggers Printed method with "pFileNode" name + * @see \Rector\PhpParser\Printer\BetterStandardPrinter::pStmt_FileNode() + */ + public function getType(): string + { + return 'Stmt_FileNode'; + } + + /** + * @return array + */ + public function getSubNodeNames(): array + { + return ['stmts']; + } + + public function isNamespaced(): bool + { + foreach ($this->stmts as $stmt) { + if ($stmt instanceof Namespace_) { + return true; + } + } + + return false; + } + + public function getNamespace(): ?Namespace_ + { + /** @var Namespace_[] $namespaces */ + $namespaces = array_filter($this->stmts, static fn (Stmt $stmt): bool => $stmt instanceof Namespace_); + + if (count($namespaces) === 1) { + return current($namespaces); + } + + return null; + } + + /** + * @return array + */ + public function getUsesAndGroupUses(): array + { + $rootNode = $this->getNamespace(); + if (! $rootNode instanceof Namespace_) { + $rootNode = $this; + } + + return array_filter( + $rootNode->stmts, + static fn (Stmt $stmt): bool => $stmt instanceof Use_ || $stmt instanceof GroupUse + ); + } + + /** + * @return Use_[] + */ + public function getUses(): array + { + $rootNode = $this->getNamespace(); + if (! $rootNode instanceof Namespace_) { + $rootNode = $this; + } + + return array_filter($rootNode->stmts, static fn (Stmt $stmt): bool => $stmt instanceof Use_); + } +} diff --git a/src/PhpParser/Node/NodeFactory.php b/src/PhpParser/Node/NodeFactory.php index 3321634ff91..311ac81bac1 100644 --- a/src/PhpParser/Node/NodeFactory.php +++ b/src/PhpParser/Node/NodeFactory.php @@ -2,132 +2,94 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Node; +namespace Rector\PhpParser\Node; +use PhpParser\Builder\Method; +use PhpParser\Builder\Param as ParamBuilder; +use PhpParser\Builder\Property as PropertyBuilder; use PhpParser\BuilderFactory; use PhpParser\BuilderHelpers; +use PhpParser\Modifiers; use PhpParser\Node; use PhpParser\Node\Arg; -use PhpParser\Node\Const_; +use PhpParser\Node\ArrayItem; +use PhpParser\Node\DeclareItem; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BinaryOp\Concat; +use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\Cast; use PhpParser\Node\Expr\ClassConstFetch; -use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\Clone_; use PhpParser\Node\Expr\ConstFetch; -use PhpParser\Node\Expr\Error; use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\New_; +use PhpParser\Node\Expr\NullsafeMethodCall; +use PhpParser\Node\Expr\NullsafePropertyFetch; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticPropertyFetch; +use PhpParser\Node\Expr\UnaryMinus; +use PhpParser\Node\Expr\UnaryPlus; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Param; use PhpParser\Node\Scalar; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; -use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Declare_; use PhpParser\Node\Stmt\Property; -use PhpParser\Node\Stmt\Return_; -use PhpParser\Node\Stmt\Use_; -use PhpParser\Node\Stmt\UseUse; -use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; -use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; -use PHPStan\Reflection\MethodReflection; -use PHPStan\Type\Generic\GenericObjectType; -use PHPStan\Type\MixedType; use PHPStan\Type\Type; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; -use Rector\Core\Configuration\CurrentNodeProvider; -use Rector\Core\Exception\NotImplementedYetException; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\Php\PhpVersionProvider; -use Rector\Core\PhpParser\AstResolver; -use Rector\Core\ValueObject\MethodName; -use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\NodeNameResolver\NodeNameResolver; +use Rector\Enum\ObjectReference; +use Rector\Exception\ShouldNotHappenException; +use Rector\NodeDecorator\PropertyTypeDecorator; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind; +use Rector\Php\PhpVersionProvider; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\PostRector\ValueObject\PropertyMetadata; use Rector\StaticTypeMapper\StaticTypeMapper; -use Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder; -use Symplify\Astral\ValueObject\NodeBuilder\ParamBuilder; -use Symplify\Astral\ValueObject\NodeBuilder\PropertyBuilder; +use Rector\ValueObject\PhpVersionFeature; /** - * @see \Rector\Core\Tests\PhpParser\Node\NodeFactoryTest + * @see \Rector\Tests\PhpParser\Node\NodeFactoryTest */ -final class NodeFactory +final readonly class NodeFactory { - /** - * @var string - */ - private const THIS = 'this'; - - /** - * @var string - */ - private const REFERENCE_PARENT = 'parent'; - - /** - * @var string - */ - private const REFERENCE_SELF = 'self'; - - /** - * @var string - */ - private const REFERENCE_STATIC = 'static'; - - /** - * @var string[] - */ - private const REFERENCES = [self::REFERENCE_STATIC, self::REFERENCE_PARENT, self::REFERENCE_SELF]; + private const string THIS = 'this'; public function __construct( private BuilderFactory $builderFactory, private PhpDocInfoFactory $phpDocInfoFactory, - private PhpVersionProvider $phpVersionProvider, private StaticTypeMapper $staticTypeMapper, - private NodeNameResolver $nodeNameResolver, - private PhpDocTypeChanger $phpDocTypeChanger, - private CurrentNodeProvider $currentNodeProvider, - private AstResolver $reflectionAstResolver, + private PropertyTypeDecorator $propertyTypeDecorator, + private SimpleCallableNodeTraverser $simpleCallableNodeTraverser, + private PhpVersionProvider $phpVersionProvider, ) { } /** - * Creates "SomeClass::CONSTANT" - */ - public function createShortClassConstFetch(string $shortClassName, string $constantName): ClassConstFetch - { - $name = new Name($shortClassName); - return $this->createClassConstFetchFromName($name, $constantName); - } - - /** + * @param string|ObjectReference::* $className * Creates "\SomeClass::CONSTANT" */ public function createClassConstFetch(string $className, string $constantName): ClassConstFetch { - $classNameNode = in_array($className, self::REFERENCES, true) ? new Name( - $className - ) : new FullyQualified($className); - - return $this->createClassConstFetchFromName($classNameNode, $constantName); + $name = $this->createName($className); + return $this->createClassConstFetchFromName($name, $constantName); } /** + * @param string|ObjectReference::* $className * Creates "\SomeClass::class" */ public function createClassConstReference(string $className): ClassConstFetch @@ -163,12 +125,20 @@ public function createArray(array $items): Array_ */ public function createArgs(array $values): array { - $normalizedValues = []; foreach ($values as $key => $value) { - $normalizedValues[$key] = $this->normalizeArgValue($value); + if ($value instanceof ArrayItem) { + $values[$key] = $value->value; + } } - return $this->builderFactory->args($normalizedValues); + return $this->builderFactory->args($values); + } + + public function createDeclaresStrictType(): Declare_ + { + $declareItem = new DeclareItem(new Identifier('strict_types'), new Int_(1)); + + return new Declare_([$declareItem]); } /** @@ -180,56 +150,40 @@ public function createPropertyAssignment(string $propertyName): Assign return $this->createPropertyAssignmentWithExpr($propertyName, $variable); } + /** + * @api + */ public function createPropertyAssignmentWithExpr(string $propertyName, Expr $expr): Assign { $propertyFetch = $this->createPropertyFetch(self::THIS, $propertyName); return new Assign($propertyFetch, $expr); } - /** - * @param mixed $argument - */ - public function createArg($argument): Arg + public function createArg(mixed $argument): Arg { return new Arg(BuilderHelpers::normalizeValue($argument)); } public function createPublicMethod(string $name): ClassMethod { - $methodBuilder = new MethodBuilder($name); - $methodBuilder->makePublic(); + $method = new Method($name); + $method->makePublic(); - return $methodBuilder->getNode(); + return $method->getNode(); } public function createParamFromNameAndType(string $name, ?Type $type): Param { - $paramBuilder = new ParamBuilder($name); + $param = new ParamBuilder($name); - if ($type !== null) { - $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM()); - if ($typeNode !== null) { - $paramBuilder->setType($typeNode); + if ($type instanceof Type) { + $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM); + if ($typeNode instanceof Node) { + $param->setType($typeNode); } } - return $paramBuilder->getNode(); - } - - public function createPublicInjectPropertyFromNameAndType(string $name, ?Type $type): Property - { - $propertyBuilder = new PropertyBuilder($name); - $propertyBuilder->makePublic(); - - $property = $propertyBuilder->getNode(); - - $this->addPropertyType($property, $type); - - // add @inject - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - $phpDocInfo->addPhpDocTagNode(new PhpDocTagNode('@inject', new GenericTagValueNode(''))); - - return $property; + return $param->getNode(); } public function createPrivatePropertyFromNameAndType(string $name, ?Type $type): Property @@ -238,12 +192,13 @@ public function createPrivatePropertyFromNameAndType(string $name, ?Type $type): $propertyBuilder->makePrivate(); $property = $propertyBuilder->getNode(); - $this->addPropertyType($property, $type); + $this->propertyTypeDecorator->decorate($property, $type); return $property; } /** + * @api symfony * @param mixed[] $arguments */ public function createLocalMethodCall(string $method, array $arguments = []): MethodCall @@ -255,68 +210,21 @@ public function createLocalMethodCall(string $method, array $arguments = []): Me /** * @param mixed[] $arguments */ - public function createMethodCall(string | Expr $variable, string $method, array $arguments = []): MethodCall + public function createMethodCall(Expr|string $exprOrVariableName, string $method, array $arguments = []): MethodCall { - if (is_string($variable)) { - $variable = new Variable($variable); - } - - if ($variable instanceof PropertyFetch) { - $variable = new PropertyFetch($variable->var, $variable->name); - } - - if ($variable instanceof StaticPropertyFetch) { - $variable = new StaticPropertyFetch($variable->class, $variable->name); - } - - if ($variable instanceof MethodCall) { - $variable = new MethodCall($variable->var, $variable->name, $variable->args); - } - - return $this->builderFactory->methodCall($variable, $method, $arguments); + $callerExpr = $this->createMethodCaller($exprOrVariableName); + return $this->builderFactory->methodCall($callerExpr, $method, $arguments); } - public function createPropertyFetch(string | Expr $variable, string $property): PropertyFetch + public function createPropertyFetch(string | Expr $variableNameOrExpr, string $property): PropertyFetch { - if (is_string($variable)) { - $variable = new Variable($variable); - } - - return $this->builderFactory->propertyFetch($variable, $property); + $fetcherExpr = is_string($variableNameOrExpr) ? new Variable($variableNameOrExpr) : $variableNameOrExpr; + return $this->builderFactory->propertyFetch($fetcherExpr, $property); } /** - * @param Param[] $params + * @api doctrine */ - public function createParentConstructWithParams(array $params): StaticCall - { - return new StaticCall( - new Name(self::REFERENCE_PARENT), - new Identifier(MethodName::CONSTRUCT), - $this->createArgsFromParams($params) - ); - } - - public function createStaticProtectedPropertyWithDefault(string $name, Node $node): Property - { - $propertyBuilder = new PropertyBuilder($name); - $propertyBuilder->makeProtected(); - $propertyBuilder->makeStatic(); - $propertyBuilder->setDefault($node); - - return $propertyBuilder->getNode(); - } - - public function createProperty(string $name): Property - { - $propertyBuilder = new PropertyBuilder($name); - - $property = $propertyBuilder->getNode(); - $this->phpDocInfoFactory->createFromNode($property); - - return $property; - } - public function createPrivateProperty(string $name): Property { $propertyBuilder = new PropertyBuilder($name); @@ -329,59 +237,7 @@ public function createPrivateProperty(string $name): Property return $property; } - public function createPublicProperty(string $name): Property - { - $propertyBuilder = new PropertyBuilder($name); - $propertyBuilder->makePublic(); - - $property = $propertyBuilder->getNode(); - - $this->phpDocInfoFactory->createFromNode($property); - - return $property; - } - - public function createGetterClassMethod(string $propertyName, Type $type): ClassMethod - { - $methodBuilder = new MethodBuilder('get' . ucfirst($propertyName)); - $methodBuilder->makePublic(); - - $propertyFetch = new PropertyFetch(new Variable(self::THIS), $propertyName); - - $return = new Return_($propertyFetch); - $methodBuilder->addStmt($return); - - $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::RETURN()); - if ($typeNode !== null) { - $methodBuilder->setReturnType($typeNode); - } - - return $methodBuilder->getNode(); - } - - public function createSetterClassMethod(string $propertyName, Type $type): ClassMethod - { - $methodBuilder = new MethodBuilder('set' . ucfirst($propertyName)); - $methodBuilder->makePublic(); - - $variable = new Variable($propertyName); - - $param = $this->createParamWithType($variable, $type); - $methodBuilder->addParam($param); - - $propertyFetch = new PropertyFetch(new Variable(self::THIS), $propertyName); - $assign = new Assign($propertyFetch, $variable); - $methodBuilder->addStmt($assign); - - if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::VOID_TYPE)) { - $methodBuilder->setReturnType(new Name('void')); - } - - return $methodBuilder->getNode(); - } - /** - * @todo decouple to StackNodeFactory * @param Expr[] $exprs */ public function createConcat(array $exprs): ?Concat @@ -390,55 +246,28 @@ public function createConcat(array $exprs): ?Concat return null; } - /** @var Expr $previousConcat */ $previousConcat = array_shift($exprs); foreach ($exprs as $expr) { $previousConcat = new Concat($previousConcat, $expr); } - /** @var Concat $previousConcat */ - return $previousConcat; - } - - public function createClosureFromClassMethod(ClassMethod $classMethod): Closure - { - $classMethodName = $this->nodeNameResolver->getName($classMethod); - $args = $this->createArgs($classMethod->params); - - $methodCall = new MethodCall(new Variable(self::THIS), $classMethodName, $args); - $return = new Return_($methodCall); - - return new Closure([ - 'params' => $classMethod->params, - 'stmts' => [$return], - 'returnType' => $classMethod->returnType, - ]); - } - - /** - * @param string[] $names - * @return Use_[] - */ - public function createUsesFromNames(array $names): array - { - $uses = []; - foreach ($names as $name) { - $useUse = new UseUse(new Name($name)); - $uses[] = new Use_([$useUse]); + if (! $previousConcat instanceof Concat) { + throw new ShouldNotHappenException(); } - return $uses; + return $previousConcat; } /** + * @param string|ObjectReference::* $class * @param Node[] $args */ public function createStaticCall(string $class, string $method, array $args = []): StaticCall { - $class = $this->createClassPart($class); - $staticCall = new StaticCall($class, $method); - $staticCall->args = $this->createArgs($args); - return $staticCall; + $name = $this->createName($class); + $args = $this->createArgs($args); + + return new StaticCall($name, $method, $args); } /** @@ -450,25 +279,11 @@ public function createFuncCall(string $name, array $arguments = []): FuncCall return new FuncCall(new Name($name), $arguments); } - public function createSelfFetchConstant(string $constantName, Node $node): ClassConstFetch - { - $name = new Name('self'); - $name->setAttribute(AttributeKey::CLASS_NAME, $node->getAttribute(AttributeKey::CLASS_NAME)); - return new ClassConstFetch($name, $constantName); - } - - /** - * @param Param[] $params - * @return Arg[] - */ - public function createArgsFromParams(array $params): array + public function createSelfFetchConstant(string $constantName): ClassConstFetch { - $args = []; - foreach ($params as $param) { - $args[] = new Arg($param->var); - } + $name = new Name(ObjectReference::SELF); - return $args; + return new ClassConstFetch($name, $constantName); } public function createNull(): ConstFetch @@ -480,17 +295,22 @@ public function createPromotedPropertyParam(PropertyMetadata $propertyMetadata): { $paramBuilder = new ParamBuilder($propertyMetadata->getName()); $propertyType = $propertyMetadata->getType(); - if ($propertyType !== null) { - $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyType, TypeKind::PROPERTY()); + if ($propertyType instanceof Type) { + $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyType, TypeKind::PROPERTY); - if ($typeNode !== null) { + if ($typeNode instanceof Node) { $paramBuilder->setType($typeNode); } } $param = $paramBuilder->getNode(); $propertyFlags = $propertyMetadata->getFlags(); - $param->flags = $propertyFlags !== 0 ? $propertyFlags : Class_::MODIFIER_PRIVATE; + $param->flags = $propertyFlags !== 0 ? $propertyFlags : Modifiers::PRIVATE; + + // make readonly by default + if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::READONLY_PROPERTY)) { + $param->flags |= Modifiers::READONLY; + } return $param; } @@ -505,37 +325,17 @@ public function createTrue(): ConstFetch return new ConstFetch(new Name('true')); } - public function createClosureFromMethodReflection(MethodReflection $methodReflection): Closure - { - $classMethod = $this->reflectionAstResolver->resolveClassMethodFromMethodReflection($methodReflection); - if (! $classMethod instanceof ClassMethod) { - throw new ShouldNotHappenException(); - } - - return $this->createClosureFromClassMethod($classMethod); - } - + /** + * @api phpunit + * @param string|ObjectReference::* $constantName + */ public function createClassConstFetchFromName(Name $className, string $constantName): ClassConstFetch { - $classConstFetch = $this->builderFactory->classConstFetch($className, $constantName); - - $classNameString = $className->toString(); - if (in_array($classNameString, ['self', 'static'], true)) { - $currentNode = $this->currentNodeProvider->getNode(); - if ($currentNode !== null) { - $className = $currentNode->getAttribute(AttributeKey::CLASS_NAME); - $classConstFetch->class->setAttribute(AttributeKey::RESOLVED_NAME, $className); - $classConstFetch->class->setAttribute(AttributeKey::CLASS_NAME, $className); - } - } else { - $classConstFetch->class->setAttribute(AttributeKey::RESOLVED_NAME, $classNameString); - } - - return $classConstFetch; + return $this->builderFactory->classConstFetch($className, $constantName); } /** - * @param array $newNodes + * @param array $newNodes */ public function createReturnBooleanAnd(array $newNodes): ?Expr { @@ -550,29 +350,32 @@ public function createReturnBooleanAnd(array $newNodes): ?Expr return $this->createBooleanAndFromNodes($newNodes); } - public function createClassConstant(string $name, Expr $expr, int $modifier): ClassConst + /** + * Setting all child nodes to null is needed to avoid reprint of invalid tokens + * @see https://github.com/rectorphp/rector/issues/8712 + * + * @template TNode as Node + * + * @param TNode $node + * @return TNode + */ + public function createReprintedNode(Node $node): Node { - $expr = BuilderHelpers::normalizeValue($expr); - - $const = new Const_($name, $expr); - $classConst = new ClassConst([$const]); - $classConst->flags |= $modifier; + // reset original node, to allow the printer to re-use the node + $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); - // add @var type by default - $staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($expr); - - if (! $staticType instanceof MixedType) { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classConst); - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $staticType); - } + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $node, + static function (Node $subNode): Node { + $subNode->setAttribute(AttributeKey::ORIGINAL_NODE, null); + return $subNode; + } + ); - return $classConst; + return $node; } - /** - * @param mixed $item - */ - private function createArrayItem($item, string | int | null $key = null): ArrayItem + private function createArrayItem(mixed $item, string | int | null $key = null): ArrayItem { $arrayItem = null; @@ -583,6 +386,13 @@ private function createArrayItem($item, string | int | null $key = null): ArrayI || $item instanceof Concat || $item instanceof Scalar || $item instanceof Cast + || $item instanceof ConstFetch + || $item instanceof PropertyFetch + || $item instanceof StaticPropertyFetch + || $item instanceof NullsafePropertyFetch + || $item instanceof NullsafeMethodCall + || $item instanceof Clone_ + || $item instanceof Instanceof_ ) { $arrayItem = new ArrayItem($item); } elseif ($item instanceof Identifier) { @@ -593,106 +403,100 @@ private function createArrayItem($item, string | int | null $key = null): ArrayI $arrayItem = new ArrayItem($itemValue); } elseif (is_array($item)) { $arrayItem = new ArrayItem($this->createArray($item)); - } - - if ($item instanceof ClassConstFetch) { + } elseif ($item === null || $item instanceof ClassConstFetch) { $itemValue = BuilderHelpers::normalizeValue($item); $arrayItem = new ArrayItem($itemValue); - } - - if ($item instanceof Arg) { + } elseif ($item instanceof Arg) { $arrayItem = new ArrayItem($item->value); } - if ($arrayItem !== null) { - $this->decoreateArrayItemWithKey($key, $arrayItem); + if ($arrayItem instanceof ArrayItem) { + $this->decorateArrayItemWithKey($key, $arrayItem); return $arrayItem; } - $nodeClass = is_object($item) ? $item::class : $item; - throw new NotImplementedYetException(sprintf( - 'Not implemented yet. Go to "%s()" and add check for "%s" node.', - __METHOD__, - (string) $nodeClass - )); - } - - /** - * @param mixed $value - * @return mixed|Error|Variable - */ - private function normalizeArgValue($value) - { - if ($value instanceof Param) { - return $value->var; + if ($item instanceof New_) { + $arrayItem = new ArrayItem($item); + $this->decorateArrayItemWithKey($key, $arrayItem); + return $arrayItem; } - return $value; - } - - private function addPropertyType(Property $property, ?Type $type): void - { - if ($type === null) { - return; + if ($item instanceof UnaryPlus || $item instanceof UnaryMinus) { + $arrayItem = new ArrayItem($item); + $this->decorateArrayItemWithKey($key, $arrayItem); + return $arrayItem; } - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - - if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::TYPED_PROPERTIES)) { - $phpParserType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PROPERTY()); - - if ($phpParserType !== null) { - $property->type = $phpParserType; - - if ($type instanceof GenericObjectType) { - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $type); - } + // fallback to other nodes + if ($item instanceof Expr) { + $arrayItem = new ArrayItem($item); + $this->decorateArrayItemWithKey($key, $arrayItem); - return; - } + return $arrayItem; } - $this->phpDocTypeChanger->changeVarType($phpDocInfo, $type); + $itemValue = BuilderHelpers::normalizeValue($item); + $arrayItem = new ArrayItem($itemValue); + $this->decorateArrayItemWithKey($key, $arrayItem); + + return $arrayItem; } - private function createClassPart(string $class): Name | FullyQualified + private function decorateArrayItemWithKey(int | string | null $key, ArrayItem $arrayItem): void { - if (in_array($class, self::REFERENCES, true)) { - return new Name($class); + if ($key === null) { + return; } - return new FullyQualified($class); + $arrayItem->key = BuilderHelpers::normalizeValue($key); } - private function decoreateArrayItemWithKey(int | string | null $key, ArrayItem $arrayItem): void + /** + * @param Expr\BinaryOp[] $binaryOps + */ + private function createBooleanAndFromNodes(array $binaryOps): BooleanAnd { - if ($key !== null) { - $arrayItem->key = BuilderHelpers::normalizeValue($key); + /** @var NotIdentical|BooleanAnd $mainBooleanAnd */ + $mainBooleanAnd = array_shift($binaryOps); + foreach ($binaryOps as $binaryOp) { + $mainBooleanAnd = new BooleanAnd($mainBooleanAnd, $binaryOp); } + + /** @var BooleanAnd $mainBooleanAnd */ + return $mainBooleanAnd; } /** - * @param NotIdentical[]|BooleanAnd[] $exprs + * @param string|ObjectReference::* $className */ - private function createBooleanAndFromNodes(array $exprs): BooleanAnd + private function createName(string $className): Name|FullyQualified { - /** @var NotIdentical|BooleanAnd $booleanAnd */ - $booleanAnd = array_shift($exprs); - foreach ($exprs as $expr) { - $booleanAnd = new BooleanAnd($booleanAnd, $expr); + if (in_array($className, [ObjectReference::PARENT, ObjectReference::SELF, ObjectReference::STATIC], true)) { + return new Name($className); } - /** @var BooleanAnd $booleanAnd */ - return $booleanAnd; + return new FullyQualified($className); } - private function createParamWithType(Variable $variable, Type $type): Param - { - $param = new Param($variable); + private function createMethodCaller( + Expr|string $exprOrVariableName + ): PropertyFetch|Variable|MethodCall|StaticPropertyFetch|Expr { + if (is_string($exprOrVariableName)) { + return new Variable($exprOrVariableName); + } - $phpParserTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM()); + if ($exprOrVariableName instanceof PropertyFetch) { + return new PropertyFetch($exprOrVariableName->var, $exprOrVariableName->name); + } - $param->type = $phpParserTypeNode; - return $param; + if ($exprOrVariableName instanceof StaticPropertyFetch) { + return new StaticPropertyFetch($exprOrVariableName->class, $exprOrVariableName->name); + } + + if ($exprOrVariableName instanceof MethodCall) { + return new MethodCall($exprOrVariableName->var, $exprOrVariableName->name, $exprOrVariableName->args); + } + + return $exprOrVariableName; } } diff --git a/src/PhpParser/Node/Value/TernaryBracketWrapper.php b/src/PhpParser/Node/Value/TernaryBracketWrapper.php deleted file mode 100644 index 0faa445c8dd..00000000000 --- a/src/PhpParser/Node/Value/TernaryBracketWrapper.php +++ /dev/null @@ -1,17 +0,0 @@ -setAttribute(AttributeKey::KIND, 'wrapped_with_brackets'); - $ternary->setAttribute(AttributeKey::ORIGINAL_NODE, null); - } -} diff --git a/src/PhpParser/Node/Value/ValueResolver.php b/src/PhpParser/Node/Value/ValueResolver.php index 991ccf36d71..9fe7f1413e3 100644 --- a/src/PhpParser/Node/Value/ValueResolver.php +++ b/src/PhpParser/Node/Value/ValueResolver.php @@ -2,56 +2,70 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Node\Value; +namespace Rector\PhpParser\Node\Value; +use ArithmeticError; use PhpParser\ConstExprEvaluationException; use PhpParser\ConstExprEvaluator; -use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\ConstFetch; +use PhpParser\Node\InterpolatedStringPart; +use PhpParser\Node\Name; +use PhpParser\Node\Scalar\MagicConst\Class_; use PhpParser\Node\Scalar\MagicConst\Dir; use PhpParser\Node\Scalar\MagicConst\File; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\Constant\ConstantArrayType; +use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ConstantScalarType; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\NodeAnalyzer\ConstFetchAnalyzer; -use Rector\Core\Provider\CurrentFileProvider; +use PHPStan\Type\Type; +use Rector\Application\Provider\CurrentFileProvider; +use Rector\Enum\ObjectReference; +use Rector\Exception\ShouldNotHappenException; +use Rector\NodeAnalyzer\ConstFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\Reflection\ClassReflectionAnalyzer; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; +use TypeError; /** - * @see \Rector\Core\Tests\PhpParser\Node\Value\ValueResolverTest + * @see \Rector\Tests\PhpParser\Node\Value\ValueResolverTest + * @todo make use of constant type of $scope->getType() */ final class ValueResolver { private ?ConstExprEvaluator $constExprEvaluator = null; public function __construct( - private NodeNameResolver $nodeNameResolver, - private NodeTypeResolver $nodeTypeResolver, - private ConstFetchAnalyzer $constFetchAnalyzer, - private ReflectionProvider $reflectionProvider, - private CurrentFileProvider $currentFileProvider + private readonly NodeNameResolver $nodeNameResolver, + private readonly NodeTypeResolver $nodeTypeResolver, + private readonly ConstFetchAnalyzer $constFetchAnalyzer, + private readonly ReflectionProvider $reflectionProvider, + private readonly ReflectionResolver $reflectionResolver, + private readonly ClassReflectionAnalyzer $classReflectionAnalyzer, + private readonly CurrentFileProvider $currentFileProvider, ) { } - /** - * @param mixed $value - */ - public function isValue(Expr $expr, $value): bool + public function isValue(Expr $expr, mixed $value): bool { return $this->getValue($expr) === $value; } - /** - * @return mixed|null - */ - public function getValue(Expr $expr, bool $resolvedClassReference = false) + public function getValue(Arg|Expr|InterpolatedStringPart $expr, bool $resolvedClassReference = false): mixed { + if ($expr instanceof Arg) { + $expr = $expr->value; + } + if ($expr instanceof Concat) { return $this->processConcat($expr, $resolvedClassReference); } @@ -59,8 +73,11 @@ public function getValue(Expr $expr, bool $resolvedClassReference = false) if ($expr instanceof ClassConstFetch && $resolvedClassReference) { $class = $this->nodeNameResolver->getName($expr->class); - if (in_array($class, ['self', 'static'], true)) { - return $expr->getAttribute(AttributeKey::CLASS_NAME); + if (in_array($class, [ObjectReference::SELF, ObjectReference::STATIC], true)) { + $classReflection = $this->reflectionResolver->resolveClassReflection($expr); + if ($classReflection instanceof ClassReflection) { + return $classReflection->getName(); + } } if ($this->nodeNameResolver->isName($expr->name, 'class')) { @@ -68,35 +85,34 @@ public function getValue(Expr $expr, bool $resolvedClassReference = false) } } - try { - $constExprEvaluator = $this->getConstExprEvaluator(); - $value = $constExprEvaluator->evaluateDirectly($expr); - } catch (ConstExprEvaluationException) { - $value = null; - } + $value = $this->resolveExprValueForConst($expr); if ($value !== null) { return $value; } if ($expr instanceof ConstFetch) { - return $this->nodeNameResolver->getName($expr); - } + if ($this->isNull($expr)) { + return null; + } - $nodeStaticType = $this->nodeTypeResolver->getStaticType($expr); + if ($this->isTrue($expr)) { + return true; + } - if ($nodeStaticType instanceof ConstantArrayType) { - return $this->extractConstantArrayTypeValue($nodeStaticType); - } + if ($this->isFalse($expr)) { + return false; + } - if ($nodeStaticType instanceof ConstantScalarType) { - return $nodeStaticType->getValue(); + return $this->nodeNameResolver->getName($expr); } - return null; + $nodeStaticType = $this->nodeTypeResolver->getType($expr); + return $this->resolveConstantType($nodeStaticType); } /** + * @api symfony * @param mixed[] $expectedValues */ public function isValues(Expr $expr, array $expectedValues): bool @@ -110,32 +126,24 @@ public function isValues(Expr $expr, array $expectedValues): bool return false; } - public function isFalse(Node $node): bool - { - return $this->constFetchAnalyzer->isFalse($node); - } - - public function isTrueOrFalse(Node $node): bool + public function isFalse(Expr $expr): bool { - return $this->constFetchAnalyzer->isTrueOrFalse($node); + return $this->constFetchAnalyzer->isFalse($expr); } - public function isTrue(Node $node): bool + public function isTrueOrFalse(Expr $expr): bool { - return $this->constFetchAnalyzer->isTrue($node); + return $this->constFetchAnalyzer->isTrueOrFalse($expr); } - public function isNull(Node $node): bool + public function isTrue(Expr $expr): bool { - return $this->constFetchAnalyzer->isNull($node); + return $this->constFetchAnalyzer->isTrue($expr); } - public function isValueEqual(Expr $firstExpr, Expr $secondExpr): bool + public function isNull(Expr $expr): bool { - $firstValue = $this->getValue($firstExpr); - $secondValue = $this->getValue($secondExpr); - - return $firstValue === $secondValue; + return $this->constFetchAnalyzer->isNull($expr); } /** @@ -145,7 +153,7 @@ public function isValueEqual(Expr $firstExpr, Expr $secondExpr): bool public function areValuesEqual(array $nodes, array $expectedValues): bool { foreach ($nodes as $i => $node) { - if ($node === null) { + if (! $node instanceof Expr) { return false; } @@ -157,6 +165,28 @@ public function areValuesEqual(array $nodes, array $expectedValues): bool return true; } + private function resolveExprValueForConst(Expr|InterpolatedStringPart $expr): mixed + { + if ($expr instanceof InterpolatedStringPart) { + return $expr->value; + } + + try { + $constExprEvaluator = $this->getConstExprEvaluator(); + return $constExprEvaluator->evaluateDirectly($expr); + } catch (ConstExprEvaluationException|TypeError|ArithmeticError) { + } + + if ($expr instanceof Class_) { + $type = $this->nodeTypeResolver->getNativeType($expr); + if ($type instanceof ConstantStringType) { + return $type->getValue(); + } + } + + return null; + } + private function processConcat(Concat $concat, bool $resolvedClassReference): string { return $this->getValue($concat->left, $resolvedClassReference) . $this->getValue( @@ -167,7 +197,7 @@ private function processConcat(Concat $concat, bool $resolvedClassReference): st private function getConstExprEvaluator(): ConstExprEvaluator { - if ($this->constExprEvaluator !== null) { + if ($this->constExprEvaluator instanceof ConstExprEvaluator) { return $this->constExprEvaluator; } @@ -183,7 +213,7 @@ private function getConstExprEvaluator(): ConstExprEvaluator } // resolve "SomeClass::SOME_CONST" - if ($expr instanceof ClassConstFetch) { + if ($expr instanceof ClassConstFetch && $expr->class instanceof Name) { return $this->resolveClassConstFetch($expr); } @@ -196,14 +226,33 @@ private function getConstExprEvaluator(): ConstExprEvaluator return $this->constExprEvaluator; } + private function resolveDirConstant(): string + { + $file = $this->currentFileProvider->getFile(); + if (! $file instanceof \Rector\ValueObject\Application\File) { + throw new ShouldNotHappenException(); + } + + return dirname($file->getFilePath()); + } + + private function resolveFileConstant(File $file): string + { + $file = $this->currentFileProvider->getFile(); + if (! $file instanceof \Rector\ValueObject\Application\File) { + throw new ShouldNotHappenException(); + } + + return $file->getFilePath(); + } + /** - * @return mixed[] + * @return mixed[]|null */ - private function extractConstantArrayTypeValue(ConstantArrayType $constantArrayType): array + private function extractConstantArrayTypeValue(ConstantArrayType $constantArrayType): ?array { $keys = []; foreach ($constantArrayType->getKeyTypes() as $i => $keyType) { - /** @var ConstantScalarType $keyType */ $keys[$i] = $keyType->getValue(); } @@ -213,9 +262,10 @@ private function extractConstantArrayTypeValue(ConstantArrayType $constantArrayT $value = $this->extractConstantArrayTypeValue($valueType); } elseif ($valueType instanceof ConstantScalarType) { $value = $valueType->getValue(); - } else { - // not sure about value + } elseif (ClassNameFromObjectTypeResolver::resolve($valueType) !== null) { continue; + } else { + return null; } $values[$keys[$i]] = $value; @@ -224,21 +274,6 @@ private function extractConstantArrayTypeValue(ConstantArrayType $constantArrayT return $values; } - private function resolveDirConstant(): string - { - $file = $this->currentFileProvider->getFile(); - $smartFileInfo = $file->getSmartFileInfo(); - return $smartFileInfo->getPath(); - } - - private function resolveFileConstant(File $file): string - { - $file = $this->currentFileProvider->getFile(); - - $smartFileInfo = $file->getSmartFileInfo(); - return $smartFileInfo->getPathname(); - } - /** * @return string|mixed */ @@ -255,8 +290,8 @@ private function resolveClassConstFetch(ClassConstFetch $classConstFetch) throw new ShouldNotHappenException(); } - if ($class === 'self') { - $class = (string) $classConstFetch->class->getAttribute(AttributeKey::CLASS_NAME); + if (in_array($class, [ObjectReference::SELF, ObjectReference::STATIC, ObjectReference::PARENT], true)) { + $class = $this->resolveClassFromSelfStaticParent($classConstFetch, $class); } if ($constant === 'class') { @@ -268,16 +303,78 @@ private function resolveClassConstFetch(ClassConstFetch $classConstFetch) return constant($classConstantReference); } - if ($this->reflectionProvider->hasClass($class)) { - $classReflection = $this->reflectionProvider->getClass($class); + if (! $this->reflectionProvider->hasClass($class)) { + // fallback to constant reference itself, to avoid fatal error + return $classConstantReference; + } + + $classReflection = $this->reflectionProvider->getClass($class); - if ($classReflection->hasConstant($constant)) { - $constantReflection = $classReflection->getConstant($constant); - return $constantReflection->getValue(); - } + if (! $classReflection->hasConstant($constant)) { + // fallback to constant reference itself, to avoid fatal error + return $classConstantReference; + } + + if ($classReflection->isEnum()) { + // fallback to constant reference itself, to avoid fatal error + return $classConstantReference; } - // fallback to constant reference itself, to avoid fatal error - return $classConstantReference; + $classConstantReflection = $classReflection->getConstant($constant); + $valueExpr = $classConstantReflection->getValueExpr(); + + if ($valueExpr instanceof ConstFetch) { + return $this->resolveExprValueForConst($valueExpr); + } + + return $this->getValue($valueExpr); + } + + private function resolveClassFromSelfStaticParent(ClassConstFetch $classConstFetch, string $class): string + { + // Scope may be loaded too late, so return empty string early + // it will be resolved on next traverse + $scope = $classConstFetch->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return ''; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($classConstFetch); + if (! $classReflection instanceof ClassReflection) { + throw new ShouldNotHappenException( + 'Complete class parent node for to class const fetch, so "self" or "static" references is resolvable to a class name' + ); + } + + if ($class !== ObjectReference::PARENT) { + return $classReflection->getName(); + } + + if (! $classReflection->isClass()) { + throw new ShouldNotHappenException( + 'Complete class parent node for to class const fetch, so "parent" references is resolvable to lookup parent class' + ); + } + + // ensure parent class name still resolved even not autoloaded + $parentClassName = $this->classReflectionAnalyzer->resolveParentClassName($classReflection); + if ($parentClassName === null) { + throw new ShouldNotHappenException(); + } + + return $parentClassName; + } + + private function resolveConstantType(Type $constantType): mixed + { + if ($constantType instanceof ConstantArrayType) { + return $this->extractConstantArrayTypeValue($constantType); + } + + if ($constantType instanceof ConstantScalarType) { + return $constantType->getValue(); + } + + return null; } } diff --git a/src/PhpParser/NodeFinder/LocalConstantFinder.php b/src/PhpParser/NodeFinder/LocalConstantFinder.php deleted file mode 100644 index 16191a028ee..00000000000 --- a/src/PhpParser/NodeFinder/LocalConstantFinder.php +++ /dev/null @@ -1,61 +0,0 @@ -getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { - return null; - } - - $constantClassType = $this->nodeTypeResolver->resolve($classConstFetch->class); - if (! $constantClassType instanceof TypeWithClassName) { - return null; - } - - if (! $this->nodeNameResolver->isName($class, $constantClassType->getClassName())) { - return null; - } - - $constatName = $this->nodeNameResolver->getName($classConstFetch->name); - if ($constatName === null) { - return null; - } - - return $this->findConstantByName($class, $constatName); - } - - private function findConstantByName(Class_ $class, string $constatName): ?Const_ - { - foreach ($class->getConstants() as $classConsts) { - foreach ($classConsts->consts as $const) { - if (! $this->nodeNameResolver->isName($const->name, $constatName)) { - continue; - } - - return $const; - } - } - - return null; - } -} diff --git a/src/PhpParser/NodeFinder/LocalMethodCallFinder.php b/src/PhpParser/NodeFinder/LocalMethodCallFinder.php index 4b8a2b09855..5c346dfb306 100644 --- a/src/PhpParser/NodeFinder/LocalMethodCallFinder.php +++ b/src/PhpParser/NodeFinder/LocalMethodCallFinder.php @@ -2,18 +2,19 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\NodeFinder; +namespace Rector\PhpParser\NodeFinder; +use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PHPStan\Type\TypeWithClassName; -use Rector\Core\PhpParser\Node\BetterNodeFinder; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; -final class LocalMethodCallFinder +final readonly class LocalMethodCallFinder { public function __construct( private BetterNodeFinder $betterNodeFinder, @@ -23,43 +24,36 @@ public function __construct( } /** - * @return MethodCall[] + * @return MethodCall[]|StaticCall[] */ - public function match(ClassMethod $classMethod): array + public function match(Class_ $class, ClassMethod $classMethod): array { - $class = $classMethod->getAttribute(AttributeKey::CLASS_NODE); - if (! $class instanceof Class_) { - return []; - } - $className = $this->nodeNameResolver->getName($class); if (! is_string($className)) { return []; } - /** @var MethodCall[] $methodCalls */ - $methodCalls = $this->betterNodeFinder->findInstanceOf($class, MethodCall::class); - $classMethodName = $this->nodeNameResolver->getName($classMethod); - $matchingMethodCalls = []; + /** @var MethodCall[]|StaticCall[] $matchingMethodCalls */ + $matchingMethodCalls = $this->betterNodeFinder->find( + $class->getMethods(), + function (Node $subNode) use ($className, $classMethodName): bool { + if (! $subNode instanceof MethodCall && ! $subNode instanceof StaticCall) { + return false; + } - foreach ($methodCalls as $methodCall) { - $callerType = $this->nodeTypeResolver->resolve($methodCall->var); - if (! $callerType instanceof TypeWithClassName) { - continue; - } + if (! $this->nodeNameResolver->isName($subNode->name, $classMethodName)) { + return false; + } - if ($callerType->getClassName() !== $className) { - continue; - } + $callerType = $subNode instanceof MethodCall + ? $this->nodeTypeResolver->getType($subNode->var) + : $this->nodeTypeResolver->getType($subNode->class); - if (! $this->nodeNameResolver->isName($methodCall->name, $classMethodName)) { - continue; + return ClassNameFromObjectTypeResolver::resolve($callerType) === $className; } - - $matchingMethodCalls[] = $methodCall; - } + ); return $matchingMethodCalls; } diff --git a/src/PhpParser/NodeFinder/PropertyFetchFinder.php b/src/PhpParser/NodeFinder/PropertyFetchFinder.php index 797f6ac5250..ee9fbbcf43c 100644 --- a/src/PhpParser/NodeFinder/PropertyFetchFinder.php +++ b/src/PhpParser/NodeFinder/PropertyFetchFinder.php @@ -2,126 +2,299 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\NodeFinder; +namespace Rector\PhpParser\NodeFinder; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ArrayDimFetch; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\NullsafePropertyFetch; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Param; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use PHPStan\Reflection\ReflectionProvider; -use Rector\Core\NodeAnalyzer\ClassAnalyzer; -use Rector\Core\PhpParser\AstResolver; -use Rector\Core\PhpParser\Node\BetterNodeFinder; +use PhpParser\Node\Stmt\Trait_; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Type\ObjectType; +use PHPStan\Type\StaticType; +use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\AstResolver; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\Reflection\ReflectionResolver; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; -final class PropertyFetchFinder +final readonly class PropertyFetchFinder { public function __construct( private BetterNodeFinder $betterNodeFinder, private NodeNameResolver $nodeNameResolver, - private ReflectionProvider $reflectionProvider, + private ReflectionResolver $reflectionResolver, private AstResolver $astResolver, - private ClassAnalyzer $classAnalyzer + private NodeTypeResolver $nodeTypeResolver, + private PropertyFetchAnalyzer $propertyFetchAnalyzer, + private SimpleCallableNodeTraverser $simpleCallableNodeTraverser ) { } /** - * @return PropertyFetch[]|StaticPropertyFetch[] + * @return array */ - public function findPrivatePropertyFetches(Property | Param $propertyOrPromotedParam): array - { - $classLike = $propertyOrPromotedParam->getAttribute(AttributeKey::CLASS_NODE); - if (! $classLike instanceof Class_) { - return []; - } - + public function findPrivatePropertyFetches( + Class_ $class, + Property | Param $propertyOrPromotedParam, + Scope $scope + ): array { $propertyName = $this->resolvePropertyName($propertyOrPromotedParam); if ($propertyName === null) { return []; } - $className = (string) $this->nodeNameResolver->getName($classLike); - if (! $this->reflectionProvider->hasClass($className)) { - /** @var PropertyFetch[]|StaticPropertyFetch[] $propertyFetches */ - $propertyFetches = $this->findPropertyFetchesInClassLike($classLike->stmts, $propertyName); - return $propertyFetches; - } + $classReflection = $this->reflectionResolver->resolveClassAndAnonymousClass($class); - $classReflection = $this->reflectionProvider->getClass($className); + $nodes = [$class]; + $nodesTrait = $this->astResolver->parseClassReflectionTraits($classReflection); + $hasTrait = $nodesTrait !== []; + $nodes = [...$nodes, ...$nodesTrait]; - $nodes = [$classLike]; - $nodes = array_merge($nodes, $this->astResolver->parseClassReflectionTraits($classReflection)); - - return $this->findPropertyFetchesInNonAnonymousClassLike($nodes, $propertyName); + return $this->findPropertyFetchesInClassLike($class, $nodes, $propertyName, $hasTrait, $scope); } /** - * @return PropertyFetch[] + * @api used by other Rector packages + * @return PropertyFetch[]|StaticPropertyFetch[]|NullsafePropertyFetch[] */ - public function findLocalPropertyFetchesByName(Class_ $class, string $paramName): array + public function findLocalPropertyFetchesByName(Class_|ClassMethod $node, string $paramName): array { - /** @var PropertyFetch[] $propertyFetches */ - $propertyFetches = $this->betterNodeFinder->findInstanceOf($class, PropertyFetch::class); + /** @var PropertyFetch[]|StaticPropertyFetch[]|NullsafePropertyFetch[] $foundPropertyFetches */ + $foundPropertyFetches = $this->betterNodeFinder->find( + $this->resolveNodesToLocate($node), + function (Node $subNode) use ($paramName): bool { + if ($subNode instanceof PropertyFetch) { + return $this->propertyFetchAnalyzer->isLocalPropertyFetchName($subNode, $paramName); + } - $foundPropertyFetches = []; + if ($subNode instanceof NullsafePropertyFetch) { + return $this->propertyFetchAnalyzer->isLocalPropertyFetchName($subNode, $paramName); + } - foreach ($propertyFetches as $propertyFetch) { - if (! $this->nodeNameResolver->isName($propertyFetch->var, 'this')) { - continue; - } + if ($subNode instanceof StaticPropertyFetch) { + return $this->propertyFetchAnalyzer->isLocalPropertyFetchName($subNode, $paramName); + } - if (! $this->nodeNameResolver->isName($propertyFetch->name, $paramName)) { - continue; + return false; } - - $foundPropertyFetches[] = $propertyFetch; - } + ); return $foundPropertyFetches; } /** - * @param Stmt[] $nodes - * @return PropertyFetch[]|StaticPropertyFetch[] + * @return ArrayDimFetch[] */ - private function findPropertyFetchesInNonAnonymousClassLike(array $nodes, string $propertyName): array + public function findLocalPropertyArrayDimFetchesAssignsByName(Class_ $class, Property $property): array { - /** @var PropertyFetch[]|StaticPropertyFetch[] $propertyFetches */ - $propertyFetches = $this->findPropertyFetchesInClassLike($nodes, $propertyName); + $propertyName = $this->nodeNameResolver->getName($property); + /** @var ArrayDimFetch[] $propertyArrayDimFetches */ + $propertyArrayDimFetches = []; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $this->resolveNodesToLocate($class), + function (Node $subNode) use (&$propertyArrayDimFetches, $propertyName): null { + if (! $subNode instanceof Assign) { + return null; + } + + if (! $subNode->var instanceof ArrayDimFetch) { + return null; + } + + $dimFetchVar = $subNode->var; - foreach ($propertyFetches as $key => $propertyFetch) { - $currentClassLike = $propertyFetch->getAttribute(AttributeKey::CLASS_NODE); - if ($this->classAnalyzer->isAnonymousClass($currentClassLike)) { - unset($propertyFetches[$key]); + if (! $dimFetchVar->var instanceof PropertyFetch && ! $dimFetchVar->var instanceof StaticPropertyFetch) { + return null; + } + + if (! $this->propertyFetchAnalyzer->isLocalPropertyFetchName($dimFetchVar->var, $propertyName)) { + return null; + } + + $propertyArrayDimFetches[] = $dimFetchVar; + return null; } + ); + + return $propertyArrayDimFetches; + } + + public function isLocalPropertyFetchByName(Expr $expr, Class_|Trait_ $class, string $propertyName): bool + { + if (! $expr instanceof PropertyFetch) { + return false; } - return $propertyFetches; + if (! $this->nodeNameResolver->isName($expr->name, $propertyName)) { + return false; + } + + if ($this->nodeNameResolver->isName($expr->var, 'this')) { + return true; + } + + $type = $this->nodeTypeResolver->getType($expr->var); + + if ($type instanceof ObjectType || $type instanceof StaticType) { + return $this->nodeNameResolver->isName($class, $type->getClassName()); + } + + return false; } /** - * @param Stmt[] $nodes - * @return PropertyFetch[]|StaticPropertyFetch[] + * @return Stmt[] */ - private function findPropertyFetchesInClassLike(array $nodes, string $propertyName): array + private function resolveNodesToLocate(Class_|ClassMethod $node): array { + if ($node instanceof ClassMethod) { + return [$node]; + } + + $propertyWithHooks = array_filter( + $node->getProperties(), + fn (Property $property): bool => $property->hooks !== [] + ); + + return [...$propertyWithHooks, ...$node->getMethods()]; + } + + /** + * @param Stmt[] $stmts + * @return PropertyFetch[]|StaticPropertyFetch[] + */ + private function findPropertyFetchesInClassLike( + Class_|Trait_ $class, + array $stmts, + string $propertyName, + bool $hasTrait, + Scope $scope + ): array { /** @var PropertyFetch[]|StaticPropertyFetch[] $propertyFetches */ - $propertyFetches = $this->betterNodeFinder->find($nodes, function (Node $node) use ($propertyName): bool { - // property + static fetch - if ($node instanceof PropertyFetch) { - return $this->nodeNameResolver->isName($node, $propertyName); + $propertyFetches = $this->betterNodeFinder->find( + $stmts, + function (Node $subNode) use ($class, $hasTrait, $propertyName, $scope): bool { + if ($subNode instanceof MethodCall || $subNode instanceof StaticCall || $subNode instanceof FuncCall) { + $this->decoratePropertyFetch($subNode, $scope); + return false; + } + + if ($subNode instanceof PropertyFetch) { + if ($this->isInAnonymous($subNode, $class, $hasTrait)) { + return false; + } + + return $this->isNamePropertyNameEquals($subNode, $propertyName, $class); + } + + if ($subNode instanceof StaticPropertyFetch) { + return $this->nodeNameResolver->isName($subNode->name, $propertyName); + } + + return false; + } + ); + + return $propertyFetches; + } + + private function decoratePropertyFetch(Node $node, Scope $scope): void + { + if (! $node instanceof MethodCall && ! $node instanceof StaticCall && ! $node instanceof FuncCall) { + return; + } + + if ($node->isFirstClassCallable()) { + return; + } + + foreach ($node->getArgs() as $key => $arg) { + if (! $arg->value instanceof PropertyFetch && ! $arg->value instanceof StaticPropertyFetch) { + continue; } - if ($node instanceof StaticPropertyFetch) { - return $this->nodeNameResolver->isName($node, $propertyName); + + if (! $this->isFoundByRefParam($node, $key, $scope)) { + continue; } + + $arg->value->setAttribute(AttributeKey::IS_USED_AS_ARG_BY_REF_VALUE, true); + } + } + + private function isFoundByRefParam(MethodCall | StaticCall | FuncCall $node, int $key, Scope $scope): bool + { + $functionLikeReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + if ($functionLikeReflection === null) { return false; - }); + } - return $propertyFetches; + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select( + $functionLikeReflection, + $node, + $scope + ); + + $parameters = $parametersAcceptor->getParameters(); + if (! isset($parameters[$key])) { + return false; + } + + return $parameters[$key]->passedByReference()->yes(); + } + + private function isInAnonymous(PropertyFetch $propertyFetch, Class_|Trait_ $class, bool $hasTrait): bool + { + $classReflection = $this->reflectionResolver->resolveClassReflection($propertyFetch); + if (! $classReflection instanceof ClassReflection || ! $classReflection->isClass()) { + return false; + } + + if ($classReflection->getName() === $this->nodeNameResolver->getName($class)) { + return false; + } + + return ! $hasTrait; + } + + private function isNamePropertyNameEquals( + PropertyFetch $propertyFetch, + string $propertyName, + Class_|Trait_ $class + ): bool { + // early check if property fetch name is not equals with property name + // so next check is check var name and var type only + if (! $this->isLocalPropertyFetchByName($propertyFetch, $class, $propertyName)) { + return false; + } + + $propertyFetchVarType = $this->nodeTypeResolver->getType($propertyFetch->var); + $propertyFetchVarTypeClassName = ClassNameFromObjectTypeResolver::resolve($propertyFetchVarType); + + if ($propertyFetchVarTypeClassName === null) { + return false; + } + + $classLikeName = $this->nodeNameResolver->getName($class); + return $propertyFetchVarTypeClassName === $classLikeName; } private function resolvePropertyName(Property | Param $propertyOrPromotedParam): ?string diff --git a/src/PhpParser/NodeTransformer.php b/src/PhpParser/NodeTransformer.php index b1e4505ef24..278b65c1b28 100644 --- a/src/PhpParser/NodeTransformer.php +++ b/src/PhpParser/NodeTransformer.php @@ -2,29 +2,36 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser; +namespace Rector\PhpParser; -use Nette\Utils\Strings; +use PhpParser\BuilderHelpers; +use PhpParser\Node\Arg; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Yield_; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Expression; -use Rector\Core\ValueObject\SprintfStringAndArgs; +use Rector\Exception\ShouldNotHappenException; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Util\StringUtils; +use Rector\ValueObject\SprintfStringAndArgs; +/** + * @api used in phpunit + */ final class NodeTransformer { /** - * @var string * @see https://regex101.com/r/XFc3qA/1 */ - private const PERCENT_TEXT_REGEX = '#^%\w$#'; + private const string PERCENT_TEXT_REGEX = '#^%\w$#'; /** + * @api used in phpunit symfony + * * From: * - sprintf("Hi %s", $name); * @@ -46,7 +53,7 @@ public function transformSprintfToArray(FuncCall $sprintfFuncCall): ?Array_ $arrayMessageParts = []; foreach ($messageParts as $messagePart) { - if (Strings::match($messagePart, self::PERCENT_TEXT_REGEX)) { + if (StringUtils::isMatch($messagePart, self::PERCENT_TEXT_REGEX)) { /** @var Expr $messagePartNode */ $messagePartNode = array_shift($arrayItems); } else { @@ -64,30 +71,36 @@ public function transformSprintfToArray(FuncCall $sprintfFuncCall): ?Array_ */ public function transformArrayToYields(Array_ $array): array { - $yieldNodes = []; + $yields = []; foreach ($array->items as $arrayItem) { - if ($arrayItem === null) { - continue; - } + $yield = new Yield_($arrayItem->value, $arrayItem->key); + $expression = new Expression($yield); - $expressionNode = new Expression(new Yield_($arrayItem->value, $arrayItem->key)); $arrayItemComments = $arrayItem->getComments(); if ($arrayItemComments !== []) { - $expressionNode->setAttribute(AttributeKey::COMMENTS, $arrayItemComments); + $expression->setAttribute(AttributeKey::COMMENTS, $arrayItemComments); } - $yieldNodes[] = $expressionNode; + $yields[] = $expression; } - return $yieldNodes; + return $yields; } + /** + * @api symfony + */ public function transformConcatToStringArray(Concat $concat): Array_ { $arrayItems = $this->transformConcatToItems($concat); - return new Array_($arrayItems); + $expr = BuilderHelpers::normalizeValue($arrayItems); + if (! $expr instanceof Array_) { + throw new ShouldNotHappenException(); + } + + return $expr; } private function splitMessageAndArgs(FuncCall $sprintfFuncCall): ?SprintfStringAndArgs @@ -95,6 +108,10 @@ private function splitMessageAndArgs(FuncCall $sprintfFuncCall): ?SprintfStringA $stringArgument = null; $arrayItems = []; foreach ($sprintfFuncCall->args as $i => $arg) { + if (! $arg instanceof Arg) { + continue; + } + if ($i === 0) { $stringArgument = $arg->value; } else { @@ -118,7 +135,7 @@ private function splitMessageAndArgs(FuncCall $sprintfFuncCall): ?SprintfStringA */ private function splitBySpace(string $value): array { - $value = str_getcsv($value, ' '); + $value = str_getcsv($value, ' ', '"', '\\'); return array_filter($value); } @@ -130,7 +147,7 @@ private function transformConcatToItems(Concat $concat): array { $arrayItems = $this->transformConcatItemToArrayItems($concat->left); - return array_merge($arrayItems, $this->transformConcatItemToArrayItems($concat->right)); + return [...$arrayItems, ...$this->transformConcatItemToArrayItems($concat->right)]; } /** diff --git a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php index a5cf70eae8f..f27610da739 100644 --- a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php +++ b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php @@ -2,52 +2,310 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\NodeTraverser; +namespace Rector\PhpParser\NodeTraverser; +use LogicException; +use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Namespace_; -use PhpParser\NodeFinder; -use PhpParser\NodeTraverser; -use Rector\Core\Contract\Rector\PhpRectorInterface; -use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace; +use PhpParser\NodeTraverserInterface; +use PhpParser\NodeVisitor; +use Rector\Configuration\ConfigurationRuleFilter; +use Rector\Contract\Rector\RectorInterface; +use Rector\Exception\ShouldNotHappenException; +use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; +use Rector\PhpParser\Node\FileNode; +use Rector\VersionBonding\ComposerPackageConstraintFilter; use Rector\VersionBonding\PhpVersionedFilter; +use Webmozart\Assert\Assert; -final class RectorNodeTraverser extends NodeTraverser +/** + * Based on native NodeTraverser class, but heavily customized for Rector needs. + * + * The main differences are: + * - no leaveNode(), as we do all in enterNode() that calls refactor() method + * - cached visitors per node class for performance, e.g. when we find rules for Class_ node, they're cached for next time + * - immutability features, register Rector rules once, then use; no changes on the fly + * + * @see \Rector\Tests\PhpParser\NodeTraverser\RectorNodeTraverserTest + * @internal No BC promise on this class, it might change any time. + */ +final class RectorNodeTraverser implements NodeTraverserInterface { + /** + * @var RectorInterface[] + */ + private array $visitors = []; + + private bool $stopTraversal; + private bool $areNodeVisitorsPrepared = false; /** - * @param PhpRectorInterface[] $phpRectors + * @var array, RectorInterface[]> + */ + private array $visitorsPerNodeClass = []; + + /** + * @param RectorInterface[] $rectors */ public function __construct( - private array $phpRectors, - private NodeFinder $nodeFinder, - private PhpVersionedFilter $phpVersionedFilter + private array $rectors, + private readonly PhpVersionedFilter $phpVersionedFilter, + private readonly ComposerPackageConstraintFilter $composerPackageConstraintFilter, + private readonly ConfigurationRuleFilter $configurationRuleFilter, ) { } + public function addVisitor(NodeVisitor $visitor): void + { + throw new ShouldNotHappenException('The immutable node traverser does not support adding visitors.'); + } + + public function removeVisitor(NodeVisitor $visitor): void + { + throw new ShouldNotHappenException('The immutable node traverser does not support removing visitors.'); + } + /** - * @param Stmt[] $nodes - * @return Stmt[] + * @param Node[] $nodes + * @return Node[] */ public function traverse(array $nodes): array { $this->prepareNodeVisitors(); - $hasNamespace = (bool) $this->nodeFinder->findFirstInstanceOf($nodes, Namespace_::class); - if (! $hasNamespace && $nodes !== []) { - $fileWithoutNamespace = new FileWithoutNamespace($nodes); - return parent::traverse([$fileWithoutNamespace]); + $this->stopTraversal = false; + foreach ($this->visitors as $visitor) { + if (null !== $return = $visitor->beforeTraverse($nodes)) { + $nodes = $return; + } + } + + $nodes = $this->traverseArray($nodes); + for ($i = \count($this->visitors) - 1; $i >= 0; --$i) { + $visitor = $this->visitors[$i]; + if (null !== $return = $visitor->afterTraverse($nodes)) { + $nodes = $return; + } } - return parent::traverse($nodes); + return $nodes; } /** - * This must happen after $this->configuration is set after ProcessCommand::execute() is run, - * otherwise we get default false positives. + * @param RectorInterface[] $rectors + * @api used in tests to update the active rules * - * This hack should be removed after https://github.com/rectorphp/rector/issues/5584 is resolved + * @internal Used only in Rector core, not supported outside. Might change any time. + */ + public function refreshPhpRectors(array $rectors): void + { + Assert::allIsInstanceOf($rectors, RectorInterface::class); + + $this->rectors = $rectors; + $this->visitors = []; + $this->visitorsPerNodeClass = []; + + $this->areNodeVisitorsPrepared = false; + + $this->prepareNodeVisitors(); + } + + /** + * @return RectorInterface[] + * + * @api used in tests + */ + public function getVisitorsForNode(Node $node): array + { + $nodeClass = $node::class; + + if (! isset($this->visitorsPerNodeClass[$nodeClass])) { + $this->visitorsPerNodeClass[$nodeClass] = []; + + /** @var RectorInterface $visitor */ + foreach ($this->visitors as $visitor) { + foreach ($visitor->getNodeTypes() as $nodeType) { + // BC layer matching + if ($nodeType === FileWithoutNamespace::class && $nodeClass === FileNode::class) { + $this->visitorsPerNodeClass[$nodeClass][] = $visitor; + continue; + } + + if (is_a($nodeClass, $nodeType, true)) { + $this->visitorsPerNodeClass[$nodeClass][] = $visitor; + continue 2; + } + } + } + } + + return $this->visitorsPerNodeClass[$nodeClass]; + } + + private function traverseNode(Node $node): void + { + foreach ($node->getSubNodeNames() as $name) { + $subNode = $node->{$name}; + if (\is_array($subNode)) { + $node->{$name} = $this->traverseArray($subNode); + if ($this->stopTraversal) { + break; + } + + continue; + } + + if (! $subNode instanceof Node) { + continue; + } + + $traverseChildren = true; + $currentNodeVisitors = $this->getVisitorsForNode($subNode); + + foreach ($currentNodeVisitors as $currentNodeVisitor) { + $return = $currentNodeVisitor->enterNode($subNode); + if ($return !== null) { + if ($return instanceof Node) { + $originalSubNodeClass = $subNode::class; + + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + $node->{$name} = $return; + + if ($originalSubNodeClass !== $subNode::class) { + // stop traversing as node type changed and visitors won't work + continue 2; + } + + } elseif ($return === NodeVisitor::DONT_TRAVERSE_CHILDREN) { + $traverseChildren = false; + } elseif ($return === NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + $traverseChildren = false; + break; + } elseif ($return === NodeVisitor::STOP_TRAVERSAL) { + $this->stopTraversal = true; + break 2; + } elseif ($return === NodeVisitor::REPLACE_WITH_NULL) { + $node->{$name} = null; + continue 2; + } else { + throw new LogicException('enterNode() returned invalid value of type ' . gettype($return)); + } + } + } + + if ($traverseChildren) { + $this->traverseNode($subNode); + if ($this->stopTraversal) { + break; + } + } + } + } + + /** + * @param Node[] $nodes + * @return Node[] + */ + private function traverseArray(array $nodes): array + { + $doNodes = []; + foreach ($nodes as $i => $node) { + if (! $node instanceof Node) { + if (\is_array($node)) { + throw new LogicException('Invalid node structure: Contains nested arrays'); + } + + continue; + } + + $traverseChildren = true; + $currentNodeVisitors = $this->getVisitorsForNode($node); + + foreach ($currentNodeVisitors as $currentNodeVisitor) { + $return = $currentNodeVisitor->enterNode($node); + if ($return !== null) { + if ($return instanceof Node) { + $originalNodeNodeClass = $node::class; + $this->ensureReplacementReasonable($node, $return); + $nodes[$i] = $node = $return; + + if ($originalNodeNodeClass !== $return::class) { + // stop traversing as node type changed and visitors won't work + continue 2; + } + } elseif (\is_array($return)) { + $doNodes[] = [$i, $return]; + continue 2; + } elseif ($return === NodeVisitor::REMOVE_NODE) { + $doNodes[] = [$i, []]; + continue 2; + } elseif ($return === NodeVisitor::DONT_TRAVERSE_CHILDREN) { + $traverseChildren = false; + } elseif ($return === NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + $traverseChildren = false; + break; + } elseif ($return === NodeVisitor::STOP_TRAVERSAL) { + $this->stopTraversal = true; + break 2; + } elseif ($return === NodeVisitor::REPLACE_WITH_NULL) { + throw new LogicException( + 'REPLACE_WITH_NULL can not be used if the parent structure is an array' + ); + } else { + throw new LogicException('enterNode() returned invalid value of type ' . gettype($return)); + } + } + } + + if ($traverseChildren) { + $this->traverseNode($node); + if ($this->stopTraversal) { + break; + } + } + } + + if ($doNodes !== []) { + while ([$i, $replace] = array_pop($doNodes)) { + array_splice($nodes, $i, 1, $replace); + } + } + + return $nodes; + } + + private function ensureReplacementReasonable(Node $old, Node $new): void + { + if ($old instanceof Stmt) { + if ($new instanceof Expr) { + throw new LogicException( + sprintf('Trying to replace statement (%s) ', $old->getType()) . sprintf( + 'with expression (%s). Are you missing a ', + $new->getType() + ) . 'Stmt_Expression wrapper?' + ); + } + + return; + } + + if ($new instanceof Stmt) { + throw new LogicException( + sprintf('Trying to replace expression (%s) ', $old->getType()) . sprintf( + 'with statement (%s)', + $new->getType() + ) + ); + } + } + + /** + * This must happen after $this->configuration is set after ProcessCommand::execute() is run, otherwise we get default false positives. + * + * This should be removed after https://github.com/rectorphp/rector/issues/5584 is resolved */ private function prepareNodeVisitors(): void { @@ -55,11 +313,14 @@ private function prepareNodeVisitors(): void return; } - // filer out by version - $activePhpRectors = $this->phpVersionedFilter->filter($this->phpRectors); - foreach ($activePhpRectors as $activePhpRector) { - $this->addVisitor($activePhpRector); - } + // filter out by PHP version + $this->visitors = $this->phpVersionedFilter->filter($this->rectors); + + // filter out by composer package constraint + $this->visitors = $this->composerPackageConstraintFilter->filter($this->visitors); + + // filter by configuration + $this->visitors = $this->configurationRuleFilter->filter($this->visitors); $this->areNodeVisitorsPrepared = true; } diff --git a/src/PhpParser/NodeTraverser/SimpleNodeTraverser.php b/src/PhpParser/NodeTraverser/SimpleNodeTraverser.php new file mode 100644 index 00000000000..13d841678ec --- /dev/null +++ b/src/PhpParser/NodeTraverser/SimpleNodeTraverser.php @@ -0,0 +1,47 @@ +setAttribute($this->attributeKey, $this->value); + return null; + } + }; + + $nodeTraverser = new NodeTraverser($callableNodeVisitor); + + $nodes = $nodesOrNode instanceof Node ? [$nodesOrNode] : $nodesOrNode; + $nodeTraverser->traverse($nodes); + } +} diff --git a/src/PhpParser/NodeVisitor/ArgNodeVisitor.php b/src/PhpParser/NodeVisitor/ArgNodeVisitor.php new file mode 100644 index 00000000000..c539f208310 --- /dev/null +++ b/src/PhpParser/NodeVisitor/ArgNodeVisitor.php @@ -0,0 +1,38 @@ +name instanceof Name) { + return null; + } + + // has no args + if ($node->isFirstClassCallable()) { + return null; + } + + $funcCallName = $node->name->toString(); + foreach ($node->getArgs() as $arg) { + $arg->value->setAttribute(AttributeKey::FROM_FUNC_CALL_NAME, $funcCallName); + } + + return null; + } +} diff --git a/src/PhpParser/NodeVisitor/AssignedToNodeVisitor.php b/src/PhpParser/NodeVisitor/AssignedToNodeVisitor.php new file mode 100644 index 00000000000..45347398457 --- /dev/null +++ b/src/PhpParser/NodeVisitor/AssignedToNodeVisitor.php @@ -0,0 +1,57 @@ +var->setAttribute(AttributeKey::IS_ASSIGN_OP_VAR, true); + return null; + } + + if ($node instanceof AssignRef) { + $node->expr->setAttribute(AttributeKey::IS_ASSIGN_REF_EXPR, true); + return null; + } + + if (! $node instanceof Assign) { + return null; + } + + $node->var->setAttribute(AttributeKey::IS_BEING_ASSIGNED, true); + if ($node->var instanceof List_) { + foreach ($node->var->items as $item) { + if ($item instanceof ArrayItem) { + $item->value->setAttribute(AttributeKey::IS_BEING_ASSIGNED, true); + } + } + } + + $node->expr->setAttribute(AttributeKey::IS_ASSIGNED_TO, true); + + if ($node->expr instanceof Assign) { + $node->var->setAttribute(AttributeKey::IS_MULTI_ASSIGN, true); + $node->expr->setAttribute(AttributeKey::IS_MULTI_ASSIGN, true); + $node->expr->var->setAttribute(AttributeKey::IS_ASSIGNED_TO, true); + } + + return null; + } +} diff --git a/src/PhpParser/NodeVisitor/ByRefReturnNodeVisitor.php b/src/PhpParser/NodeVisitor/ByRefReturnNodeVisitor.php new file mode 100644 index 00000000000..158874a2075 --- /dev/null +++ b/src/PhpParser/NodeVisitor/ByRefReturnNodeVisitor.php @@ -0,0 +1,61 @@ +returnsByRef()) { + return null; + } + + $stmts = $node->getStmts(); + if ($stmts === null) { + return null; + } + + SimpleNodeTraverser::decorateWithAttributeValue($stmts, AttributeKey::IS_INSIDE_BYREF_FUNCTION_LIKE, true); + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $stmts, + static function (Node $node): int|null|Node { + // avoid nested functions or classes + if ($node instanceof Class_ || $node instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $node instanceof Return_) { + return null; + } + + $node->setAttribute(AttributeKey::IS_BYREF_RETURN, true); + return $node; + } + ); + + return null; + } +} diff --git a/src/PhpParser/NodeVisitor/ByRefVariableNodeVisitor.php b/src/PhpParser/NodeVisitor/ByRefVariableNodeVisitor.php new file mode 100644 index 00000000000..562ba48f8ad --- /dev/null +++ b/src/PhpParser/NodeVisitor/ByRefVariableNodeVisitor.php @@ -0,0 +1,108 @@ +expr->setAttribute(AttributeKey::IS_BYREF_VAR, true); + return null; + } + + if (! $node instanceof FunctionLike) { + return null; + } + + $byRefVariableNames = $this->resolveClosureUseIsByRefAttribute($node, []); + $byRefVariableNames = $this->resolveParamIsByRefAttribute($node, $byRefVariableNames); + + $stmts = $node->getStmts(); + if ($stmts === null) { + return null; + } + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $stmts, + function (Node $subNode) use (&$byRefVariableNames): null|Variable { + if ($subNode instanceof Closure) { + $byRefVariableNames = $this->resolveClosureUseIsByRefAttribute($subNode, $byRefVariableNames); + return null; + } + + if (! $subNode instanceof Variable) { + return null; + } + + if (! in_array($subNode->name, $byRefVariableNames, true)) { + return null; + } + + $subNode->setAttribute(AttributeKey::IS_BYREF_VAR, true); + return $subNode; + } + ); + + return null; + } + + /** + * @param string[] $byRefVariableNames + * @return string[] + */ + private function resolveParamIsByRefAttribute(FunctionLike $functionLike, array $byRefVariableNames): array + { + foreach ($functionLike->getParams() as $param) { + if ($param->byRef && $param->var instanceof Variable && ! $param->var->name instanceof Expr) { + $param->var->setAttribute(AttributeKey::IS_BYREF_VAR, true); + /** @var string $paramVarName */ + $paramVarName = $param->var->name; + $byRefVariableNames[] = $paramVarName; + } + } + + return $byRefVariableNames; + } + + /** + * @param string[] $byRefVariableNames + * @return string[] + */ + private function resolveClosureUseIsByRefAttribute(FunctionLike $functionLike, array $byRefVariableNames): array + { + if (! $functionLike instanceof Closure) { + return $byRefVariableNames; + } + + foreach ($functionLike->uses as $closureUse) { + if ($closureUse->byRef && ! $closureUse->var->name instanceof Expr) { + $closureUse->var->setAttribute(AttributeKey::IS_BYREF_VAR, true); + + /** @var string $closureVarName */ + $closureVarName = $closureUse->var->name; + $byRefVariableNames[] = $closureVarName; + } + } + + return $byRefVariableNames; + } +} diff --git a/src/PhpParser/NodeVisitor/CallLikeThisBoundClosureArgsNodeVisitor.php b/src/PhpParser/NodeVisitor/CallLikeThisBoundClosureArgsNodeVisitor.php new file mode 100644 index 00000000000..96a5ce9c64a --- /dev/null +++ b/src/PhpParser/NodeVisitor/CallLikeThisBoundClosureArgsNodeVisitor.php @@ -0,0 +1,52 @@ +isFirstClassCallable()) { + return null; + } + + $args = $this->callLikeExpectsThisBindedClosureArgsAnalyzer->getArgsUsingThisBoundClosure($node); + + if ($args === []) { + return null; + } + + foreach ($args as $arg) { + if ($arg->value instanceof Closure && ! $arg->hasAttribute(AttributeKey::IS_CLOSURE_USES_THIS)) { + $arg->value->setAttribute(AttributeKey::IS_CLOSURE_USES_THIS, true); + } + } + + return $node; + } +} diff --git a/src/PhpParser/NodeVisitor/ClassConstFetchNodeVisitor.php b/src/PhpParser/NodeVisitor/ClassConstFetchNodeVisitor.php new file mode 100644 index 00000000000..3d9e66587dc --- /dev/null +++ b/src/PhpParser/NodeVisitor/ClassConstFetchNodeVisitor.php @@ -0,0 +1,31 @@ +name instanceof Identifier) { + return null; + } + + $node->class->setAttribute(AttributeKey::CLASS_CONST_FETCH_NAME, $node->name->toString()); + + return null; + } +} diff --git a/src/PhpParser/NodeVisitor/ClosureWithVariadicParametersNodeVisitor.php b/src/PhpParser/NodeVisitor/ClosureWithVariadicParametersNodeVisitor.php new file mode 100644 index 00000000000..0d9cb1ca054 --- /dev/null +++ b/src/PhpParser/NodeVisitor/ClosureWithVariadicParametersNodeVisitor.php @@ -0,0 +1,70 @@ +isFirstClassCallable()) { + return null; + } + + if ($node->getArgs() === []) { + return null; + } + + $methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + + foreach ($node->getArgs() as $arg) { + if (! $arg->value instanceof Closure && ! $arg->value instanceof ArrowFunction) { + continue; + } + + if ($methodReflection instanceof NativeFunctionReflection) { + $parametersAcceptors = ParametersAcceptorSelector::combineAcceptors( + $methodReflection->getVariants() + ); + + foreach ($parametersAcceptors->getParameters() as $extendedParameterReflection) { + if ($extendedParameterReflection->getType() instanceof CallableType && $extendedParameterReflection->getType() ->isVariadic()) { + $arg->value->setAttribute(AttributeKey::HAS_CLOSURE_WITH_VARIADIC_ARGS, true); + } + } + + return null; + } + + $arg->value->setAttribute(AttributeKey::HAS_CLOSURE_WITH_VARIADIC_ARGS, true); + } + + return null; + } +} diff --git a/src/PhpParser/NodeVisitor/ContextNodeVisitor.php b/src/PhpParser/NodeVisitor/ContextNodeVisitor.php new file mode 100644 index 00000000000..4d147b6a9cb --- /dev/null +++ b/src/PhpParser/NodeVisitor/ContextNodeVisitor.php @@ -0,0 +1,193 @@ +processContextInLoop($node); + return null; + } + + if ($node instanceof ArrayDimFetch) { + $this->processInsideArrayDimFetch($node); + return null; + } + + if ($node instanceof Unset_) { + foreach ($node->vars as $var) { + $var->setAttribute(AttributeKey::IS_UNSET_VAR, true); + } + + return null; + } + + if ($node instanceof TryCatch) { + SimpleNodeTraverser::decorateWithAttributeValue($node->stmts, AttributeKey::IS_IN_TRY_BLOCK, true); + + return null; + } + + if ($node instanceof Isset_) { + foreach ($node->vars as $var) { + $var->setAttribute(AttributeKey::IS_ISSET_VAR, true); + } + + return null; + } + + if ($node instanceof Attribute) { + $this->processContextInAttribute($node); + return null; + } + + if ($node instanceof If_ || $node instanceof Else_ || $node instanceof ElseIf_) { + $this->processContextInIf($node); + return null; + } + + if ($node instanceof Arg) { + $node->value->setAttribute(AttributeKey::IS_ARG_VALUE, true); + return null; + } + + if ($node instanceof Param) { + $node->var->setAttribute(AttributeKey::IS_PARAM_VAR, true); + return null; + } + + if ($node instanceof PostDec || $node instanceof PostInc || $node instanceof PreDec || $node instanceof PreInc) { + $node->var->setAttribute(AttributeKey::IS_INCREMENT_OR_DECREMENT, true); + return null; + } + + if ($node instanceof BooleanAnd) { + $node->right->setAttribute(AttributeKey::IS_RIGHT_AND, true); + return null; + } + + $this->processContextInClass($node); + return null; + } + + private function processInsideArrayDimFetch(ArrayDimFetch $arrayDimFetch): void + { + if ($arrayDimFetch->var instanceof PropertyFetch || $arrayDimFetch->var instanceof StaticPropertyFetch) { + $arrayDimFetch->var->setAttribute(AttributeKey::INSIDE_ARRAY_DIM_FETCH, true); + } + } + + private function processContextInClass(Node $node): void + { + if ($node instanceof Class_) { + if ($node->extends instanceof FullyQualified) { + $node->extends->setAttribute(AttributeKey::IS_CLASS_EXTENDS, true); + } + + foreach ($node->implements as $implement) { + $implement->setAttribute(AttributeKey::IS_CLASS_IMPLEMENT, true); + } + } + } + + private function processContextInAttribute(Attribute $attribute): void + { + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $attribute->args, + static function (Node $subNode): null { + if ($subNode instanceof Array_) { + $subNode->setAttribute(AttributeKey::IS_ARRAY_IN_ATTRIBUTE, true); + } + + if ($subNode instanceof Closure) { + $subNode->setAttribute(AttributeKey::IS_CLOSURE_IN_ATTRIBUTE, true); + } + + return null; + } + ); + } + + private function processContextInIf(If_|Else_|ElseIf_ $node): void + { + foreach ($node->stmts as $stmt) { + if ($stmt instanceof Break_) { + $stmt->setAttribute(AttributeKey::IS_IN_IF, true); + } + } + } + + private function processContextInLoop(For_|Foreach_|While_|Do_|Switch_ $node): void + { + if ($node instanceof Foreach_) { + if ($node->keyVar instanceof Variable) { + $node->keyVar->setAttribute(AttributeKey::IS_VARIABLE_LOOP, true); + } + + $node->valueVar->setAttribute(AttributeKey::IS_VARIABLE_LOOP, true); + } + + $stmts = $node instanceof Switch_ ? $node->cases : $node->stmts; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $stmts, + static function (Node $subNode): ?int { + if ($subNode instanceof Class_ || $subNode instanceof Function_ || $subNode instanceof Closure) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($subNode instanceof If_ || $subNode instanceof Break_) { + $subNode->setAttribute(AttributeKey::IS_IN_LOOP_OR_SWITCH, true); + } + + return null; + } + ); + } +} diff --git a/src/PhpParser/NodeVisitor/CreatedByRuleNodeVisitor.php b/src/PhpParser/NodeVisitor/CreatedByRuleNodeVisitor.php deleted file mode 100644 index bc70549d4c3..00000000000 --- a/src/PhpParser/NodeVisitor/CreatedByRuleNodeVisitor.php +++ /dev/null @@ -1,23 +0,0 @@ -setAttribute(AttributeKey::CREATED_BY_RULE, $this->rectorClass); - return $node; - } -} diff --git a/src/PhpParser/NodeVisitor/GlobalVariableNodeVisitor.php b/src/PhpParser/NodeVisitor/GlobalVariableNodeVisitor.php new file mode 100644 index 00000000000..f872d0c4279 --- /dev/null +++ b/src/PhpParser/NodeVisitor/GlobalVariableNodeVisitor.php @@ -0,0 +1,95 @@ +stmts === null) { + return null; + } + + /** @var string[] $globalVariableNames */ + $globalVariableNames = []; + + foreach ($node->stmts as $stmt) { + if (! $stmt instanceof Global_) { + $this->setIsGlobalVarAttribute($stmt, $globalVariableNames); + continue; + } + + foreach ($stmt->vars as $variable) { + if ($variable instanceof Variable && ! $variable->name instanceof Expr) { + $variable->setAttribute(AttributeKey::IS_GLOBAL_VAR, true); + + /** @var string $variableName */ + $variableName = $variable->name; + $globalVariableNames[] = $variableName; + } + } + } + + return null; + } + + /** + * @param string[] $globalVariableNames + */ + private function setIsGlobalVarAttribute(Stmt $stmt, array $globalVariableNames): void + { + if ($globalVariableNames === []) { + return; + } + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $stmt, + static function (Node $subNode) use ($globalVariableNames): int|null|Variable { + if ($subNode instanceof Class_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $subNode instanceof Variable) { + return null; + } + + if ($subNode->name instanceof Expr) { + return null; + } + + if (! in_array($subNode->name, $globalVariableNames, true)) { + return null; + } + + $subNode->setAttribute(AttributeKey::IS_GLOBAL_VAR, true); + return $subNode; + } + ); + } +} diff --git a/src/PhpParser/NodeVisitor/NameNodeVisitor.php b/src/PhpParser/NodeVisitor/NameNodeVisitor.php new file mode 100644 index 00000000000..5fbd56bb35f --- /dev/null +++ b/src/PhpParser/NodeVisitor/NameNodeVisitor.php @@ -0,0 +1,47 @@ +name instanceof Name) { + $node->name->setAttribute(AttributeKey::IS_FUNCCALL_NAME, true); + return null; + } + + if ($node instanceof ConstFetch) { + $node->name->setAttribute(AttributeKey::IS_CONSTFETCH_NAME, true); + return null; + } + + if ($node instanceof New_ && $node->class instanceof Name) { + $node->class->setAttribute(AttributeKey::IS_NEW_INSTANCE_NAME, true); + return null; + } + + if (! $node instanceof StaticCall) { + return null; + } + + if (! $node->class instanceof Name) { + return null; + } + + $node->class->setAttribute(AttributeKey::IS_STATICCALL_CLASS_NAME, true); + return null; + } +} diff --git a/src/PhpParser/NodeVisitor/ParamDefaultNodeVisitor.php b/src/PhpParser/NodeVisitor/ParamDefaultNodeVisitor.php new file mode 100644 index 00000000000..68822bf50f1 --- /dev/null +++ b/src/PhpParser/NodeVisitor/ParamDefaultNodeVisitor.php @@ -0,0 +1,31 @@ +default instanceof Expr) { + return null; + } + + SimpleNodeTraverser::decorateWithAttributeValue($node->default, AttributeKey::IS_PARAM_DEFAULT, true); + + return null; + } +} diff --git a/src/PhpParser/NodeVisitor/PhpVersionConditionNodeVisitor.php b/src/PhpParser/NodeVisitor/PhpVersionConditionNodeVisitor.php new file mode 100644 index 00000000000..8fb36770664 --- /dev/null +++ b/src/PhpParser/NodeVisitor/PhpVersionConditionNodeVisitor.php @@ -0,0 +1,52 @@ +hasVersionCompareCond($node)) { + if ($node instanceof Ternary) { + $nodes = [$node->else]; + if ($node->if instanceof Node) { + $nodes[] = $node->if; + } + } else { + $nodes = $node->stmts; + } + + SimpleNodeTraverser::decorateWithAttributeValue($nodes, AttributeKey::PHP_VERSION_CONDITIONED, true); + } + + return null; + } + + private function hasVersionCompareCond(If_|Ternary $ifOrTernary): bool + { + if (! $ifOrTernary->cond instanceof FuncCall) { + return false; + } + + $versionCompare = $this->conditionResolver->resolveFromExpr($ifOrTernary->cond); + return $versionCompare instanceof VersionCompareCondition; + } +} diff --git a/src/PhpParser/NodeVisitor/PropertyOrClassConstDefaultNodeVisitor.php b/src/PhpParser/NodeVisitor/PropertyOrClassConstDefaultNodeVisitor.php new file mode 100644 index 00000000000..e4ca8759d47 --- /dev/null +++ b/src/PhpParser/NodeVisitor/PropertyOrClassConstDefaultNodeVisitor.php @@ -0,0 +1,47 @@ +props as $propertyItem) { + $default = $propertyItem->default; + if (! $default instanceof Expr) { + continue; + } + + SimpleNodeTraverser::decorateWithAttributeValue( + $default, + AttributeKey::IS_DEFAULT_PROPERTY_VALUE, + true + ); + } + } + + if ($node instanceof ClassConst) { + foreach ($node->consts as $const) { + SimpleNodeTraverser::decorateWithAttributeValue( + $const->value, + AttributeKey::IS_CLASS_CONST_VALUE, + true + ); + } + } + + return null; + } +} diff --git a/src/PhpParser/NodeVisitor/StaticVariableNodeVisitor.php b/src/PhpParser/NodeVisitor/StaticVariableNodeVisitor.php new file mode 100644 index 00000000000..43829b955b5 --- /dev/null +++ b/src/PhpParser/NodeVisitor/StaticVariableNodeVisitor.php @@ -0,0 +1,96 @@ +stmts === null) { + return null; + } + + /** @var string[] $staticVariableNames */ + $staticVariableNames = []; + + foreach ($node->stmts as $stmt) { + if (! $stmt instanceof Static_) { + $this->setIsStaticVarAttribute($stmt, $staticVariableNames); + continue; + } + + foreach ($stmt->vars as $staticVar) { + $staticVariableName = $staticVar->var->name; + + if (! is_string($staticVariableName)) { + continue; + } + + $staticVar->var->setAttribute(AttributeKey::IS_STATIC_VAR, true); + $staticVariableNames[] = $staticVariableName; + } + } + + return null; + } + + /** + * @param string[] $staticVariableNames + */ + private function setIsStaticVarAttribute(Stmt $stmt, array $staticVariableNames): void + { + if ($staticVariableNames === []) { + return; + } + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable( + $stmt, + static function (Node $subNode) use ($staticVariableNames): int|null|Variable { + if ($subNode instanceof Class_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $subNode instanceof Variable) { + return null; + } + + if ($subNode->name instanceof Expr) { + return null; + } + + if (! in_array($subNode->name, $staticVariableNames, true)) { + return null; + } + + $subNode->setAttribute(AttributeKey::IS_STATIC_VAR, true); + return $subNode; + } + ); + } +} diff --git a/src/PhpParser/NodeVisitor/SymfonyClosureNodeVisitor.php b/src/PhpParser/NodeVisitor/SymfonyClosureNodeVisitor.php new file mode 100644 index 00000000000..d6df91cb96c --- /dev/null +++ b/src/PhpParser/NodeVisitor/SymfonyClosureNodeVisitor.php @@ -0,0 +1,40 @@ +symfonyPhpClosureDetector->detect($node)) { + return null; + } + + SimpleNodeTraverser::decorateWithAttributeValue( + $node->stmts, + AttributeKey::IS_INSIDE_SYMFONY_PHP_CLOSURE, + true + ); + + return null; + } +} diff --git a/src/PhpParser/Parser/InlineCodeParser.php b/src/PhpParser/Parser/InlineCodeParser.php index 322b487ef2f..ca64905f5e5 100644 --- a/src/PhpParser/Parser/InlineCodeParser.php +++ b/src/PhpParser/Parser/InlineCodeParser.php @@ -2,98 +2,183 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Parser; +namespace Rector\PhpParser\Parser; +use Nette\Utils\FileSystem; use Nette\Utils\Strings; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Concat; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\StaticPropertyFetch; -use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Scalar\Encapsed; +use PhpParser\Node\Scalar\InterpolatedString; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt; -use PhpParser\Parser; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\PhpParser\Printer\BetterStandardPrinter; -use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator; -use Symplify\SmartFileSystem\SmartFileSystem; +use Rector\PhpParser\Node\Value\ValueResolver; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Util\StringUtils; -final class InlineCodeParser +final readonly class InlineCodeParser { /** - * @var string * @see https://regex101.com/r/dwe4OW/1 */ - private const PRESLASHED_DOLLAR_REGEX = '#\\\\\$#'; + private const string PRESLASHED_DOLLAR_REGEX = '#\\\\\$#'; /** - * @var string * @see https://regex101.com/r/tvwhWq/1 */ - private const CURLY_BRACKET_WRAPPER_REGEX = "#'{(\\\$.*?)}'#"; + private const string CURLY_BRACKET_WRAPPER_REGEX = "#'{(\\\$.*?)}'#"; /** - * @var string * @see https://regex101.com/r/TBlhoR/1 */ - private const OPEN_PHP_TAG_REGEX = '#^\<\?php\s+#'; + private const string OPEN_PHP_TAG_REGEX = '#^\<\?php\s+#'; /** - * @var string * @see https://regex101.com/r/TUWwKw/1/ */ - private const ENDING_SEMI_COLON_REGEX = '#;(\s+)?$#'; + private const string ENDING_SEMI_COLON_REGEX = '#;(\s+)?$#'; + + /** + * @see https://regex101.com/r/8fDjnR/1 + */ + private const string VARIABLE_IN_SINGLE_QUOTED_REGEX = '#\'(?\$.*)\'#U'; + + /** + * @see https://regex101.com/r/1lzQZv/1 + */ + private const string BACKREFERENCE_NO_QUOTE_REGEX = '#(?\\\\\d+)(?!")#'; + + /** + * @see https://regex101.com/r/nSO3Eq/1 + */ + private const string BACKREFERENCE_NO_DOUBLE_QUOTE_START_REGEX = '#(?\$\d+)#'; + + /** + * @see https://regex101.com/r/13mVVg/1 + */ + private const string HEX_BACKREFERENCE_REGEX = '#0x(?\$\d+)#'; public function __construct( private BetterStandardPrinter $betterStandardPrinter, - private NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator, - private Parser $parser, - private SmartFileSystem $smartFileSystem + private SimplePhpParser $simplePhpParser, + private ValueResolver $valueResolver, ) { } /** + * @api downgrade + * * @return Stmt[] */ - public function parse(string $content): array + public function parseFile(string $fileName): array { - // to cover files too - if (is_file($content)) { - $content = $this->smartFileSystem->readFile($content); - } - - // wrap code so php-parser can interpret it - $content = Strings::match($content, self::OPEN_PHP_TAG_REGEX) ? $content : 'parseCode($fileContent); + } - $nodes = (array) $this->parser->parse($content); - return $this->nodeScopeAndMetadataDecorator->decorateNodesFromString($nodes); + /** + * @return Stmt[] + */ + public function parseString(string $fileContent): array + { + return $this->parseCode($fileContent); } public function stringify(Expr $expr): string { if ($expr instanceof String_) { - return $expr->value; + if (! str_contains($expr->value, "'") && ! str_contains($expr->value, '"') && StringUtils::isMatch( + $expr->value, + self::HEX_BACKREFERENCE_REGEX + )) { + return Strings::replace( + $expr->value, + self::HEX_BACKREFERENCE_REGEX, + static function (array $match): string { + $number = ltrim((string) $match['backreference'], '\\$'); + return 'hexdec($matches[' . $number . '])'; + } + ); + } + + if (! StringUtils::isMatch($expr->value, self::BACKREFERENCE_NO_QUOTE_REGEX)) { + return Strings::replace( + $expr->value, + self::BACKREFERENCE_NO_DOUBLE_QUOTE_START_REGEX, + static fn (array $match): string => '"' . $match['backreference'] . '"' + ); + } + + return Strings::replace( + $expr->value, + self::BACKREFERENCE_NO_QUOTE_REGEX, + static fn (array $match): string => '"\\' . $match['backreference'] . '"' + ); } - if ($expr instanceof Encapsed) { - // remove " - $expr = trim($this->betterStandardPrinter->print($expr), '""'); - // use \$ → $ - $expr = Strings::replace($expr, self::PRESLASHED_DOLLAR_REGEX, '$'); - // use \'{$...}\' → $... - return Strings::replace($expr, self::CURLY_BRACKET_WRAPPER_REGEX, '$1'); + if ($expr instanceof InterpolatedString) { + return $this->resolveEncapsedValue($expr); } if ($expr instanceof Concat) { - return $this->stringify($expr->left) . $this->stringify($expr->right); + return $this->resolveConcatValue($expr); + } + + return $this->betterStandardPrinter->print($expr); + } + + /** + * @return Stmt[] + */ + private function parseCode(string $code): array + { + // wrap code so php-parser can interpret it + $code = StringUtils::isMatch($code, self::OPEN_PHP_TAG_REGEX) ? $code : 'simplePhpParser->parseString($code); + } + + private function resolveEncapsedValue(InterpolatedString $interpolatedString): string + { + $value = ''; + $isRequirePrint = false; + foreach ($interpolatedString->parts as $part) { + $partValue = (string) $this->valueResolver->getValue($part); + if (str_ends_with($partValue, "'")) { + $isRequirePrint = true; + break; + } + + $value .= $partValue; + } + + $printedExpr = $isRequirePrint ? $this->betterStandardPrinter->print($interpolatedString) : $value; + + // remove " + $printedExpr = trim($printedExpr, '""'); + + // use \$ → $ + $printedExpr = Strings::replace($printedExpr, self::PRESLASHED_DOLLAR_REGEX, '$'); + // use \'{$...}\' → $... + return Strings::replace($printedExpr, self::CURLY_BRACKET_WRAPPER_REGEX, '$1'); + } + + private function resolveConcatValue(Concat $concat): string + { + if ($concat->left instanceof Concat && + $concat->right instanceof String_ && str_starts_with($concat->right->value, '$')) { + $concat->right->value = '.' . $concat->right->value; } - if ($expr instanceof Variable || $expr instanceof PropertyFetch || $expr instanceof StaticPropertyFetch) { - return $this->betterStandardPrinter->print($expr); + if ($concat->right instanceof String_ && str_starts_with($concat->right->value, '($')) { + $concat->right->value .= '.'; } - throw new ShouldNotHappenException($expr::class . ' ' . __METHOD__); + $string = $this->stringify($concat->left) . $this->stringify($concat->right); + return Strings::replace( + $string, + self::VARIABLE_IN_SINGLE_QUOTED_REGEX, + static fn (array $match): string => (string) $match['variable'] + ); } } diff --git a/src/PhpParser/Parser/NikicPhpParserFactory.php b/src/PhpParser/Parser/NikicPhpParserFactory.php deleted file mode 100644 index d53c6207ebd..00000000000 --- a/src/PhpParser/Parser/NikicPhpParserFactory.php +++ /dev/null @@ -1,28 +0,0 @@ -parserFactory->create(ParserFactory::PREFER_PHP7, $this->lexer, [ - 'useIdentifierNodes' => true, - 'useConsistentVariableNodes' => true, - 'useExpressionStatements' => true, - 'useNopStatements' => false, - ]); - } -} diff --git a/src/PhpParser/Parser/Parser.php b/src/PhpParser/Parser/Parser.php deleted file mode 100644 index 966eba7597d..00000000000 --- a/src/PhpParser/Parser/Parser.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ - private array $nodesByFile = []; - - public function __construct( - private NikicParser $nikicParser, - private SmartFileSystem $smartFileSystem - ) { - } - - /** - * @return Stmt[] - */ - public function parseFileInfo(SmartFileInfo $smartFileInfo): array - { - $fileRealPath = $smartFileInfo->getRealPath(); - - if (isset($this->nodesByFile[$fileRealPath])) { - return $this->nodesByFile[$fileRealPath]; - } - - $fileContent = $this->smartFileSystem->readFile($fileRealPath); - - $nodes = $this->nikicParser->parse($fileContent); - if ($nodes === null) { - $nodes = []; - } - - $this->nodesByFile[$fileRealPath] = $nodes; - return $this->nodesByFile[$fileRealPath]; - } -} diff --git a/src/PhpParser/Parser/ParserErrors.php b/src/PhpParser/Parser/ParserErrors.php new file mode 100644 index 00000000000..d852a96ca96 --- /dev/null +++ b/src/PhpParser/Parser/ParserErrors.php @@ -0,0 +1,30 @@ +message = $parserErrorsException->getMessage(); + $this->line = $parserErrorsException->getAttributes()['startLine'] ?? $parserErrorsException->getLine(); + } + + public function getMessage(): string + { + return $this->message; + } + + public function getLine(): int + { + return $this->line; + } +} diff --git a/src/PhpParser/Parser/PhpParserLexerFactory.php b/src/PhpParser/Parser/PhpParserLexerFactory.php deleted file mode 100644 index e1ef7f5e4d7..00000000000 --- a/src/PhpParser/Parser/PhpParserLexerFactory.php +++ /dev/null @@ -1,23 +0,0 @@ - ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'], - // do not set php version, so we have a lexer that can parse any PHP version available, - // helps with issues like https://github.com/rectorphp/rector/issues/6593 - ]); - } -} diff --git a/src/PhpParser/Parser/RectorParser.php b/src/PhpParser/Parser/RectorParser.php new file mode 100644 index 00000000000..81f72aeb206 --- /dev/null +++ b/src/PhpParser/Parser/RectorParser.php @@ -0,0 +1,75 @@ +parser->parseFile($filePath); + } + + /** + * @return Stmt[] + */ + public function parseString(string $fileContent): array + { + return $this->parser->parseString($fileContent); + } + + public function parseFileContentToStmtsAndTokens( + string $fileContent, + bool $forNewestSupportedVersion = true + ): StmtsAndTokens { + if (! $forNewestSupportedVersion) { + // don't directly change PHPStan Parser service + // to avoid reuse on next file + $phpstanParser = clone $this->parser; + + $parserFactory = new ParserFactory(); + $parser = $parserFactory->createForVersion(PhpVersion::fromString('7.0')); + $this->privatesAccessor->setPrivateProperty($phpstanParser, 'parser', $parser); + + return $this->resolveStmtsAndTokens($phpstanParser, $fileContent); + } + + return $this->resolveStmtsAndTokens($this->parser, $fileContent); + } + + private function resolveStmtsAndTokens(Parser $parser, string $fileContent): StmtsAndTokens + { + $stmts = $parser->parseString($fileContent); + + $innerParser = $this->privatesAccessor->getPrivateProperty($parser, 'parser'); + $tokens = $innerParser->getTokens(); + + return new StmtsAndTokens($stmts, $tokens); + } +} diff --git a/src/PhpParser/Parser/SimplePhpParser.php b/src/PhpParser/Parser/SimplePhpParser.php index 419d372d56b..b1afa8cf229 100644 --- a/src/PhpParser/Parser/SimplePhpParser.php +++ b/src/PhpParser/Parser/SimplePhpParser.php @@ -2,37 +2,97 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Parser; +namespace Rector\PhpParser\Parser; +use Nette\Utils\FileSystem; use PhpParser\Node; +use PhpParser\Node\Stmt\Expression; use PhpParser\NodeTraverser; -use PhpParser\NodeVisitor\NodeConnectingVisitor; use PhpParser\Parser; -use Symplify\SmartFileSystem\SmartFileSystem; +use PhpParser\ParserFactory; +use Rector\PhpParser\NodeVisitor\AssignedToNodeVisitor; +use Throwable; -final class SimplePhpParser +final readonly class SimplePhpParser { - public function __construct( - private Parser $parser, - private SmartFileSystem $smartFileSystem - ) { + private Parser $phpParser; + + private NodeTraverser $nodeTraverser; + + public function __construct() + { + $parserFactory = new ParserFactory(); + $this->phpParser = $parserFactory->createForNewestSupportedVersion(); + + $this->nodeTraverser = new NodeTraverser(new AssignedToNodeVisitor()); } /** + * @api tests * @return Node[] */ public function parseFile(string $filePath): array { - $fileContent = $this->smartFileSystem->readFile($filePath); - $nodes = $this->parser->parse($fileContent); + $fileContent = FileSystem::read($filePath); + return $this->parseString($fileContent); + } + + /** + * @return Node[] + */ + public function parseString(string $fileContent): array + { + $fileContent = $this->ensureFileContentsHasOpeningTag($fileContent); + + $hasAddedSemicolon = false; + + try { + $nodes = $this->phpParser->parse($fileContent); + } catch (Throwable) { + // try adding missing closing semicolon ; + $fileContent .= ';'; + $hasAddedSemicolon = true; + $nodes = $this->phpParser->parse($fileContent); + } if ($nodes === null) { return []; } - $nodeTraverser = new NodeTraverser(); - $nodeTraverser->addVisitor(new NodeConnectingVisitor()); + $nodes = $this->restoreExpressionPreWrap($nodes, $hasAddedSemicolon); + return $this->nodeTraverser->traverse($nodes); + } + + private function ensureFileContentsHasOpeningTag(string $fileContent): string + { + if (! str_starts_with(trim($fileContent), 'traverse($nodes); + return [$onlyStmt->expr]; } } diff --git a/src/PhpParser/Printer/BetterStandardPrinter.php b/src/PhpParser/Printer/BetterStandardPrinter.php index 688e500b843..9dac4166179 100644 --- a/src/PhpParser/Printer/BetterStandardPrinter.php +++ b/src/PhpParser/Printer/BetterStandardPrinter.php @@ -2,89 +2,59 @@ declare(strict_types=1); -namespace Rector\Core\PhpParser\Printer; +namespace Rector\PhpParser\Printer; use Nette\Utils\Strings; +use Override; +use PhpParser\Comment; +use PhpParser\Internal\TokenStream; use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\ArrowFunction; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\Expr\BinaryOp\Pipe; +use PhpParser\Node\Expr\CallLike; +use PhpParser\Node\Expr\Instanceof_; +use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Yield_; -use PhpParser\Node\Name; -use PhpParser\Node\Name\FullyQualified; -use PhpParser\Node\Scalar\DNumber; -use PhpParser\Node\Scalar\EncapsedStringPart; +use PhpParser\Node\InterpolatedStringPart; +use PhpParser\Node\Scalar\InterpolatedString; use PhpParser\Node\Scalar\String_; -use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Declare_; -use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\InlineHTML; use PhpParser\Node\Stmt\Nop; -use PhpParser\Node\Stmt\TraitUse; -use PhpParser\Node\Stmt\Use_; use PhpParser\PrettyPrinter\Standard; -use Rector\Comments\NodeDocBlock\DocBlockUpdater; -use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace; -use Rector\Core\PhpParser\Printer\Whitespace\IndentCharacterDetector; +use PhpParser\Token; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\NodeAnalyzer\ExprAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\PhpParser\Node\FileNode; +use Rector\Util\NewLineSplitter; +use Rector\Util\Reflection\PrivatesAccessor; /** - * @see \Rector\Core\Tests\PhpParser\Printer\BetterStandardPrinterTest + * @see \Rector\Tests\PhpParser\Printer\BetterStandardPrinterTest + * + * @property array $insertionMap */ final class BetterStandardPrinter extends Standard { - /** - * @var string - * @see https://regex101.com/r/jUFizd/1 - */ - private const NEWLINE_END_REGEX = "#\n$#"; - - /** - * @var string - * @see https://regex101.com/r/F5x783/1 - */ - private const USE_REGEX = '#( use)\(#'; - - /** - * @var string - * @see https://regex101.com/r/DrsMY4/1 - */ - private const QUOTED_SLASH_REGEX = "#'|\\\\(?=[\\\\']|$)#"; - /** * Remove extra spaces before new Nop_ nodes * @see https://regex101.com/r/iSvroO/1 - * @var string - */ - private const EXTRA_SPACE_BEFORE_NOP_REGEX = '#^[ \t]+$#m'; - - /** - * @see https://regex101.com/r/qZiqGo/13 - * @var string - */ - private const REPLACE_COLON_WITH_SPACE_REGEX = '#(^.*function .*\(.*\)) : #'; - - /** - * Use space by default */ - private string $tabOrSpaceIndentCharacter = ' '; + private const string EXTRA_SPACE_BEFORE_NOP_REGEX = '#^[ \t]+$#m'; - /** - * @param mixed[] $options - */ public function __construct( - private IndentCharacterDetector $indentCharacterDetector, - private DocBlockUpdater $docBlockUpdater, - array $options = [] + private readonly ExprAnalyzer $exprAnalyzer, + private readonly PrivatesAccessor $privatesAccessor, ) { - parent::__construct($options); - - // print return type double colon right after the bracket "function(): string" - $this->initializeInsertionMap(); - $this->insertionMap['Stmt_ClassMethod->returnType'] = [')', false, ': ', null]; - $this->insertionMap['Stmt_Function->returnType'] = [')', false, ': ', null]; - $this->insertionMap['Expr_Closure->returnType'] = [')', false, ': ', null]; - $this->insertionMap['Expr_ArrowFunction->returnType'] = [')', false, ': ', null]; + parent::__construct(); } /** @@ -92,17 +62,16 @@ public function __construct( * @param Node[] $origStmts * @param mixed[] $origTokens */ + #[Override] public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string { - $newStmts = $this->resolveNewStmts($stmts); - - // detect per print - $this->tabOrSpaceIndentCharacter = $this->indentCharacterDetector->detect($origTokens); + $newStmts = $this->unwrapFileNode($stmts); + $origStmts = $this->unwrapFileNode($origStmts); $content = parent::printFormatPreserving($newStmts, $origStmts, $origTokens); // add new line in case of added stmts - if (count($stmts) !== count($origStmts) && ! (bool) Strings::match($content, self::NEWLINE_END_REGEX)) { + if (count($newStmts) !== count($origStmts) && ! str_ends_with($content, "\n")) { $content .= $this->nl; } @@ -114,6 +83,7 @@ public function printFormatPreserving(array $stmts, array $origStmts, array $ori */ public function print(Node | array | null $node): string { + if ($node === null) { $node = []; } @@ -122,12 +92,15 @@ public function print(Node | array | null $node): string $node = [$node]; } + $node = $this->unwrapFileNode($node); + return $this->prettyPrint($node); } /** * @param Node[] $stmts */ + #[Override] public function prettyPrintFile(array $stmts): string { // to keep indexes from 0 @@ -136,73 +109,136 @@ public function prettyPrintFile(array $stmts): string return parent::prettyPrintFile($stmts) . PHP_EOL; } - public function pFileWithoutNamespace(FileWithoutNamespace $fileWithoutNamespace): string + /** + * Use for standalone InterpolatedStringPart printing, that is not support by php-parser natively. + * Used e.g. in \Rector\PhpParser\Comparing\NodeComparator::printWithoutComments + */ + protected function pInterpolatedStringPart(InterpolatedStringPart $interpolatedStringPart): string { - $content = $this->pStmts($fileWithoutNamespace->stmts, false); + return $interpolatedStringPart->value; + } + + #[Override] + protected function p( + Node $node, + int $precedence = self::MAX_PRECEDENCE, + int $lhsPrecedence = self::MAX_PRECEDENCE, + bool $parentFormatPreserved = false + ): string { + $this->wrapBinaryOpWithBrackets($node); + + $content = parent::p($node, $precedence, $lhsPrecedence, $parentFormatPreserved); + + // remove once its fixed in php-parser, https://github.com/nikic/PHP-Parser/pull/1126 + if ($node instanceof CallLike) { + $this->cleanVariadicPlaceHolderTrailingComma($node); + } + + if ($node->getAttribute(AttributeKey::WRAPPED_IN_PARENTHESES)) { + return '(' . $content . ')'; + } + + return $content; + } - return ltrim($content); + protected function pStmt_FileNode(FileNode $fileNode): string + { + return $this->pStmts($fileNode->stmts); + } + + #[Override] + protected function pExpr_ArrowFunction(ArrowFunction $arrowFunction, int $precedence, int $lhsPrecedence): string + { + if (! $arrowFunction->hasAttribute(AttributeKey::COMMENTS)) { + return parent::pExpr_ArrowFunction($arrowFunction, $precedence, $lhsPrecedence); + } + + $expr = $arrowFunction->expr; + + /** @var Comment[] $comments */ + $comments = $expr->getAttribute(AttributeKey::COMMENTS) ?? []; + if ($comments === []) { + return parent::pExpr_ArrowFunction($arrowFunction, $precedence, $lhsPrecedence); + } + + $isMaxPrecedence = $precedence === self::MAX_PRECEDENCE; + $isNewLineAndIndent = $arrowFunction->getAttribute(AttributeKey::IS_ARG_VALUE) === true; + $indent = $this->resolveIndentSpaces($isMaxPrecedence); + + $text = $isMaxPrecedence ? '' : "\n" . $indent; + if ($isNewLineAndIndent) { + $indent = $this->resolveIndentSpaces(); + $text = "\n" . $indent; + } + + foreach ($comments as $key => $comment) { + $commentText = $key > 0 ? $indent . $comment->getText() : $comment->getText(); + $text .= $commentText . "\n"; + } + + return $text . $indent . parent::pExpr_ArrowFunction($arrowFunction, $precedence, $lhsPrecedence); } /** * This allows to use both spaces and tabs vs. original space-only */ + #[Override] protected function setIndentLevel(int $level): void { $level = max($level, 0); $this->indentLevel = $level; - $this->nl = "\n" . str_repeat($this->tabOrSpaceIndentCharacter, $level); + $this->nl = "\n" . str_repeat($this->getIndentCharacter(), $level); } /** * This allows to use both spaces and tabs vs. original space-only */ + #[Override] protected function indent(): void { - $multiplier = $this->tabOrSpaceIndentCharacter === ' ' ? 4 : 1; + $indentSize = SimpleParameterProvider::provideIntParameter(Option::INDENT_SIZE); - $this->indentLevel += $multiplier; - $this->nl .= str_repeat($this->tabOrSpaceIndentCharacter, $multiplier); + $this->indentLevel += $indentSize; + $this->nl .= str_repeat($this->getIndentCharacter(), $indentSize); } /** * This allows to use both spaces and tabs vs. original space-only */ + #[Override] protected function outdent(): void { - if ($this->tabOrSpaceIndentCharacter === ' ') { - // - 4 spaces - assert($this->indentLevel >= 4); - $this->indentLevel -= 4; + if ($this->getIndentCharacter() === ' ') { + $indentSize = SimpleParameterProvider::provideIntParameter(Option::INDENT_SIZE); + assert($this->indentLevel >= $indentSize); + $this->indentLevel -= $indentSize; } else { // - 1 tab assert($this->indentLevel >= 1); --$this->indentLevel; } - $this->nl = "\n" . str_repeat($this->tabOrSpaceIndentCharacter, $this->indentLevel); + $this->nl = "\n" . str_repeat($this->getIndentCharacter(), $this->indentLevel); } /** * @param mixed[] $nodes * @param mixed[] $origNodes - * @param int|null $fixup */ + #[Override] protected function pArray( array $nodes, array $origNodes, int &$pos, int $indentAdjustment, - string $parentNodeType, + string $parentNodeClass, string $subNodeName, - $fixup + ?int $fixup ): ?string { // reindex positions for printer $nodes = array_values($nodes); - $this->moveCommentsFromAttributeObjectToCommentsAttribute($nodes); - - $content = parent::pArray($nodes, $origNodes, $pos, $indentAdjustment, $parentNodeType, $subNodeName, $fixup); - + $content = parent::pArray($nodes, $origNodes, $pos, $indentAdjustment, $parentNodeClass, $subNodeName, $fixup); if ($content === null) { return $content; } @@ -211,239 +247,301 @@ protected function pArray( return $content; } - return Strings::replace($content, self::EXTRA_SPACE_BEFORE_NOP_REGEX, ''); - } - - /** - * Do not preslash all slashes (parent behavior), but only those: - * - * - followed by "\" - * - by "'" - * - or the end of the string - * - * Prevents `Vendor\Class` => `Vendor\\Class`. - */ - protected function pSingleQuotedString(string $string): string - { - return "'" . Strings::replace($string, self::QUOTED_SLASH_REGEX, '\\\\$0') . "'"; - } - - /** - * Emulates 1_000 in PHP 7.3- version - */ - protected function pScalar_DNumber(DNumber $dNumber): string - { - if (is_string($dNumber->value)) { - return $dNumber->value; - } - - return parent::pScalar_DNumber($dNumber); - } - - /** - * Add space: - * "use(" - * ↓ - * "use (" - */ - protected function pExpr_Closure(Closure $closure): string - { - $closureContent = parent::pExpr_Closure($closure); - - return Strings::replace($closureContent, self::USE_REGEX, '$1 ('); + return Strings::replace($content, self::EXTRA_SPACE_BEFORE_NOP_REGEX); } /** * Do not add "()" on Expressions * @see https://github.com/rectorphp/rector/pull/401#discussion_r181487199 */ - protected function pExpr_Yield(Yield_ $yield): string + #[Override] + protected function pExpr_Yield(Yield_ $yield, int $precedence, int $lhsPrecedence): string { - if ($yield->value === null) { + if (! $yield->value instanceof Expr) { return 'yield'; } - $parentNode = $yield->getAttribute(AttributeKey::PARENT_NODE); - $shouldAddBrackets = ! $parentNode instanceof Expression; + // brackets are needed only in case of assign, @see https://www.php.net/manual/en/language.generators.syntax.php + $shouldAddBrackets = (bool) $yield->getAttribute(AttributeKey::IS_ASSIGNED_TO); return sprintf( '%syield %s%s%s', $shouldAddBrackets ? '(' : '', - $yield->key !== null ? $this->p($yield->key) . ' => ' : '', + $yield->key instanceof Expr ? $this->p($yield->key) . ' => ' : '', $this->p($yield->value), $shouldAddBrackets ? ')' : '' ); } /** - * Print arrays in short [] by default, - * to prevent manual explicit array shortening. + * Print new lined array items when newlined_array_print is set to true */ + #[Override] protected function pExpr_Array(Array_ $array): string { - if (! $array->hasAttribute(AttributeKey::KIND)) { - $array->setAttribute(AttributeKey::KIND, Array_::KIND_SHORT); + if ($array->getAttribute(AttributeKey::NEWLINED_ARRAY_PRINT) === true) { + $printedArray = '['; + $printedArray .= $this->pCommaSeparatedMultiline($array->items, true); + + return $printedArray . ($this->nl . ']'); } return parent::pExpr_Array($array); } + #[Override] + protected function pExpr_BinaryOp_Pipe(Pipe $node, int $precedence, int $lhsPrecedence): string + { + return $this->pInfixOp( + Pipe::class, + $node->left, + "\n" . $this->resolveIndentSpaces() . '|> ', + $node->right, + $precedence, + $lhsPrecedence + ); + } + /** * Fixes escaping of regular patterns */ + #[Override] protected function pScalar_String(String_ $string): string { - $isRegularPattern = $string->getAttribute(AttributeKey::IS_REGULAR_PATTERN); + if ($string->getAttribute(AttributeKey::DOC_INDENTATION) === '__REMOVED__') { + $content = parent::pScalar_String($string); + return $this->cleanStartIndentationOnHeredocNowDoc($content); + } + + $isRegularPattern = (bool) $string->getAttribute(AttributeKey::IS_REGULAR_PATTERN, false); if (! $isRegularPattern) { return parent::pScalar_String($string); } $kind = $string->getAttribute(AttributeKey::KIND, String_::KIND_SINGLE_QUOTED); if ($kind === String_::KIND_DOUBLE_QUOTED) { - return $this->wrapValueWith($string, '"'); + return '"' . $string->value . '"'; } if ($kind === String_::KIND_SINGLE_QUOTED) { - return $this->wrapValueWith($string, "'"); + return "'" . $string->value . "'"; } return parent::pScalar_String($string); } /** - * @param Node[] $nodes + * It remove all spaces extra to parent */ - protected function pStmts(array $nodes, bool $indent = true): string + #[Override] + protected function pStmt_Declare(Declare_ $declare): string { - $this->moveCommentsFromAttributeObjectToCommentsAttribute($nodes); + $declareString = parent::pStmt_Declare($declare); - return parent::pStmts($nodes, $indent); + return Strings::replace($declareString, '#\s+#'); } - /** - * "...$params) : ReturnType" - * ↓ - * "...$params): ReturnType" - */ - protected function pStmt_ClassMethod(ClassMethod $classMethod): string + #[Override] + protected function pExpr_Ternary(Ternary $ternary, int $precedence, int $lhsPrecedence): string { - $content = parent::pStmt_ClassMethod($classMethod); + $kind = $ternary->getAttribute(AttributeKey::KIND); + if ($kind === AttributeKey::WRAPPED_IN_PARENTHESES) { + $pExprTernary = parent::pExpr_Ternary($ternary, $precedence, $lhsPrecedence); + return '(' . $pExprTernary . ')'; + } - // this approach is chosen, to keep changes in parent pStmt_ClassMethod() updated - return Strings::replace($content, self::REPLACE_COLON_WITH_SPACE_REGEX, '$1: '); + return parent::pExpr_Ternary($ternary, $precedence, $lhsPrecedence); } /** - * Clean class and trait from empty "use x;" for traits causing invalid code + * Used in rector-downgrade-php */ - protected function pStmt_Class(Class_ $class): string + #[Override] + protected function pScalar_InterpolatedString(InterpolatedString $interpolatedString): string { - $shouldReindex = false; + $content = parent::pScalar_InterpolatedString($interpolatedString); - foreach ($class->stmts as $key => $stmt) { - // remove empty ones - if ($stmt instanceof TraitUse && $stmt->traits === []) { - unset($class->stmts[$key]); - $shouldReindex = true; - } - } - - if ($shouldReindex) { - $class->stmts = array_values($class->stmts); + if ($interpolatedString->getAttribute(AttributeKey::DOC_INDENTATION) === '__REMOVED__') { + return $this->cleanStartIndentationOnHeredocNowDoc($content); } - return parent::pStmt_Class($class); + return $content; } - /** - * It remove all spaces extra to parent - */ - protected function pStmt_Declare(Declare_ $declare): string + #[Override] + protected function pExpr_MethodCall(MethodCall $methodCall): string { - $declareString = parent::pStmt_Declare($declare); + if (! $methodCall->var instanceof CallLike) { + return parent::pExpr_MethodCall($methodCall); + } - return Strings::replace($declareString, '#\s+#', ''); - } + $globalFlag = SimpleParameterProvider::provideBoolParameter(Option::NEW_LINE_ON_FLUENT_CALL); + $perNodeFlag = (bool) $methodCall->getAttribute(AttributeKey::NEWLINE_ON_FLUENT_CALL, false); - protected function pExpr_Ternary(Ternary $ternary): string - { - $kind = $ternary->getAttribute(AttributeKey::KIND); - if ($kind === 'wrapped_with_brackets') { - $pExprTernary = parent::pExpr_Ternary($ternary); - return '(' . $pExprTernary . ')'; + if ($globalFlag === false && $perNodeFlag === false) { + return parent::pExpr_MethodCall($methodCall); } - return parent::pExpr_Ternary($ternary); + foreach ($methodCall->args as $arg) { + if (! $arg instanceof Arg) { + continue; + } + + $arg->value->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + + return $this->pDereferenceLhs($methodCall->var) . "\n" + . $this->resolveIndentSpaces() . '->' + . $this->pObjectProperty($methodCall->name) + . '(' . $this->pMaybeMultiline($methodCall->args) . ')'; + } + + #[Override] + protected function pInfixOp( + string $class, + Node $leftNode, + string $operatorString, + Node $rightNode, + int $precedence, + int $lhsPrecedence + ): string { + $this->wrapAssign($leftNode, $rightNode); + return parent::pInfixOp($class, $leftNode, $operatorString, $rightNode, $precedence, $lhsPrecedence); + } + + #[Override] + protected function pExpr_Instanceof(Instanceof_ $instanceof, int $precedence, int $lhsPrecedence): string + { + $this->wrapAssign($instanceof->expr, $instanceof->class); + return parent::pExpr_Instanceof($instanceof, $precedence, $lhsPrecedence); } /** - * Remove extra \\ from FQN use imports, for easier use in the code + * @todo remove once https://github.com/nikic/PHP-Parser/pull/1125 is merged and released */ - protected function pStmt_Use(Use_ $use): string + private function cleanVariadicPlaceHolderTrailingComma(CallLike $callLike): void { - if ($use->type !== Use_::TYPE_NORMAL) { - return parent::pStmt_Use($use); + $originalNode = $callLike->getAttribute(AttributeKey::ORIGINAL_NODE); + if (! $originalNode instanceof CallLike) { + return; } - foreach ($use->uses as $useUse) { - if (! $useUse->name instanceof FullyQualified) { + if ($originalNode->isFirstClassCallable()) { + return; + } + + if (! $callLike->isFirstClassCallable()) { + return; + } + + if (! $this->origTokens instanceof TokenStream) { + return; + } + + /** @var Token[] $tokens */ + $tokens = $this->privatesAccessor->getPrivateProperty($this->origTokens, 'tokens'); + + $iteration = 1; + while (isset($tokens[$callLike->getEndTokenPos() - $iteration])) { + $text = trim((string) $tokens[$callLike->getEndTokenPos() - $iteration]->text); + + if (in_array($text, [')', ''], true)) { + ++$iteration; continue; } - $useUse->name = new Name($useUse->name->toString()); - } + if ($text === ',') { + $tokens[$callLike->getEndTokenPos() - $iteration]->text = ''; + } - return parent::pStmt_Use($use); + break; + } } - protected function pScalar_EncapsedStringPart(EncapsedStringPart $encapsedStringPart): string + private function wrapBinaryOpWithBrackets(Node $node): void { - // parent throws exception, but we need to compare string - return '`' . $encapsedStringPart->value . '`'; - } + if ($this->exprAnalyzer->isExprWithExprPropertyWrappable($node)) { + $node->expr->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } - protected function pCommaSeparated(array $nodes): string - { - $result = parent::pCommaSeparated($nodes); + if (! $node instanceof BinaryOp) { + return; + } - $last = end($nodes); + if ($node->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { + return; + } - if ($last instanceof Node) { - $trailingComma = $last->getAttribute(AttributeKey::FUNC_ARGS_TRAILING_COMMA); - if ($trailingComma === false) { - $result = rtrim($result, ','); - } + if ($node->left instanceof Assign + && $this->origTokens instanceof TokenStream + && ! $this->origTokens->haveParens($node->left->getStartTokenPos(), $node->left->getEndTokenPos())) { + $node->left->setAttribute(AttributeKey::ORIGINAL_NODE, null); } - return $result; + if ($node->left instanceof BinaryOp && + $node->left->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { + $node->left->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } + + if ($node->right instanceof BinaryOp && + $node->right->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { + $node->right->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } } /** - * @param Node[] $stmts - * @return Node[]|mixed[] + * ensure left side is assign and right side is just created + * + * @see https://github.com/rectorphp/rector-src/pull/6668 + * @see https://github.com/rectorphp/rector/issues/8980 + * @see https://github.com/rectorphp/rector-src/pull/6653 */ - private function resolveNewStmts(array $stmts): array + private function wrapAssign(Node $leftNode, Node $rightNode): void { - if (count($stmts) === 1 && $stmts[0] instanceof FileWithoutNamespace) { - return $stmts[0]->stmts; + if ($leftNode instanceof Assign && $leftNode->getStartTokenPos() > 0 && $rightNode->getStartTokenPos() < 0) { + $leftNode->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); } + } - return $stmts; + private function cleanStartIndentationOnHeredocNowDoc(string $content): string + { + $lines = NewLineSplitter::split($content); + $trimmedLines = array_map(ltrim(...), $lines); + + return implode("\n", $trimmedLines); + } + + private function resolveIndentSpaces(bool $onlyLevel = false): string + { + $indentSize = SimpleParameterProvider::provideIntParameter(Option::INDENT_SIZE); + + if ($onlyLevel) { + return str_repeat($this->getIndentCharacter(), $this->indentLevel); + } + + return str_repeat($this->getIndentCharacter(), $this->indentLevel) . + str_repeat($this->getIndentCharacter(), $indentSize); } /** - * @param array $nodes + * Must be a method to be able to react to changed parameter in tests */ - private function moveCommentsFromAttributeObjectToCommentsAttribute(array $nodes): void + private function getIndentCharacter(): string { - // move phpdoc from node to "comment" attribute - foreach ($nodes as $node) { - if (! $node instanceof Node) { - continue; - } + return SimpleParameterProvider::provideStringParameter(Option::INDENT_CHAR, ' '); + } - $this->docBlockUpdater->updateNodeWithPhpDocInfo($node); + /** + * @param Node[] $stmts + * @return Node[]|mixed[] + */ + private function unwrapFileNode(array $stmts): array + { + if (count($stmts) === 1 && $stmts[0] instanceof FileNode) { + return array_values($stmts[0]->stmts); } + + return $stmts; } /** @@ -451,17 +549,19 @@ private function moveCommentsFromAttributeObjectToCommentsAttribute(array $nodes */ private function containsNop(array $nodes): bool { + $hasNop = false; foreach ($nodes as $node) { + // early false when visited Node is InlineHTML + if ($node instanceof InlineHTML) { + return false; + } + + // use flag to avoid next is InlineHTML that returns early if ($node instanceof Nop) { - return true; + $hasNop = true; } } - return false; - } - - private function wrapValueWith(String_ $string, string $wrap): string - { - return $wrap . $string->value . $wrap; + return $hasNop; } } diff --git a/src/PhpParser/Printer/FormatPerservingPrinter.php b/src/PhpParser/Printer/FormatPerservingPrinter.php deleted file mode 100644 index 66c884c52a9..00000000000 --- a/src/PhpParser/Printer/FormatPerservingPrinter.php +++ /dev/null @@ -1,77 +0,0 @@ -betterStandardPrinter->printFormatPreserving($newStmts, $oldStmts, $oldTokens); - - $this->smartFileSystem->dumpFile($fileInfo->getRealPath(), $newContent); - $this->smartFileSystem->chmod($fileInfo->getRealPath(), $fileInfo->getPerms()); - - return $newContent; - } - - public function printParsedStmstAndTokensToString(File $file): string - { - $newStmts = $this->resolveNewStmts($file); - - return $this->betterStandardPrinter->printFormatPreserving( - $newStmts, - $file->getOldStmts(), - $file->getOldTokens() - ); - } - - public function printParsedStmstAndTokens(File $file): string - { - $newStmts = $this->resolveNewStmts($file); - - return $this->printToFile( - $file->getSmartFileInfo(), - $newStmts, - $file->getOldStmts(), - $file->getOldTokens() - ); - } - - /** - * @return Stmt[]|mixed[] - */ - private function resolveNewStmts(File $file): array - { - if (count($file->getNewStmts()) === 1) { - $onlyStmt = $file->getNewStmts()[0]; - if ($onlyStmt instanceof FileWithoutNamespace) { - return $onlyStmt->stmts; - } - } - - return $file->getNewStmts(); - } -} diff --git a/src/PhpParser/Printer/NodesWithFileDestinationPrinter.php b/src/PhpParser/Printer/NodesWithFileDestinationPrinter.php deleted file mode 100644 index 84bd81a4677..00000000000 --- a/src/PhpParser/Printer/NodesWithFileDestinationPrinter.php +++ /dev/null @@ -1,23 +0,0 @@ -postFileProcessor->traverse($fileWithNodes->getNodes()); - return $this->betterStandardPrinter->prettyPrintFile($nodes); - } -} diff --git a/src/PhpParser/Printer/Whitespace/IndentCharacterDetector.php b/src/PhpParser/Printer/Whitespace/IndentCharacterDetector.php deleted file mode 100644 index 1f81c525948..00000000000 --- a/src/PhpParser/Printer/Whitespace/IndentCharacterDetector.php +++ /dev/null @@ -1,32 +0,0 @@ - $tokens + */ + public function __construct( + private array $stmts, + private array $tokens + ) { + } + + /** + * @return Stmt[] + */ + public function getStmts(): array + { + return $this->stmts; + } + + /** + * @return array + */ + public function getTokens(): array + { + return $this->tokens; + } +} diff --git a/src/PostRector/Application/PostFileProcessor.php b/src/PostRector/Application/PostFileProcessor.php new file mode 100644 index 00000000000..63536e11e70 --- /dev/null +++ b/src/PostRector/Application/PostFileProcessor.php @@ -0,0 +1,129 @@ +postRectors = []; + } + + /** + * @param Stmt[] $stmts + * @return Stmt[] + */ + public function traverse(array $stmts, File $file): array + { + foreach ($this->getPostRectors() as $postRector) { + // file must be set early into PostRector class to ensure its usage + // always match on skipping process + $postRector->setFile($file); + + if ($this->shouldSkipPostRector($postRector, $file->getFilePath(), $stmts)) { + continue; + } + + $nodeTraverser = new NodeTraverser($postRector); + $stmts = $nodeTraverser->traverse($stmts); + } + + return $stmts; + } + + /** + * @param Stmt[] $stmts + */ + private function shouldSkipPostRector(PostRectorInterface $postRector, string $filePath, array $stmts): bool + { + if ($this->skipper->shouldSkipElementAndFilePath($postRector, $filePath)) { + return true; + } + + // skip renaming if rename class rector is skipped + if ($postRector instanceof ClassRenamingPostRector && $this->skipper->shouldSkipElementAndFilePath( + RenameClassRector::class, + $filePath + )) { + return true; + } + + return ! $postRector->shouldTraverse($stmts); + } + + /** + * Lazy load, to enable test reset with different configuration + * @return PostRectorInterface[] + */ + private function getPostRectors(): array + { + if ($this->postRectors !== []) { + return $this->postRectors; + } + + $isRenamedClassEnabled = $this->renamedClassesDataCollector->getOldToNewClasses() !== []; + $isNameImportingEnabled = SimpleParameterProvider::provideBoolParameter(Option::AUTO_IMPORT_NAMES); + + $isRemovingUnusedImportsEnabled = SimpleParameterProvider::provideBoolParameter(Option::REMOVE_UNUSED_IMPORTS); + + $postRectors = []; + + // sorted by priority, to keep removed imports in order + if ($isRenamedClassEnabled && $isNameImportingEnabled) { + $postRectors[] = $this->classRenamingPostRector; + } + + // import names + if ($isNameImportingEnabled) { + $postRectors[] = $this->nameImportingPostRector; + + // import docblocks + if (SimpleParameterProvider::provideBoolParameter(Option::AUTO_IMPORT_DOC_BLOCK_NAMES)) { + $postRectors[] = $this->docblockNameImportingPostRector; + } + } + + $postRectors[] = $this->useAddingPostRector; + + if ($isRemovingUnusedImportsEnabled) { + $postRectors[] = $this->unusedImportRemovingPostRector; + } + + $this->postRectors = $postRectors; + return $this->postRectors; + } +} diff --git a/src/PostRector/Collector/UseNodesToAddCollector.php b/src/PostRector/Collector/UseNodesToAddCollector.php new file mode 100644 index 00000000000..ebc27a8357e --- /dev/null +++ b/src/PostRector/Collector/UseNodesToAddCollector.php @@ -0,0 +1,193 @@ + + */ + private array $constantUseImportTypesInFilePath = []; + + /** + * @var array + */ + private array $functionUseImportTypesInFilePath = []; + + /** + * @var array + */ + private array $useImportTypesInFilePath = []; + + public function __construct( + private readonly CurrentFileProvider $currentFileProvider, + private readonly UseImportsResolver $useImportsResolver, + ) { + } + + public function addUseImport(FullyQualifiedObjectType $fullyQualifiedObjectType): void + { + // @todo consider using FileNode directly + /** @var File $file */ + $file = $this->currentFileProvider->getFile(); + + $this->useImportTypesInFilePath[$file->getFilePath()][] = $fullyQualifiedObjectType; + } + + public function addConstantUseImport(FullyQualifiedObjectType $fullyQualifiedObjectType): void + { + /** @var File $file */ + $file = $this->currentFileProvider->getFile(); + + $this->constantUseImportTypesInFilePath[$file->getFilePath()][] = $fullyQualifiedObjectType; + } + + public function addFunctionUseImport(FullyQualifiedObjectType $fullyQualifiedObjectType): void + { + /** @var File $file */ + $file = $this->currentFileProvider->getFile(); + + $this->functionUseImportTypesInFilePath[$file->getFilePath()][] = $fullyQualifiedObjectType; + } + + /** + * @return AliasedObjectType[]|FullyQualifiedObjectType[] + */ + public function getUseImportTypesByNode(File $file): array + { + $filePath = $file->getFilePath(); + $objectTypes = $this->useImportTypesInFilePath[$filePath] ?? []; + + $uses = $this->useImportsResolver->resolve(); + + foreach ($uses as $use) { + $prefix = $this->useImportsResolver->resolvePrefix($use); + + foreach ($use->uses as $useUse) { + if ($useUse->alias instanceof Identifier) { + $objectTypes[] = new AliasedObjectType($useUse->alias->toString(), $prefix . $useUse->name); + } else { + $objectTypes[] = new FullyQualifiedObjectType($prefix . $useUse->name); + } + } + } + + return $objectTypes; + } + + public function hasImport(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType): bool + { + $useImports = $this->getUseImportTypesByNode($file); + + foreach ($useImports as $useImport) { + if ($useImport->equals($fullyQualifiedObjectType)) { + return true; + } + } + + return false; + } + + public function isShortImported(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType): bool + { + $shortName = $fullyQualifiedObjectType->getShortName(); + $filePath = $file->getFilePath(); + + $fileConstantUseImportTypes = $this->constantUseImportTypesInFilePath[$filePath] ?? []; + + foreach ($fileConstantUseImportTypes as $fileConstantUseImportType) { + // don't compare strtolower for use const as insensitive is allowed, see https://3v4l.org/lteVa + if ($fileConstantUseImportType->getShortName() === $shortName) { + return true; + } + } + + $shortName = strtolower($shortName); + if ($this->isShortClassImported($filePath, $shortName)) { + return true; + } + + $fileFunctionUseImportTypes = $this->functionUseImportTypesInFilePath[$filePath] ?? []; + foreach ($fileFunctionUseImportTypes as $fileFunctionUseImportType) { + if (strtolower($fileFunctionUseImportType->getShortName()) === $shortName) { + return true; + } + } + + return false; + } + + public function isImportShortable(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType): bool + { + $filePath = $file->getFilePath(); + $fileUseImportTypes = $this->useImportTypesInFilePath[$filePath] ?? []; + + foreach ($fileUseImportTypes as $fileUseImportType) { + if ($fullyQualifiedObjectType->equals($fileUseImportType)) { + return true; + } + } + + $constantImports = $this->constantUseImportTypesInFilePath[$filePath] ?? []; + foreach ($constantImports as $constantImport) { + if ($fullyQualifiedObjectType->equals($constantImport)) { + return true; + } + } + + $functionImports = $this->functionUseImportTypesInFilePath[$filePath] ?? []; + foreach ($functionImports as $functionImport) { + if ($fullyQualifiedObjectType->equals($functionImport)) { + return true; + } + } + + return false; + } + + /** + * @return AliasedObjectType[]|FullyQualifiedObjectType[] + */ + public function getObjectImportsByFilePath(string $filePath): array + { + return $this->useImportTypesInFilePath[$filePath] ?? []; + } + + /** + * @return FullyQualifiedObjectType[] + */ + public function getConstantImportsByFilePath(string $filePath): array + { + return $this->constantUseImportTypesInFilePath[$filePath] ?? []; + } + + /** + * @return FullyQualifiedObjectType[] + */ + public function getFunctionImportsByFilePath(string $filePath): array + { + return $this->functionUseImportTypesInFilePath[$filePath] ?? []; + } + + private function isShortClassImported(string $filePath, string $shortName): bool + { + $fileUseImports = $this->useImportTypesInFilePath[$filePath] ?? []; + + foreach ($fileUseImports as $fileUseImport) { + if (strtolower($fileUseImport->getShortName()) === $shortName) { + return true; + } + } + + return false; + } +} diff --git a/src/PostRector/Contract/Rector/PostRectorInterface.php b/src/PostRector/Contract/Rector/PostRectorInterface.php new file mode 100644 index 00000000000..ab68cad6d24 --- /dev/null +++ b/src/PostRector/Contract/Rector/PostRectorInterface.php @@ -0,0 +1,22 @@ + + */ + private array $shouldTraverseOnFiles = []; + + public function __construct( + private readonly BetterNodeFinder $betterNodeFinder + ) { + } + + /** + * @param Stmt[] $stmts + */ + public function shouldTraverse(array $stmts, string $filePath): bool + { + if (isset($this->shouldTraverseOnFiles[$filePath])) { + return $this->shouldTraverseOnFiles[$filePath]; + } + + $totalNamespaces = 0; + + // just loop the first level stmts to locate namespace to improve performance + // as namespace is always on first level + if (isset($stmts[0]) && $stmts[0] instanceof FileNode) { + $stmts = $stmts[0]->stmts; + } + + foreach ($stmts as $stmt) { + if ($stmt instanceof Namespace_) { + ++$totalNamespaces; + } + + // skip if 2 namespaces are present + if ($totalNamespaces === 2) { + return $this->shouldTraverseOnFiles[$filePath] = false; + } + } + + return $this->shouldTraverseOnFiles[$filePath] = ! $this->betterNodeFinder->hasInstancesOf( + $stmts, + [InlineHTML::class] + ); + } +} diff --git a/src/PostRector/Rector/AbstractPostRector.php b/src/PostRector/Rector/AbstractPostRector.php new file mode 100644 index 00000000000..341100666b9 --- /dev/null +++ b/src/PostRector/Rector/AbstractPostRector.php @@ -0,0 +1,46 @@ +file = $file; + } + + public function getFile(): File + { + Assert::isInstanceOf($this->file, File::class); + + return $this->file; + } + + protected function addRectorClassWithLine(Node $node): void + { + Assert::isInstanceOf($this->file, File::class); + + $rectorWithLineChange = new RectorWithLineChange(static::class, $node->getStartLine()); + $this->file->addRectorClassWithLine($rectorWithLineChange); + } +} diff --git a/src/PostRector/Rector/ClassRenamingPostRector.php b/src/PostRector/Rector/ClassRenamingPostRector.php new file mode 100644 index 00000000000..51622e7e14d --- /dev/null +++ b/src/PostRector/Rector/ClassRenamingPostRector.php @@ -0,0 +1,77 @@ + + */ + private array $oldToNewClasses = []; + + public function __construct( + private readonly RenamedClassesDataCollector $renamedClassesDataCollector, + private readonly UseImportsRemover $useImportsRemover, + private readonly RenamedNameCollector $renamedNameCollector, + private readonly AddUseStatementGuard $addUseStatementGuard, + ) { + } + + public function enterNode(Node $node): Namespace_|FileNode|int|null + { + if ($node instanceof FileNode) { + // handle in Namespace_ node + if ($node->isNamespaced()) { + return null; + } + + // handle here + $removedUses = $this->renamedClassesDataCollector->getOldClasses(); + if ($this->useImportsRemover->removeImportsFromStmts($node, $removedUses)) { + $this->addRectorClassWithLine($node); + } + + $this->renamedNameCollector->reset(); + + return $node; + } + + if ($node instanceof Namespace_) { + $removedUses = $this->renamedClassesDataCollector->getOldClasses(); + if ($this->useImportsRemover->removeImportsFromStmts($node, $removedUses)) { + $this->addRectorClassWithLine($node); + } + + $this->renamedNameCollector->reset(); + + return $node; + } + + // nothing else to handle here, as first 2 nodes we'll hit are handled above + return NodeVisitor::STOP_TRAVERSAL; + } + + #[Override] + public function shouldTraverse(array $stmts): bool + { + $this->oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses(); + + if ($this->oldToNewClasses === []) { + return false; + } + + return $this->addUseStatementGuard->shouldTraverse($stmts, $this->getFile()->getFilePath()); + } +} diff --git a/src/PostRector/Rector/DocblockNameImportingPostRector.php b/src/PostRector/Rector/DocblockNameImportingPostRector.php new file mode 100644 index 00000000000..2089d5f0d89 --- /dev/null +++ b/src/PostRector/Rector/DocblockNameImportingPostRector.php @@ -0,0 +1,56 @@ +phpDocInfoFactory->createFromNode($node); + if (! $phpDocInfo instanceof PhpDocInfo) { + return null; + } + + $hasDocChanged = $this->docBlockNameImporter->importNames($phpDocInfo->getPhpDocNode(), $node); + if (! $hasDocChanged) { + return null; + } + + $this->addRectorClassWithLine($node); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + return $node; + } + + /** + * @param Stmt[] $stmts + */ + #[Override] + public function shouldTraverse(array $stmts): bool + { + return $this->addUseStatementGuard->shouldTraverse($stmts, $this->getFile()->getFilePath()); + } +} diff --git a/src/PostRector/Rector/NameImportingPostRector.php b/src/PostRector/Rector/NameImportingPostRector.php new file mode 100644 index 00000000000..7052b406ba3 --- /dev/null +++ b/src/PostRector/Rector/NameImportingPostRector.php @@ -0,0 +1,65 @@ + + */ + private array $currentUses = []; + + public function __construct( + private readonly NameImporter $nameImporter, + private readonly UseImportsResolver $useImportsResolver, + private readonly AddUseStatementGuard $addUseStatementGuard + ) { + } + + /** + * @return Stmt[] + */ + public function beforeTraverse(array $nodes): array + { + $this->currentUses = $this->useImportsResolver->resolve(); + return $nodes; + } + + public function enterNode(Node $node): Name|null + { + if (! $node instanceof FullyQualified) { + return null; + } + + $name = $this->nameImporter->importName($node, $this->getFile(), $this->currentUses); + if (! $name instanceof Name) { + return null; + } + + $this->addRectorClassWithLine($node); + + return $name; + } + + /** + * @param Stmt[] $stmts + */ + #[Override] + public function shouldTraverse(array $stmts): bool + { + return $this->addUseStatementGuard->shouldTraverse($stmts, $this->getFile()->getFilePath()); + } +} diff --git a/src/PostRector/Rector/UnusedImportRemovingPostRector.php b/src/PostRector/Rector/UnusedImportRemovingPostRector.php new file mode 100644 index 00000000000..0d933319891 --- /dev/null +++ b/src/PostRector/Rector/UnusedImportRemovingPostRector.php @@ -0,0 +1,263 @@ +name instanceof Name + ? $node->name->toString() + : null; + $namesInOriginalCase = $this->resolveUsedPhpAndDocNames($node); + $namesInLowerCase = array_map(strtolower(...), $namesInOriginalCase); + + $firstStmtKey = 0; + foreach ($node->stmts as $key => $stmt) { + if ($stmt instanceof Declare_ && $key === 0) { + ++$firstStmtKey; + } + + if (! $stmt instanceof Use_) { + continue; + } + + if ($stmt->uses === [] || $namesInOriginalCase === []) { + unset($node->stmts[$key]); + $hasChanged = true; + + continue; + } + + $isCaseSensitive = $stmt->type === Use_::TYPE_CONSTANT; + $names = $isCaseSensitive ? $namesInOriginalCase : $namesInLowerCase; + $namespaceName = $namespaceOriginalCase === null + ? null + : ($isCaseSensitive + ? $namespaceOriginalCase + : strtolower($namespaceOriginalCase)); + + foreach ($stmt->uses as $useUseKey => $useUse) { + if ($this->isUseImportUsed($useUse, $isCaseSensitive, $names, $namespaceName)) { + continue; + } + + unset($stmt->uses[$useUseKey]); + $hasChanged = true; + } + + if ($stmt->uses === []) { + $comments = $node->stmts[$key]->getComments(); + + if ($key === $firstStmtKey && $comments !== []) { + $node->stmts[$key] = new Nop(); + $node->stmts[$key]->setAttribute(AttributeKey::COMMENTS, $comments); + } else { + unset($node->stmts[$key]); + } + } + } + + if ($hasChanged === false) { + return null; + } + + $this->addRectorClassWithLine($node); + $node->stmts = array_values($node->stmts); + return $node; + } + + /** + * @return string[] + */ + private function findNonUseImportNames(Namespace_|FileNode $fileNode): array + { + $names = []; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($fileNode->stmts, static function (Node $node) use ( + &$names + ): int|null|Name { + if ($node instanceof Use_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $node instanceof Name) { + return null; + } + + if ($node instanceof FullyQualified) { + $originalName = $node->getAttribute(AttributeKey::ORIGINAL_NAME); + + if ($originalName instanceof Name) { + // collect original Name as cover namespaced used + $names[] = $originalName->toString(); + return $node; + } + } + + $names[] = $node->toString(); + return $node; + }); + + return $names; + } + + /** + * @return string[] + */ + private function findNamesInDocBlocks(Namespace_|FileNode $rootNode): array + { + $names = []; + + $this->simpleCallableNodeTraverser->traverseNodesWithCallable($rootNode, function (Node $node) use ( + &$names + ) { + $comments = $node->getComments(); + if ($comments === []) { + return null; + } + + $docs = array_filter($comments, static fn (Comment $comment): bool => $comment instanceof Doc); + if ($docs === []) { + return null; + } + + $totalDocs = count($docs); + foreach ($docs as $doc) { + $nodeToCheck = $totalDocs === 1 ? $node : clone $node; + if ($totalDocs > 1) { + $nodeToCheck->setDocComment($doc); + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($nodeToCheck); + $names = [...$names, ...$phpDocInfo->getAnnotationClassNames()]; + + $constFetchNodeNames = $phpDocInfo->getConstFetchNodeClassNames(); + $names = [...$names, ...$constFetchNodeNames]; + + $genericTagClassNames = $phpDocInfo->getGenericTagClassNames(); + $names = [...$names, ...$genericTagClassNames]; + + $arrayItemTagClassNames = $phpDocInfo->getArrayItemNodeClassNames(); + $names = [...$names, ...$arrayItemTagClassNames]; + } + }); + + return $names; + } + + /** + * @return string[] + */ + private function resolveUsedPhpAndDocNames(Namespace_|FileNode $rootNode): array + { + $phpNames = $this->findNonUseImportNames($rootNode); + $docBlockNames = $this->findNamesInDocBlocks($rootNode); + + $names = [...$phpNames, ...$docBlockNames]; + return array_unique($names); + } + + /** + * @param string[] $names + */ + private function isUseImportUsed( + UseItem $useItem, + bool $isCaseSensitive, + array $names, + ?string $namespaceName + ): bool { + $comparedName = $useItem->alias instanceof Identifier + ? $useItem->alias->toString() + : $useItem->name->toString(); + + if (! $isCaseSensitive) { + $comparedName = strtolower($comparedName); + } + + if (in_array($comparedName, $names, true)) { + return true; + } + + $lastName = Strings::after($comparedName, '\\', -1); + $namespacedPrefix = $lastName . '\\'; + + if ($namespacedPrefix === '\\') { + $namespacedPrefix = $comparedName . '\\'; + } + + // match partial import + foreach ($names as $name) { + if (str_starts_with($name, '\\')) { + continue; + } + + if ($this->isSubNamespace($name, $comparedName, $namespacedPrefix)) { + return true; + } + + if (! str_starts_with($name, $lastName . '\\')) { + if (str_starts_with($name, $comparedName . '\\')) { + return true; + } + + continue; + } + + if ($namespaceName === null) { + return true; + } + + if (! str_starts_with($name, $namespaceName . '\\')) { + return true; + } + } + + return false; + } + + private function isSubNamespace(string $name, string $comparedName, string $namespacedPrefix): bool + { + if (str_ends_with($comparedName, '\\' . $name)) { + return true; + } + + if (str_starts_with($name, $namespacedPrefix)) { + $subNamespace = substr($name, strlen($namespacedPrefix)); + return ! str_contains($subNamespace, '\\'); + } + + return false; + } +} diff --git a/src/PostRector/Rector/UseAddingPostRector.php b/src/PostRector/Rector/UseAddingPostRector.php new file mode 100644 index 00000000000..7b9e70784f0 --- /dev/null +++ b/src/PostRector/Rector/UseAddingPostRector.php @@ -0,0 +1,166 @@ +resolveRootNode($nodes); + if (! $rootNode instanceof FileNode && ! $rootNode instanceof Namespace_) { + return $nodes; + } + + $useImportTypes = $this->useNodesToAddCollector->getObjectImportsByFilePath($this->getFile()->getFilePath()); + $constantUseImportTypes = $this->useNodesToAddCollector->getConstantImportsByFilePath( + $this->getFile() + ->getFilePath() + ); + + $functionUseImportTypes = $this->useNodesToAddCollector->getFunctionImportsByFilePath( + $this->getFile() + ->getFilePath() + ); + + if ($useImportTypes === [] && $constantUseImportTypes === [] && $functionUseImportTypes === []) { + return $nodes; + } + + /** @var FullyQualifiedObjectType[] $useImportTypes */ + $useImportTypes = $this->typeFactory->uniquateTypes($useImportTypes); + $stmts = $rootNode instanceof FileNode ? $rootNode->stmts : $nodes; + + if ($this->processStmtsWithImportedUses( + $stmts, + $useImportTypes, + $constantUseImportTypes, + $functionUseImportTypes, + $rootNode + )) { + $this->addRectorClassWithLine($rootNode); + } + + return $nodes; + } + + public function enterNode(Node $node): int + { + /** + * We stop the traversal because all the work has already been done in the beforeTraverse() function + * + * Using STOP_TRAVERSAL is usually dangerous as it will stop the processing of all your nodes for all visitors + * but since the PostFileProcessor is using direct new NodeTraverser() and traverse() for only a single + * visitor per execution, using stop traversal here is safe, + * ref https://github.com/rectorphp/rector-src/blob/fc1e742fa4d9861ccdc5933f3b53613b8223438d/src/PostRector/Application/PostFileProcessor.php#L59-L61 + */ + return NodeVisitor::STOP_TRAVERSAL; + } + + /** + * @param Stmt[] $stmts + * @param FullyQualifiedObjectType[] $useImportTypes + * @param FullyQualifiedObjectType[] $constantUseImportTypes + * @param FullyQualifiedObjectType[] $functionUseImportTypes + */ + private function processStmtsWithImportedUses( + array $stmts, + array $useImportTypes, + array $constantUseImportTypes, + array $functionUseImportTypes, + FileNode|Namespace_ $namespace + ): bool { + // A. has namespace? add under it + if ($namespace instanceof Namespace_) { + // then add, to prevent adding + removing false positive of same short use + return $this->useImportsAdder->addImportsToNamespace( + $namespace, + $useImportTypes, + $constantUseImportTypes, + $functionUseImportTypes + ); + } + + // B. no namespace? add in the top + $useImportTypes = $this->filterOutNonNamespacedNames($useImportTypes); + + // then add, to prevent adding + removing false positive of same short use + return $this->useImportsAdder->addImportsToStmts( + $namespace, + $stmts, + $useImportTypes, + $constantUseImportTypes, + $functionUseImportTypes + ); + } + + /** + * Prevents + * @param FullyQualifiedObjectType[] $useImportTypes + * @return FullyQualifiedObjectType[] + */ + private function filterOutNonNamespacedNames(array $useImportTypes): array + { + $namespacedUseImportTypes = []; + + foreach ($useImportTypes as $useImportType) { + if (! \str_contains($useImportType->getClassName(), '\\')) { + continue; + } + + $namespacedUseImportTypes[] = $useImportType; + } + + return $namespacedUseImportTypes; + } + + /** + * @param Stmt[] $nodes + */ + private function resolveRootNode(array $nodes): Namespace_|FileNode|null + { + if ($nodes === []) { + return null; + } + + $firstStmt = $nodes[0]; + if (! $firstStmt instanceof FileNode) { + return null; + } + + foreach ($firstStmt->stmts as $stmt) { + if ($stmt instanceof Namespace_) { + return $stmt; + } + } + + return $firstStmt; + } +} diff --git a/packages/PostRector/ValueObject/PropertyMetadata.php b/src/PostRector/ValueObject/PropertyMetadata.php similarity index 79% rename from packages/PostRector/ValueObject/PropertyMetadata.php rename to src/PostRector/ValueObject/PropertyMetadata.php index 80d33771078..07df33b07b4 100644 --- a/packages/PostRector/ValueObject/PropertyMetadata.php +++ b/src/PostRector/ValueObject/PropertyMetadata.php @@ -4,14 +4,15 @@ namespace Rector\PostRector\ValueObject; +use PhpParser\Modifiers; use PHPStan\Type\Type; -final class PropertyMetadata +final readonly class PropertyMetadata { public function __construct( private string $name, private ?Type $type, - private int $flags + private int $flags = Modifiers::PRIVATE, ) { } diff --git a/src/ProcessAnalyzer/RectifiedAnalyzer.php b/src/ProcessAnalyzer/RectifiedAnalyzer.php new file mode 100644 index 00000000000..8130b64f251 --- /dev/null +++ b/src/ProcessAnalyzer/RectifiedAnalyzer.php @@ -0,0 +1,77 @@ += 0 + */ +final readonly class RectifiedAnalyzer +{ + public function __construct( + private ScopeAnalyzer $scopeAnalyzer + ) { + } + + /** + * @param class-string $rectorClass + */ + public function hasRectified(string $rectorClass, Node $node): bool + { + $originalNode = $node->getAttribute(AttributeKey::ORIGINAL_NODE); + if ($this->hasConsecutiveCreatedByRule($rectorClass, $node, $originalNode)) { + return true; + } + + return $this->isJustReprintedOverlappedTokenStart($node, $originalNode); + } + + /** + * @param class-string $rectorClass + */ + private function hasConsecutiveCreatedByRule(string $rectorClass, Node $node, ?Node $originalNode): bool + { + $createdByRuleNode = $originalNode ?? $node; + /** @var class-string[] $createdByRule */ + $createdByRule = $createdByRuleNode->getAttribute(AttributeKey::CREATED_BY_RULE) ?? []; + + if ($createdByRule === []) { + return false; + } + + return end($createdByRule) === $rectorClass; + } + + private function isJustReprintedOverlappedTokenStart(Node $node, ?Node $originalNode): bool + { + if ($originalNode instanceof Node) { + return false; + } + + /** + * Start token pos must be < 0 to continue, as the node and parent node just re-printed + * + * - Node's original node is null + * - Parent Node's original node is null + */ + $startTokenPos = $node->getStartTokenPos(); + if ($startTokenPos >= 0) { + return true; + } + + if (! $this->scopeAnalyzer->isRefreshable($node)) { + return false; + } + + return ! in_array(AttributeKey::SCOPE, array_keys($node->getAttributes()), true); + } +} diff --git a/src/Provider/CurrentFileProvider.php b/src/Provider/CurrentFileProvider.php deleted file mode 100644 index 943191dcd9e..00000000000 --- a/src/Provider/CurrentFileProvider.php +++ /dev/null @@ -1,22 +0,0 @@ -file = $file; - } - - public function getFile(): ?File - { - return $this->file; - } -} diff --git a/src/Rector/AbstractRector.php b/src/Rector/AbstractRector.php index 4124fa2897a..63770a565f5 100644 --- a/src/Rector/AbstractRector.php +++ b/src/Rector/AbstractRector.php @@ -2,302 +2,194 @@ declare(strict_types=1); -namespace Rector\Core\Rector; +namespace Rector\Rector; use PhpParser\Node; -use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Name; -use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\PropertyItem; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\Function_; -use PhpParser\NodeTraverser; -use PhpParser\NodeVisitor\ParentConnectingVisitor; +use PhpParser\Node\Stmt\Const_; +use PhpParser\Node\Stmt\Interface_; +use PhpParser\Node\Stmt\Property; +use PhpParser\Node\Stmt\Trait_; +use PhpParser\NodeVisitor; use PhpParser\NodeVisitorAbstract; +use PHPStan\Analyser\MutatingScope; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\ChangesReporting\Collector\RectorChangeCollector; +use Rector\Application\ChangedNodeScopeRefresher; +use Rector\Application\Provider\CurrentFileProvider; +use Rector\BetterPhpDocParser\Comment\CommentsMerger; use Rector\ChangesReporting\ValueObject\RectorWithLineChange; -use Rector\Core\Application\FileSystem\RemovedAndAddedFilesCollector; -use Rector\Core\Configuration\CurrentNodeProvider; -use Rector\Core\Contract\Rector\PhpRectorInterface; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\Exclusion\ExclusionManager; -use Rector\Core\Logging\CurrentRectorProvider; -use Rector\Core\NodeAnalyzer\ChangedNodeAnalyzer; -use Rector\Core\Php\PhpVersionProvider; -use Rector\Core\PhpParser\Comparing\NodeComparator; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\Node\NodeFactory; -use Rector\Core\PhpParser\Node\Value\ValueResolver; -use Rector\Core\PhpParser\Printer\BetterStandardPrinter; -use Rector\Core\Provider\CurrentFileProvider; -use Rector\Core\Validation\InfiniteLoopValidator; -use Rector\Core\ValueObject\Application\File; +use Rector\Contract\Rector\HTMLAverseRectorInterface; +use Rector\Contract\Rector\RectorInterface; +use Rector\Exception\ShouldNotHappenException; +use Rector\NodeDecorator\CreatedByRuleDecorator; use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeRemoval\NodeRemover; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; -use Rector\PostRector\Collector\NodesToAddCollector; -use Rector\PostRector\Collector\NodesToRemoveCollector; -use Rector\Privatization\NodeManipulator\VisibilityManipulator; -use Rector\StaticTypeMapper\StaticTypeMapper; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Contracts\Service\Attribute\Required; -use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; -use Symplify\PackageBuilder\Parameter\ParameterProvider; -use Symplify\Skipper\Skipper\Skipper; - -/** - * @see \Rector\Testing\PHPUnit\AbstractRectorTestCase - */ -abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorInterface -{ - /** - * @var string[] - */ - private const ATTRIBUTES_TO_MIRROR = [ - AttributeKey::CLASS_NODE, - AttributeKey::CLASS_NAME, - AttributeKey::METHOD_NODE, - AttributeKey::USE_NODES, - AttributeKey::SCOPE, - AttributeKey::RESOLVED_NAME, - AttributeKey::PARENT_NODE, - AttributeKey::CURRENT_STATEMENT, - AttributeKey::PREVIOUS_STATEMENT, - ]; - - protected NodeNameResolver $nodeNameResolver; +use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\Comparing\NodeComparator; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Skipper\Skipper\Skipper; +use Rector\ValueObject\Application\File; - protected NodeTypeResolver $nodeTypeResolver; +abstract class AbstractRector extends NodeVisitorAbstract implements RectorInterface +{ + private const string EMPTY_NODE_ARRAY_MESSAGE = <<refactor()" returns non-empty array for Nodes. - protected BetterStandardPrinter $betterStandardPrinter; +A) Direct return null for no change: - protected RemovedAndAddedFilesCollector $removedAndAddedFilesCollector; + return null; - protected ParameterProvider $parameterProvider; +B) Remove the Node: - protected PhpVersionProvider $phpVersionProvider; + return \PhpParser\NodeVisitor::REMOVE_NODE; +CODE_SAMPLE; - protected StaticTypeMapper $staticTypeMapper; + protected NodeNameResolver $nodeNameResolver; - protected PhpDocInfoFactory $phpDocInfoFactory; + protected NodeTypeResolver $nodeTypeResolver; protected NodeFactory $nodeFactory; - protected VisibilityManipulator $visibilityManipulator; - - protected ValueResolver $valueResolver; - - protected BetterNodeFinder $betterNodeFinder; - - protected NodeRemover $nodeRemover; - - protected RectorChangeCollector $rectorChangeCollector; - protected NodeComparator $nodeComparator; - protected NodesToRemoveCollector $nodesToRemoveCollector; - + /** + * @deprecated Use getFile() instead. + */ protected File $file; - private SimpleCallableNodeTraverser $simpleCallableNodeTraverser; - - private SymfonyStyle $symfonyStyle; - - private ExclusionManager $exclusionManager; - - private CurrentRectorProvider $currentRectorProvider; + protected Skipper $skipper; - private CurrentNodeProvider $currentNodeProvider; + private ChangedNodeScopeRefresher $changedNodeScopeRefresher; - private Skipper $skipper; - - /** - * @var string|null - */ - private $previousAppliedClass; - - private NodesToAddCollector $nodesToAddCollector; + private SimpleCallableNodeTraverser $simpleCallableNodeTraverser; private CurrentFileProvider $currentFileProvider; - private ChangedNodeAnalyzer $changedNodeAnalyzer; + private CommentsMerger $commentsMerger; - /** - * @var array - */ - private array $nodesToReturn = []; - - private InfiniteLoopValidator $infiniteLoopValidator; - - #[Required] - public function autowireAbstractRector( - NodesToRemoveCollector $nodesToRemoveCollector, - NodesToAddCollector $nodesToAddCollector, - RectorChangeCollector $rectorChangeCollector, - NodeRemover $nodeRemover, - RemovedAndAddedFilesCollector $removedAndAddedFilesCollector, - BetterStandardPrinter $betterStandardPrinter, + private CreatedByRuleDecorator $createdByRuleDecorator; + + public function autowire( NodeNameResolver $nodeNameResolver, NodeTypeResolver $nodeTypeResolver, SimpleCallableNodeTraverser $simpleCallableNodeTraverser, - VisibilityManipulator $visibilityManipulator, NodeFactory $nodeFactory, - PhpDocInfoFactory $phpDocInfoFactory, - SymfonyStyle $symfonyStyle, - PhpVersionProvider $phpVersionProvider, - ExclusionManager $exclusionManager, - StaticTypeMapper $staticTypeMapper, - ParameterProvider $parameterProvider, - CurrentRectorProvider $currentRectorProvider, - CurrentNodeProvider $currentNodeProvider, Skipper $skipper, - ValueResolver $valueResolver, - BetterNodeFinder $betterNodeFinder, NodeComparator $nodeComparator, CurrentFileProvider $currentFileProvider, - ChangedNodeAnalyzer $changedNodeAnalyzer, - InfiniteLoopValidator $infiniteLoopValidator + CreatedByRuleDecorator $createdByRuleDecorator, + ChangedNodeScopeRefresher $changedNodeScopeRefresher, + CommentsMerger $commentsMerger ): void { - $this->nodesToRemoveCollector = $nodesToRemoveCollector; - $this->nodesToAddCollector = $nodesToAddCollector; - $this->rectorChangeCollector = $rectorChangeCollector; - $this->nodeRemover = $nodeRemover; - $this->removedAndAddedFilesCollector = $removedAndAddedFilesCollector; - $this->betterStandardPrinter = $betterStandardPrinter; $this->nodeNameResolver = $nodeNameResolver; $this->nodeTypeResolver = $nodeTypeResolver; $this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser; - $this->visibilityManipulator = $visibilityManipulator; $this->nodeFactory = $nodeFactory; - $this->phpDocInfoFactory = $phpDocInfoFactory; - $this->symfonyStyle = $symfonyStyle; - $this->phpVersionProvider = $phpVersionProvider; - $this->exclusionManager = $exclusionManager; - $this->staticTypeMapper = $staticTypeMapper; - $this->parameterProvider = $parameterProvider; - $this->currentRectorProvider = $currentRectorProvider; - $this->currentNodeProvider = $currentNodeProvider; $this->skipper = $skipper; - $this->valueResolver = $valueResolver; - $this->betterNodeFinder = $betterNodeFinder; $this->nodeComparator = $nodeComparator; $this->currentFileProvider = $currentFileProvider; - $this->changedNodeAnalyzer = $changedNodeAnalyzer; - $this->infiniteLoopValidator = $infiniteLoopValidator; + $this->createdByRuleDecorator = $createdByRuleDecorator; + $this->changedNodeScopeRefresher = $changedNodeScopeRefresher; + $this->commentsMerger = $commentsMerger; } /** + * @final Avoid override to prevent unintended side-effects. Use enterNode() or @see \Rector\Contract\PhpParser\DecoratingNodeVisitorInterface instead. + * + * @internal + * * @return Node[]|null */ public function beforeTraverse(array $nodes): ?array { - $this->previousAppliedClass = null; - // workaround for file around refactor() $file = $this->currentFileProvider->getFile(); if (! $file instanceof File) { - throw new ShouldNotHappenException('File is missing'); + throw new ShouldNotHappenException( + 'File object is missing. Make sure you call $this->currentFileProvider->setFile(...) before traversing.' + ); } $this->file = $file; - return parent::beforeTraverse($nodes); + return null; } /** - * @return Expression|Node|Node[]|int|null + * @return NodeVisitor::REMOVE_NODE|Node|null|Node[] */ - final public function enterNode(Node $node) + final public function enterNode(Node $node): int|Node|null|array { - $nodeClass = $node::class; - if (! $this->isMatchingNodeType($nodeClass)) { + if (is_a($this, HTMLAverseRectorInterface::class, true) && $this->getFile()->containsHTML()) { return null; } - if ($this->shouldSkipCurrentNode($node)) { + $filePath = $this->getFile() + ->getFilePath(); + if ($this->skipper->shouldSkipCurrentNode($this, $filePath, static::class, $node)) { return null; } - $this->currentRectorProvider->changeCurrentRector($this); - // for PHP doc info factory and change notifier - $this->currentNodeProvider->setNode($node); - - // show current Rector class on --debug - $this->printDebugApplying(); - - $originalAttributes = $node->getAttributes(); - $originalNode = $node->getAttribute(AttributeKey::ORIGINAL_NODE) ?? clone $node; - - $node = $this->refactor($node); - - if (is_array($node)) { - $originalNodeHash = spl_object_hash($originalNode); - $this->nodesToReturn[$originalNodeHash] = $node; - - if ($node !== []) { - $firstNodeKey = array_key_first($node); - $this->mirrorComments($node[$firstNodeKey], $originalNode); - } + // ensure origNode pulled before refactor to avoid changed during refactor, ref https://3v4l.org/YMEGN + $originalNode = $node->getAttribute(AttributeKey::ORIGINAL_NODE) ?? $node; - // will be replaced in leaveNode() the original node must be passed - return $originalNode; - } + $refactoredNodeOrState = $this->refactor($node); // nothing to change → continue - if (! $node instanceof Node) { + if ($refactoredNodeOrState === null) { return null; } - // changed! - if ($this->changedNodeAnalyzer->hasNodeChanged($originalNode, $node)) { - $rectorWithLineChange = new RectorWithLineChange($this, $originalNode->getLine()); - $this->file->addRectorClassWithLine($rectorWithLineChange); - - // update parents relations - must run before connectParentNodes() - $this->mirrorAttributes($originalAttributes, $node); - $this->connectParentNodes($node); - - // is different node type? do not traverse children to avoid looping - if ($originalNode::class !== $node::class) { - $this->infiniteLoopValidator->process($node, $originalNode, static::class); - - // search "infinite recursion" in https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown - $originalNodeHash = spl_object_hash($originalNode); + if ($refactoredNodeOrState === []) { + $errorMessage = sprintf(self::EMPTY_NODE_ARRAY_MESSAGE, static::class); + throw new ShouldNotHappenException($errorMessage); + } - if ($originalNode instanceof Stmt && $node instanceof Expr) { - $node = new Expression($node); - } + $isState = is_int($refactoredNodeOrState); - $this->nodesToReturn[$originalNodeHash] = $node; + if ($isState) { + $this->createdByRuleDecorator->decorate($node, $originalNode, static::class); - return $node; + // only remove node is supported + if ($refactoredNodeOrState !== NodeVisitor::REMOVE_NODE) { + // @todo warn about unsupported state in the future + return null; } - } - // if Stmt ("$value;") was replaced by Expr ("$value"), add Expression (the ending ";") to prevent breaking the code - if ($originalNode instanceof Stmt && $node instanceof Expr) { - $node = new Expression($node); + // notify this rule changed code + $rectorWithLineChange = new RectorWithLineChange(static::class, $originalNode->getStartLine()); + $this->getFile() + ->addRectorClassWithLine($rectorWithLineChange); + + return $refactoredNodeOrState; } - return $node; + return $this->postRefactorProcess($originalNode, $node, $refactoredNodeOrState, $filePath); } /** - * Replacing nodes in leaveNode() method avoids infinite recursion - * see"infinite recursion" in https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown + * @deprecated no longer used */ - public function leaveNode(Node $node) + final public function leaveNode(Node $node): array|int|Node|null + { + return null; + } + + protected function getFile(): File { - $objectHash = spl_object_hash($node); + $file = $this->currentFileProvider->getFile(); + if (! $file instanceof File) { + throw new ShouldNotHappenException( + 'File object is missing. Make sure you call $this->currentFileProvider->setFile(...) before traversing.' + ); + } - // update parents relations!!! - return $this->nodesToReturn[$objectHash] ?? $node; + return $file; } protected function isName(Node $node, string $name): bool @@ -313,6 +205,21 @@ protected function isNames(Node $node, array $names): bool return $this->nodeNameResolver->isNames($node, $names); } + /** + * Some nodes have always-known string name. This makes PHPStan smarter. + * @see https://phpstan.org/writing-php-code/phpdoc-types#conditional-return-types + * + * @return ($node is Node\Param ? string : + * ($node is ClassMethod ? string : + * ($node is Property ? string : + * ($node is PropertyItem ? string : + * ($node is Trait_ ? string : + * ($node is Interface_ ? string : + * ($node is Const_ ? string : + * ($node is Node\Const_ ? string : + * ($node is Name ? string : + * string|null ))))))))) + */ protected function getName(Node $node): ?string { return $this->nodeNameResolver->getName($node); @@ -323,220 +230,69 @@ protected function isObjectType(Node $node, ObjectType $objectType): bool return $this->nodeTypeResolver->isObjectType($node, $objectType); } - protected function getStaticType(Node $node): Type + /** + * Use this method for getting expr|node type + */ + protected function getType(Node $node): Type { - return $this->nodeTypeResolver->getStaticType($node); + return $this->nodeTypeResolver->getType($node); } /** - * @deprecated - * Use getStaticType() instead, as single method to get types + * Use this method for getting native expr type */ - protected function getObjectType(Node $node): Type + protected function getNativeType(Expr $expr): Type { - return $this->nodeTypeResolver->resolve($node); + return $this->nodeTypeResolver->getNativeType($expr); } /** * @param Node|Node[] $nodes + * @param callable(Node): (int|Node|null|Node[]) $callable */ protected function traverseNodesWithCallable(Node | array $nodes, callable $callable): void { $this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, $callable); } - /** - * @param Node|Node[]|null $node - */ - protected function print(Node | array | null $node): string - { - return $this->betterStandardPrinter->print($node); - } - - /** - * @deprecated Use FQN PhpVersionProvider service directly instead or implements provideMinPhpVersion, this method will be removed soon - * Or implement \Rector\VersionBonding\Contract\MinPhpVersionInterface - */ - protected function isAtLeastPhpVersion(int $version): bool - { - return $this->phpVersionProvider->isAtLeastPhpVersion($version); - } - protected function mirrorComments(Node $newNode, Node $oldNode): void { - $newNode->setAttribute(AttributeKey::PHP_DOC_INFO, $oldNode->getAttribute(AttributeKey::PHP_DOC_INFO)); - $newNode->setAttribute(AttributeKey::COMMENTS, $oldNode->getAttribute(AttributeKey::COMMENTS)); + $this->commentsMerger->mirrorComments($newNode, $oldNode); } /** - * @deprecated Return array of stmts directly - * @param Stmt[] $stmts + * @param Node|Node[] $refactoredNode + * @return Node|Node[] */ - protected function unwrapStmts(array $stmts, Node $node): void - { - // move /* */ doc block from if to first element to keep it - $currentPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - foreach ($stmts as $key => $ifStmt) { - if ($key === 0) { - $ifStmt->setAttribute(AttributeKey::PHP_DOC_INFO, $currentPhpDocInfo); - - // move // comments - $ifStmt->setAttribute(AttributeKey::COMMENTS, $node->getComments()); - } - - $this->addNodeAfterNode($ifStmt, $node); - } + private function postRefactorProcess( + Node $originalNode, + Node $node, + Node|array $refactoredNode, + string $filePath + ): Node|array { + /** @var non-empty-array|Node $refactoredNode */ + $this->createdByRuleDecorator->decorate($refactoredNode, $originalNode, static::class); + + $rectorWithLineChange = new RectorWithLineChange(static::class, $originalNode->getStartLine()); + $this->getFile() + ->addRectorClassWithLine($rectorWithLineChange); + + /** @var MutatingScope|null $currentScope */ + $currentScope = $node->getAttribute(AttributeKey::SCOPE); + $this->refreshScopeNodes($refactoredNode, $filePath, $currentScope); + + return $refactoredNode; } /** - * @param Arg[] $currentArgs - * @param Arg[] $appendingArgs - * @return Arg[] + * @param Node[]|Node $node */ - protected function appendArgs(array $currentArgs, array $appendingArgs): array + private function refreshScopeNodes(array | Node $node, string $filePath, ?MutatingScope $mutatingScope): void { - foreach ($appendingArgs as $appendingArg) { - $currentArgs[] = new Arg($appendingArg->value); - } - - return $currentArgs; - } + $nodes = $node instanceof Node ? [$node] : $node; - protected function unwrapExpression(Stmt $stmt): Expr | Stmt - { - if ($stmt instanceof Expression) { - return $stmt->expr; + foreach ($nodes as $node) { + $this->changedNodeScopeRefresher->refresh($node, $filePath, $mutatingScope); } - - return $stmt; - } - - /** - * @deprecated Use refactor() return of [] or directly $nodesToAddCollector - * @param Node[] $newNodes - */ - protected function addNodesAfterNode(array $newNodes, Node $positionNode): void - { - $this->nodesToAddCollector->addNodesAfterNode($newNodes, $positionNode); - } - - /** - * @param Node[] $newNodes - * @deprecated Use refactor() return of [] or directly $nodesToAddCollector - */ - protected function addNodesBeforeNode(array $newNodes, Node $positionNode): void - { - $this->nodesToAddCollector->addNodesBeforeNode($newNodes, $positionNode); - } - - /** - * @deprecated Use refactor() return of [] or directly $nodesToAddCollector - */ - protected function addNodeAfterNode(Node $newNode, Node $positionNode): void - { - $this->nodesToAddCollector->addNodeAfterNode($newNode, $positionNode); - } - - /** - * @deprecated Use refactor() return of [] or directly $nodesToAddCollector - */ - protected function addNodeBeforeNode(Node $newNode, Node $positionNode): void - { - $this->nodesToAddCollector->addNodeBeforeNode($newNode, $positionNode); - } - - protected function removeNode(Node $node): void - { - $this->nodeRemover->removeNode($node); - } - - protected function removeNodeFromStatements( - Class_ | ClassMethod | Function_ $nodeWithStatements, - Node $toBeRemovedNode - ): void { - $this->nodeRemover->removeNodeFromStatements($nodeWithStatements, $toBeRemovedNode); - } - - /** - * @param Node[] $nodes - */ - protected function removeNodes(array $nodes): void - { - $this->nodeRemover->removeNodes($nodes); - } - - /** - * @param class-string $nodeClass - */ - private function isMatchingNodeType(string $nodeClass): bool - { - foreach ($this->getNodeTypes() as $nodeType) { - if (is_a($nodeClass, $nodeType, true)) { - return true; - } - } - - return false; - } - - private function shouldSkipCurrentNode(Node $node): bool - { - if ($this->nodesToRemoveCollector->isNodeRemoved($node)) { - return true; - } - - if ($this->exclusionManager->isNodeSkippedByRector($node, $this)) { - return true; - } - - $smartFileInfo = $this->file->getSmartFileInfo(); - return $this->skipper->shouldSkipElementAndFileInfo($this, $smartFileInfo); - } - - private function printDebugApplying(): void - { - if (! $this->symfonyStyle->isDebug()) { - return; - } - - if ($this->previousAppliedClass === static::class) { - return; - } - - // prevent spamming with the same class over and over - // indented on purpose to improve log nesting under [refactoring] - $this->symfonyStyle->writeln(' [applying] ' . static::class); - $this->previousAppliedClass = static::class; - } - - /** - * @param array $originalAttributes - */ - private function mirrorAttributes(array $originalAttributes, Node $newNode): void - { - if ($newNode instanceof Name) { - $newNode->setAttribute(AttributeKey::RESOLVED_NAME, $newNode->toString()); - } - - foreach ($originalAttributes as $attributeName => $oldAttributeValue) { - if (! in_array($attributeName, self::ATTRIBUTES_TO_MIRROR, true)) { - continue; - } - - $newNode->setAttribute($attributeName, $oldAttributeValue); - } - } - - /** - * @param Node|Node[] $node - */ - private function connectParentNodes(Node | array $node): void - { - $nodes = is_array($node) ? $node : [$node]; - - $nodeTraverser = new NodeTraverser(); - $nodeTraverser->addVisitor(new ParentConnectingVisitor()); - $nodeTraverser->traverse($nodes); } } diff --git a/src/Reflection/ClassModifierChecker.php b/src/Reflection/ClassModifierChecker.php new file mode 100644 index 00000000000..063e03054df --- /dev/null +++ b/src/Reflection/ClassModifierChecker.php @@ -0,0 +1,36 @@ +reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + return $classReflection->isFinalByKeyword(); + } + + public function isInsideAbstractClass(Node $node): bool + { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + return $classReflection->isAbstract(); + } +} diff --git a/src/Reflection/ClassReflectionAnalyzer.php b/src/Reflection/ClassReflectionAnalyzer.php new file mode 100644 index 00000000000..8ab97134741 --- /dev/null +++ b/src/Reflection/ClassReflectionAnalyzer.php @@ -0,0 +1,21 @@ +getNativeReflection(); + if ($nativeReflection instanceof ReflectionEnum) { + return null; + } + + return $nativeReflection->getParentClassName(); + } +} diff --git a/src/Reflection/MethodReflectionResolver.php b/src/Reflection/MethodReflectionResolver.php new file mode 100644 index 00000000000..863e2618dd7 --- /dev/null +++ b/src/Reflection/MethodReflectionResolver.php @@ -0,0 +1,40 @@ +reflectionProvider->hasClass($className)) { + return null; + } + + $classReflection = $this->reflectionProvider->getClass($className); + + // better, with support for "@method" annotation methods + if ($scope instanceof Scope) { + if ($classReflection->hasMethod($methodName)) { + return $classReflection->getMethod($methodName, $scope); + } + } elseif ($classReflection->hasNativeMethod($methodName)) { + return $classReflection->getNativeMethod($methodName); + } + + return null; + } +} diff --git a/src/Reflection/ReflectionResolver.php b/src/Reflection/ReflectionResolver.php index 034145fcbc2..1e37c4072e2 100644 --- a/src/Reflection/ReflectionResolver.php +++ b/src/Reflection/ReflectionResolver.php @@ -2,84 +2,151 @@ declare(strict_types=1); -namespace Rector\Core\Reflection; +namespace Rector\Reflection; +use PhpParser\Node; +use PhpParser\Node\Attribute; +use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\New_; +use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Name; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\Php\PhpPropertyReflection; use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Type\TypeUtils; -use PHPStan\Type\TypeWithClassName; -use Rector\Core\PHPStan\Reflection\TypeToCallReflectionResolver\TypeToCallReflectionResolverRegistry; -use Rector\Core\ValueObject\MethodName; +use PHPStan\Type\BenevolentUnionType; +use PHPStan\Type\TypeCombinator; +use Rector\Exception\ShouldNotHappenException; +use Rector\NodeAnalyzer\ClassAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; -use ReflectionMethod; +use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; +use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType; +use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType; +use Rector\ValueObject\MethodName; -final class ReflectionResolver +final readonly class ReflectionResolver { public function __construct( private ReflectionProvider $reflectionProvider, private NodeTypeResolver $nodeTypeResolver, private NodeNameResolver $nodeNameResolver, - private TypeToCallReflectionResolverRegistry $typeToCallReflectionResolverRegistry + private ClassAnalyzer $classAnalyzer, + private MethodReflectionResolver $methodReflectionResolver ) { } /** - * @param class-string $className + * @api */ - public function resolveMethodReflection(string $className, string $methodName, ?Scope $scope): ?MethodReflection + public function resolveClassAndAnonymousClass(ClassLike $classLike): ClassReflection + { + if ($classLike instanceof Class_ && $this->classAnalyzer->isAnonymousClass($classLike)) { + $classLikeScope = $classLike->getAttribute(AttributeKey::SCOPE); + if (! $classLikeScope instanceof Scope) { + throw new ShouldNotHappenException(); + } + + return $this->reflectionProvider->getAnonymousClassReflection($classLike, $classLikeScope); + } + + $className = (string) $this->nodeNameResolver->getName($classLike); + return $this->reflectionProvider->getClass($className); + } + + public function resolveClassReflection(Node $node): ?ClassReflection { + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + return $scope->getClassReflection(); + } + + public function resolveClassReflectionSourceObject( + MethodCall|NullsafeMethodCall|StaticCall|PropertyFetch|StaticPropertyFetch $node + ): ?ClassReflection { + $objectType = $node instanceof StaticCall || $node instanceof StaticPropertyFetch + ? $this->nodeTypeResolver->getType($node->class) + : $this->nodeTypeResolver->getType($node->var); + + $className = ClassNameFromObjectTypeResolver::resolve($objectType); + if ($className === null) { + return null; + } + if (! $this->reflectionProvider->hasClass($className)) { return null; } $classReflection = $this->reflectionProvider->getClass($className); - // better, with support for "@method" annotation methods - if ($scope instanceof Scope) { - if ($classReflection->hasMethod($methodName)) { - return $classReflection->getMethod($methodName, $scope); + if ($node instanceof PropertyFetch || $node instanceof StaticPropertyFetch) { + $propertyName = (string) $this->nodeNameResolver->getName($node->name); + if (! $classReflection->hasNativeProperty($propertyName)) { + return null; } - } elseif ($classReflection->hasNativeMethod($methodName)) { - return $classReflection->getNativeMethod($methodName); + + $property = $classReflection->getNativeProperty($propertyName); + if ($property->isPrivate()) { + return $classReflection; + } + + if ($this->reflectionProvider->hasClass($property->getDeclaringClass()->getName())) { + return $this->reflectionProvider->getClass($property->getDeclaringClass()->getName()); + } + + return $classReflection; } - return null; + $methodName = (string) $this->nodeNameResolver->getName($node->name); + if (! $classReflection->hasNativeMethod($methodName)) { + return null; + } + + $extendedMethodReflection = $classReflection->getNativeMethod($methodName); + if ($extendedMethodReflection->isPrivate()) { + return $classReflection; + } + + if ($this->reflectionProvider->hasClass($extendedMethodReflection->getDeclaringClass()->getName())) { + return $this->reflectionProvider->getClass($extendedMethodReflection->getDeclaringClass()->getName()); + } + + return $classReflection; } /** * @param class-string $className */ - public function resolveNativeClassMethodReflection(string $className, string $methodName): ?ReflectionMethod + public function resolveMethodReflection(string $className, string $methodName, ?Scope $scope): ?MethodReflection { - if (! $this->reflectionProvider->hasClass($className)) { - return null; - } - - $classReflection = $this->reflectionProvider->getClass($className); - $reflectionClass = $classReflection->getNativeReflection(); - - return $reflectionClass->hasMethod($methodName) ? $reflectionClass->getMethod($methodName) : null; + return $this->methodReflectionResolver->resolveMethodReflection($className, $methodName, $scope); } public function resolveMethodReflectionFromStaticCall(StaticCall $staticCall): ?MethodReflection { - $objectType = $this->nodeTypeResolver->resolve($staticCall->class); - - /** @var array $classes */ - $classes = TypeUtils::getDirectClassNames($objectType); + $objectType = $this->nodeTypeResolver->getType($staticCall->class); + + if ($objectType instanceof ShortenedObjectType || $objectType instanceof AliasedObjectType) { + /** @var array $classNames */ + $classNames = [$objectType->getFullyQualifiedName()]; + } else { + /** @var array $classNames */ + $classNames = $objectType->getObjectClassNames(); + } $methodName = $this->nodeNameResolver->getName($staticCall->name); if ($methodName === null) { @@ -88,8 +155,8 @@ public function resolveMethodReflectionFromStaticCall(StaticCall $staticCall): ? $scope = $staticCall->getAttribute(AttributeKey::SCOPE); - foreach ($classes as $class) { - $methodReflection = $this->resolveMethodReflection($class, $methodName, $scope); + foreach ($classNames as $className) { + $methodReflection = $this->resolveMethodReflection($className, $methodName, $scope); if ($methodReflection instanceof MethodReflection) { return $methodReflection; } @@ -100,8 +167,15 @@ public function resolveMethodReflectionFromStaticCall(StaticCall $staticCall): ? public function resolveMethodReflectionFromMethodCall(MethodCall $methodCall): ?MethodReflection { - $callerType = $this->nodeTypeResolver->resolve($methodCall->var); - if (! $callerType instanceof TypeWithClassName) { + $callerType = $this->nodeTypeResolver->getType($methodCall->var); + + if ($callerType instanceof BenevolentUnionType) { + $callerType = TypeCombinator::removeFalsey($callerType); + } + + $className = ClassNameFromObjectTypeResolver::resolve($callerType); + if ($className === null) { + return null; } @@ -111,80 +185,98 @@ public function resolveMethodReflectionFromMethodCall(MethodCall $methodCall): ? } $scope = $methodCall->getAttribute(AttributeKey::SCOPE); - return $this->resolveMethodReflection($callerType->getClassName(), $methodName, $scope); + return $this->resolveMethodReflection($className, $methodName, $scope); } public function resolveFunctionLikeReflectionFromCall( - MethodCall | StaticCall | FuncCall $call + CallLike $callLike ): MethodReflection | FunctionReflection | null { - if ($call instanceof MethodCall) { - return $this->resolveMethodReflectionFromMethodCall($call); + if ($callLike instanceof MethodCall) { + return $this->resolveMethodReflectionFromMethodCall($callLike); + } + + if ($callLike instanceof StaticCall) { + return $this->resolveMethodReflectionFromStaticCall($callLike); + } + + if ($callLike instanceof New_) { + return $this->resolveMethodReflectionFromNew($callLike); } - if ($call instanceof StaticCall) { - return $this->resolveMethodReflectionFromStaticCall($call); + if ($callLike instanceof FuncCall) { + return $this->resolveFunctionReflectionFromFuncCall($callLike); } - return $this->resolveFunctionReflectionFromFuncCall($call); + // todo: support NullsafeMethodCall + return null; } - public function resolveMethodReflectionFromClassMethod(ClassMethod $classMethod): ?MethodReflection + /** + * @api used in rector-laravel + */ + public function resolveMethodReflectionFromClassMethod(ClassMethod $classMethod, Scope $scope): ?MethodReflection { - $class = $classMethod->getAttribute(AttributeKey::CLASS_NAME); - if ($class === null) { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { return null; } + $className = $classReflection->getName(); $methodName = $this->nodeNameResolver->getName($classMethod); - $scope = $classMethod->getAttribute(AttributeKey::SCOPE); - return $this->resolveMethodReflection($class, $methodName, $scope); + return $this->resolveMethodReflection($className, $methodName, $scope); } public function resolveMethodReflectionFromNew(New_ $new): ?MethodReflection { - $newClassType = $this->nodeTypeResolver->resolve($new->class); - if (! $newClassType instanceof TypeWithClassName) { + $newClassType = $this->nodeTypeResolver->getType($new->class); + $className = ClassNameFromObjectTypeResolver::resolve($newClassType); + + if ($className === null) { return null; } $scope = $new->getAttribute(AttributeKey::SCOPE); - return $this->resolveMethodReflection($newClassType->getClassName(), MethodName::CONSTRUCT, $scope); + return $this->resolveMethodReflection($className, MethodName::CONSTRUCT, $scope); } - public function resolvePropertyReflectionFromPropertyFetch( - PropertyFetch | StaticPropertyFetch $propertyFetch - ): ?PhpPropertyReflection { - $fetcheeType = $propertyFetch instanceof PropertyFetch - ? $this->nodeTypeResolver->resolve($propertyFetch->var) - : $this->nodeTypeResolver->resolve($propertyFetch->class); + public function resolveConstructorReflectionFromAttribute(Attribute $attribute): ?MethodReflection + { + $attributeClassType = $this->nodeTypeResolver->getType($attribute->name); + $className = ClassNameFromObjectTypeResolver::resolve($attributeClassType); - if (! $fetcheeType instanceof TypeWithClassName) { + if ($className === null) { return null; } - if (! $this->reflectionProvider->hasClass($fetcheeType->getClassName())) { + $scope = $attribute->getAttribute(AttributeKey::SCOPE); + return $this->resolveMethodReflection($className, MethodName::CONSTRUCT, $scope); + } + + public function resolvePropertyReflectionFromPropertyFetch( + PropertyFetch | StaticPropertyFetch $propertyFetch + ): ?PhpPropertyReflection { + $propertyName = $this->nodeNameResolver->getName($propertyFetch->name); + if ($propertyName === null) { return null; } - $classReflection = $this->reflectionProvider->getClass($fetcheeType->getClassName()); + $fetcheeType = $propertyFetch instanceof PropertyFetch + ? $this->nodeTypeResolver->getType($propertyFetch->var) + : $this->nodeTypeResolver->getType($propertyFetch->class); - $propertyName = $this->nodeNameResolver->getName($propertyFetch->name); - if ($propertyName === null) { + $className = ClassNameFromObjectTypeResolver::resolve($fetcheeType); + if ($className === null) { return null; } - if (! $classReflection->hasProperty($propertyName)) { + if (! $this->reflectionProvider->hasClass($className)) { return null; } - $scope = $propertyFetch->getAttribute(AttributeKey::SCOPE); - if ($scope instanceof Scope) { - $propertyRelfection = $classReflection->getProperty($propertyName, $scope); - if ($propertyRelfection instanceof PhpPropertyReflection) { - return $propertyRelfection; - } + $classReflection = $this->reflectionProvider->getClass($className); + if (! $classReflection->hasNativeProperty($propertyName)) { return null; } @@ -194,22 +286,15 @@ public function resolvePropertyReflectionFromPropertyFetch( private function resolveFunctionReflectionFromFuncCall( FuncCall $funcCall ): FunctionReflection | MethodReflection | null { - $scope = $funcCall->getAttribute(AttributeKey::SCOPE); - - if ($funcCall->name instanceof Name) { - if ($this->reflectionProvider->hasFunction($funcCall->name, $scope)) { - return $this->reflectionProvider->getFunction($funcCall->name, $scope); - } - + if (! $funcCall->name instanceof Name) { return null; } - if (! $scope instanceof Scope) { - return null; + $functionName = new Name((string) $this->nodeNameResolver->getName($funcCall)); + if ($this->reflectionProvider->hasFunction($functionName, null)) { + return $this->reflectionProvider->getFunction($functionName, null); } - // fallback to callable - $funcCallNameType = $scope->getType($funcCall->name); - return $this->typeToCallReflectionResolverRegistry->resolve($funcCallNameType, $scope); + return null; } } diff --git a/src/Reporting/DeprecatedRulesReporter.php b/src/Reporting/DeprecatedRulesReporter.php new file mode 100644 index 00000000000..092301f7022 --- /dev/null +++ b/src/Reporting/DeprecatedRulesReporter.php @@ -0,0 +1,126 @@ +symfonyStyle->warning( + sprintf( + 'Registered rule "%s" is deprecated and will be removed. Upgrade your config to use another rule or remove it', + $registeredRectorRule + ) + ); + } + } + + public function reportDeprecatedSkippedRules(): void + { + /** @var string[] $skippedRectorRules */ + $skippedRectorRules = SimpleParameterProvider::provideArrayParameter(Option::SKIPPED_RECTOR_RULES); + + foreach ($skippedRectorRules as $skippedRectorRule) { + if (! is_a($skippedRectorRule, DeprecatedInterface::class, true)) { + continue; + } + + $this->symfonyStyle->warning(sprintf('Skipped rule "%s" is deprecated', $skippedRectorRule)); + } + } + + public function reportDeprecatedRectorUnsupportedMethods(): void + { + // to be added in related PR + if (! class_exists(FileNode::class)) { + return; + } + + foreach ($this->rectors as $rector) { + $beforeTraverseMethodReflection = new ReflectionMethod($rector, 'beforeTraverse'); + if ($beforeTraverseMethodReflection->getDeclaringClass()->getName() === $rector::class) { + $this->symfonyStyle->warning(sprintf( + 'Rector rule "%s" uses deprecated "beforeTraverse" method. It should not be used, as will be marked as final. Not part of RectorInterface contract. Use "%s" to hook into file-level changes instead.', + $rector::class, + FileNode::class + )); + } + } + } + + public function reportDeprecatedNodeTypes(): void + { + // helper property to avoid reporting multiple times + static $reportedClasses = []; + + foreach ($this->rectors as $rector) { + if (in_array(FileWithoutNamespace::class, $rector->getNodeTypes(), true)) { + $this->reportDeprecatedFileWithoutNamespace($rector); + continue; + } + + if (! in_array(StmtsAwareInterface::class, $rector->getNodeTypes())) { + continue; + } + + // already reported, skip + if (in_array($rector::class, $reportedClasses, true)) { + continue; + } + + $reportedClasses[] = $rector::class; + + $this->symfonyStyle->warning(sprintf( + 'Rector rule "%s" uses StmtsAwareInterface that is now deprecated.%sUse "%s::%s" instead.%sSee %s for more', + $rector::class, + PHP_EOL, + NodeGroup::class, + 'STMTS_AWARE', + PHP_EOL . PHP_EOL, + 'https://github.com/rectorphp/rector-src/pull/7679' + )); + } + } + + private function reportDeprecatedFileWithoutNamespace(RectorInterface $rector): void + { + $this->symfonyStyle->warning(sprintf( + 'Node type "%s" is deprecated and will be removed. Use "%s" in the "%s" rule instead instead.%sSee %s for upgrade path', + FileWithoutNamespace::class, + FileNode::class, + $rector::class, + PHP_EOL . PHP_EOL, + 'https://github.com/rectorphp/rector-src/blob/main/UPGRADING.md' + )); + } +} diff --git a/src/Reporting/MissConfigurationReporter.php b/src/Reporting/MissConfigurationReporter.php new file mode 100644 index 00000000000..208220c3c7e --- /dev/null +++ b/src/Reporting/MissConfigurationReporter.php @@ -0,0 +1,83 @@ + ! is_a($skippedRule, PostRectorInterface::class, true) + ); + + if ($neverRegisteredSkippedRules === []) { + return; + } + + $this->symfonyStyle->warning(sprintf( + '%s never registered. You can remove %s from "->withSkip()"', + count($neverRegisteredSkippedRules) > 1 ? 'These skipped rules are' : 'This skipped rule is', + count($neverRegisteredSkippedRules) > 1 ? 'them' : 'it' + )); + + $this->symfonyStyle->listing($neverRegisteredSkippedRules); + } + + /** + * @param string[] $filePaths + */ + public function reportVendorInPaths(array $filePaths): void + { + if (! $this->vendorMissAnalyseGuard->isVendorAnalyzed($filePaths)) { + return; + } + + $this->symfonyStyle->warning(sprintf( + 'Rector has detected a "/vendor" directory in your configured paths. If this is Composer\'s vendor directory, this is not necessary as it will be autoloaded. Scanning the Composer /vendor directory will cause Rector to run much slower and possibly with errors.%sRemove "/vendor" from Rector paths and run again.', + "\n\n" + )); + + sleep(3); + } + + public function reportStartWithShortOpenTag(): void + { + $files = SimpleParameterProvider::provideArrayParameter(Option::SKIPPED_START_WITH_SHORT_OPEN_TAG_FILES); + if ($files === []) { + return; + } + + $suffix = count($files) > 1 ? 's were' : ' was'; + $fileList = implode("\n", $files); + + $this->symfonyStyle->warning(sprintf( + 'The following file%s skipped as starting with short open tag. Migrate to long open PHP tag first: %s%s', + $suffix, + "\n\n", + $fileList + )); + + sleep(3); + } +} diff --git a/src/Reporting/MissingRectorRulesReporter.php b/src/Reporting/MissingRectorRulesReporter.php deleted file mode 100644 index f3829ff3aa0..00000000000 --- a/src/Reporting/MissingRectorRulesReporter.php +++ /dev/null @@ -1,63 +0,0 @@ -rectors, - function (RectorInterface $rector): bool { - if ($rector instanceof PostRectorInterface) { - return false; - } - - return ! $rector instanceof ComplementaryRectorInterface; - } - ); - - if ($activeRectors !== []) { - return null; - } - - $this->report(); - - return Command::FAILURE; - } - - public function report(): void - { - $this->symfonyStyle->warning('We could not find any Rector rules to run. You have 2 options to add them:'); - - $this->symfonyStyle->title('1. Add single rule to "rector.php"'); - $this->symfonyStyle->writeln(' $services = $containerConfigurator->services();'); - $this->symfonyStyle->writeln(' $services->set(...);'); - $this->symfonyStyle->newLine(1); - - $this->symfonyStyle->title('2. Add set of rules to "rector.php"'); - $this->symfonyStyle->writeln(' $containerConfigurator->import(SetList::...);'); - $this->symfonyStyle->newLine(1); - - $this->symfonyStyle->title('Missing "rector.php" in your project? Let Rector create it for you'); - $this->symfonyStyle->writeln(' vendor/bin/rector init'); - $this->symfonyStyle->newLine(); - } -} diff --git a/src/Set/Contract/SetInterface.php b/src/Set/Contract/SetInterface.php new file mode 100644 index 00000000000..3f3fb3d0455 --- /dev/null +++ b/src/Set/Contract/SetInterface.php @@ -0,0 +1,14 @@ +setProviderCollector->provideComposerTriggeredSets() as $composerTriggeredSet) { + if ($composerTriggeredSet->getGroupName() === $groupName) { + $matchedSets[] = $composerTriggeredSet; + } + } + + return $matchedSets; + } + + /** + * @param SetGroup::*[] $setGroups + * @return string[] + */ + public function matchBySetGroups(array $setGroups): array + { + $installedComposerPackages = $this->installedPackageResolver->resolve(); + $groupLoadedSets = []; + + foreach ($setGroups as $setGroup) { + $composerTriggeredSets = $this->matchComposerTriggered($setGroup); + + foreach ($composerTriggeredSets as $composerTriggeredSet) { + if ($composerTriggeredSet->matchInstalledPackages($installedComposerPackages)) { + // it matched composer package + version requirements → load set + $groupLoadedSets[] = realpath($composerTriggeredSet->getSetFilePath()); + } + } + } + + return $groupLoadedSets; + } +} diff --git a/src/Set/SetProvider/CoreSetProvider.php b/src/Set/SetProvider/CoreSetProvider.php new file mode 100644 index 00000000000..bfe2aa071e2 --- /dev/null +++ b/src/Set/SetProvider/CoreSetProvider.php @@ -0,0 +1,41 @@ +packageName, self::PACKAGE_REGEX); + Assert::fileExists($setFilePath); + } + + public function getGroupName(): string + { + return $this->groupName; + } + + public function getSetFilePath(): string + { + return $this->setFilePath; + } + + /** + * @param array $installedPackages + */ + public function matchInstalledPackages(array $installedPackages): bool + { + $package = $installedPackages[$this->packageName] ?? null; + + if (! $package instanceof InstalledPackage) { + return false; + } + + return Semver::satisfies($package->getVersion(), '^' . $this->version); + } + + public function getName(): string + { + return $this->packageName . ' ' . $this->version; + } +} diff --git a/src/Set/ValueObject/LevelSetList.php b/src/Set/ValueObject/LevelSetList.php new file mode 100644 index 00000000000..32c78c978cc --- /dev/null +++ b/src/Set/ValueObject/LevelSetList.php @@ -0,0 +1,43 @@ +groupName; + } + + public function getName(): string + { + return $this->setName; + } + + public function getSetFilePath(): string + { + return $this->setFilePath; + } +} diff --git a/src/Set/ValueObject/SetList.php b/src/Set/ValueObject/SetList.php new file mode 100644 index 00000000000..5acfe0ade65 --- /dev/null +++ b/src/Set/ValueObject/SetList.php @@ -0,0 +1,89 @@ +doesFileMatchPattern($filePath, $filePattern)) { + return true; + } + } + + return false; + } + + /** + * Supports both relative and absolute $file path. They differ for PHP-CS-Fixer and PHP_CodeSniffer. + */ + private function doesFileMatchPattern(string $filePath, string $ignoredPath): bool + { + // in rector.php, the path can be absolute + if ($filePath === $ignoredPath) { + return true; + } + + $ignoredPath = $this->fnMatchPathNormalizer->normalizeForFnmatch($ignoredPath); + if ($ignoredPath === '') { + return false; + } + + if (str_starts_with($filePath, $ignoredPath)) { + return true; + } + + if (str_ends_with($filePath, $ignoredPath)) { + return true; + } + + if ($this->fnmatcher->match($ignoredPath, $filePath)) { + return true; + } + + return $this->realpathMatcher->match($ignoredPath, $filePath); + } +} diff --git a/src/Skipper/RealpathMatcher.php b/src/Skipper/RealpathMatcher.php new file mode 100644 index 00000000000..6a947fd26b1 --- /dev/null +++ b/src/Skipper/RealpathMatcher.php @@ -0,0 +1,35 @@ + + */ + private null|array $skippedClassesToFiles = null; + + /** + * @return array> + */ + public function resolveDeprecatedSkippedClasses(): array + { + $skippedClassNames = array_keys($this->resolve()); + + return array_filter( + $skippedClassNames, + fn (string $class): bool => is_a($class, DeprecatedInterface::class, true) + ); + } + + /** + * @return array + */ + public function resolve(): array + { + // disable cache in tests + if (StaticPHPUnitEnvironment::isPHPUnitRun()) { + $this->skippedClassesToFiles = null; + } + + // already cached, even only empty array + if ($this->skippedClassesToFiles !== null) { + return $this->skippedClassesToFiles; + } + + $skip = SimpleParameterProvider::provideArrayParameter(Option::SKIP); + $this->skippedClassesToFiles = []; + + foreach ($skip as $key => $value) { + // e.g. [SomeClass::class] → shift values to [SomeClass::class => null] + if (is_int($key)) { + $key = $value; + $value = null; + } + + if (! is_string($key)) { + continue; + } + + // this only checks for Rector rules, that are always autoloaded + if (! class_exists($key) && ! interface_exists($key)) { + continue; + } + + $this->skippedClassesToFiles[$key] = $value; + } + + return $this->skippedClassesToFiles; + } +} diff --git a/src/Skipper/SkipCriteriaResolver/SkippedPathsResolver.php b/src/Skipper/SkipCriteriaResolver/SkippedPathsResolver.php new file mode 100644 index 00000000000..424e05f9969 --- /dev/null +++ b/src/Skipper/SkipCriteriaResolver/SkippedPathsResolver.php @@ -0,0 +1,62 @@ +skippedPaths = null; + } + + // already cached, even only empty array + if ($this->skippedPaths !== null) { + return $this->skippedPaths; + } + + $skip = SimpleParameterProvider::provideArrayParameter(Option::SKIP); + $this->skippedPaths = []; + + foreach ($skip as $key => $value) { + if (! is_int($key)) { + continue; + } + + if (\str_contains((string) $value, '*')) { + $this->skippedPaths[] = $this->filePathHelper->normalizePathAndSchema($value); + continue; + } + + if (file_exists($value)) { + $this->skippedPaths[] = $this->filePathHelper->normalizePathAndSchema($value); + } + } + + return $this->skippedPaths; + } +} diff --git a/src/Skipper/SkipVoter/ClassSkipVoter.php b/src/Skipper/SkipVoter/ClassSkipVoter.php new file mode 100644 index 00000000000..46f20c3c165 --- /dev/null +++ b/src/Skipper/SkipVoter/ClassSkipVoter.php @@ -0,0 +1,34 @@ +reflectionProvider->hasClass($element); + } + + public function shouldSkip(string | object $element, string $filePath): bool + { + $skippedClasses = $this->skippedClassResolver->resolve(); + return $this->skipSkipper->doesMatchSkip($element, $filePath, $skippedClasses); + } +} diff --git a/src/Skipper/Skipper/PathSkipper.php b/src/Skipper/Skipper/PathSkipper.php new file mode 100644 index 00000000000..e48ef8a6180 --- /dev/null +++ b/src/Skipper/Skipper/PathSkipper.php @@ -0,0 +1,23 @@ +skippedPathsResolver->resolve(); + return $this->fileInfoMatcher->doesFileInfoMatchPatterns($filePath, $skippedPaths); + } +} diff --git a/src/Skipper/Skipper/SkipSkipper.php b/src/Skipper/Skipper/SkipSkipper.php new file mode 100644 index 00000000000..0c382e35b9b --- /dev/null +++ b/src/Skipper/Skipper/SkipSkipper.php @@ -0,0 +1,38 @@ + $skippedClasses + */ + public function doesMatchSkip(object | string $checker, string $filePath, array $skippedClasses): bool + { + foreach ($skippedClasses as $skippedClass => $skippedFiles) { + if (! is_a($checker, $skippedClass, true)) { + continue; + } + + // skip everywhere + if (! is_array($skippedFiles)) { + return true; + } + + if ($this->fileInfoMatcher->doesFileInfoMatchPatterns($filePath, $skippedFiles)) { + return true; + } + } + + return false; + } +} diff --git a/src/Skipper/Skipper/Skipper.php b/src/Skipper/Skipper/Skipper.php new file mode 100644 index 00000000000..e7ff1645d25 --- /dev/null +++ b/src/Skipper/Skipper/Skipper.php @@ -0,0 +1,59 @@ +shouldSkipElementAndFilePath($element, __FILE__); + } + + public function shouldSkipFilePath(string $filePath): bool + { + return $this->pathSkipper->shouldSkip($filePath); + } + + public function shouldSkipElementAndFilePath(string | object $element, string $filePath): bool + { + if (! $this->classSkipVoter->match($element)) { + return false; + } + + return $this->classSkipVoter->shouldSkip($element, $filePath); + } + + /** + * @param class-string $rectorClass + */ + public function shouldSkipCurrentNode( + string | object $element, + string $filePath, + string $rectorClass, + Node $node + ): bool { + if ($this->shouldSkipElementAndFilePath($element, $filePath)) { + return true; + } + + return $this->rectifiedAnalyzer->hasRectified($rectorClass, $node); + } +} diff --git a/src/StaticReflection/DynamicSourceLocatorDecorator.php b/src/StaticReflection/DynamicSourceLocatorDecorator.php index ecb4c326305..fa2c16af46d 100644 --- a/src/StaticReflection/DynamicSourceLocatorDecorator.php +++ b/src/StaticReflection/DynamicSourceLocatorDecorator.php @@ -2,47 +2,48 @@ declare(strict_types=1); -namespace Rector\Core\StaticReflection; +namespace Rector\StaticReflection; -use Rector\Core\FileSystem\PhpFilesFinder; +use Rector\FileSystem\FileAndDirectoryFilter; +use Rector\FileSystem\FilesystemTweaker; use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider; -use Symplify\SmartFileSystem\FileSystemFilter; /** * @see https://phpstan.org/blog/zero-config-analysis-with-static-reflection * @see https://github.com/rectorphp/rector/issues/3490 */ -final class DynamicSourceLocatorDecorator +final readonly class DynamicSourceLocatorDecorator { public function __construct( - private FileSystemFilter $fileSystemFilter, private DynamicSourceLocatorProvider $dynamicSourceLocatorProvider, - private PhpFilesFinder $phpFilesFinder + private FileAndDirectoryFilter $fileAndDirectoryFilter, + private FilesystemTweaker $filesystemTweaker ) { } /** * @param string[] $paths + * @return string[] */ - public function addPaths(array $paths): void + public function addPaths(array $paths): array { if ($paths === []) { - return; + return []; } - $files = $this->fileSystemFilter->filterFiles($paths); + $paths = $this->filesystemTweaker->resolveWithFnmatch($paths); + $files = $this->fileAndDirectoryFilter->filterFiles($paths); + $this->dynamicSourceLocatorProvider->addFiles($files); - $directories = $this->fileSystemFilter->filterDirectories($paths); - foreach ($directories as $directory) { - $filesInfosInDirectory = $this->phpFilesFinder->findInPaths([$directory]); + $directories = $this->fileAndDirectoryFilter->filterDirectories($paths); + $this->dynamicSourceLocatorProvider->addDirectories($directories); - $filesInDirectory = []; - foreach ($filesInfosInDirectory as $fileInfoInDirectory) { - $filesInDirectory[] = $fileInfoInDirectory->getRealPath(); - } + return array_merge($files, $directories); + } - $this->dynamicSourceLocatorProvider->addFilesByDirectory($directory, $filesInDirectory); - } + public function arePathsEmpty(): bool + { + return $this->dynamicSourceLocatorProvider->arePathsEmpty(); } } diff --git a/src/StaticReflection/SourceLocator/ParentAttributeSourceLocator.php b/src/StaticReflection/SourceLocator/ParentAttributeSourceLocator.php deleted file mode 100644 index a052dd9ad58..00000000000 --- a/src/StaticReflection/SourceLocator/ParentAttributeSourceLocator.php +++ /dev/null @@ -1,78 +0,0 @@ -astResolver = $astResolver; - } - - public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?Reflection - { - if ($identifier->getName() === 'Symfony\Component\DependencyInjection\Attribute\Autoconfigure') { - if ($this->reflectionProvider->hasClass($identifier->getName())) { - $classReflection = $this->reflectionProvider->getClass($identifier->getName()); - - $class = $this->astResolver->resolveClassFromClassReflection( - $classReflection, - $identifier->getName() - ); - if ($class === null) { - return null; - } - - $class->namespacedName = new FullyQualified($identifier->getName()); - - $fakeLocatedSource = new LocatedSource('virtual', null); - - $classReflector = new ClassReflector($this); - return ReflectionClass::createFromNode( - $classReflector, - $class, - $fakeLocatedSource, - new Namespace_(new Name('Symfony\Component\DependencyInjection\Attribute')) - ); - } - } - - return null; - } - - public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array - { - return []; - } -} diff --git a/src/StaticReflection/SourceLocator/RenamedClassesSourceLocator.php b/src/StaticReflection/SourceLocator/RenamedClassesSourceLocator.php deleted file mode 100644 index 78395e48b61..00000000000 --- a/src/StaticReflection/SourceLocator/RenamedClassesSourceLocator.php +++ /dev/null @@ -1,56 +0,0 @@ -renamedClassesDataCollector->getOldToNewClasses() as $oldClass => $newClass) { - if ($identifier->getName() !== $oldClass) { - continue; - } - - // inspired at https://github.com/phpstan/phpstan-src/blob/a9dd9af959fb0c1e0a09d4850f78e05e8dff3d91/src/Reflection/BetterReflection/BetterReflectionProvider.php#L220-L225 - return $this->createFakeReflectionClassFromClassName($oldClass); - } - - return null; - } - - public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array - { - return []; - } - - private function createFakeReflectionClassFromClassName(string $oldClass): ReflectionClass - { - $classBuilder = new ClassBuilder($oldClass); - $class = $classBuilder->getNode(); - $fakeLocatedSource = new LocatedSource('virtual', null); - - $classReflector = new ClassReflector($this); - return ReflectionClass::createFromNode($classReflector, $class, $fakeLocatedSource); - } -} diff --git a/packages/StaticTypeMapper/Contract/PhpDocParser/PhpDocTypeMapperInterface.php b/src/StaticTypeMapper/Contract/PhpDocParser/PhpDocTypeMapperInterface.php similarity index 75% rename from packages/StaticTypeMapper/Contract/PhpDocParser/PhpDocTypeMapperInterface.php rename to src/StaticTypeMapper/Contract/PhpDocParser/PhpDocTypeMapperInterface.php index 905174fd5d7..baa48cecc8a 100644 --- a/packages/StaticTypeMapper/Contract/PhpDocParser/PhpDocTypeMapperInterface.php +++ b/src/StaticTypeMapper/Contract/PhpDocParser/PhpDocTypeMapperInterface.php @@ -9,12 +9,18 @@ use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Type\Type; +/** + * @template TTypeNode as TypeNode + */ interface PhpDocTypeMapperInterface { /** - * @return class-string + * @return class-string */ public function getNodeType(): string; + /** + * @param TTypeNode $typeNode + */ public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type; } diff --git a/src/StaticTypeMapper/Contract/PhpParser/PhpParserNodeMapperInterface.php b/src/StaticTypeMapper/Contract/PhpParser/PhpParserNodeMapperInterface.php new file mode 100644 index 00000000000..7d387517957 --- /dev/null +++ b/src/StaticTypeMapper/Contract/PhpParser/PhpParserNodeMapperInterface.php @@ -0,0 +1,24 @@ + + */ + public function getNodeType(): string; + + /** + * @param TNode $node + */ + public function mapToPHPStan(Node $node): Type; +} diff --git a/src/StaticTypeMapper/Mapper/PhpParserNodeMapper.php b/src/StaticTypeMapper/Mapper/PhpParserNodeMapper.php new file mode 100644 index 00000000000..2a244aa6086 --- /dev/null +++ b/src/StaticTypeMapper/Mapper/PhpParserNodeMapper.php @@ -0,0 +1,34 @@ +phpParserNodeMappers as $phpParserNodeMapper) { + if (! is_a($node, $phpParserNodeMapper->getNodeType())) { + continue; + } + + return $phpParserNodeMapper->mapToPHPStan($node); + } + + throw new NotImplementedYetException($node::class); + } +} diff --git a/src/StaticTypeMapper/Mapper/ScalarStringToTypeMapper.php b/src/StaticTypeMapper/Mapper/ScalarStringToTypeMapper.php new file mode 100644 index 00000000000..36d0e79d0e8 --- /dev/null +++ b/src/StaticTypeMapper/Mapper/ScalarStringToTypeMapper.php @@ -0,0 +1,100 @@ +, string[]> + */ + private const array SCALAR_NAME_BY_TYPE = [ + StringType::class => ['string'], + AccessoryNonEmptyStringType::class => ['non-empty-string'], + NonEmptyArrayType::class => ['non-empty-array'], + ClassStringType::class => ['class-string'], + FloatType::class => ['float', 'real', 'double'], + IntegerType::class => ['int', 'integer'], + BooleanType::class => ['bool', 'boolean'], + NullType::class => ['null'], + VoidType::class => ['void'], + ResourceType::class => ['resource'], + CallableType::class => ['callback', 'callable'], + ObjectWithoutClassType::class => ['object'], + NeverType::class => ['never', 'never-return', 'never-returns', 'no-return'], + ]; + + public function mapScalarStringToType(string $scalarName): Type + { + $loweredScalarName = Strings::lower($scalarName); + + if ($loweredScalarName === 'false') { + return new ConstantBooleanType(false); + } + + if ($loweredScalarName === 'true') { + return new ConstantBooleanType(true); + } + + if ($loweredScalarName === 'positive-int') { + return IntegerRangeType::createAllGreaterThan(0); + } + + if ($loweredScalarName === 'negative-int') { + return IntegerRangeType::createAllSmallerThan(0); + } + + foreach (self::SCALAR_NAME_BY_TYPE as $objectType => $scalarNames) { + if (! in_array($loweredScalarName, $scalarNames, true)) { + continue; + } + + return new $objectType(); + } + + if ($loweredScalarName === 'list') { + return TypeCombinator::intersect( + new ArrayType(new MixedType(), new MixedType()), + new AccessoryArrayListType() + ); + } + + if ($loweredScalarName === 'array') { + return new ArrayType(new MixedType(), new MixedType()); + } + + if ($loweredScalarName === 'iterable') { + return new IterableType(new MixedType(), new MixedType()); + } + + if ($loweredScalarName === 'mixed') { + return new MixedType(true); + } + + return new MixedType(); + } +} diff --git a/src/StaticTypeMapper/Naming/NameScopeFactory.php b/src/StaticTypeMapper/Naming/NameScopeFactory.php new file mode 100644 index 00000000000..bcb29e237e6 --- /dev/null +++ b/src/StaticTypeMapper/Naming/NameScopeFactory.php @@ -0,0 +1,69 @@ +getAttribute(AttributeKey::SCOPE); + if ($scope instanceof Scope) { + $namespace = $scope->getNamespace(); + $classReflection = $scope->getClassReflection(); + $className = $classReflection instanceof ClassReflection ? $classReflection->getName() : null; + } else { + $namespace = null; + $className = null; + } + + $uses = $this->useImportsResolver->resolve(); + $usesAliasesToNames = $this->resolveUseNamesByAlias($uses); + + return new NameScope($namespace, $usesAliasesToNames, $className); + } + + /** + * @param array $useNodes + * @return array + */ + private function resolveUseNamesByAlias(array $useNodes): array + { + $useNamesByAlias = []; + + foreach ($useNodes as $useNode) { + $prefix = $this->useImportsResolver->resolvePrefix($useNode); + foreach ($useNode->uses as $useUse) { + /** @var UseItem $useUse */ + $aliasName = $useUse->getAlias() + ->name; + + // uses must be lowercase, as PHPStan lowercases it + $lowercasedAliasName = strtolower($aliasName); + + $useNamesByAlias[$lowercasedAliasName] = $prefix . $useUse->name->toString(); + } + } + + return $useNamesByAlias; + } +} diff --git a/packages/StaticTypeMapper/PhpDoc/PhpDocTypeMapper.php b/src/StaticTypeMapper/PhpDoc/PhpDocTypeMapper.php similarity index 90% rename from packages/StaticTypeMapper/PhpDoc/PhpDocTypeMapper.php rename to src/StaticTypeMapper/PhpDoc/PhpDocTypeMapper.php index 5b80a8f6396..6d75c7ea100 100644 --- a/packages/StaticTypeMapper/PhpDoc/PhpDocTypeMapper.php +++ b/src/StaticTypeMapper/PhpDoc/PhpDocTypeMapper.php @@ -10,11 +10,12 @@ use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Type\Type; use Rector\StaticTypeMapper\Contract\PhpDocParser\PhpDocTypeMapperInterface; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\StaticTypeMapper\PhpDoc\PhpDocTypeMapperTest */ -final class PhpDocTypeMapper +final readonly class PhpDocTypeMapper { /** * @param PhpDocTypeMapperInterface[] $phpDocTypeMappers @@ -23,6 +24,7 @@ public function __construct( private array $phpDocTypeMappers, private TypeNodeResolver $typeNodeResolver ) { + Assert::notEmpty($phpDocTypeMappers); } public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type diff --git a/src/StaticTypeMapper/PhpDocParser/IdentifierPhpDocTypeMapper.php b/src/StaticTypeMapper/PhpDocParser/IdentifierPhpDocTypeMapper.php new file mode 100644 index 00000000000..76a64dc7db9 --- /dev/null +++ b/src/StaticTypeMapper/PhpDocParser/IdentifierPhpDocTypeMapper.php @@ -0,0 +1,171 @@ + + */ +final readonly class IdentifierPhpDocTypeMapper implements PhpDocTypeMapperInterface +{ + public function __construct( + private ObjectTypeSpecifier $objectTypeSpecifier, + private ScalarStringToTypeMapper $scalarStringToTypeMapper, + private ReflectionProvider $reflectionProvider, + private ReflectionResolver $reflectionResolver + ) { + } + + public function getNodeType(): string + { + return IdentifierTypeNode::class; + } + + /** + * @param IdentifierTypeNode $typeNode + */ + public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type + { + return $this->mapIdentifierTypeNode($typeNode, $node); + } + + public function mapIdentifierTypeNode(IdentifierTypeNode $identifierTypeNode, Node $node): Type + { + $type = $this->scalarStringToTypeMapper->mapScalarStringToType($identifierTypeNode->name); + if (! $type instanceof MixedType) { + return $type; + } + + if ($type->isExplicitMixed()) { + return $type; + } + + $loweredName = strtolower($identifierTypeNode->name); + if ($loweredName === ObjectReference::SELF) { + return $this->mapSelf($node); + } + + if ($loweredName === ObjectReference::PARENT) { + return $this->mapParent($node); + } + + if ($loweredName === ObjectReference::STATIC) { + return $this->mapStatic($node); + } + + if ($loweredName === 'iterable') { + return new IterableType(new MixedType(), new MixedType()); + } + + $withPreslash = false; + if (str_starts_with($identifierTypeNode->name, '\\')) { + $typeWithoutPreslash = Strings::substring($identifierTypeNode->name, 1); + $objectType = new FullyQualifiedObjectType($typeWithoutPreslash); + $withPreslash = true; + } else { + if ($identifierTypeNode->name === 'scalar') { + // pseudo type, see https://www.php.net/manual/en/language.types.intro.php + $scalarTypes = [new BooleanType(), new StringType(), new IntegerType(), new FloatType()]; + return new UnionType($scalarTypes); + } + + $objectType = new ObjectType(ltrim($identifierTypeNode->name, '@')); + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType( + $node, + $objectType, + $scope, + $withPreslash + ); + } + + private function mapSelf(Node $node): MixedType | SelfObjectType + { + // @todo check FQN + $className = $this->resolveClassName($node); + if (! is_string($className)) { + // self outside the class, e.g. in a function + return new MixedType(); + } + + return new SelfObjectType($className); + } + + private function mapParent(Node $node): ParentStaticType | MixedType + { + $className = $this->resolveClassName($node); + if (! is_string($className)) { + // parent outside the class, e.g. in a function + return new MixedType(); + } + + if (! $this->reflectionProvider->hasClass($className)) { + return new MixedType(); + } + + $classReflection = $this->reflectionProvider->getClass($className); + $parentClassReflection = $classReflection->getParentClass(); + + if (! $parentClassReflection instanceof ClassReflection) { + return new MixedType(); + } + + return new ParentStaticType($parentClassReflection); + } + + private function mapStatic(Node $node): MixedType | StaticType + { + $className = $this->resolveClassName($node); + if (! is_string($className)) { + // static outside the class, e.g. in a function + return new MixedType(); + } + + if (! $this->reflectionProvider->hasClass($className)) { + return new MixedType(); + } + + $classReflection = $this->reflectionProvider->getClass($className); + return new StaticType($classReflection); + } + + private function resolveClassName(Node $node): ?string + { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + return $classReflection->getName(); + } +} diff --git a/src/StaticTypeMapper/PhpDocParser/IntersectionPhpDocTypeMapper.php b/src/StaticTypeMapper/PhpDocParser/IntersectionPhpDocTypeMapper.php new file mode 100644 index 00000000000..fdf46a25596 --- /dev/null +++ b/src/StaticTypeMapper/PhpDocParser/IntersectionPhpDocTypeMapper.php @@ -0,0 +1,51 @@ + + */ +final readonly class IntersectionPhpDocTypeMapper implements PhpDocTypeMapperInterface +{ + public function __construct( + private IdentifierPhpDocTypeMapper $identifierPhpDocTypeMapper + ) { + } + + public function getNodeType(): string + { + return IntersectionTypeNode::class; + } + + /** + * @param IntersectionTypeNode $typeNode + */ + public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type + { + $intersectionedTypes = []; + foreach ($typeNode->types as $intersectionedTypeNode) { + if (! $intersectionedTypeNode instanceof IdentifierTypeNode) { + return new MixedType(); + } + + $intersectionedTypes[] = $this->identifierPhpDocTypeMapper->mapIdentifierTypeNode( + $intersectionedTypeNode, + $node + ); + } + + return new IntersectionType($intersectionedTypes); + } +} diff --git a/src/StaticTypeMapper/PhpDocParser/NullablePhpDocTypeMapper.php b/src/StaticTypeMapper/PhpDocParser/NullablePhpDocTypeMapper.php new file mode 100644 index 00000000000..a8ea9c88835 --- /dev/null +++ b/src/StaticTypeMapper/PhpDocParser/NullablePhpDocTypeMapper.php @@ -0,0 +1,52 @@ + + */ +final readonly class NullablePhpDocTypeMapper implements PhpDocTypeMapperInterface +{ + public function __construct( + private IdentifierPhpDocTypeMapper $identifierPhpDocTypeMapper, + private TypeNodeResolver $typeNodeResolver + ) { + } + + public function getNodeType(): string + { + return NullableTypeNode::class; + } + + /** + * @param NullableTypeNode $typeNode + */ + public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type + { + if ($typeNode->type instanceof IdentifierTypeNode) { + $type = $this->identifierPhpDocTypeMapper->mapToPHPStanType($typeNode->type, $node, $nameScope); + + if ($type instanceof UnionType) { + return new UnionType([new NullType(), ...$type->getTypes()]); + } + + return new UnionType([new NullType(), $type]); + } + + // fallback to PHPStan resolver + return $this->typeNodeResolver->resolve($typeNode, $nameScope); + } +} diff --git a/src/StaticTypeMapper/PhpDocParser/UnionPhpDocTypeMapper.php b/src/StaticTypeMapper/PhpDocParser/UnionPhpDocTypeMapper.php new file mode 100644 index 00000000000..79d12e89acc --- /dev/null +++ b/src/StaticTypeMapper/PhpDocParser/UnionPhpDocTypeMapper.php @@ -0,0 +1,67 @@ + + */ +final readonly class UnionPhpDocTypeMapper implements PhpDocTypeMapperInterface +{ + public function __construct( + private TypeFactory $typeFactory, + private IdentifierPhpDocTypeMapper $identifierPhpDocTypeMapper, + private IntersectionPhpDocTypeMapper $intersectionPhpDocTypeMapper, + private TypeNodeResolver $typeNodeResolver + ) { + } + + public function getNodeType(): string + { + return UnionTypeNode::class; + } + + /** + * @param UnionTypeNode $typeNode + */ + public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type + { + $unionedTypes = []; + foreach ($typeNode->types as $unionedTypeNode) { + if ($unionedTypeNode instanceof IdentifierTypeNode) { + $unionedTypes[] = $this->identifierPhpDocTypeMapper->mapToPHPStanType( + $unionedTypeNode, + $node, + $nameScope + ); + continue; + } + + if ($unionedTypeNode instanceof IntersectionTypeNode) { + $unionedTypes[] = $this->intersectionPhpDocTypeMapper->mapToPHPStanType( + $unionedTypeNode, + $node, + $nameScope + ); + continue; + } + + $unionedTypes[] = $this->typeNodeResolver->resolve($unionedTypeNode, $nameScope); + } + + // to prevent missing class error, e.g. in tests + return $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($unionedTypes); + } +} diff --git a/packages/StaticTypeMapper/PhpParser/ExprNodeMapper.php b/src/StaticTypeMapper/PhpParser/ExprNodeMapper.php similarity index 93% rename from packages/StaticTypeMapper/PhpParser/ExprNodeMapper.php rename to src/StaticTypeMapper/PhpParser/ExprNodeMapper.php index bf18bce9265..3a7b34962b8 100644 --- a/packages/StaticTypeMapper/PhpParser/ExprNodeMapper.php +++ b/src/StaticTypeMapper/PhpParser/ExprNodeMapper.php @@ -12,11 +12,11 @@ use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface; +/** + * @implements PhpParserNodeMapperInterface + */ final class ExprNodeMapper implements PhpParserNodeMapperInterface { - /** - * @return class-string - */ public function getNodeType(): string { return Expr::class; diff --git a/packages/StaticTypeMapper/PhpParser/FullyQualifiedNodeMapper.php b/src/StaticTypeMapper/PhpParser/FullyQualifiedNodeMapper.php similarity index 95% rename from packages/StaticTypeMapper/PhpParser/FullyQualifiedNodeMapper.php rename to src/StaticTypeMapper/PhpParser/FullyQualifiedNodeMapper.php index c3964ce21f5..4beb434b1ea 100644 --- a/packages/StaticTypeMapper/PhpParser/FullyQualifiedNodeMapper.php +++ b/src/StaticTypeMapper/PhpParser/FullyQualifiedNodeMapper.php @@ -12,11 +12,11 @@ use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +/** + * @implements PhpParserNodeMapperInterface + */ final class FullyQualifiedNodeMapper implements PhpParserNodeMapperInterface { - /** - * @return class-string - */ public function getNodeType(): string { return FullyQualified::class; diff --git a/packages/StaticTypeMapper/PhpParser/IdentifierNodeMapper.php b/src/StaticTypeMapper/PhpParser/IdentifierNodeMapper.php similarity index 82% rename from packages/StaticTypeMapper/PhpParser/IdentifierNodeMapper.php rename to src/StaticTypeMapper/PhpParser/IdentifierNodeMapper.php index 223e97d791c..e1818c4aad9 100644 --- a/packages/StaticTypeMapper/PhpParser/IdentifierNodeMapper.php +++ b/src/StaticTypeMapper/PhpParser/IdentifierNodeMapper.php @@ -10,16 +10,16 @@ use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface; use Rector\StaticTypeMapper\Mapper\ScalarStringToTypeMapper; -final class IdentifierNodeMapper implements PhpParserNodeMapperInterface +/** + * @implements PhpParserNodeMapperInterface + */ +final readonly class IdentifierNodeMapper implements PhpParserNodeMapperInterface { public function __construct( private ScalarStringToTypeMapper $scalarStringToTypeMapper ) { } - /** - * @return class-string - */ public function getNodeType(): string { return Identifier::class; diff --git a/src/StaticTypeMapper/PhpParser/IntersectionTypeNodeMapper.php b/src/StaticTypeMapper/PhpParser/IntersectionTypeNodeMapper.php new file mode 100644 index 00000000000..b3b2873817f --- /dev/null +++ b/src/StaticTypeMapper/PhpParser/IntersectionTypeNodeMapper.php @@ -0,0 +1,52 @@ + + */ +final readonly class IntersectionTypeNodeMapper implements PhpParserNodeMapperInterface +{ + public function __construct( + private FullyQualifiedNodeMapper $fullyQualifiedNodeMapper, + private NameNodeMapper $nameNodeMapper, + private IdentifierNodeMapper $identifierNodeMapper + ) { + } + + public function getNodeType(): string + { + return Node\IntersectionType::class; + } + + /** + * @param Node\IntersectionType $node + */ + public function mapToPHPStan(Node $node): IntersectionType + { + $types = []; + foreach ($node->types as $intersectionedType) { + if ($intersectionedType instanceof FullyQualified) { + $types[] = $this->fullyQualifiedNodeMapper->mapToPHPStan($intersectionedType); + continue; + } + + if ($intersectionedType instanceof Name) { + $types[] = $this->nameNodeMapper->mapToPHPStan($intersectionedType); + continue; + } + + $types[] = $this->identifierNodeMapper->mapToPHPStan($intersectionedType); + } + + return new IntersectionType($types); + } +} diff --git a/src/StaticTypeMapper/PhpParser/NameNodeMapper.php b/src/StaticTypeMapper/PhpParser/NameNodeMapper.php new file mode 100644 index 00000000000..3d2e11c7aff --- /dev/null +++ b/src/StaticTypeMapper/PhpParser/NameNodeMapper.php @@ -0,0 +1,95 @@ + + */ +final readonly class NameNodeMapper implements PhpParserNodeMapperInterface +{ + public function __construct( + private ReflectionResolver $reflectionResolver, + private FullyQualifiedNodeMapper $fullyQualifiedNodeMapper + ) { + } + + public function getNodeType(): string + { + return Name::class; + } + + /** + * @param Name $node + */ + public function mapToPHPStan(Node $node): Type + { + $name = $node->toString(); + + if ($node->isSpecialClassName()) { + return $this->createClassReferenceType($node, $name); + } + + $expandedNamespacedName = $this->expandedNamespacedName($node); + if ($expandedNamespacedName instanceof FullyQualified) { + return $this->fullyQualifiedNodeMapper->mapToPHPStan($expandedNamespacedName); + } + + return new MixedType(); + } + + private function expandedNamespacedName(Name $name): ?FullyQualified + { + if ($name::class !== Name::class) { + return null; + } + + if (! $name->hasAttribute(AttributeKey::NAMESPACED_NAME)) { + return null; + } + + return new FullyQualified($name->getAttribute(AttributeKey::NAMESPACED_NAME)); + } + + private function createClassReferenceType( + Name $name, + string $reference + ): MixedType | StaticType | SelfStaticType | ObjectWithoutClassType { + $classReflection = $this->reflectionResolver->resolveClassReflection($name); + if (! $classReflection instanceof ClassReflection) { + return new MixedType(); + } + + if ($reference === ObjectReference::STATIC) { + return new StaticType($classReflection); + } + + if ($reference === ObjectReference::SELF) { + return new SelfStaticType($classReflection); + } + + $parentClassReflection = $classReflection->getParentClass(); + if ($parentClassReflection instanceof ClassReflection) { + return new ParentStaticType($parentClassReflection); + } + + return new ParentObjectWithoutClassType(); + } +} diff --git a/src/StaticTypeMapper/PhpParser/NullableTypeNodeMapper.php b/src/StaticTypeMapper/PhpParser/NullableTypeNodeMapper.php new file mode 100644 index 00000000000..9d2582fb0e5 --- /dev/null +++ b/src/StaticTypeMapper/PhpParser/NullableTypeNodeMapper.php @@ -0,0 +1,51 @@ + + */ +final readonly class NullableTypeNodeMapper implements PhpParserNodeMapperInterface +{ + public function __construct( + private TypeFactory $typeFactory, + private FullyQualifiedNodeMapper $fullyQualifiedNodeMapper, + private NameNodeMapper $nameNodeMapper, + private IdentifierNodeMapper $identifierNodeMapper + ) { + } + + public function getNodeType(): string + { + return NullableType::class; + } + + /** + * @param NullableType $node + */ + public function mapToPHPStan(Node $node): Type + { + if ($node->type instanceof FullyQualified) { + $type = $this->fullyQualifiedNodeMapper->mapToPHPStan($node->type); + } elseif ($node->type instanceof Name) { + $type = $this->nameNodeMapper->mapToPHPStan($node->type); + } else { + $type = $this->identifierNodeMapper->mapToPHPStan($node->type); + } + + $types = [$type, new NullType()]; + + return $this->typeFactory->createMixedPassedOrUnionType($types); + } +} diff --git a/packages/StaticTypeMapper/PhpParser/StringNodeMapper.php b/src/StaticTypeMapper/PhpParser/StringNodeMapper.php similarity index 79% rename from packages/StaticTypeMapper/PhpParser/StringNodeMapper.php rename to src/StaticTypeMapper/PhpParser/StringNodeMapper.php index dcc7e55003e..39d31288f82 100644 --- a/packages/StaticTypeMapper/PhpParser/StringNodeMapper.php +++ b/src/StaticTypeMapper/PhpParser/StringNodeMapper.php @@ -7,14 +7,13 @@ use PhpParser\Node; use PhpParser\Node\Scalar\String_; use PHPStan\Type\StringType; -use PHPStan\Type\Type; use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface; +/** + * @implements PhpParserNodeMapperInterface + */ final class StringNodeMapper implements PhpParserNodeMapperInterface { - /** - * @return class-string - */ public function getNodeType(): string { return String_::class; @@ -23,7 +22,7 @@ public function getNodeType(): string /** * @param String_ $node */ - public function mapToPHPStan(Node $node): Type + public function mapToPHPStan(Node $node): StringType { return new StringType(); } diff --git a/src/StaticTypeMapper/PhpParser/UnionTypeNodeMapper.php b/src/StaticTypeMapper/PhpParser/UnionTypeNodeMapper.php new file mode 100644 index 00000000000..58cb4e845ef --- /dev/null +++ b/src/StaticTypeMapper/PhpParser/UnionTypeNodeMapper.php @@ -0,0 +1,62 @@ + + */ +final readonly class UnionTypeNodeMapper implements PhpParserNodeMapperInterface +{ + public function __construct( + private TypeFactory $typeFactory, + private FullyQualifiedNodeMapper $fullyQualifiedNodeMapper, + private NameNodeMapper $nameNodeMapper, + private IdentifierNodeMapper $identifierNodeMapper, + private IntersectionTypeNodeMapper $intersectionTypeNodeMapper + ) { + } + + public function getNodeType(): string + { + return UnionType::class; + } + + /** + * @param UnionType $node + */ + public function mapToPHPStan(Node $node): Type + { + $types = []; + foreach ($node->types as $unionedType) { + if ($unionedType instanceof FullyQualified) { + $types[] = $this->fullyQualifiedNodeMapper->mapToPHPStan($unionedType); + continue; + } + + if ($unionedType instanceof Name) { + $types[] = $this->nameNodeMapper->mapToPHPStan($unionedType); + continue; + } + + if ($unionedType instanceof Identifier) { + $types[] = $this->identifierNodeMapper->mapToPHPStan($unionedType); + continue; + } + + $types[] = $this->intersectionTypeNodeMapper->mapToPHPStan($unionedType); + } + + return $this->typeFactory->createMixedPassedOrUnionType($types, true); + } +} diff --git a/src/StaticTypeMapper/Resolver/ClassNameFromObjectTypeResolver.php b/src/StaticTypeMapper/Resolver/ClassNameFromObjectTypeResolver.php new file mode 100644 index 00000000000..397ae423964 --- /dev/null +++ b/src/StaticTypeMapper/Resolver/ClassNameFromObjectTypeResolver.php @@ -0,0 +1,21 @@ +getObjectClassNames(); + + if (count($objectClassNames) !== 1) { + return null; + } + + return $objectClassNames[0]; + } +} diff --git a/src/StaticTypeMapper/StaticTypeMapper.php b/src/StaticTypeMapper/StaticTypeMapper.php new file mode 100644 index 00000000000..98e9705766f --- /dev/null +++ b/src/StaticTypeMapper/StaticTypeMapper.php @@ -0,0 +1,84 @@ + PHPStan <=> PHPStan doc <=> string type nodes between all possible formats + * @see \Rector\Tests\NodeTypeResolver\StaticTypeMapper\StaticTypeMapperTest + */ +final readonly class StaticTypeMapper +{ + public function __construct( + private NameScopeFactory $nameScopeFactory, + private PHPStanStaticTypeMapper $phpStanStaticTypeMapper, + private PhpDocTypeMapper $phpDocTypeMapper, + private PhpParserNodeMapper $phpParserNodeMapper + ) { + } + + public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType): TypeNode + { + return $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($phpStanType); + } + + /** + * @param TypeKind::* $typeKind + * @return Name|ComplexType|Identifier|null + */ + public function mapPHPStanTypeToPhpParserNode(Type $phpStanType, string $typeKind): ?Node + { + return $this->phpStanStaticTypeMapper->mapToPhpParserNode($phpStanType, $typeKind); + } + + public function mapPhpParserNodePHPStanType(Node $node): Type + { + return $this->phpParserNodeMapper->mapToPHPStanType($node); + } + + public function mapPHPStanPhpDocTypeToPHPStanType(PhpDocTagValueNode $phpDocTagValueNode, Node $node): Type + { + if ($phpDocTagValueNode instanceof TemplateTagValueNode) { + // special case + if (! $phpDocTagValueNode->bound instanceof TypeNode) { + return new MixedType(); + } + + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($node); + return $this->phpDocTypeMapper->mapToPHPStanType($phpDocTagValueNode->bound, $node, $nameScope); + } + + if ($phpDocTagValueNode instanceof ReturnTagValueNode || $phpDocTagValueNode instanceof ParamTagValueNode || $phpDocTagValueNode instanceof VarTagValueNode || $phpDocTagValueNode instanceof ThrowsTagValueNode) { + return $this->mapPHPStanPhpDocTypeNodeToPHPStanType($phpDocTagValueNode->type, $node); + } + + throw new NotImplementedYetException(__METHOD__ . ' for ' . $phpDocTagValueNode::class); + } + + public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $node): Type + { + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($node); + return $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $node, $nameScope); + } +} diff --git a/src/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php b/src/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php new file mode 100644 index 00000000000..c9a14f997fe --- /dev/null +++ b/src/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php @@ -0,0 +1,75 @@ +fullyQualifiedClass; + } + + /** + * @param Use_::TYPE_* $useType + */ + public function getUseNode(int $useType): Use_ + { + $name = new Name($this->fullyQualifiedClass); + + $useItem = new UseItem($name, $this->getClassName()); + + $use = new Use_([$useItem]); + $use->type = $useType; + + return $use; + } + + public function getShortName(): string + { + return $this->getClassName(); + } + + public function areShortNamesEqual(self | FullyQualifiedObjectType $comparedObjectType): bool + { + return $this->getShortName() === $comparedObjectType->getShortName(); + } + + #[Override] + public function equals(Type $type): bool + { + $className = ClassNameFromObjectTypeResolver::resolve($type); + + // compare with FQN classes + if ($className !== null) { + if ($type instanceof self && $this->fullyQualifiedClass === $type->getFullyQualifiedName()) { + return true; + } + + if ($this->fullyQualifiedClass === $className) { + return true; + } + } + + return parent::equals($type); + } +} diff --git a/packages/StaticTypeMapper/ValueObject/Type/FullyQualifiedGenericObjectType.php b/src/StaticTypeMapper/ValueObject/Type/FullyQualifiedGenericObjectType.php similarity index 100% rename from packages/StaticTypeMapper/ValueObject/Type/FullyQualifiedGenericObjectType.php rename to src/StaticTypeMapper/ValueObject/Type/FullyQualifiedGenericObjectType.php diff --git a/src/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php b/src/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php new file mode 100644 index 00000000000..b917ce438de --- /dev/null +++ b/src/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php @@ -0,0 +1,85 @@ +getShortName(), $this->getClassName()); + } + + public function areShortNamesEqual(AliasedObjectType | self $comparedObjectType): bool + { + return $this->getShortName() === $comparedObjectType->getShortName(); + } + + public function getShortName(): string + { + $className = $this->getClassName(); + if (! \str_contains($className, '\\')) { + return $className; + } + + return (string) Strings::after($className, '\\', -1); + } + + public function getShortNameNode(): Name + { + $name = new Name($this->getShortName()); + + // keep original to avoid loss on while importing + $name->setAttribute(AttributeKey::NAMESPACED_NAME, $this->getClassName()); + + return $name; + } + + /** + * @param Use_::TYPE_* $useType + */ + public function getUseNode(int $useType): Use_ + { + $name = new Name($this->getClassName()); + $useItem = new UseItem($name); + + $use = new Use_([$useItem]); + $use->type = $useType; + + return $use; + } + + public function getShortNameLowered(): string + { + return strtolower($this->getShortName()); + } + + #[Override] + public function equals(Type $type): bool + { + $isEqual = parent::equals($type); + + if ($isEqual) { + return true; + } + + if ($type instanceof self || $type::class === ObjectType::class) { + return $type->getClassName() === $this->getClassName(); + } + + return false; + } +} diff --git a/src/StaticTypeMapper/ValueObject/Type/NonExistingObjectType.php b/src/StaticTypeMapper/ValueObject/Type/NonExistingObjectType.php new file mode 100644 index 00000000000..be77cd201cd --- /dev/null +++ b/src/StaticTypeMapper/ValueObject/Type/NonExistingObjectType.php @@ -0,0 +1,28 @@ +getClassName() === $this->getClassName(); + } + + return false; + } +} diff --git a/src/StaticTypeMapper/ValueObject/Type/ParentObjectWithoutClassType.php b/src/StaticTypeMapper/ValueObject/Type/ParentObjectWithoutClassType.php new file mode 100644 index 00000000000..47c98cb247e --- /dev/null +++ b/src/StaticTypeMapper/ValueObject/Type/ParentObjectWithoutClassType.php @@ -0,0 +1,11 @@ +fullyQualifiedName, $this->getTypes()); + return $genericObjectType->isSuperTypeOf($type); + } + + public function getShortName(): string + { + return $this->getClassName(); + } +} diff --git a/src/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php b/src/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php new file mode 100644 index 00000000000..396d28626e9 --- /dev/null +++ b/src/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php @@ -0,0 +1,62 @@ +fullyQualifiedName); + return $fullyQualifiedObjectType->isSuperTypeOf($type); + } + + public function getShortName(): string + { + return $this->getClassName(); + } + + /** + * @return class-string + */ + public function getFullyQualifiedName(): string + { + return $this->fullyQualifiedName; + } + + #[Override] + public function equals(Type $type): bool + { + $isEqual = parent::equals($type); + + if ($isEqual) { + return true; + } + + if ($type instanceof self || $type::class === ObjectType::class) { + return $type->getClassName() === $this->fullyQualifiedName; + } + + return false; + } +} diff --git a/src/StaticTypeMapper/ValueObject/Type/SimpleStaticType.php b/src/StaticTypeMapper/ValueObject/Type/SimpleStaticType.php new file mode 100644 index 00000000000..d97ba4c2d84 --- /dev/null +++ b/src/StaticTypeMapper/ValueObject/Type/SimpleStaticType.php @@ -0,0 +1,22 @@ +className; + } +} diff --git a/src/Stubs/PHPStanStubLoader.php b/src/Stubs/PHPStanStubLoader.php deleted file mode 100644 index 031947091c9..00000000000 --- a/src/Stubs/PHPStanStubLoader.php +++ /dev/null @@ -1,80 +0,0 @@ -areStubsLoaded) { - return; - } - - foreach (self::VENDOR_PATHS as $vendorPath) { - $vendorPath = realpath($vendorPath); - if (! $vendorPath) { - continue; - } - - foreach (self::STUBS as $stub) { - $path = $this->getStubPath($vendorPath, $stub); - if ($path === null) { - continue 2; - } - - require_once $path; - } - - $this->areStubsLoaded = true; - - // already loaded? stop loop - break; - } - } - - private function getStubPath(string $vendorPath, string $stub): ?string - { - // @todo: need to be switched when phpstan-extracted used after scoped php 7.0 works - $path = sprintf('phar://%s/phpstan/phpstan/phpstan.phar/stubs/runtime/%s', $vendorPath, $stub); - $isExists = file_exists($path); - - if (! $isExists) { - // this is to handle phpstan's stubs got from phpstan-extracted instead of the .phar when exists after scoped php 7.0 applied - $path = sprintf('%s/phpstan/phpstan-extracted/stubs/runtime/%s', $vendorPath, $stub); - $isExists = file_exists($path); - } - - if ($isExists) { - return $path; - } - - return null; - } -} diff --git a/src/Template/DefaultResolver.php b/src/Template/DefaultResolver.php deleted file mode 100644 index 2a926ed4063..00000000000 --- a/src/Template/DefaultResolver.php +++ /dev/null @@ -1,30 +0,0 @@ -> + */ + public static function yieldDirectory(string $directory, string $suffix = '*.php.inc'): Iterator + { + $finder = (new Finder()) + ->in($directory) + ->files() + ->name($suffix) + ->sortByName(); + + foreach ($finder as $fileInfo) { + yield [$fileInfo->getRealPath()]; + } + } +} diff --git a/src/Testing/Fixture/FixtureFileUpdater.php b/src/Testing/Fixture/FixtureFileUpdater.php new file mode 100644 index 00000000000..b14c4d2f09a --- /dev/null +++ b/src/Testing/Fixture/FixtureFileUpdater.php @@ -0,0 +1,36 @@ + + */ + public static function split(string $filePath): array + { + $fixtureFileContents = FileSystem::read($filePath); + + return self::splitFixtureFileContents($fixtureFileContents); + } + + /** + * @return array + */ + public static function splitFixtureFileContents(string $fixtureFileContents): array + { + $fixtureFileContents = str_replace("\r\n", "\n", $fixtureFileContents); + return explode("-----\n", $fixtureFileContents); + } +} diff --git a/src/Testing/Fixture/FixtureTempFileDumper.php b/src/Testing/Fixture/FixtureTempFileDumper.php new file mode 100644 index 00000000000..f1de9f7eec0 --- /dev/null +++ b/src/Testing/Fixture/FixtureTempFileDumper.php @@ -0,0 +1,29 @@ +includePreloadFilesAndScoperAutoload(); + } + + /** + * @api + * @param string[] $configFiles + */ + protected function bootFromConfigFiles(array $configFiles): void + { + $rectorConfig = self::getContainer(); + + foreach ($configFiles as $configFile) { + $rectorConfig->import($configFile); + } + } + + /** + * @template TType as object + * @param class-string $class + * @return TType + */ + protected function make(string $class): object + { + return self::getContainer()->make($class); + } + + protected static function getContainer(): RectorConfig + { + if (! self::$rectorConfig instanceof RectorConfig) { + $lazyContainerFactory = new LazyContainerFactory(); + self::$rectorConfig = $lazyContainerFactory->create(); + } + + self::$rectorConfig->boot(); + + return self::$rectorConfig; + } + + protected function isWindows(): bool + { + return strncasecmp(PHP_OS, 'WIN', 3) === 0; + } + + private function includePreloadFilesAndScoperAutoload(): void + { + if (file_exists(__DIR__ . '/../../../preload.php')) { + if (file_exists(__DIR__ . '/../../../vendor')) { + /** + * On PHPUnit 12+, when classmap autoloaded, it means preload already loaded early + */ + if (! class_exists(Version::class, true) || (int) Version::id() < 12) { + require_once __DIR__ . '/../../../preload.php'; + } + + // test case in rector split package + } elseif (file_exists(__DIR__ . '/../../../../../../vendor')) { + require_once __DIR__ . '/../../../preload-split-package.php'; + } + } + + if (\file_exists(__DIR__ . '/../../../vendor/scoper-autoload.php')) { + require_once __DIR__ . '/../../../vendor/scoper-autoload.php'; + } + } +} diff --git a/src/Testing/PHPUnit/AbstractRectorTestCase.php b/src/Testing/PHPUnit/AbstractRectorTestCase.php new file mode 100644 index 00000000000..f0477faa32e --- /dev/null +++ b/src/Testing/PHPUnit/AbstractRectorTestCase.php @@ -0,0 +1,301 @@ + + */ + private static array $cacheByRuleAndConfig = []; + + /** + * Restore default parameters + */ + public static function tearDownAfterClass(): void + { + SimpleParameterProvider::setParameter(Option::AUTO_IMPORT_NAMES, false); + SimpleParameterProvider::setParameter(Option::AUTO_IMPORT_DOC_BLOCK_NAMES, false); + SimpleParameterProvider::setParameter(Option::REMOVE_UNUSED_IMPORTS, false); + SimpleParameterProvider::setParameter(Option::IMPORT_SHORT_CLASSES, true); + + SimpleParameterProvider::setParameter(Option::INDENT_CHAR, ' '); + SimpleParameterProvider::setParameter(Option::INDENT_SIZE, 4); + SimpleParameterProvider::setParameter(Option::POLYFILL_PACKAGES, []); + SimpleParameterProvider::setParameter(Option::NEW_LINE_ON_FLUENT_CALL, false); + + SimpleParameterProvider::setParameter(Option::TREAT_CLASSES_AS_FINAL, false); + } + + protected function setUp(): void + { + parent::setUp(); + + $configFile = $this->provideConfigFilePath(); + + // cleanup all registered rectors, so you can use only the new ones + $rectorConfig = self::getContainer(); + + // boot once for config + test case to avoid booting again and again for every test fixture + $cacheKey = sha1($configFile . static::class); + + if (! isset(self::$cacheByRuleAndConfig[$cacheKey])) { + // reset + /** @var RewindableGenerator $resettables */ + $resettables = $rectorConfig->tagged(ResettableInterface::class); + + foreach ($resettables as $resettable) { + /** @var ResettableInterface $resettable */ + $resettable->reset(); + } + + $this->forgetRectorsRules(); + $rectorConfig->resetRuleConfigurations(); + + // this has to be always empty, so we can add new rules with their configuration + $this->assertEmpty($rectorConfig->tagged(RectorInterface::class)); + + $this->bootFromConfigFiles([$configFile]); + + $rectorsGenerator = $rectorConfig->tagged(RectorInterface::class); + $rectors = $rectorsGenerator instanceof RewindableGenerator + ? iterator_to_array($rectorsGenerator->getIterator()) + // no rules at all, e.g. in case of only post rector run + : []; + + /** @var RectorNodeTraverser $rectorNodeTraverser */ + $rectorNodeTraverser = $rectorConfig->make(RectorNodeTraverser::class); + $rectorNodeTraverser->refreshPhpRectors($rectors); + + // store cache + self::$cacheByRuleAndConfig[$cacheKey] = true; + } + + $this->applicationFileProcessor = $this->make(ApplicationFileProcessor::class); + $this->dynamicSourceLocatorProvider = $this->make(DynamicSourceLocatorProvider::class); + + /** @var AdditionalAutoloader $additionalAutoloader */ + $additionalAutoloader = $this->make(AdditionalAutoloader::class); + $additionalAutoloader->autoloadPaths(); + + /** @var BootstrapFilesIncluder $bootstrapFilesIncluder */ + $bootstrapFilesIncluder = $this->make(BootstrapFilesIncluder::class); + $bootstrapFilesIncluder->includeBootstrapFiles(); + } + + protected function tearDown(): void + { + // clear temporary file + if (is_string($this->inputFilePath)) { + FileSystem::delete($this->inputFilePath); + } + } + + protected static function yieldFilesFromDirectory(string $directory, string $suffix = '*.php.inc'): Iterator + { + return FixtureFileFinder::yieldDirectory($directory, $suffix); + } + + protected function doTestFile(string $fixtureFilePath, bool $includeFixtureDirectoryAsSource = false): void + { + // prepare input file contents and expected file output contents + $fixtureFileContents = FileSystem::read($fixtureFilePath); + + if (FixtureSplitter::containsSplit($fixtureFileContents)) { + // changed content + [$inputFileContents, $expectedFileContents] = FixtureSplitter::splitFixtureFileContents( + $fixtureFileContents + ); + } else { + // no change + $inputFileContents = $fixtureFileContents; + $expectedFileContents = $fixtureFileContents; + } + + $inputFilePath = $this->createInputFilePath($fixtureFilePath); + + // to remove later in tearDown() + $this->inputFilePath = $inputFilePath; + + if ($fixtureFilePath === $inputFilePath) { + throw new ShouldNotHappenException('Fixture file and input file cannot be the same: ' . $fixtureFilePath); + } + + // write temp file + FileSystem::write($inputFilePath, $inputFileContents, null); + + $this->doTestFileMatchesExpectedContent( + $inputFilePath, + $inputFileContents, + $expectedFileContents, + $fixtureFilePath, + $includeFixtureDirectoryAsSource + ); + } + + protected function doTestFileExpectingWarningAboutRuleApplied( + string $fixtureFilePath, + string $expectedRuleApplied + ): void { + ob_start(); + $this->doTestFile($fixtureFilePath); + $content = ob_get_clean(); + $fixtureName = basename($fixtureFilePath); + $testClass = static::class; + $this->assertSame( + PHP_EOL . 'WARNING: On fixture file "' . $fixtureName . '" for test "' . $testClass . '"' . PHP_EOL . + 'File not changed but some Rector rules applied:' . PHP_EOL . + ' * ' . $expectedRuleApplied . PHP_EOL, + $content + ); + } + + private function forgetRectorsRules(): void + { + $rectorConfig = self::getContainer(); + + // 1. forget tagged services + ContainerMemento::forgetTag($rectorConfig, RectorInterface::class); + + // 2. remove after binding too, to avoid setting configuration over and over again + $privatesAccessor = new PrivatesAccessor(); + $privatesAccessor->propertyClosure( + $rectorConfig, + 'afterResolvingCallbacks', + static function (array $afterResolvingCallbacks): array { + foreach (array_keys($afterResolvingCallbacks) as $key) { + if ($key === AbstractRector::class) { + continue; + } + + if (is_a($key, RectorInterface::class, true)) { + unset($afterResolvingCallbacks[$key]); + } + } + + return $afterResolvingCallbacks; + } + ); + } + + private function doTestFileMatchesExpectedContent( + string $originalFilePath, + string $inputFileContents, + string $expectedFileContents, + string $fixtureFilePath, + bool $includeFixtureDirectoryAsSource + ): void { + SimpleParameterProvider::setParameter(Option::SOURCE, [$originalFilePath]); + + // the file is now changed (if any rule matches) + $rectorTestResult = $this->processFilePath($originalFilePath, $includeFixtureDirectoryAsSource); + + $changedContents = $rectorTestResult->getChangedContents(); + + $fixtureFilename = basename($fixtureFilePath); + $failureMessage = sprintf('Failed on fixture file "%s"', $fixtureFilename); + + $numAppliedRectorClasses = count($rectorTestResult->getAppliedRectorClasses()); + // give more context about used rules in case of set testing + $appliedRulesList = ''; + if ($numAppliedRectorClasses > 0) { + foreach ($rectorTestResult->getAppliedRectorClasses() as $appliedRectorClass) { + $appliedRulesList .= ' * ' . $appliedRectorClass . PHP_EOL; + } + } + + if ($numAppliedRectorClasses > 1) { + $failureMessage .= PHP_EOL . PHP_EOL . 'Applied Rector rules:' . PHP_EOL . $appliedRulesList; + } + + try { + $this->assertSame($expectedFileContents, $changedContents, $failureMessage); + } catch (ExpectationFailedException) { + FixtureFileUpdater::updateFixtureContent($inputFileContents, $changedContents, $fixtureFilePath); + + // if not exact match, check the regex version (useful for generated hashes/uuids in the code) + $this->assertStringMatchesFormat($expectedFileContents, $changedContents, $failureMessage); + } + + if ($inputFileContents === $expectedFileContents && $numAppliedRectorClasses > 0) { + $failureMessage = PHP_EOL . sprintf( + 'WARNING: On fixture file "%s" for test "%s"', + $fixtureFilename, + static::class + ) . PHP_EOL + . 'File not changed but some Rector rules applied:' . PHP_EOL . $appliedRulesList; + echo $failureMessage; + } + } + + private function processFilePath(string $filePath, bool $includeFixtureDirectoryAsSource): RectorTestResult + { + if ($includeFixtureDirectoryAsSource) { + $fixtureDirectory = dirname($filePath); + $this->dynamicSourceLocatorProvider->addDirectories([$fixtureDirectory]); + } else { + $this->dynamicSourceLocatorProvider->setFilePath($filePath); + } + + /** @var ConfigurationFactory $configurationFactory */ + $configurationFactory = $this->make(ConfigurationFactory::class); + $configuration = $configurationFactory->createForTests([$filePath]); + + $processResult = $this->applicationFileProcessor->processFiles([$filePath], $configuration); + + // return changed file contents + $changedFileContents = FileSystem::read($filePath); + return new RectorTestResult($changedFileContents, $processResult); + } + + private function createInputFilePath(string $fixtureFilePath): string + { + $inputFileDirectory = dirname($fixtureFilePath); + + // remove ".inc" suffix + if (str_ends_with($fixtureFilePath, '.inc')) { + $trimmedFixtureFilePath = Strings::substring($fixtureFilePath, 0, -4); + } else { + $trimmedFixtureFilePath = $fixtureFilePath; + } + + $fixtureBasename = pathinfo($trimmedFixtureFilePath, PATHINFO_BASENAME); + return $inputFileDirectory . '/' . $fixtureBasename; + } +} diff --git a/packages/Testing/PHPUnit/StaticPHPUnitEnvironment.php b/src/Testing/PHPUnit/StaticPHPUnitEnvironment.php similarity index 78% rename from packages/Testing/PHPUnit/StaticPHPUnitEnvironment.php rename to src/Testing/PHPUnit/StaticPHPUnitEnvironment.php index 0237c4973a8..08b13fc1620 100644 --- a/packages/Testing/PHPUnit/StaticPHPUnitEnvironment.php +++ b/src/Testing/PHPUnit/StaticPHPUnitEnvironment.php @@ -11,6 +11,6 @@ final class StaticPHPUnitEnvironment */ public static function isPHPUnitRun(): bool { - return defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__'); + return defined('PHPUNIT_COMPOSER_INSTALL'); } } diff --git a/src/Testing/PHPUnit/ValueObject/RectorTestResult.php b/src/Testing/PHPUnit/ValueObject/RectorTestResult.php new file mode 100644 index 00000000000..3b234b3b359 --- /dev/null +++ b/src/Testing/PHPUnit/ValueObject/RectorTestResult.php @@ -0,0 +1,40 @@ +changedContents; + } + + /** + * @return array> + */ + public function getAppliedRectorClasses(): array + { + $rectorClasses = []; + + foreach ($this->processResult->getFileDiffs(false) as $fileDiff) { + $rectorClasses = array_merge($rectorClasses, $fileDiff->getRectorClasses()); + } + + return RectorClassesSorter::sortAndFilterOutPostRectors($rectorClasses); + } +} diff --git a/src/Testing/TestingParser/TestingParser.php b/src/Testing/TestingParser/TestingParser.php new file mode 100644 index 00000000000..5bace90461a --- /dev/null +++ b/src/Testing/TestingParser/TestingParser.php @@ -0,0 +1,65 @@ +parseToFileAndStmts($filePath); + return $file; + } + + /** + * @return Node[] + */ + public function parseFileToDecoratedNodes(string $filePath): array + { + [$file, $stmts] = $this->parseToFileAndStmts($filePath); + return $stmts; + } + + /** + * @return array{0: File, 1: Node[]} + */ + private function parseToFileAndStmts(string $filePath): array + { + // needed for PHPStan reflection, as it caches the last processed file + $this->dynamicSourceLocatorProvider->setFilePath($filePath); + + $fileContent = FileSystem::read($filePath); + $file = new File($filePath, $fileContent); + $stmts = $this->rectorParser->parseString($fileContent); + + // wrap in FileNode to enable file-level rules + $stmts = [new FileNode($stmts)]; + $stmts = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($filePath, $stmts); + + $file->hydrateStmtsAndTokens($stmts, $stmts, []); + $this->currentFileProvider->setFile($file); + + return [$file, $stmts]; + } +} diff --git a/src/Util/ArrayChecker.php b/src/Util/ArrayChecker.php new file mode 100644 index 00000000000..97bc180beeb --- /dev/null +++ b/src/Util/ArrayChecker.php @@ -0,0 +1,24 @@ +mergeLeftToRightWithCallable($left, $right, $this->merge(...)); + } + + if ($left !== null) { + return $left; + } + + if (! is_array($right)) { + return $left; + } + + return $right; + } + + /** + * @param array $left + * @param array $right + * @return mixed[] + */ + private function mergeLeftToRightWithCallable(array $left, array $right, callable $mergeCallback): array + { + foreach ($left as $key => $val) { + if (is_int($key)) { + // prevent duplicated values in unindexed arrays + if (! in_array($val, $right, true)) { + $right[] = $val; + } + } else { + if (isset($right[$key])) { + $val = $mergeCallback($val, $right[$key]); + } + + $right[$key] = $val; + } + } + + return $right; + } +} diff --git a/src/Util/FileHasher.php b/src/Util/FileHasher.php new file mode 100644 index 00000000000..a7aade2d4c2 --- /dev/null +++ b/src/Util/FileHasher.php @@ -0,0 +1,53 @@ +getAlgo(), $string); + } + + /** + * cryptographic insecure hashing of files + * + * @param string[] $files + */ + public function hashFiles(array $files): string + { + $configHash = ''; + $algo = $this->getAlgo(); + foreach ($files as $file) { + $hash = hash_file($algo, $file); + if ($hash === false) { + throw new ShouldNotHappenException(sprintf('File %s is not readable', $file)); + } + + $configHash .= $hash; + } + + return $configHash; + } + + private function getAlgo(): string + { + //see https://php.watch/articles/php-hash-benchmark + if (\PHP_VERSION_ID >= 80100) { + // if xxh128 is available use it, as it is way faster + return 'xxh128'; + } + + return 'md4'; + } +} diff --git a/src/Util/MemoryLimiter.php b/src/Util/MemoryLimiter.php new file mode 100644 index 00000000000..17f06b75fd5 --- /dev/null +++ b/src/Util/MemoryLimiter.php @@ -0,0 +1,48 @@ +getMemoryLimit(); + if ($memoryLimit === null) { + return; + } + + $this->validateMemoryLimitFormat($memoryLimit); + + $memorySetResult = ini_set('memory_limit', $memoryLimit); + + if ($memorySetResult === false) { + $errorMessage = sprintf('Memory limit "%s" cannot be set.', $memoryLimit); + throw new InvalidConfigurationException($errorMessage); + } + } + + private function validateMemoryLimitFormat(string $memoryLimit): void + { + $memoryLimitFormatMatch = Strings::match($memoryLimit, self::VALID_MEMORY_LIMIT_REGEX); + if ($memoryLimitFormatMatch !== null) { + return; + } + + $errorMessage = sprintf('Invalid memory limit format "%s".', $memoryLimit); + throw new InvalidConfigurationException($errorMessage); + } +} diff --git a/src/Util/NewLineSplitter.php b/src/Util/NewLineSplitter.php new file mode 100644 index 00000000000..af11ca9a66e --- /dev/null +++ b/src/Util/NewLineSplitter.php @@ -0,0 +1,23 @@ +PhpParser(.*?))\(#ms'; + + /** + * @see https://regex101.com/r/uQFuvL/1 + */ + private const string PROPERTY_KEY_REGEX = '#(?[\w\d]+)\:#'; + + public function __construct( + private SymfonyStyle $symfonyStyle + ) { + } + + /** + * @param Node|Node[] $nodes + */ + public function printNodes(Node|array $nodes): void + { + $dumpedNodesContents = SimpleNodeDumper::dump($nodes); + + // colorize + $colorContents = $this->addConsoleColors($dumpedNodesContents); + $this->symfonyStyle->writeln($colorContents); + + $this->symfonyStyle->newLine(); + } + + private function addConsoleColors(string $contents): string + { + // decorate class names + $colorContents = Strings::replace( + $contents, + self::CLASS_NAME_REGEX, + static fn (array $match): string => '' . $match['class_name'] . '(' + ); + + // decorate keys + return Strings::replace( + $colorContents, + self::PROPERTY_KEY_REGEX, + static fn (array $match): string => '' . $match['key'] . ':' + ); + } +} diff --git a/src/Util/PhpVersionFactory.php b/src/Util/PhpVersionFactory.php index ce8a56ba156..6dfc02b8fcf 100644 --- a/src/Util/PhpVersionFactory.php +++ b/src/Util/PhpVersionFactory.php @@ -2,11 +2,16 @@ declare(strict_types=1); -namespace Rector\Core\Util; +namespace Rector\Util; + +use Rector\ValueObject\PhpVersion; final class PhpVersionFactory { - public function createIntVersion(string $version): int + /** + * @return PhpVersion::* + */ + public static function createIntVersion(string $version): int { $explodeDash = explode('-', $version); if (count($explodeDash) > 1) { @@ -16,14 +21,10 @@ public function createIntVersion(string $version): int $explodeVersion = explode('.', $version); $countExplodedVersion = count($explodeVersion); - if ($countExplodedVersion === 2) { + if ($countExplodedVersion >= 2) { return (int) $explodeVersion[0] * 10000 + (int) $explodeVersion[1] * 100; } - if ($countExplodedVersion >= 3) { - return (int) $explodeVersion[0] * 10000 + (int) $explodeVersion[1] * 100 + (int) $explodeVersion[2]; - } - return (int) $version; } } diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php new file mode 100644 index 00000000000..830ab6f2325 --- /dev/null +++ b/src/Util/RectorClassesSorter.php @@ -0,0 +1,28 @@ +> $rectorClasses + * @return array> + */ + public static function sortAndFilterOutPostRectors(array $rectorClasses): array + { + $rectorClasses = array_unique($rectorClasses); + + $mainRectorClasses = array_filter( + $rectorClasses, + fn (string $rectorClass): bool => is_a($rectorClass, RectorInterface::class, true) + ); + sort($mainRectorClasses); + + return $mainRectorClasses; + } +} diff --git a/src/Util/Reflection/PrivatesAccessor.php b/src/Util/Reflection/PrivatesAccessor.php new file mode 100644 index 00000000000..3d7512277ce --- /dev/null +++ b/src/Util/Reflection/PrivatesAccessor.php @@ -0,0 +1,80 @@ +getPrivateProperty($object, $propertyName); + + // modify value + $property = $closure($property); + + $this->setPrivateProperty($object, $propertyName, $property); + } + + /** + * @param object|class-string $object + * @param mixed[] $arguments + * @api + */ + public function callPrivateMethod(object|string $object, string $methodName, array $arguments): mixed + { + if (is_string($object)) { + $reflectionClass = new ReflectionClass($object); + $object = $reflectionClass->newInstanceWithoutConstructor(); + } + + $reflectionMethod = $this->createAccessibleMethodReflection($object, $methodName); + + return $reflectionMethod->invokeArgs($object, $arguments); + } + + public function getPrivateProperty(object $object, string $propertyName): mixed + { + $reflectionProperty = $this->resolvePropertyReflection($object, $propertyName); + + return $reflectionProperty->getValue($object); + } + + public function setPrivateProperty(object $object, string $propertyName, mixed $value): void + { + $reflectionProperty = $this->resolvePropertyReflection($object, $propertyName); + + $reflectionProperty->setValue($object, $value); + } + + private function createAccessibleMethodReflection(object $object, string $methodName): ReflectionMethod + { + return new ReflectionMethod($object, $methodName); + } + + private function resolvePropertyReflection(object $object, string $propertyName): ReflectionProperty + { + if (property_exists($object, $propertyName)) { + return new ReflectionProperty($object, $propertyName); + } + + $parentClass = get_parent_class($object); + if ($parentClass !== false) { + return new ReflectionProperty($parentClass, $propertyName); + } + + $errorMessage = sprintf('Property "$%s" was not found in "%s" class', $propertyName, $object::class); + throw new MissingPrivatePropertyException($errorMessage); + } +} diff --git a/src/Util/StaticRectorStrings.php b/src/Util/StaticRectorStrings.php deleted file mode 100644 index ca15ee769a1..00000000000 --- a/src/Util/StaticRectorStrings.php +++ /dev/null @@ -1,77 +0,0 @@ - $rectorClass - */ - public function process(Node $node, Node $originalNode, string $rectorClass): void - { - if ($rectorClass === DowngradeNullsafeToTernaryOperatorRector::class) { - return; - } - - $createdByRule = $originalNode->getAttribute(AttributeKey::CREATED_BY_RULE); - - // special case - if ($createdByRule === $rectorClass) { - // does it contain the same node type as input? - $originalNodeClass = $originalNode::class; - - $hasNestedOriginalNodeType = $this->betterNodeFinder->findInstanceOf($node, $originalNodeClass); - if ($hasNestedOriginalNodeType !== []) { - throw new InfiniteLoopTraversingException($rectorClass); - } - } - - $this->decorateNode($originalNode, $rectorClass); - } - - /** - * @param class-string $rectorClass - */ - private function decorateNode(Node $node, string $rectorClass): void - { - $nodeTraverser = new NodeTraverser(); - - $createdByRuleNodeVisitor = new CreatedByRuleNodeVisitor($rectorClass); - $nodeTraverser->addVisitor($createdByRuleNodeVisitor); - - $nodeTraverser->traverse([$node]); - } -} diff --git a/src/Validation/RectorAssert.php b/src/Validation/RectorAssert.php new file mode 100644 index 00000000000..3cd55b4f17b --- /dev/null +++ b/src/Validation/RectorAssert.php @@ -0,0 +1,83 @@ + $value) { + if (self::isRectorClassValue($key)) { + if (class_exists($key)) { + $skippedRectorRules[] = $key; + } else { + $nonExistingRules[] = $key; + } + + continue; + } + + if (! self::isRectorClassValue($value)) { + continue; + } + + if (class_exists($value)) { + $skippedRectorRules[] = $value; + continue; + } + + $nonExistingRules[] = $value; + } + + SimpleParameterProvider::addParameter(Option::SKIPPED_RECTOR_RULES, $skippedRectorRules); + + if ($nonExistingRules === []) { + return; + } + + $nonExistingRulesString = ''; + foreach ($nonExistingRules as $nonExistingRule) { + $nonExistingRulesString .= ' * ' . $nonExistingRule . PHP_EOL; + } + + throw new ShouldNotHappenException( + 'These rules from "$rectorConfig->skip()" do not exist - remove them or fix their names:' . PHP_EOL . $nonExistingRulesString + ); + } + + private static function isRectorClassValue(mixed $value): bool + { + // only validate string + if (! is_string($value)) { + return false; + } + + // not regex path + if (str_contains($value, '*')) { + return false; + } + + // not if no Rector suffix + if (! str_ends_with($value, 'Rector')) { + return false; + } + + // not directory + if (is_dir($value)) { + return false; + } + + // not file + return ! is_file($value); + } + + /** + * @param string[] $values + * @return string[] + */ + private static function resolveDuplicatedValues(array $values): array + { + $counted = array_count_values($values); + $duplicates = []; + + foreach ($counted as $value => $count) { + if ($count > 1) { + $duplicates[] = $value; + } + } + + return array_unique($duplicates); + } +} diff --git a/src/ValueObject/Application/File.php b/src/ValueObject/Application/File.php index 69b142f3f50..33c354d4a05 100644 --- a/src/ValueObject/Application/File.php +++ b/src/ValueObject/Application/File.php @@ -2,37 +2,39 @@ declare(strict_types=1); -namespace Rector\Core\ValueObject\Application; +namespace Rector\ValueObject\Application; +use PhpParser\Node; use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\InlineHTML; +use PhpParser\Node\Stmt\Namespace_; +use PhpParser\NodeFinder; +use PhpParser\Token; use Rector\ChangesReporting\ValueObject\RectorWithLineChange; -use Rector\Core\Exception\ShouldNotHappenException; -use Rector\Core\ValueObject\Reporting\FileDiff; -use Symplify\SmartFileSystem\SmartFileInfo; +use Rector\Exception\ShouldNotHappenException; +use Rector\PhpParser\Node\FileNode; +use Rector\ValueObject\Reporting\FileDiff; -/** - * @see \Rector\Core\ValueObjectFactory\Application\FileFactory - */ final class File { private bool $hasChanged = false; - private string $originalFileContent; + private readonly string $originalFileContent; private ?FileDiff $fileDiff = null; /** - * @var Stmt[] + * @var Node[] */ private array $oldStmts = []; /** - * @var Stmt[] + * @var Node[] */ private array $newStmts = []; /** - * @var mixed[] + * @var array */ private array $oldTokens = []; @@ -42,20 +44,20 @@ final class File private array $rectorWithLineChanges = []; /** - * @var RectorError[] + * Cached result per file */ - private array $rectorErrors = []; + private ?bool $containsHtml = null; public function __construct( - private SmartFileInfo $smartFileInfo, + private readonly string $filePath, private string $fileContent ) { $this->originalFileContent = $fileContent; } - public function getSmartFileInfo(): SmartFileInfo + public function getFilePath(): string { - return $this->smartFileInfo; + return $this->filePath; } public function getFileContent(): string @@ -101,7 +103,7 @@ public function getFileDiff(): ?FileDiff /** * @param Stmt[] $newStmts * @param Stmt[] $oldStmts - * @param mixed[] $oldTokens + * @param array $oldTokens */ public function hydrateStmtsAndTokens(array $newStmts, array $oldStmts, array $oldTokens): void { @@ -131,7 +133,7 @@ public function getNewStmts(): array } /** - * @return mixed[] + * @return array */ public function getOldTokens(): array { @@ -139,7 +141,7 @@ public function getOldTokens(): array } /** - * @param Stmt[] $newStmts + * @param Node[] $newStmts */ public function changeNewStmts(array $newStmts): void { @@ -151,6 +153,39 @@ public function addRectorClassWithLine(RectorWithLineChange $rectorWithLineChang $this->rectorWithLineChanges[] = $rectorWithLineChange; } + /** + * This node returns top most node, + * that includes use imports + */ + public function getUseImportsRootNode(): Namespace_|FileNode|null + { + if ($this->newStmts === []) { + return null; + } + + $firstStmt = $this->newStmts[0]; + if ($firstStmt instanceof FileNode) { + if (! $firstStmt->isNamespaced()) { + return $firstStmt; + } + + // return sole Namespace, or none + $namespaces = []; + foreach ($firstStmt->stmts as $stmt) { + if ($stmt instanceof Namespace_) { + $namespaces[] = $stmt; + } + } + + if (count($namespaces) === 1) { + return $namespaces[0]; + } + } + + return null; + + } + /** * @return RectorWithLineChange[] */ @@ -159,21 +194,33 @@ public function getRectorWithLineChanges(): array return $this->rectorWithLineChanges; } - public function addRectorError(RectorError $rectorError): void + public function containsHTML(): bool { - $this->rectorErrors[] = $rectorError; + if ($this->containsHtml !== null) { + return $this->containsHtml; + } + + $nodeFinder = new NodeFinder(); + + $this->containsHtml = (bool) $nodeFinder->findFirstInstanceOf($this->oldStmts, InlineHTML::class); + return $this->containsHtml; } - public function hasErrors(): bool + public function getFileNode(): ?FileNode { - return $this->rectorErrors !== []; + if ($this->newStmts === []) { + return null; + } + + if ($this->newStmts[0] instanceof FileNode) { + return $this->newStmts[0]; + } + + return null; } - /** - * @return RectorError[] - */ - public function getErrors(): array + public function hasShebang(): bool { - return $this->rectorErrors; + return str_starts_with($this->fileContent, '#!'); } } diff --git a/src/ValueObject/Application/MovedFile.php b/src/ValueObject/Application/MovedFile.php deleted file mode 100644 index 49ab9e64495..00000000000 --- a/src/ValueObject/Application/MovedFile.php +++ /dev/null @@ -1,41 +0,0 @@ -file; - } - - public function getNewFilePath(): string - { - return $this->newFilePath; - } - - /** - * @return Stmt[] - */ - public function getNodes(): array - { - return $this->file->getNewStmts(); - } - - public function getFilePath(): string - { - $smartFileInfo = $this->file->getSmartFileInfo(); - return $smartFileInfo->getRelativeFilePath(); - } -} diff --git a/src/ValueObject/Application/RectorError.php b/src/ValueObject/Application/RectorError.php deleted file mode 100644 index 116fc5f9224..00000000000 --- a/src/ValueObject/Application/RectorError.php +++ /dev/null @@ -1,43 +0,0 @@ -fileInfo->getRelativeFilePathFromCwd(); - } - - public function getFileInfo(): SmartFileInfo - { - return $this->fileInfo; - } - - public function getMessage(): string - { - return $this->message; - } - - public function getLine(): ?int - { - return $this->line; - } - - public function getRectorClass(): ?string - { - return $this->rectorClass; - } -} diff --git a/src/ValueObject/Bootstrap/BootstrapConfigs.php b/src/ValueObject/Bootstrap/BootstrapConfigs.php index 005e0a79058..c31a52258ac 100644 --- a/src/ValueObject/Bootstrap/BootstrapConfigs.php +++ b/src/ValueObject/Bootstrap/BootstrapConfigs.php @@ -2,35 +2,34 @@ declare(strict_types=1); -namespace Rector\Core\ValueObject\Bootstrap; +namespace Rector\ValueObject\Bootstrap; -use Symplify\SmartFileSystem\SmartFileInfo; - -final class BootstrapConfigs +final readonly class BootstrapConfigs { /** - * @param SmartFileInfo[] $setConfigFileInfos + * @param string[] $setConfigFiles */ public function __construct( - private ?SmartFileInfo $mainConfigFileInfo, - private array $setConfigFileInfos + private ?string $mainConfigFile, + private array $setConfigFiles ) { } - public function getMainConfigFileInfo(): ?SmartFileInfo + public function getMainConfigFile(): ?string { - return $this->mainConfigFileInfo; + return $this->mainConfigFile; } /** - * @return SmartFileInfo[] + * @return string[] */ - public function getConfigFileInfos(): array + public function getConfigFiles(): array { - $configFileInfos = []; - if ($this->mainConfigFileInfo !== null) { - $configFileInfos[] = $this->mainConfigFileInfo; + $configFiles = []; + if ($this->mainConfigFile !== null) { + $configFiles[] = $this->mainConfigFile; } - return array_merge($configFileInfos, $this->setConfigFileInfos); + + return array_merge($configFiles, $this->setConfigFiles); } } diff --git a/src/ValueObject/Configuration.php b/src/ValueObject/Configuration.php index b965a4a05fb..e1905416cac 100644 --- a/src/ValueObject/Configuration.php +++ b/src/ValueObject/Configuration.php @@ -2,19 +2,20 @@ declare(strict_types=1); -namespace Rector\Core\ValueObject; +namespace Rector\ValueObject; -use JetBrains\PhpStorm\Immutable; use Rector\ChangesReporting\Output\ConsoleOutputFormatter; -use Rector\Core\ValueObject\Bootstrap\BootstrapConfigs; -use Symplify\SmartFileSystem\SmartFileInfo; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\ValueObject\Configuration\LevelOverflow; +use Webmozart\Assert\Assert; -#[Immutable] -final class Configuration +final readonly class Configuration { /** * @param string[] $fileExtensions * @param string[] $paths + * @param LevelOverflow[] $levelOverflows */ public function __construct( private bool $isDryRun = false, @@ -24,7 +25,16 @@ public function __construct( private array $fileExtensions = ['php'], private array $paths = [], private bool $showDiffs = true, - private ?BootstrapConfigs $bootstrapConfigs = null, + private string | null $parallelPort = null, + private string | null $parallelIdentifier = null, + private bool $isParallel = false, + private string|null $memoryLimit = null, + private bool $isDebug = false, + private bool $reportingWithRealPath = false, + private ?string $onlyRule = null, + private ?string $onlySuffix = null, + private array $levelOverflows = [], + private bool $showRulesSummary = false, ) { } @@ -48,9 +58,15 @@ public function shouldClearCache(): bool */ public function getFileExtensions(): array { + Assert::notEmpty($this->fileExtensions); return $this->fileExtensions; } + public function getOnlyRule(): ?string + { + return $this->onlyRule; + } + /** * @return string[] */ @@ -69,17 +85,66 @@ public function shouldShowDiffs(): bool return $this->showDiffs; } - public function getMainConfigFilePath(): ?string + public function getParallelPort(): ?string + { + return $this->parallelPort; + } + + public function getParallelIdentifier(): ?string + { + return $this->parallelIdentifier; + } + + public function isParallel(): bool { - if ($this->bootstrapConfigs === null) { - return null; - } + return $this->isParallel; + } - $mainConfigFileInfo = $this->bootstrapConfigs->getMainConfigFileInfo(); - if (! $mainConfigFileInfo instanceof SmartFileInfo) { - return null; - } + public function getMemoryLimit(): ?string + { + return $this->memoryLimit; + } + + public function isDebug(): bool + { + return $this->isDebug; + } + + public function isReportingWithRealPath(): bool + { + return $this->reportingWithRealPath; + } - return $mainConfigFileInfo->getRelativeFilePathFromCwd(); + public function getOnlySuffix(): ?string + { + return $this->onlySuffix; + } + + /** + * @return LevelOverflow[] + */ + public function getLevelOverflows(): array + { + return $this->levelOverflows; + } + + /** + * @return string[] + */ + public function getBothSetAndRulesDuplicatedRegistrations(): array + { + $rootStandaloneRegisteredRules = SimpleParameterProvider::provideArrayParameter( + Option::ROOT_STANDALONE_REGISTERED_RULES + ); + $setRegisteredRules = SimpleParameterProvider::provideArrayParameter(Option::SET_REGISTERED_RULES); + + $ruleDuplicatedRegistrations = array_intersect($rootStandaloneRegisteredRules, $setRegisteredRules); + + return array_unique($ruleDuplicatedRegistrations); + } + + public function shouldShowRulesSummary(): bool + { + return $this->showRulesSummary; } } diff --git a/src/ValueObject/Configuration/LevelOverflow.php b/src/ValueObject/Configuration/LevelOverflow.php new file mode 100644 index 00000000000..525793eab8a --- /dev/null +++ b/src/ValueObject/Configuration/LevelOverflow.php @@ -0,0 +1,42 @@ +configurationName; + } + + public function getLevel(): int + { + return $this->level; + } + + public function getRuleCount(): int + { + return $this->ruleCount; + } + + public function getSuggestedRuleset(): string + { + return $this->suggestedRuleset; + } + + public function getSuggestedSetListConstant(): string + { + return $this->suggestedSetListConstant; + } +} diff --git a/src/ValueObject/Error/SystemError.php b/src/ValueObject/Error/SystemError.php new file mode 100644 index 00000000000..860d7328135 --- /dev/null +++ b/src/ValueObject/Error/SystemError.php @@ -0,0 +1,96 @@ +message; + } + + public function getLine(): int|null + { + return $this->line; + } + + public function getRelativeFilePath(): ?string + { + return $this->relativeFilePath; + } + + public function getAbsoluteFilePath(): ?string + { + if ($this->relativeFilePath === null) { + return null; + } + + return \realpath($this->relativeFilePath); + } + + /** + * @return array{ + * message: string, + * relative_file_path: string|null, + * absolute_file_path: string|null, + * line: int|null, + * rector_class: string|null + * } + */ + public function jsonSerialize(): array + { + return [ + BridgeItem::MESSAGE => $this->message, + BridgeItem::RELATIVE_FILE_PATH => $this->relativeFilePath, + BridgeItem::ABSOLUTE_FILE_PATH => $this->getAbsoluteFilePath(), + BridgeItem::LINE => $this->line, + BridgeItem::RECTOR_CLASS => $this->rectorClass, + ]; + } + + /** + * @param array $json + */ + public static function decode(array $json): self + { + return new self( + $json[BridgeItem::MESSAGE], + $json[BridgeItem::RELATIVE_FILE_PATH], + $json[BridgeItem::LINE], + $json[BridgeItem::RECTOR_CLASS] + ); + } + + public function getRectorClass(): ?string + { + return $this->rectorClass; + } + + public function getRectorShortClass(): ?string + { + $rectorClass = $this->rectorClass; + + if (! in_array($rectorClass, [null, ''], true)) { + return (string) Strings::after($rectorClass, '\\', -1); + } + + return null; + } +} diff --git a/src/ValueObject/FileProcessResult.php b/src/ValueObject/FileProcessResult.php new file mode 100644 index 00000000000..80054efe18a --- /dev/null +++ b/src/ValueObject/FileProcessResult.php @@ -0,0 +1,41 @@ +systemErrors; + } + + public function getFileDiff(): ?FileDiff + { + return $this->fileDiff; + } + + public function hasChanged(): bool + { + return $this->hasChanged; + } +} diff --git a/src/ValueObject/FrameworkName.php b/src/ValueObject/FrameworkName.php deleted file mode 100644 index ea77c49f198..00000000000 --- a/src/ValueObject/FrameworkName.php +++ /dev/null @@ -1,21 +0,0 @@ -funcCall; + } + + public function getExpr(): Expr + { + return $this->expr; + } +} diff --git a/src/ValueObject/MethodName.php b/src/ValueObject/MethodName.php index cb52cd6eecb..7119ab7b8c2 100644 --- a/src/ValueObject/MethodName.php +++ b/src/ValueObject/MethodName.php @@ -2,61 +2,46 @@ declare(strict_types=1); -namespace Rector\Core\ValueObject; +namespace Rector\ValueObject; +/** + * @api + * @enum + */ final class MethodName { - /** - * @var string - */ - public const CONSTRUCT = '__construct'; + public const string __SET = '__set'; - /** - * @var string - */ - public const DESCTRUCT = '__destruct'; + public const string __GET = '__get'; - /** - * @var string - */ - public const CLONE = '__clone'; + public const string CONSTRUCT = '__construct'; + + public const string DESTRUCT = '__destruct'; + + public const string CLONE = '__clone'; /** * Mostly used in unit tests * @see https://phpunit.readthedocs.io/en/9.3/fixtures.html#more-setup-than-teardown - * @var string */ - public const SET_UP = 'setUp'; + public const string SET_UP = 'setUp'; - /** - * Mostly used in unit tests - * @var string - */ - public const TEAR_DOWN = 'tearDown'; - - /** - * @var string - */ - public const SET_STATE = '__set_state'; + public const string SET_STATE = '__set_state'; /** * @see https://phpunit.readthedocs.io/en/9.3/fixtures.html#fixtures-sharing-fixture-examples-databasetest-php - * @var string */ - public const SET_UP_BEFORE_CLASS = 'setUpBeforeClass'; + public const string SET_UP_BEFORE_CLASS = 'setUpBeforeClass'; - /** - * @var string - */ - public const CALL = '__call'; + public const string CALL = '__call'; - /** - * @var string - */ - public const TO_STRING = '__toString'; + public const string CALL_STATIC = '__callStatic'; - /** - * @var string - */ - public const INVOKE = '__invoke'; + public const string TO_STRING = '__toString'; + + public const string INVOKE = '__invoke'; + + public const string ISSET = '__isset'; + + public const string UNSET = '__unset'; } diff --git a/src/ValueObject/PhpVersion.php b/src/ValueObject/PhpVersion.php index beee70ec641..566c15b8dc8 100644 --- a/src/ValueObject/PhpVersion.php +++ b/src/ValueObject/PhpVersion.php @@ -2,79 +2,46 @@ declare(strict_types=1); -namespace Rector\Core\ValueObject; +namespace Rector\ValueObject; +/** + * @api + */ final class PhpVersion { - /** - * @api - * @var int - */ - public const PHP_53 = 50300; - - /** - * @api - * @var int - */ - public const PHP_54 = 50400; - - /** - * @api - * @var int - */ - public const PHP_55 = 50500; - - /** - * @api - * @var int - */ - public const PHP_56 = 50600; - - /** - * @api - * @var int - */ - public const PHP_70 = 70000; - - /** - * @api - * @var int - */ - public const PHP_71 = 70100; - - /** - * @api - * @var int - */ - public const PHP_72 = 70200; - - /** - * @api - * @var int - */ - public const PHP_73 = 70300; - - /** - * @api - * @var int - */ - public const PHP_74 = 70400; - - /** - * @api - * @var int - */ - public const PHP_80 = 80000; - - /** - * @api - * @var int - */ - public const PHP_81 = 81000; - - /** - * @api - * @var int - */ - public const PHP_10 = 100000; + public const int PHP_52 = 50200; + + public const int PHP_53 = 50300; + + public const int PHP_54 = 50400; + + public const int PHP_55 = 50500; + + public const int PHP_56 = 50600; + + public const int PHP_70 = 70000; + + public const int PHP_71 = 70100; + + public const int PHP_72 = 70200; + + public const int PHP_73 = 70300; + + public const int PHP_74 = 70400; + + public const int PHP_80 = 80000; + + public const int PHP_81 = 80100; + + public const int PHP_82 = 80200; + + public const int PHP_83 = 80300; + + public const int PHP_84 = 80400; + + public const int PHP_85 = 80500; + + public const int PHP_86 = 80600; + + public const int PHP_10 = 100000; } diff --git a/src/ValueObject/PhpVersionFeature.php b/src/ValueObject/PhpVersionFeature.php index 3c34538a460..3fc5b8ede3b 100644 --- a/src/ValueObject/PhpVersionFeature.php +++ b/src/ValueObject/PhpVersionFeature.php @@ -2,208 +2,507 @@ declare(strict_types=1); -namespace Rector\Core\ValueObject; +namespace Rector\ValueObject; +/** + * @api + */ final class PhpVersionFeature { + public const int PROPERTY_MODIFIER = PhpVersion::PHP_52; + + public const int CONTINUE_TO_BREAK = PhpVersion::PHP_52; + + public const int NO_REFERENCE_IN_NEW = PhpVersion::PHP_53; + + public const int SERVER_VAR = PhpVersion::PHP_53; + + public const int DIR_CONSTANT = PhpVersion::PHP_53; + + public const int ELVIS_OPERATOR = PhpVersion::PHP_53; + + public const int ANONYMOUS_FUNCTION_PARAM_TYPE = PhpVersion::PHP_53; + + public const int NO_ZERO_BREAK = PhpVersion::PHP_54; + + public const int NO_REFERENCE_IN_ARG = PhpVersion::PHP_54; + + public const int SHORT_ARRAY = PhpVersion::PHP_54; + + public const int DATE_TIME_INTERFACE = PhpVersion::PHP_55; + /** - * @var int + * @see https://wiki.php.net/rfc/class_name_scalars */ - public const DIR_CONSTANT = PhpVersion::PHP_53; + public const int CLASSNAME_CONSTANT = PhpVersion::PHP_55; + + public const int PREG_REPLACE_CALLBACK_E_MODIFIER = PhpVersion::PHP_55; + + public const int EXP_OPERATOR = PhpVersion::PHP_56; + + public const int REQUIRE_DEFAULT_VALUE = PhpVersion::PHP_56; + + public const int SCALAR_TYPES = PhpVersion::PHP_70; + + public const int HAS_RETURN_TYPE = PhpVersion::PHP_70; + + public const int NULL_COALESCE = PhpVersion::PHP_70; + + public const int LIST_SWAP_ORDER = PhpVersion::PHP_70; + + public const int SPACESHIP = PhpVersion::PHP_70; + + public const int DIRNAME_LEVELS = PhpVersion::PHP_70; + + public const int CSPRNG_FUNCTIONS = PhpVersion::PHP_70; + + public const int THROWABLE_TYPE = PhpVersion::PHP_70; + + public const int NO_LIST_SPLIT_STRING = PhpVersion::PHP_70; + + public const int NO_BREAK_OUTSIDE_LOOP = PhpVersion::PHP_70; + + public const int NO_PHP4_CONSTRUCTOR = PhpVersion::PHP_70; + + public const int NO_CALL_USER_METHOD = PhpVersion::PHP_70; + + public const int NO_EREG_FUNCTION = PhpVersion::PHP_70; + + public const int VARIABLE_ON_FUNC_CALL = PhpVersion::PHP_70; + + public const int NO_MKTIME_WITHOUT_ARG = PhpVersion::PHP_70; + + public const int NO_EMPTY_LIST = PhpVersion::PHP_70; /** - * @var int + * @see https://php.watch/versions/8.0/non-static-static-call-fatal-error + * Deprecated since PHP 7.0 */ - public const ELVIS_OPERATOR = PhpVersion::PHP_53; + public const int STATIC_CALL_ON_NON_STATIC = PhpVersion::PHP_70; + + public const int INSTANCE_CALL = PhpVersion::PHP_70; + + public const int NO_MULTIPLE_DEFAULT_SWITCH = PhpVersion::PHP_70; + + public const int WRAP_VARIABLE_VARIABLE = PhpVersion::PHP_70; + + public const int ANONYMOUS_FUNCTION_RETURN_TYPE = PhpVersion::PHP_70; + + public const int ITERABLE_TYPE = PhpVersion::PHP_71; + + public const int VOID_TYPE = PhpVersion::PHP_71; + + public const int CONSTANT_VISIBILITY = PhpVersion::PHP_71; + + public const int ARRAY_DESTRUCT = PhpVersion::PHP_71; + + public const int MULTI_EXCEPTION_CATCH = PhpVersion::PHP_71; + + public const int NO_ASSIGN_ARRAY_TO_STRING = PhpVersion::PHP_71; + + public const int BINARY_OP_NUMBER_STRING = PhpVersion::PHP_71; + + public const int NO_EXTRA_PARAMETERS = PhpVersion::PHP_71; + + public const int RESERVED_OBJECT_KEYWORD = PhpVersion::PHP_71; + + public const int DEPRECATE_EACH = PhpVersion::PHP_72; + + public const int OBJECT_TYPE = PhpVersion::PHP_72; + + public const int NO_EACH_OUTSIDE_LOOP = PhpVersion::PHP_72; + + public const int DEPRECATE_CREATE_FUNCTION = PhpVersion::PHP_72; + + public const int NO_NULL_ON_GET_CLASS = PhpVersion::PHP_72; + + public const int INVERTED_BOOL_IS_OBJECT_INCOMPLETE_CLASS = PhpVersion::PHP_72; + + public const int RESULT_ARG_IN_PARSE_STR = PhpVersion::PHP_72; + + public const int STRING_IN_FIRST_DEFINE_ARG = PhpVersion::PHP_72; + + public const int STRING_IN_ASSERT_ARG = PhpVersion::PHP_72; + + public const int NO_UNSET_CAST = PhpVersion::PHP_72; + + public const int IS_COUNTABLE = PhpVersion::PHP_73; + + public const int ARRAY_KEY_FIRST_LAST = PhpVersion::PHP_73; /** - * @var int + * @see https://php.watch/versions/8.5/array_first-array_last */ - public const DATE_TIME_INTERFACE = PhpVersion::PHP_55; + public const int ARRAY_FIRST_LAST = PhpVersion::PHP_85; + + public const int JSON_EXCEPTION = PhpVersion::PHP_73; + + public const int SETCOOKIE_ACCEPT_ARRAY_OPTIONS = PhpVersion::PHP_73; + + public const int DEPRECATE_INSENSITIVE_CONSTANT_NAME = PhpVersion::PHP_73; + + public const int ESCAPE_DASH_IN_REGEX = PhpVersion::PHP_73; + + public const int DEPRECATE_INSENSITIVE_CONSTANT_DEFINE = PhpVersion::PHP_73; + + public const int DEPRECATE_INT_IN_STR_NEEDLES = PhpVersion::PHP_73; + + public const int SENSITIVE_HERE_NOW_DOC = PhpVersion::PHP_73; + + public const int ARROW_FUNCTION = PhpVersion::PHP_74; + + public const int LITERAL_SEPARATOR = PhpVersion::PHP_74; + + public const int NULL_COALESCE_ASSIGN = PhpVersion::PHP_74; + + public const int TYPED_PROPERTIES = PhpVersion::PHP_74; /** - * @see https://wiki.php.net/rfc/class_name_scalars - * @var int + * @see https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters + */ + public const int COVARIANT_RETURN = PhpVersion::PHP_74; + + public const int ARRAY_SPREAD = PhpVersion::PHP_74; + + public const int DEPRECATE_CURLY_BRACKET_ARRAY_STRING = PhpVersion::PHP_74; + + public const int DEPRECATE_REAL = PhpVersion::PHP_74; + + public const int DEPRECATE_MONEY_FORMAT = PhpVersion::PHP_74; + + public const int ARRAY_KEY_EXISTS_TO_PROPERTY_EXISTS = PhpVersion::PHP_74; + + public const int FILTER_VAR_TO_ADD_SLASHES = PhpVersion::PHP_74; + + public const int CHANGE_MB_STRPOS_ARG_POSITION = PhpVersion::PHP_74; + + public const int RESERVED_FN_FUNCTION_NAME = PhpVersion::PHP_74; + + public const int REFLECTION_TYPE_GETNAME = PhpVersion::PHP_74; + + public const int EXPORT_TO_REFLECTION_FUNCTION = PhpVersion::PHP_74; + + public const int DEPRECATE_NESTED_TERNARY = PhpVersion::PHP_74; + + public const int DEPRECATE_RESTORE_INCLUDE_PATH = PhpVersion::PHP_74; + + public const int DEPRECATE_HEBREVC = PhpVersion::PHP_74; + + public const int UNION_TYPES = PhpVersion::PHP_80; + + public const int CLASS_ON_OBJECT = PhpVersion::PHP_80; + + public const int STATIC_RETURN_TYPE = PhpVersion::PHP_80; + + public const int NO_FINAL_PRIVATE = PhpVersion::PHP_80; + + public const int DEPRECATE_REQUIRED_PARAMETER_AFTER_OPTIONAL = PhpVersion::PHP_80; + + public const int STATIC_VISIBILITY_SET_STATE = PhpVersion::PHP_80; + + public const int NULLSAFE_OPERATOR = PhpVersion::PHP_80; + + public const int IS_ITERABLE = PhpVersion::PHP_71; + + public const int NULLABLE_TYPE = PhpVersion::PHP_71; + + public const int PARENT_VISIBILITY_OVERRIDE = PhpVersion::PHP_72; + + public const int COUNT_ON_NULL = PhpVersion::PHP_71; + + /** + * @see https://wiki.php.net/rfc/constructor_promotion */ - public const CLASSNAME_CONSTANT = PhpVersion::PHP_55; + public const int PROPERTY_PROMOTION = PhpVersion::PHP_80; /** - * @var int + * @see https://wiki.php.net/rfc/attributes_v2 */ - public const EXP_OPERATOR = PhpVersion::PHP_56; + public const int ATTRIBUTES = PhpVersion::PHP_80; + + public const int STRINGABLE = PhpVersion::PHP_80; + + public const int PHP_TOKEN = PhpVersion::PHP_80; + + public const int STR_ENDS_WITH = PhpVersion::PHP_80; + + public const int STR_STARTS_WITH = PhpVersion::PHP_80; + + public const int STR_CONTAINS = PhpVersion::PHP_80; + + public const int GET_DEBUG_TYPE = PhpVersion::PHP_80; /** - * @var int + * @see https://wiki.php.net/rfc/noreturn_type */ - public const SCALAR_TYPES = PhpVersion::PHP_70; + public const int NEVER_TYPE = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/variadics */ - public const NULL_COALESCE = PhpVersion::PHP_70; + public const int VARIADIC_PARAM = PhpVersion::PHP_56; /** - * @var int + * @see https://wiki.php.net/rfc/readonly_and_immutable_properties */ - public const LIST_SWAP_ORDER = PhpVersion::PHP_70; + public const int READONLY_PROPERTY = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/final_class_const */ - public const SPACESHIP = PhpVersion::PHP_70; + public const int FINAL_CLASS_CONSTANTS = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/enumerations */ - public const DIRNAME_LEVELS = PhpVersion::PHP_70; + public const int ENUM = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/match_expression_v2 */ - public const CSPRNG_FUNCTIONS = PhpVersion::PHP_70; + public const int MATCH_EXPRESSION = PhpVersion::PHP_80; /** - * @var int + * @see https://wiki.php.net/rfc/non-capturing_catches */ - public const THROWABLE_TYPE = PhpVersion::PHP_70; + public const int NON_CAPTURING_CATCH = PhpVersion::PHP_80; /** - * @var int + * @see https://www.php.net/manual/en/migration80.incompatible.php#migration80.incompatible.resource2object */ - public const ITERABLE_TYPE = PhpVersion::PHP_71; + public const int PHP8_RESOURCE_TO_OBJECT = PhpVersion::PHP_80; /** - * @var int + * @see https://wiki.php.net/rfc/lsp_errors */ - public const VOID_TYPE = PhpVersion::PHP_71; + public const int FATAL_ERROR_ON_INCOMPATIBLE_METHOD_SIGNATURE = PhpVersion::PHP_80; /** - * @var int + * @see https://www.php.net/manual/en/migration81.incompatible.php#migration81.incompatible.resource2object */ - public const CONSTANT_VISIBILITY = PhpVersion::PHP_71; + public const int PHP81_RESOURCE_TO_OBJECT = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/new_in_initializers */ - public const ARRAY_DESTRUCT = PhpVersion::PHP_71; + public const int NEW_INITIALIZERS = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/pure-intersection-types */ - public const MULTI_EXCEPTION_CATCH = PhpVersion::PHP_71; + public const int INTERSECTION_TYPES = PhpVersion::PHP_81; /** - * @var int + * @see https://php.watch/versions/8.2/dnf-types */ - public const OBJECT_TYPE = PhpVersion::PHP_72; + public const int UNION_INTERSECTION_TYPES = PhpVersion::PHP_82; /** - * @var int + * @see https://wiki.php.net/rfc/array_unpacking_string_keys */ - public const IS_COUNTABLE = PhpVersion::PHP_73; + public const int ARRAY_SPREAD_STRING_KEYS = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/internal_method_return_types */ - public const ARRAY_KEY_FIRST_LAST = PhpVersion::PHP_73; + public const int RETURN_TYPE_WILL_CHANGE_ATTRIBUTE = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/first_class_callable_syntax */ - public const JSON_EXCEPTION = PhpVersion::PHP_73; + public const int FIRST_CLASS_CALLABLE_SYNTAX = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/deprecate_dynamic_properties */ - public const SETCOOKIE_ACCEPT_ARRAY_OPTIONS = PhpVersion::PHP_73; + public const int DEPRECATE_DYNAMIC_PROPERTIES = PhpVersion::PHP_82; /** - * @var int + * @see https://wiki.php.net/rfc/readonly_classes */ - public const ARROW_FUNCTION = PhpVersion::PHP_74; + public const int READONLY_CLASS = PhpVersion::PHP_82; /** - * @var int + * @see https://www.php.net/manual/en/migration83.new-features.php#migration83.new-features.core.readonly-modifier-improvements */ - public const LITERAL_SEPARATOR = PhpVersion::PHP_74; + public const int READONLY_ANONYMOUS_CLASS = PhpVersion::PHP_83; /** - * @var int + * @see https://wiki.php.net/rfc/json_validate */ - public const NULL_COALESCE_ASSIGN = PhpVersion::PHP_74; + public const int JSON_VALIDATE = PhpVersion::PHP_83; /** - * @var int + * @see https://wiki.php.net/rfc/mixed_type_v2 */ - public const TYPED_PROPERTIES = PhpVersion::PHP_74; + public const int MIXED_TYPE = PhpVersion::PHP_80; /** - * @see https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters - * @var int + * @see https://3v4l.org/OWtO5 */ - public const COVARIANT_RETURN = PhpVersion::PHP_74; + public const int ARRAY_ON_ARRAY_MERGE = PhpVersion::PHP_80; + + public const int DEPRECATE_NULL_ARG_IN_STRING_FUNCTION = PhpVersion::PHP_81; /** - * @var int + * @see https://wiki.php.net/rfc/remove_utf8_decode_and_utf8_encode */ - public const ARRAY_SPREAD = PhpVersion::PHP_74; + public const int DEPRECATE_UTF8_DECODE_ENCODE_FUNCTION = PhpVersion::PHP_82; /** - * @var int + * @see https://www.php.net/manual/en/filesystemiterator.construct */ - public const UNION_TYPES = PhpVersion::PHP_80; + public const int FILESYSTEM_ITERATOR_SKIP_DOTS = PhpVersion::PHP_82; /** - * @var int + * @see https://wiki.php.net/rfc/null-false-standalone-types + * @see https://wiki.php.net/rfc/true-type */ - public const CLASS_ON_OBJECT = PhpVersion::PHP_80; + public const int NULL_FALSE_TRUE_STANDALONE_TYPE = PhpVersion::PHP_82; /** - * @var int + * @see https://wiki.php.net/rfc/redact_parameters_in_back_traces */ - public const STATIC_RETURN_TYPE = PhpVersion::PHP_80; + public const int SENSITIVE_PARAMETER_ATTRIBUTE = PhpVersion::PHP_82; /** - * @var int + * @see https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation */ - public const IS_ITERABLE = PhpVersion::PHP_71; + public const int DEPRECATE_VARIABLE_IN_STRING_INTERPOLATION = PhpVersion::PHP_82; /** - * @var int + * @see https://wiki.php.net/rfc/marking_overriden_methods */ - public const NULLABLE_TYPE = PhpVersion::PHP_71; + public const int OVERRIDE_ATTRIBUTE = PhpVersion::PHP_83; /** - * @var int + * @see https://wiki.php.net/rfc/typed_class_constants */ - public const PARENT_VISIBILITY_OVERRIDE = PhpVersion::PHP_72; + public const int TYPED_CLASS_CONSTANTS = PhpVersion::PHP_83; /** - * @var int + * @see https://wiki.php.net/rfc/dynamic_class_constant_fetch */ - public const COUNT_ON_NULL = PhpVersion::PHP_71; + public const int DYNAMIC_CLASS_CONST_FETCH = PhpVersion::PHP_83; /** - * @see https://wiki.php.net/rfc/constructor_promotion - * @var int + * @see https://wiki.php.net/rfc/deprecate-implicitly-nullable-types */ - public const PROPERTY_PROMOTION = PhpVersion::PHP_80; + public const int DEPRECATE_IMPLICIT_NULLABLE_PARAM_TYPE = PhpVersion::PHP_84; /** - * @see https://wiki.php.net/rfc/attributes_v2 - * @var int + * @see https://wiki.php.net/rfc/new_without_parentheses */ - public const ATTRIBUTES = PhpVersion::PHP_80; + public const int NEW_METHOD_CALL_WITHOUT_PARENTHESES = PhpVersion::PHP_84; /** - * @see https://wiki.php.net/rfc/noreturn_type - * @var int + * @see https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum */ - public const NEVER_TYPE = PhpVersion::PHP_81; + public const int ROUNDING_MODES = PhpVersion::PHP_84; /** - * @see https://wiki.php.net/rfc/variadics - * @var int + * @see https://php.watch/versions/8.4/csv-functions-escape-parameter + */ + public const int REQUIRED_ESCAPE_PARAMETER = PhpVersion::PHP_84; + + /** + * @see https://www.php.net/manual/en/migration83.deprecated.php#migration83.deprecated.ldap + */ + public const int DEPRECATE_HOST_PORT_SEPARATE_ARGS = PhpVersion::PHP_83; + + /** + * @see https://www.php.net/manual/en/migration83.deprecated.php#migration83.deprecated.core.get-class + * @see https://php.watch/versions/8.3/get_class-get_parent_class-parameterless-deprecated + */ + public const int DEPRECATE_GET_CLASS_WITHOUT_ARGS = PhpVersion::PHP_83; + + /** + * @see https://wiki.php.net/rfc/deprecated_attribute + */ + public const int DEPRECATED_ATTRIBUTE = PhpVersion::PHP_84; + + /** + * @see https://php.watch/versions/8.4/array_find-array_find_key-array_any-array_all + */ + public const int ARRAY_FIND = PhpVersion::PHP_84; + + /** + * @see https://php.watch/versions/8.4/array_find-array_find_key-array_any-array_all + */ + public const int ARRAY_FIND_KEY = PhpVersion::PHP_84; + + /** + * @see https://php.watch/versions/8.4/array_find-array_find_key-array_any-array_all + */ + public const int ARRAY_ALL = PhpVersion::PHP_84; + + /** + * @see https://php.watch/versions/8.4/array_find-array_find_key-array_any-array_all + */ + public const int ARRAY_ANY = PhpVersion::PHP_84; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_context_parameter_for_finfo_buffer + */ + public const int DEPRECATE_FINFO_BUFFER_CONTEXT = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_debuginfo_returning_null + */ + public const int DEPRECATED_NULL_DEBUG_INFO_RETURN = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_semicolon_after_case_in_switch_statement + */ + public const int COLON_AFTER_SWITCH_CASE = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_using_values_null_as_an_array_offset_and_when_calling_array_key_exists + */ + public const int DEPRECATE_NULL_ARG_IN_ARRAY_KEY_EXISTS_FUNCTION = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#eprecate_passing_integers_outside_the_interval_0_255_to_chr + */ + public const int DEPRECATE_OUTSIDE_INTERVEL_VAL_IN_CHR_FUNCTION = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_sleep_and_wakeup_magic_methods + */ + public const int DEPRECATED_METHOD_SLEEP = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_sleep_and_wakeup_magic_methods + */ + public const int DEPRECATED_METHOD_WAKEUP = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_passing_string_which_are_not_one_byte_long_to_ord + */ + public const int DEPRECATE_ORD_WITH_MULTIBYTE_STRING = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/property-hooks + */ + public const int PROPERTY_HOOKS = PhpVersion::PHP_84; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_backticks_as_an_alias_for_shell_exec + */ + public const int DEPRECATE_BACKTICKS = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/pipe-operator-v3 + */ + public const int PIPE_OPERATOER = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/override_properties + */ + public const int OVERRIDE_ATTRIBUTE_ON_PROPERTIES = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/clamp_v2 */ - public const VARIADIC_PARAM = PhpVersion::PHP_56; + public const int CLAMP = PhpVersion::PHP_86; } diff --git a/src/ValueObject/PolyfillPackage.php b/src/ValueObject/PolyfillPackage.php new file mode 100644 index 00000000000..f2d7822b0cd --- /dev/null +++ b/src/ValueObject/PolyfillPackage.php @@ -0,0 +1,25 @@ +fileDiffs = $fileDiffs; - $this->errors = $errors; + Assert::allIsInstanceOf($systemErrors, SystemError::class); + Assert::allIsInstanceOf($fileDiffs, FileDiff::class); } /** - * @return FileDiff[] + * @return SystemError[] */ - public function getFileDiffs(): array + public function getSystemErrors(): array { - return $this->fileDiffs; + return $this->systemErrors; } /** - * @return RectorError[] + * @return FileDiff[] */ - public function getErrors(): array + public function getFileDiffs(bool $onlyWithChanges = true): array { - return $this->errors; - } + if ($onlyWithChanges) { + return array_filter($this->fileDiffs, fn (FileDiff $fileDiff): bool => $fileDiff->getDiff() !== ''); + } - public function getAddedFilesCount(): int - { - return $this->addedFilesCount; + return $this->fileDiffs; } - public function getRemovedFilesCount(): int + /** + * @param SystemError[] $systemErrors + */ + public function addSystemErrors(array $systemErrors): void { - return $this->removedFilesCount; - } + Assert::allIsInstanceOf($systemErrors, SystemError::class); - public function getRemovedAndAddedFilesCount(): int - { - return $this->removedFilesCount + $this->addedFilesCount; + $this->systemErrors = [...$this->systemErrors, ...$systemErrors]; } - public function getRemovedNodeCount(): int + public function getTotalChanged(): int { - return $this->removedNodeCount; + return $this->totalChanged; } /** - * @return SmartFileInfo[] + * @return array, int> */ - public function getChangedFileInfos(): array + public function getRuleApplicationCounts(): array { - $fileInfos = []; + $ruleCounts = []; + foreach ($this->fileDiffs as $fileDiff) { - $fileInfos[] = $fileDiff->getFileInfo(); + foreach ($fileDiff->getRectorClasses() as $rectorClass) { + if (! isset($ruleCounts[$rectorClass])) { + $ruleCounts[$rectorClass] = 0; + } + + ++$ruleCounts[$rectorClass]; + } } - return array_unique($fileInfos); + arsort($ruleCounts); + return $ruleCounts; } } diff --git a/src/ValueObject/Reporting/FileDiff.php b/src/ValueObject/Reporting/FileDiff.php index d56055fafbe..cac1ec6978b 100644 --- a/src/ValueObject/Reporting/FileDiff.php +++ b/src/ValueObject/Reporting/FileDiff.php @@ -2,35 +2,41 @@ declare(strict_types=1); -namespace Rector\Core\ValueObject\Reporting; +namespace Rector\ValueObject\Reporting; use Nette\Utils\Strings; use Rector\ChangesReporting\ValueObject\RectorWithLineChange; -use Rector\Core\Contract\Rector\RectorInterface; -use Symplify\SmartFileSystem\SmartFileInfo; - -final class FileDiff +use Rector\Contract\Rector\RectorInterface; +use Rector\Parallel\ValueObject\BridgeItem; +use Rector\Util\RectorClassesSorter; +use Symplify\EasyParallel\Contract\SerializableInterface; +use Webmozart\Assert\Assert; + +/** + * @see \Rector\Tests\ValueObject\Reporting\FileDiffTest + */ +final readonly class FileDiff implements SerializableInterface { /** - * @var string - * @se https://regex101.com/r/AUPIX4/1 + * @see https://en.wikipedia.org/wiki/Diff#Unified_format + * @see https://regex101.com/r/AUPIX4/2 */ - private const FIRST_LINE_REGEX = '#@@(.*?)(?<' . self::FIRST_LINE_KEY . '>\d+)(.*?)@@#'; + private const string DIFF_HUNK_HEADER_REGEX = '#@@(.*?)(?<' . self::FIRST_LINE_KEY . '>\d+)(,(?<' . self::LINE_RANGE_KEY . '>\d+))?(.*?)@@#'; - /** - * @var string - */ - private const FIRST_LINE_KEY = 'first_line'; + private const string FIRST_LINE_KEY = 'first_line'; + + private const string LINE_RANGE_KEY = 'line_range'; /** - * @param RectorWithLineChange[] $rectorWithLineChanges + * @param RectorWithLineChange[] $rectorsWithLineChanges */ public function __construct( - private SmartFileInfo $smartFileInfo, + private string $relativeFilePath, private string $diff, private string $diffConsoleFormatted, - private array $rectorWithLineChanges = [] + private array $rectorsWithLineChanges = [] ) { + Assert::allIsInstanceOf($rectorsWithLineChanges, RectorWithLineChange::class); } public function getDiff(): string @@ -45,12 +51,12 @@ public function getDiffConsoleFormatted(): string public function getRelativeFilePath(): string { - return $this->smartFileInfo->getRelativeFilePath(); + return $this->relativeFilePath; } - public function getFileInfo(): SmartFileInfo + public function getAbsoluteFilePath(): ?string { - return $this->smartFileInfo; + return \realpath($this->relativeFilePath) ?: null; } /** @@ -58,7 +64,20 @@ public function getFileInfo(): SmartFileInfo */ public function getRectorChanges(): array { - return $this->rectorWithLineChanges; + return $this->rectorsWithLineChanges; + } + + /** + * @return string[] + */ + public function getRectorShortClasses(): array + { + $rectorShortClasses = []; + foreach ($this->getRectorClasses() as $rectorClass) { + $rectorShortClasses[] = (string) Strings::after($rectorClass, '\\', -1); + } + + return $rectorShortClasses; } /** @@ -67,35 +86,76 @@ public function getRectorChanges(): array public function getRectorClasses(): array { $rectorClasses = []; - foreach ($this->rectorWithLineChanges as $rectorWithLineChange) { + + foreach ($this->rectorsWithLineChanges as $rectorWithLineChange) { $rectorClasses[] = $rectorWithLineChange->getRectorClass(); } - return $this->sortClasses($rectorClasses); + return RectorClassesSorter::sortAndFilterOutPostRectors($rectorClasses); } public function getFirstLineNumber(): ?int { - $match = Strings::match($this->diff, self::FIRST_LINE_REGEX); + $match = Strings::match($this->diff, self::DIFF_HUNK_HEADER_REGEX); // probably some error in diff if (! isset($match[self::FIRST_LINE_KEY])) { return null; } - return (int) $match[self::FIRST_LINE_KEY] - 1; + return (int) $match[self::FIRST_LINE_KEY]; + } + + public function getLastLineNumber(): ?int + { + $match = Strings::match($this->diff, self::DIFF_HUNK_HEADER_REGEX); + + $firstLine = $this->getFirstLineNumber(); + + // probably some error in diff + if (! isset($match[self::LINE_RANGE_KEY])) { + return $firstLine; + } + + // line range is not mandatory + if ($match[self::LINE_RANGE_KEY] === '') { + return $firstLine; + } + + $lineRange = (int) $match[self::LINE_RANGE_KEY]; + + return $firstLine + $lineRange; } /** - * @template TType as object - * @param array> $rectorClasses - * @return array> + * @return array{relative_file_path: string, diff: string, diff_console_formatted: string, rectors_with_line_changes: RectorWithLineChange[]} */ - private function sortClasses(array $rectorClasses): array + public function jsonSerialize(): array { - $rectorClasses = array_unique($rectorClasses); - sort($rectorClasses); + return [ + BridgeItem::RELATIVE_FILE_PATH => $this->relativeFilePath, + BridgeItem::DIFF => $this->diff, + BridgeItem::DIFF_CONSOLE_FORMATTED => $this->diffConsoleFormatted, + BridgeItem::RECTORS_WITH_LINE_CHANGES => $this->rectorsWithLineChanges, + ]; + } + + /** + * @param array $json + */ + public static function decode(array $json): self + { + $rectorWithLineChanges = []; + + foreach ($json[BridgeItem::RECTORS_WITH_LINE_CHANGES] as $rectorWithLineChangesJson) { + $rectorWithLineChanges[] = RectorWithLineChange::decode($rectorWithLineChangesJson); + } - return $rectorClasses; + return new self( + $json[BridgeItem::RELATIVE_FILE_PATH], + $json[BridgeItem::DIFF], + $json[BridgeItem::DIFF_CONSOLE_FORMATTED], + $rectorWithLineChanges, + ); } } diff --git a/src/ValueObject/SprintfStringAndArgs.php b/src/ValueObject/SprintfStringAndArgs.php index 124f386be15..b3e6daef1ff 100644 --- a/src/ValueObject/SprintfStringAndArgs.php +++ b/src/ValueObject/SprintfStringAndArgs.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Rector\Core\ValueObject; +namespace Rector\ValueObject; use PhpParser\Node\Expr; use PhpParser\Node\Scalar\String_; -final class SprintfStringAndArgs +final readonly class SprintfStringAndArgs { /** * @param Expr[] $arrayItems diff --git a/src/ValueObject/StaticNonPhpFileSuffixes.php b/src/ValueObject/StaticNonPhpFileSuffixes.php deleted file mode 100644 index 743fa5e2c0b..00000000000 --- a/src/ValueObject/StaticNonPhpFileSuffixes.php +++ /dev/null @@ -1,23 +0,0 @@ -shouldClearCache()) { - $this->changedFilesDetector->clear(); - } - - $supportedFileExtensions = $this->resolveSupportedFileExtensions($configuration); - $fileInfos = $this->filesFinder->findInDirectoriesAndFiles($paths, $supportedFileExtensions); - - $files = []; - foreach ($fileInfos as $fileInfo) { - $files[] = new File($fileInfo, $fileInfo->getContents()); - } - - return $files; - } - - /** - * @return string[] - */ - private function resolveSupportedFileExtensions(Configuration $configuration): array - { - $supportedFileExtensions = []; - - foreach ($this->fileProcessors as $fileProcessor) { - $supportedFileExtensions = array_merge( - $supportedFileExtensions, - $fileProcessor->getSupportedFileExtensions() - ); - } - - // basic PHP extensions - $supportedFileExtensions = array_merge($supportedFileExtensions, $configuration->getFileExtensions()); - - return array_unique($supportedFileExtensions); - } -} diff --git a/src/ValueObjectFactory/ProcessResultFactory.php b/src/ValueObjectFactory/ProcessResultFactory.php deleted file mode 100644 index 0311cb689b8..00000000000 --- a/src/ValueObjectFactory/ProcessResultFactory.php +++ /dev/null @@ -1,46 +0,0 @@ -getErrors()); - - if ($file->getFileDiff() === null) { - continue; - } - - $fileDiffs[] = $file->getFileDiff(); - } - - return new ProcessResult( - $fileDiffs, - $errors, - $this->removedAndAddedFilesCollector->getAddedFileCount(), - $this->removedAndAddedFilesCollector->getRemovedFilesCount(), - $this->nodesToRemoveCollector->getCount(), - ); - } -} diff --git a/src/VendorLocker/Exception/UnresolvableClassException.php b/src/VendorLocker/Exception/UnresolvableClassException.php new file mode 100644 index 00000000000..d463e5aeb1c --- /dev/null +++ b/src/VendorLocker/Exception/UnresolvableClassException.php @@ -0,0 +1,11 @@ +isMagic()) { + return true; + } + + if ($classMethod->isPrivate()) { + return false; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + /** @var string $methodName */ + $methodName = $this->nodeNameResolver->getName($classMethod); + + // has interface vendor lock? → better skip it, as PHPStan has access only to just analyzed classes + return $this->hasParentInterfaceMethod($classReflection, $methodName); + } + + /** + * Has interface even in our project? + * Better skip it, as PHPStan has access only to just analyzed classes. + * This might change type, that works for current class, but breaks another implementer. + */ + private function hasParentInterfaceMethod(ClassReflection $classReflection, string $methodName): bool + { + foreach ($classReflection->getInterfaces() as $interfaceClassReflection) { + if ($interfaceClassReflection->hasMethod($methodName)) { + return true; + } + } + + return false; + } +} diff --git a/src/VendorLocker/NodeVendorLocker/ClassMethodReturnTypeOverrideGuard.php b/src/VendorLocker/NodeVendorLocker/ClassMethodReturnTypeOverrideGuard.php new file mode 100644 index 00000000000..241f2ab419b --- /dev/null +++ b/src/VendorLocker/NodeVendorLocker/ClassMethodReturnTypeOverrideGuard.php @@ -0,0 +1,108 @@ +magicClassMethodAnalyzer->isUnsafeOverridden($classMethod)) { + return true; + } + + // except magic check on above, early allow add return type on private method + if ($classMethod->isPrivate()) { + return false; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if (! $classReflection instanceof ClassReflection) { + return true; + } + + if ($classReflection->isAbstract()) { + return true; + } + + if ($classReflection->isInterface()) { + return true; + } + + return ! $this->isReturnTypeChangeAllowed($classMethod, $scope); + } + + private function isReturnTypeChangeAllowed(ClassMethod $classMethod, Scope $scope): bool + { + // make sure return type is not protected by parent contract + $parentClassMethodReflection = $this->parentClassMethodTypeOverrideGuard->getParentClassMethod($classMethod); + + // nothing to check + if (! $parentClassMethodReflection instanceof MethodReflection) { + return ! $this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($classMethod); + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select( + $parentClassMethodReflection, + $classMethod, + $scope + ); + if ($parametersAcceptor instanceof ExtendedFunctionVariant && ! $parametersAcceptor->getNativeReturnType() instanceof MixedType) { + return false; + } + + $classReflection = $parentClassMethodReflection->getDeclaringClass(); + $fileName = $classReflection->getFileName(); + + // probably internal + if ($fileName === null) { + return false; + } + + /* + * Below verify that both current file name and parent file name is not in the /vendor/, if yes, then allowed. + * This can happen when rector run into /vendor/ directory while child and parent both are there. + * + * @see https://3v4l.org/Rc0RF#v8.0.13 + * + * - both in /vendor/ -> allowed + * - one of them in /vendor/ -> not allowed + * - both not in /vendor/ -> allowed + */ + /** @var ClassReflection $currentClassReflection */ + $currentClassReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + /** @var string $currentFileName */ + $currentFileName = $currentClassReflection->getFileName(); + + // child (current) + $normalizedCurrentFileName = $this->filePathHelper->normalizePathAndSchema($currentFileName); + $isCurrentInVendor = str_contains($normalizedCurrentFileName, '/vendor/'); + + // parent + $normalizedFileName = $this->filePathHelper->normalizePathAndSchema($fileName); + $isParentInVendor = str_contains($normalizedFileName, '/vendor/'); + + return ($isCurrentInVendor && $isParentInVendor) || (! $isCurrentInVendor && ! $isParentInVendor); + } +} diff --git a/src/VendorLocker/NodeVendorLocker/ClassMethodReturnVendorLockResolver.php b/src/VendorLocker/NodeVendorLocker/ClassMethodReturnVendorLockResolver.php new file mode 100644 index 00000000000..e6bce14c795 --- /dev/null +++ b/src/VendorLocker/NodeVendorLocker/ClassMethodReturnVendorLockResolver.php @@ -0,0 +1,73 @@ +magicClassMethodAnalyzer->isUnsafeOverridden($classMethod)) { + return true; + } + + if ($classMethod->isPrivate()) { + return false; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + $methodName = $this->nodeNameResolver->getName($classMethod); + return $this->isVendorLockedByAncestors($classReflection, $methodName); + } + + private function isVendorLockedByAncestors(ClassReflection $classReflection, string $methodName): bool + { + foreach ($classReflection->getAncestors() as $ancestorClassReflections) { + if ($ancestorClassReflections === $classReflection) { + continue; + } + + $nativeClassReflection = $ancestorClassReflections->getNativeReflection(); + + // this should avoid detecting @method as real method + if (! $nativeClassReflection->hasMethod($methodName)) { + continue; + } + + if (! $ancestorClassReflections->hasNativeMethod($methodName)) { + continue; + } + + $parentClassMethodReflection = $ancestorClassReflections->getNativeMethod($methodName); + $parametersAcceptor = $parentClassMethodReflection->getVariants()[0]; + if (! $parametersAcceptor instanceof ExtendedFunctionVariant) { + continue; + } + + // here we count only on strict types, not on docs + return ! $parametersAcceptor->getNativeReturnType() instanceof MixedType; + } + + return false; + } +} diff --git a/src/VendorLocker/ParentClassMethodTypeOverrideGuard.php b/src/VendorLocker/ParentClassMethodTypeOverrideGuard.php new file mode 100644 index 00000000000..545ec182962 --- /dev/null +++ b/src/VendorLocker/ParentClassMethodTypeOverrideGuard.php @@ -0,0 +1,133 @@ +resolveParentClassMethod($classMethod); + + return $parentClassMethod instanceof MethodReflection; + } catch (UnresolvableClassException) { + // we don't know all involved parents, + // marking as parent exists which usually means the method is guarded against overrides. + return true; + } + } + + public function getParentClassMethod(ClassMethod|MethodReflection $classMethod): ?MethodReflection + { + try { + return $this->resolveParentClassMethod($classMethod); + } catch (UnresolvableClassException) { + return null; + } + } + + public function shouldSkipReturnTypeChange(ClassMethod $classMethod, Type $parentType): bool + { + if (! $classMethod->returnType instanceof Node) { + return false; + } + + $currentReturnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($classMethod->returnType); + + if ($this->typeComparator->isSubtype($currentReturnType, $parentType)) { + return true; + } + + return $this->typeComparator->areTypesEqual($currentReturnType, $parentType); + } + + private function resolveParentClassMethod(ClassMethod|MethodReflection $classMethod): ?MethodReflection + { + // early got null on private method + if ($classMethod->isPrivate()) { + return null; + } + + $classReflection = $classMethod instanceof ClassMethod + ? $this->reflectionResolver->resolveClassReflection($classMethod) + : $classMethod->getDeclaringClass(); + + if (! $classReflection instanceof ClassReflection) { + // we can't resolve the class, so we don't know. + throw new UnresolvableClassException(); + } + + /** @var string $methodName */ + $methodName = $classMethod instanceof ClassMethod + ? $this->nodeNameResolver->getName($classMethod) + : $classMethod->getName(); + + $currentClassReflection = $classReflection; + while ($this->hasClassParent($currentClassReflection)) { + $parentClassReflection = $currentClassReflection->getParentClass(); + if (! $parentClassReflection instanceof ClassReflection) { + // per AST we have a parent class, but our reflection classes are not able to load its class definition/signature + throw new UnresolvableClassException(); + } + + if ($parentClassReflection->hasNativeMethod($methodName)) { + return $parentClassReflection->getNativeMethod($methodName); + } + + $currentClassReflection = $parentClassReflection; + } + + foreach ($classReflection->getInterfaces() as $interfaceReflection) { + if (! $interfaceReflection->hasNativeMethod($methodName)) { + continue; + } + + return $interfaceReflection->getNativeMethod($methodName); + } + + foreach ($classReflection->getTraits() as $traitReflection) { + if (! $traitReflection->hasNativeMethod($methodName)) { + continue; + } + + $methodReflection = $traitReflection->getNativeMethod($methodName); + + // any signature on non abstract trait method can be overridden + if ($methodReflection->isAbstract()) { + return $methodReflection; + } + + return null; + } + + return null; + } + + private function hasClassParent(ClassReflection $classReflection): bool + { + return $this->classReflectionAnalyzer->resolveParentClassName($classReflection) !== null; + } +} diff --git a/src/VersionBonding/ComposerPackageConstraintFilter.php b/src/VersionBonding/ComposerPackageConstraintFilter.php new file mode 100644 index 00000000000..54bb124b5ab --- /dev/null +++ b/src/VersionBonding/ComposerPackageConstraintFilter.php @@ -0,0 +1,56 @@ + $rectors + * @return list + */ + public function filter(array $rectors): array + { + $activeRectors = []; + foreach ($rectors as $rector) { + if (! $rector instanceof ComposerPackageConstraintInterface) { + $activeRectors[] = $rector; + continue; + } + + if ($this->satisfiesComposerPackageConstraint($rector)) { + $activeRectors[] = $rector; + } + } + + return $activeRectors; + } + + private function satisfiesComposerPackageConstraint(ComposerPackageConstraintInterface $rector): bool + { + $composerPackageConstraint = $rector->provideComposerPackageConstraint(); + $packageVersion = $this->installedPackageResolver->resolvePackageVersion( + $composerPackageConstraint->getPackageName(), + ); + + if ($packageVersion === null) { + return false; + } + + return Semver::satisfies($packageVersion, $composerPackageConstraint->getConstraint()); + } +} diff --git a/src/VersionBonding/Contract/ComposerPackageConstraintInterface.php b/src/VersionBonding/Contract/ComposerPackageConstraintInterface.php new file mode 100644 index 00000000000..f591813e015 --- /dev/null +++ b/src/VersionBonding/Contract/ComposerPackageConstraintInterface.php @@ -0,0 +1,19 @@ + $rectors + * @return list + */ + public function filter(array $rectors): array + { + $minProjectPhpVersion = $this->phpVersionProvider->provide(); + + $activeRectors = []; + foreach ($rectors as $rector) { + if ($rector instanceof RelatedPolyfillInterface) { + $polyfillPackageNames = $this->polyfillPackagesProvider->provide(); + + if (in_array($rector->providePolyfillPackage(), $polyfillPackageNames, true)) { + $activeRectors[] = $rector; + continue; + } + } + + if (! $rector instanceof MinPhpVersionInterface) { + $activeRectors[] = $rector; + continue; + } + + // does satisfy version? → include + if ($rector->provideMinPhpVersion() <= $minProjectPhpVersion) { + $activeRectors[] = $rector; + } + } + + return $activeRectors; + } +} diff --git a/src/VersionBonding/ValueObject/ComposerPackageConstraint.php b/src/VersionBonding/ValueObject/ComposerPackageConstraint.php new file mode 100644 index 00000000000..152ace8c752 --- /dev/null +++ b/src/VersionBonding/ValueObject/ComposerPackageConstraint.php @@ -0,0 +1,27 @@ +packageName; + } + + public function getConstraint(): string + { + return $this->constraint; + } +} diff --git a/src/constants.php b/src/constants.php deleted file mode 100644 index 4396afb5be2..00000000000 --- a/src/constants.php +++ /dev/null @@ -1,13 +0,0 @@ -stmts; + } foreach ($nodes as $node) { - Dumper::dump($node, [ - Dumper::DEPTH => $depth, - ]); + $printedContent = $standard->prettyPrint([$node]); + var_dump($printedContent); } } } - -if (! function_exists('print_node')) { +if (! function_exists('dump_node')) { /** * @param Node|Node[] $node */ - function print_node(Node | array $node): void + function dump_node(Node|array $node): void { - $standard = new Standard(); + $rectorStyle = Container::getInstance() + ->make(SymfonyStyleFactory::class) + ->create(); - $nodes = is_array($node) ? $node : [$node]; + // we turn up the verbosity so it's visible in tests overriding the + // default which is to be quite during tests + $rectorStyle->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $rectorStyle->newLine(); - foreach ($nodes as $node) { - $printedContent = $standard->prettyPrint([$node]); - Dumper::dump($printedContent); - } + $nodePrinter = new NodePrinter($rectorStyle); + $nodePrinter->printNodes($node); } } diff --git a/stubs-rector/PHPUnit/Framework/TestCase.php b/stubs-rector/PHPUnit/Framework/TestCase.php deleted file mode 100644 index 09804b59db5..00000000000 --- a/stubs-rector/PHPUnit/Framework/TestCase.php +++ /dev/null @@ -1,13 +0,0 @@ - + */ + public function fetchFirstColumn(string $query, array $params = [], array $types = []): array + { + return []; + } +} diff --git a/stubs/Doctrine/Nette/Security/User.php b/stubs/Doctrine/Nette/Security/User.php new file mode 100644 index 00000000000..07b8c544ca1 --- /dev/null +++ b/stubs/Doctrine/Nette/Security/User.php @@ -0,0 +1,14 @@ +generator = $generator; + $this->count = $count; + } + + public function getIterator(): \Traversable + { + $g = $this->generator; + + return $g(); + } + + public function count(): int + { + if (\is_callable($count = $this->count)) { + $this->count = $count(); + } + + return $this->count; + } +} diff --git a/stubs/Symfony/Component/ExpressionLanguage/ExpressionFunctionProviderInterface.php b/stubs/Symfony/Component/ExpressionLanguage/ExpressionFunctionProviderInterface.php deleted file mode 100644 index 96b8498f0b6..00000000000 --- a/stubs/Symfony/Component/ExpressionLanguage/ExpressionFunctionProviderInterface.php +++ /dev/null @@ -1,17 +0,0 @@ - $value) { + $method = 'set'.str_replace('_', '', $key); + if (!method_exists($this, $method)) { + throw new \BadMethodCallException(sprintf('Unknown property "%s" on annotation "%s".', $key, \get_class($this))); + } + $this->$method($value); + } + } + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } + + public function setLocalizedPaths(array $localizedPaths) + { + $this->localizedPaths = $localizedPaths; + } + + public function getLocalizedPaths(): array + { + return $this->localizedPaths; + } + + public function setHost($pattern) + { + $this->host = $pattern; + } + + public function getHost() + { + return $this->host; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setRequirements($requirements) + { + $this->requirements = $requirements; + } + + public function getRequirements() + { + return $this->requirements; + } + + public function setOptions($options) + { + $this->options = $options; + } + + public function getOptions() + { + return $this->options; + } + + public function setDefaults($defaults) + { + $this->defaults = $defaults; + } + + public function getDefaults() + { + return $this->defaults; + } + + public function setSchemes($schemes) + { + $this->schemes = \is_array($schemes) ? $schemes : [$schemes]; + } + + public function getSchemes() + { + return $this->schemes; + } + + public function setMethods($methods) + { + $this->methods = \is_array($methods) ? $methods : [$methods]; + } + + public function getMethods() + { + return $this->methods; + } + + public function setCondition($condition) + { + $this->condition = $condition; + } + public function getCondition() + { + return $this->condition; + } } diff --git a/stubs/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php b/stubs/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php deleted file mode 100644 index 383043dc534..00000000000 --- a/stubs/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php +++ /dev/null @@ -1,37 +0,0 @@ -getGenerator()->generate($name, $parameters, $referenceType); - } - - private function getGenerator(): UrlGeneratorInterface - { - } -} diff --git a/stubs/Symfony/Component/Routing/RouterInterface.php b/stubs/Symfony/Component/Routing/RouterInterface.php deleted file mode 100644 index 3be74179652..00000000000 --- a/stubs/Symfony/Component/Routing/RouterInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -> + */ + public function getNodeTypes(): array + { + // @todo select node type + return [\PhpParser\Node\Stmt\Class_::class]; + } + + /** + * @param \PhpParser\Node\Stmt\Class_ $node + */ + public function refactor(Node $node): ?Node + { + // @todo change the node + + return $node; + } +} diff --git a/templates/custom-rule/utils/rector/tests/Rector/__Name__/Fixture/some_class.php.inc.phtml b/templates/custom-rule/utils/rector/tests/Rector/__Name__/Fixture/some_class.php.inc.phtml new file mode 100644 index 00000000000..6cf9dc365f0 --- /dev/null +++ b/templates/custom-rule/utils/rector/tests/Rector/__Name__/Fixture/some_class.php.inc.phtml @@ -0,0 +1,15 @@ + +----- + diff --git a/templates/custom-rule/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml b/templates/custom-rule/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml new file mode 100644 index 00000000000..370713d87a3 --- /dev/null +++ b/templates/custom-rule/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml @@ -0,0 +1,27 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/templates/custom-rule/utils/rector/tests/Rector/__Name__/config/configured_rule.php.phtml b/templates/custom-rule/utils/rector/tests/Rector/__Name__/config/configured_rule.php.phtml new file mode 100644 index 00000000000..25ee3a80e6d --- /dev/null +++ b/templates/custom-rule/utils/rector/tests/Rector/__Name__/config/configured_rule.php.phtml @@ -0,0 +1,9 @@ +rule(\Utils\Rector\Rector\__Name__::class); +}; diff --git a/templates/custom-rules-annotations/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml b/templates/custom-rules-annotations/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml new file mode 100644 index 00000000000..6cad0044274 --- /dev/null +++ b/templates/custom-rules-annotations/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/templates/rector-github-action-check.yaml b/templates/rector-github-action-check.yaml new file mode 100644 index 00000000000..b315f3c0820 --- /dev/null +++ b/templates/rector-github-action-check.yaml @@ -0,0 +1,36 @@ +# github action that checks code with Rector +name: Rector + +on: + pull_request: null + +jobs: + rector: + runs-on: ubuntu-latest + if: github.event.pull_request.head.repo.full_name == '__CURRENT_REPOSITORY__' + steps: + - + if: github.event.pull_request.head.repo.full_name == github.repository + uses: actions/checkout@v4 + with: + # Must be used to trigger workflow after push + token: ${{ secrets.ACCESS_TOKEN }} + + - + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + coverage: none + + - uses: "ramsey/composer-install@v3" + + - run: vendor/bin/rector --ansi + # @todo apply coding standard if used + + - + # commit only to core contributors who have repository access + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: '[rector] Rector fixes' + commit_author: 'GitHub Action ' + commit_user_email: 'action@github.com' diff --git a/templates/rector-gitlab-check.yaml b/templates/rector-gitlab-check.yaml new file mode 100644 index 00000000000..8ac3fb4c47c --- /dev/null +++ b/templates/rector-gitlab-check.yaml @@ -0,0 +1,27 @@ +stages: + - setup + - rector + - commit_changes + +setup: + stage: setup + # see https://github.com/thecodingmachine/docker-images-php + image: thecodingmachine/php:8.3-v4-slim-cli + +rector: + stage: rector + script: + - vendor/bin/rector --ansi + # @todo apply coding standard if used + +commit_changes: + stage: commit_changes + script: + - git config --global user.email "ci@gitlab.com" + - git config --global user.name "GitLab CI" + # - git checkout $CI_COMMIT_REF_NAME + - git add . + - git commit -m "[rector] Rector fixes" + - git push origin $CI_COMMIT_REF_NAME + only: + - merge_requests diff --git a/templates/rector.php.dist b/templates/rector.php.dist index ab1c6f1a1cb..93ac7276613 100644 --- a/templates/rector.php.dist +++ b/templates/rector.php.dist @@ -2,21 +2,14 @@ declare(strict_types=1); -use Rector\Core\Configuration\Option; -use Rector\Php74\Rector\Property\TypedPropertyRector; -use Rector\Set\ValueObject\SetList; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { - // get parameters - $parameters = $containerConfigurator->parameters(); - - // Define what rule sets will be applied - $containerConfigurator->import(SetList::DEAD_CODE); - - // get services (needed for register a single rule) - // $services = $containerConfigurator->services(); - - // register a single rule - // $services->set(TypedPropertyRector::class); -}; +use Rector\Config\RectorConfig; + +return RectorConfig::configure() + ->withPaths([ +__PATHS__ + ]) + // uncomment to reach your current PHP version + // ->withPhpSets() + ->withTypeCoverageLevel(0) + ->withDeadCodeLevel(0) + ->withCodeQualityLevel(0); diff --git a/tests-paths/path/NoExtensionFile b/tests-paths/path/NoExtensionFile new file mode 100644 index 00000000000..01fff1f3f99 --- /dev/null +++ b/tests-paths/path/NoExtensionFile @@ -0,0 +1,3 @@ +bootFromConfigFileInfos([new SmartFileInfo(__DIR__ . '/config/configured_rule.php')]); - - $this->applicationFileProcessor = $this->getService(ApplicationFileProcessor::class); - $this->fileFactory = $this->getService(FileFactory::class); - $this->processResultFactory = $this->getService(ProcessResultFactory::class); - } - - public function test(): void - { - $files = $this->fileFactory->createFromPaths([__DIR__ . '/Fixture'], new Configuration()); - $this->assertCount(2, $files); - - $configuration = new Configuration(isDryRun: true); - $this->applicationFileProcessor->run($files, $configuration); - - $processResult = $this->processResultFactory->create($files); - $this->assertCount(1, $processResult->getFileDiffs()); - } -} diff --git a/tests/Application/ApplicationFileProcessor/Fixture/bar_stays_bar.txt b/tests/Application/ApplicationFileProcessor/Fixture/bar_stays_bar.txt deleted file mode 100644 index ebd7525b336..00000000000 --- a/tests/Application/ApplicationFileProcessor/Fixture/bar_stays_bar.txt +++ /dev/null @@ -1 +0,0 @@ -Bar diff --git a/tests/Application/ApplicationFileProcessor/Fixture/foo_to_bar.txt b/tests/Application/ApplicationFileProcessor/Fixture/foo_to_bar.txt deleted file mode 100644 index bc56c4d8944..00000000000 --- a/tests/Application/ApplicationFileProcessor/Fixture/foo_to_bar.txt +++ /dev/null @@ -1 +0,0 @@ -Foo diff --git a/tests/Application/ApplicationFileProcessor/Source/Contract/TextRectorInterface.php b/tests/Application/ApplicationFileProcessor/Source/Contract/TextRectorInterface.php deleted file mode 100644 index 7c4a322a8b1..00000000000 --- a/tests/Application/ApplicationFileProcessor/Source/Contract/TextRectorInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -textRectors = $textRectors; - } - - public function process(File $file, Configuration $configuration): void - { - $fileContent = $file->getFileContent(); - - foreach ($this->textRectors as $textRector) { - $fileContent = $textRector->refactorContent($fileContent); - } - - $file->changeFileContent($fileContent); - } - - public function supports(File $file, Configuration $configuration): bool - { - $smartFileInfo = $file->getSmartFileInfo(); - return $smartFileInfo->getSuffix() === 'txt'; - } - - /** - * @return string[] - */ - public function getSupportedFileExtensions(): array - { - return ['txt']; - } -} diff --git a/tests/Application/ApplicationFileProcessor/config/configured_rule.php b/tests/Application/ApplicationFileProcessor/config/configured_rule.php deleted file mode 100644 index 8b3054a0ea7..00000000000 --- a/tests/Application/ApplicationFileProcessor/config/configured_rule.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(TextFileProcessor::class); - - $services->set(ChangeTextRector::class); -}; diff --git a/tests/Application/VersionResolverTest.php b/tests/Application/VersionResolverTest.php new file mode 100644 index 00000000000..59230c0c7e3 --- /dev/null +++ b/tests/Application/VersionResolverTest.php @@ -0,0 +1,26 @@ +assertSame(2, substr_count($packageVersion, '.')); + return; + } + + // should be a commit hash size, as we're in untagged test + $stringLength = strlen($packageVersion); + $this->assertSame(40, $stringLength); + } +} diff --git a/tests/Autoloading/BootstrapFilesIncluderTest.php b/tests/Autoloading/BootstrapFilesIncluderTest.php new file mode 100644 index 00000000000..74222d81b67 --- /dev/null +++ b/tests/Autoloading/BootstrapFilesIncluderTest.php @@ -0,0 +1,19 @@ +make(BootstrapFilesIncluder::class); + $bootstrapFilesIncluder->includeBootstrapFiles(); + } +} diff --git a/tests/BetterPhpDocParser/PhpDoc/ArrayItemNode/ArrayItemNodeTest.php b/tests/BetterPhpDocParser/PhpDoc/ArrayItemNode/ArrayItemNodeTest.php new file mode 100644 index 00000000000..5899c3b4961 --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDoc/ArrayItemNode/ArrayItemNodeTest.php @@ -0,0 +1,106 @@ +docBlockUpdater = $this->make(DocBlockUpdater::class); + $this->phpDocInfoFactory = $this->make(PhpDocInfoFactory::class); + $this->phpDocInfoPrinter = $this->make(PhpDocInfoPrinter::class); + $this->rectorParser = $this->make(RectorParser::class); + $this->nodeScopeAndMetadataDecorator = $this->make(NodeScopeAndMetadataDecorator::class); + } + + public function testUpdateNestedClassAnnotation(): void + { + $filePath = __DIR__ . '/FixtureNested/DoctrineNestedClassAnnotation.php.inc'; + $fileContent = UtilsFileSystem::read($filePath); + + $stmtsAndTokens = $this->rectorParser->parseFileContentToStmtsAndTokens($fileContent); + $oldStmts = $stmtsAndTokens->getStmts(); + $newStmts = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($filePath, $oldStmts); + + $classStmt = null; + $classDocComment = null; + + foreach ($newStmts as $newStmt) { + if (! NodeGroup::isStmtAwareNode($newStmt)) { + continue; + } + + Assert::propertyExists($newStmt, 'stmts'); + if ($newStmt->stmts === null) { + continue; + } + + foreach ($newStmt->stmts as $stmt) { + if (! $stmt instanceof Class_) { + continue; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($stmt); + if (! $phpDocInfo instanceof PhpDocInfo) { + continue; + } + + $phpDocNode = $phpDocInfo->getPhpDocNode(); + + foreach ($phpDocNode->children as $key => $phpDocChildNode) { + $phpDocChildNode->setAttribute('start_and_end', null); + $phpDocNode->children[$key] = $phpDocChildNode; + } + + $classStmt = $stmt; + break; + } + + if (! $classStmt instanceof Class_) { + continue; + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classStmt); + $classDocComment = $this->printNodePhpDocInfoToString($classStmt); + } + + $this->assertSame( + str_replace("\r\n", "\n", '/** + * @ORM\Table(name="doctrine_entity", uniqueConstraints={@ORM\UniqueConstraint(name="property")}) + */'), + str_replace("\r\n", "\n", (string) $classDocComment) + ); + } + + private function printNodePhpDocInfoToString(Class_ $class): string + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($class); + return $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); + } +} diff --git a/tests/BetterPhpDocParser/PhpDoc/ArrayItemNode/FixtureNested/DoctrineNestedClassAnnotation.php.inc b/tests/BetterPhpDocParser/PhpDoc/ArrayItemNode/FixtureNested/DoctrineNestedClassAnnotation.php.inc new file mode 100644 index 00000000000..0e6dda52586 --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDoc/ArrayItemNode/FixtureNested/DoctrineNestedClassAnnotation.php.inc @@ -0,0 +1,24 @@ +make(DynamicSourceLocatorProvider::class); + $dynamicSourceLocatorProvider->reset(); + + $this->phpDocInfoPrinter = $this->make(PhpDocInfoPrinter::class); + $this->docBlockTagReplacer = $this->make(DocBlockTagReplacer::class); + + $phpDocInfo = $this->createPhpDocInfoFromFile(__DIR__ . '/Source/doc.txt'); + $this->assertInstanceOf(PhpDocInfo::class, $phpDocInfo); + + $this->phpDocInfo = $phpDocInfo; + } + + public function testGetTagsByName(): void + { + $paramTags = $this->phpDocInfo->getTagsByName('param'); + $this->assertCount(2, $paramTags); + } + + public function testGetVarType(): void + { + $nonExistingObjectType = new NonExistingObjectType('SomeType'); + $this->assertEquals($nonExistingObjectType, $this->phpDocInfo->getVarType()); + } + + public function testGetReturnType(): void + { + $nonExistingObjectType = new NonExistingObjectType('SomeType'); + $this->assertEquals($nonExistingObjectType, $this->phpDocInfo->getReturnType()); + } + + public function testReplaceTagByAnother(): void + { + $phpDocInfo = $this->createPhpDocInfoFromFile(__DIR__ . '/Source/test-tag.txt'); + $this->assertInstanceOf(PhpDocInfo::class, $phpDocInfo); + + $this->docBlockTagReplacer->replaceTagByAnother($phpDocInfo, 'test', 'flow'); + + $printedPhpDocInfo = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); + $printedPhpDocInfo = str_replace("\r\n", "\n", $printedPhpDocInfo); + + $fileContent = str_replace("\r\n", "\n", FileSystem::read(__DIR__ . '/Source/expected-replaced-tag.txt')); + + $this->assertSame($fileContent, $printedPhpDocInfo); + } + + public function testDoNotAddSpaseWhenAddEmptyString(): void + { + $this->phpDocInfo->addPhpDocTagNode(new PhpDocTextNode('')); + $this->phpDocInfo->addPhpDocTagNode(new PhpDocTextNode('Some text')); + + $printedPhpDocInfo = $this->phpDocInfoPrinter->printFormatPreserving($this->phpDocInfo); + $printedPhpDocInfo = str_replace("\r\n", "\n", $printedPhpDocInfo); + + $fileContent = str_replace( + "\r\n", + "\n", + FileSystem::read(__DIR__ . '/Source/expected-without-space-when-add-empty-string.txt') + ); + + $this->assertSame($fileContent, $printedPhpDocInfo); + } + + private function createPhpDocInfoFromFile(string $path): ?PhpDocInfo + { + $phpDocInfoFactory = $this->make(PhpDocInfoFactory::class); + $phpDocContent = FileSystem::read($path); + + $nop = new Nop(); + $nop->setDocComment(new Doc($phpDocContent)); + + return $phpDocInfoFactory->createFromNode($nop); + } +} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/doc.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/doc.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/doc.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/doc.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-replaced-tag.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-replaced-tag.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-replaced-tag.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-replaced-tag.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-with-replaced-type.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-with-replaced-type.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-with-replaced-type.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-with-replaced-type.txt diff --git a/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-without-space-when-add-empty-string.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-without-space-when-add-empty-string.txt new file mode 100644 index 00000000000..5dd86fc0e64 --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/expected-without-space-when-add-empty-string.txt @@ -0,0 +1,11 @@ +/** + * @var SomeType + * @return SomeType + * @param SomeType|NoSlash|\Preslashed|null|\string $value + * @param \DOMElement $node Element to parse that represents a Route + * + * @throw \Type + * @return HelperInterface[] An array of helper instances + * + * Some text + */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/test-tag.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/test-tag.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/test-tag.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfo/Source/test-tag.txt diff --git a/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/AbstractPhpDocInfoPrinterTestCase.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/AbstractPhpDocInfoPrinterTestCase.php new file mode 100644 index 00000000000..997b88b4c5c --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/AbstractPhpDocInfoPrinterTestCase.php @@ -0,0 +1,37 @@ +filePathHelper = $this->make(FilePathHelper::class); + $this->phpDocInfoFactory = $this->make(PhpDocInfoFactory::class); + $this->phpDocInfoPrinter = $this->make(PhpDocInfoPrinter::class); + } + + protected function createPhpDocInfoFromDocCommentAndNode(string $docComment, Node $node): PhpDocInfo + { + $node->setDocComment(new Doc($docComment)); + return $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + } +} diff --git a/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/DoctrineTest.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/DoctrineTest.php new file mode 100644 index 00000000000..aacecb90d2a --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/DoctrineTest.php @@ -0,0 +1,38 @@ +createPhpDocInfoFromDocCommentAndNode($docComment, $class); + + $printedPhpDocInfo = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); + $this->assertSame($docComment, $printedPhpDocInfo); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideDataClass(): Iterator + { + yield [__DIR__ . '/Source/Doctrine/index_in_table.txt', IndexInTable::class]; + yield [__DIR__ . '/Source/Doctrine/case_sensitive.txt', CaseSensitive::class]; + yield [__DIR__ . '/Source/Doctrine/short.txt', Short::class]; + } +} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/before_param_multi_indent.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/before_param_multi_indent.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/before_param_multi_indent.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/before_param_multi_indent.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/data_provider.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/data_provider.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/data_provider.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/data_provider.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc10.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc10.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc10.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc10.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc11.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc11.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc11.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc11.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc13.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc13.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc13.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc13.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc14.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc14.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc14.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc14.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc15.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc15.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc15.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc15.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc3.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc3.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc3.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc3.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc4.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc4.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc4.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc4.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc5.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc5.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc5.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc5.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc6.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc6.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc6.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc6.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc7.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc7.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc7.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc7.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc9.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc9.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc9.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/doc9.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/double_copy.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/double_copy.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/double_copy.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/double_copy.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/double_route.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/double_route.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/double_route.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/double_route.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/examples.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/examples.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/examples.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/examples.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/fullspaces_2923.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/fullspaces_2923.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/fullspaces_2923.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/fullspaces_2923.txt diff --git a/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/generic_method.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/generic_method.txt new file mode 100644 index 00000000000..9b1e3b75f8c --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/generic_method.txt @@ -0,0 +1 @@ +/** @method T foo(T $bar) */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/method_union.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/method_union.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/method_union.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/method_union.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/method_union_with_text.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/method_union_with_text.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/method_union_with_text.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/method_union_with_text.txt diff --git a/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/minimal.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/minimal.txt new file mode 100644 index 00000000000..faf504046d5 --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/minimal.txt @@ -0,0 +1,3 @@ +/** + * @foo a, + */ diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_double_spacing.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_double_spacing.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_double_spacing.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_double_spacing.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_double_spacing_with_same_type.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_double_spacing_with_same_type.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_double_spacing_with_same_type.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_double_spacing_with_same_type.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_print_spacing.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_print_spacing.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_print_spacing.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/param_print_spacing.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/psalm_return.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/psalm_return.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/psalm_return.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/psalm_return.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/psalm_return_array_shape.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/psalm_return_array_shape.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/psalm_return_array_shape.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/psalm_return_array_shape.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_union_in_brackets.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_union_in_brackets.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_union_in_brackets.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_union_in_brackets.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_union_in_brackets_alternative.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_union_in_brackets_alternative.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_union_in_brackets_alternative.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_union_in_brackets_alternative.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_with_back_and_forth_slashes.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_with_back_and_forth_slashes.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_with_back_and_forth_slashes.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/return_with_back_and_forth_slashes.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/see_multiline.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/see_multiline.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/see_multiline.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/see_multiline.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/short_right_under.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/short_right_under.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/short_right_under.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/short_right_under.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/simple_nested_array.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/simple_nested_array.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/simple_nested_array.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/simple_nested_array.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/space.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/space.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/space.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/space.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/template_as.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/template_as.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/template_as.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/template_as.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/template_of.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/template_of.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/template_of.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/template_of.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/todo.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/todo.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/todo.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/todo.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array_slashes.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array_slashes.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array_slashes.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array_slashes.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array_slashes_array_type.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array_slashes_array_type.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array_slashes_array_type.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/union_param_array_slashes_array_type.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/var_array.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/var_array.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/var_array.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/var_array.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/with_space_cr_lf.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/with_space_cr_lf.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/with_space_cr_lf.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureBasic/with_space_cr_lf.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_empty_params.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_empty_params.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_empty_params.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_empty_params.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_int.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_int.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_int.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_int.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed_mixed.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed_mixed.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed_mixed.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_mixed_mixed.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_nullable_return.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_nullable_return.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_nullable_return.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_nullable_return.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_param.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_param.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_param.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_param.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_rich.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_rich.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_rich.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_rich.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_without_types.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_without_types.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_without_types.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureCallable/callable_without_types.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureChanged/with_space.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureChanged/with_space.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureChanged/with_space.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureChanged/with_space.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureChangedExpected/with_space_expected.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureChangedExpected/with_space_expected.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureChangedExpected/with_space_expected.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureChangedExpected/with_space_expected.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureEmpty/empty-doc.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureEmpty/empty-doc.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureEmpty/empty-doc.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/FixtureEmpty/empty-doc.txt diff --git a/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/MultilineTest.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/MultilineTest.php new file mode 100644 index 00000000000..f66d28cd34e --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/MultilineTest.php @@ -0,0 +1,40 @@ +createPhpDocInfoFromDocCommentAndNode($docComment, $nop); + $printedPhpDocInfo = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); + + $this->assertSame($docComment, $printedPhpDocInfo); + } + + /** + * @return Iterator> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Source/Multiline/multiline2.txt']; + yield [__DIR__ . '/Source/Multiline/multiline3.txt']; + + yield [__DIR__ . '/Source/Class_/some_entity_class.txt']; + yield [__DIR__ . '/Source/Multiline/table.txt']; + + yield [__DIR__ . '/Source/Multiline/assert_serialize.txt']; + yield [__DIR__ . '/Source/Multiline/multiline6.txt']; + } +} diff --git a/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php new file mode 100644 index 00000000000..7f44dedee1c --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/PhpDocInfoPrinterTest.php @@ -0,0 +1,68 @@ +doComparePrintedFileEquals($docFilePath, $docFilePath); + } + + public function testRemoveSpace(): void + { + $this->doComparePrintedFileEquals( + __DIR__ . '/FixtureChanged/with_space.txt', + __DIR__ . '/FixtureChangedExpected/with_space_expected.txt' + ); + } + + public static function provideData(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/FixtureBasic', '*.txt'); + } + + public static function provideDataCallable(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/FixtureCallable', '*.txt'); + } + + #[DataProvider('provideDataEmpty')] + public function testEmpty(string $filePath): void + { + $fileContents = FileSystem::read($filePath); + + $phpDocInfo = $this->createPhpDocInfoFromDocCommentAndNode($fileContents, new Nop()); + $this->assertEmpty($this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo)); + } + + public static function provideDataEmpty(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/FixtureEmpty', '*.txt'); + } + + private function doComparePrintedFileEquals(string $inputFilePath, string $expectedFilePath): void + { + $inputFileContents = FileSystem::read($inputFilePath); + $expectedFileContents = FileSystem::read($expectedFilePath); + + $phpDocInfo = $this->createPhpDocInfoFromDocCommentAndNode($inputFileContents, new Nop()); + $printedDocComment = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); + + $filePathHelper = $this->make(FilePathHelper::class); + $relativeInputFilePath = $filePathHelper->relativePath($inputFilePath); + + $this->assertSame($expectedFileContents, $printedDocComment, $relativeInputFilePath); + } +} diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/AnotherPropertyClass.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/AnotherPropertyClass.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/AnotherPropertyClass.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/AnotherPropertyClass.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/SomeEntityClass.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/SomeEntityClass.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/SomeEntityClass.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/SomeEntityClass.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/expected_some_entity_class.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/expected_some_entity_class.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/expected_some_entity_class.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/expected_some_entity_class.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/some_entity_class.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/some_entity_class.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/some_entity_class.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Class_/some_entity_class.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Collection.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Collection.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Collection.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Collection.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/CaseSensitive.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/CaseSensitive.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/CaseSensitive.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/CaseSensitive.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/IndexInTable.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/IndexInTable.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/IndexInTable.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/IndexInTable.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/Short.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/Short.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/Short.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/Short.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/case_sensitive.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/case_sensitive.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/case_sensitive.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/case_sensitive.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/index_in_table.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/index_in_table.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/index_in_table.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/index_in_table.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/short.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/short.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/short.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Doctrine/short.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/DoctrinePropertyClass.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/DoctrinePropertyClass.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/DoctrinePropertyClass.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/DoctrinePropertyClass.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/ManyToPropertyClass.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/ManyToPropertyClass.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/ManyToPropertyClass.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/ManyToPropertyClass.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_after.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_after.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_after.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_after.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_single_line_after.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_single_line_after.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_single_line_after.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/assert_serialize_single_line_after.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline2.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline2.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline2.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline2.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline3.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline3.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline3.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline3.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline6.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline6.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline6.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/multiline6.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/table.txt b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/table.txt similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/table.txt rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/Multiline/table.txt diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/RoutePropertyClass.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/RoutePropertyClass.php similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/RoutePropertyClass.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/RoutePropertyClass.php index 62432544286..1fdcefc6e31 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/RoutePropertyClass.php +++ b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/RoutePropertyClass.php @@ -6,7 +6,7 @@ use Symfony\Component\Routing\Annotation\Route; -class RoutePropertyClass +final class RoutePropertyClass { /** * @Route( diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/SinglePropertyClass.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/SinglePropertyClass.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/SinglePropertyClass.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/SinglePropertyClass.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/TableClass.php b/tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/TableClass.php similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/TableClass.php rename to tests/BetterPhpDocParser/PhpDocInfo/PhpDocInfoPrinter/Source/TableClass.php diff --git a/packages-tests/BetterPhpDocParser/PhpDocNodeMapperTest.php b/tests/BetterPhpDocParser/PhpDocNodeMapperTest.php similarity index 77% rename from packages-tests/BetterPhpDocParser/PhpDocNodeMapperTest.php rename to tests/BetterPhpDocParser/PhpDocNodeMapperTest.php index d8fe5eb67ba..fa0092472e7 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocNodeMapperTest.php +++ b/tests/BetterPhpDocParser/PhpDocNodeMapperTest.php @@ -11,17 +11,17 @@ use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode; use Rector\BetterPhpDocParser\PhpDocNodeMapper; use Rector\BetterPhpDocParser\ValueObject\Parser\BetterTokenIterator; -use Rector\BetterPhpDocParser\ValueObject\PhpDoc\VariadicAwareParamTagValueNode; -use Rector\Testing\PHPUnit\AbstractTestCase; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class PhpDocNodeMapperTest extends AbstractTestCase +final class PhpDocNodeMapperTest extends AbstractLazyTestCase { private PhpDocNodeMapper $phpDocNodeMapper; protected function setUp(): void { - $this->boot(); - $this->phpDocNodeMapper = $this->getService(PhpDocNodeMapper::class); + parent::setUp(); + + $this->phpDocNodeMapper = $this->make(PhpDocNodeMapper::class); } public function testParamTag(): void @@ -36,7 +36,7 @@ public function testParamTag(): void // test param tag /** @var PhpDocTagNode $childNode */ $propertyTagValueNode = $childNode->value; - $this->assertInstanceOf(VariadicAwareParamTagValueNode::class, $propertyTagValueNode); + $this->assertInstanceOf(ParamTagValueNode::class, $propertyTagValueNode); } /** @@ -46,7 +46,7 @@ public function testParamTag(): void private function createParamDocNode(): PhpDocNode { $nullableTypeNode = new NullableTypeNode(new IdentifierTypeNode('string')); - $paramTagValueNode = new ParamTagValueNode($nullableTypeNode, true, 'name', ''); + $paramTagValueNode = new ParamTagValueNode($nullableTypeNode, true, 'name', '', false); $children = [new PhpDocTagNode('@param', $paramTagValueNode)]; diff --git a/tests/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher/Fixture/ExistingClass/SiblingClass.php b/tests/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher/Fixture/ExistingClass/SiblingClass.php new file mode 100644 index 00000000000..f75aee8b099 --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher/Fixture/ExistingClass/SiblingClass.php @@ -0,0 +1,8 @@ +classAnnotationMatcher = $this->make(ClassAnnotationMatcher::class); + $this->testingParser = $this->make(TestingParser::class); + $this->betterNodeFinder = $this->make(BetterNodeFinder::class); + $this->phpDocInfoFactory = $this->make(PhpDocInfoFactory::class); + $this->nodeNameResolver = $this->make(NodeNameResolver::class); + } + + #[DataProvider('provideData')] + public function testResolvesClass(string $filePath): void + { + $nodes = $this->testingParser->parseFileToDecoratedNodes($filePath); + $properties = $this->betterNodeFinder->findInstancesOf($nodes, [Property::class]); + + foreach ($properties as $property) { + /** @var Property $property */ + $phpDoc = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + $varTagValueNode = $phpDoc->getVarTagValueNode(); + $this->assertInstanceOf(VarTagValueNode::class, $varTagValueNode); + + /** @var non-empty-string $value */ + $value = $varTagValueNode->type->__toString(); + $propertyName = strtolower($this->nodeNameResolver->getName($property)); + + $result = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($value, $property); + if (str_starts_with($propertyName, 'unknown')) { + $this->assertStringContainsString('Unknown', $result); + } elseif (str_contains($propertyName, 'aliased')) { + $unaliasedClass = str_replace('Aliased', '', $value); + Assert::notEmpty($unaliasedClass); + + $this->assertStringEndsWith($unaliasedClass, $result); + } elseif (str_starts_with($propertyName, 'known')) { + $this->assertStringEndsWith($value, $result); + } else { + throw new ShouldNotHappenException('All Variables should start with "known" or "unknown"!'); + } + } + } + + public static function provideData(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/Fixture'); + } +} diff --git a/tests/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher/Source/KnownClass.php b/tests/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher/Source/KnownClass.php new file mode 100644 index 00000000000..7f344677ac8 --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher/Source/KnownClass.php @@ -0,0 +1,7 @@ +arrayParser = $this->make(ArrayParser::class); + $this->tokenIteratorFactory = $this->make(TokenIteratorFactory::class); + } + + /** + * @param ArrayItemNode[] $expectedArrayItemNodes + */ + #[DataProvider('provideData')] + public function test(string $docContent, array $expectedArrayItemNodes): void + { + $betterTokenIterator = $this->tokenIteratorFactory->create($docContent); + + $string = new String_('some_node'); + + $arrayItemNodes = $this->arrayParser->parseCurlyArray($betterTokenIterator, $string); + $this->assertEquals($expectedArrayItemNodes, $arrayItemNodes); + } + + /** + * @return Iterator|string)>> + */ + public static function provideData(): Iterator + { + yield ['{key: "value"}', [new ArrayItemNode(new StringNode('value'), 'key')]]; + + yield ['{"key": "value"}', [new ArrayItemNode(new StringNode('value'), new StringNode('key'))]]; + + yield ['{"value", "value2"}', [ + new ArrayItemNode(new StringNode('value')), + new ArrayItemNode(new StringNode('value2')), + ]]; + } +} diff --git a/tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/StaticDoctrineAnnotationParserTest.php b/tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/StaticDoctrineAnnotationParserTest.php new file mode 100644 index 00000000000..fd7c40b3f7c --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser/StaticDoctrineAnnotationParserTest.php @@ -0,0 +1,63 @@ +tokenIteratorFactory = $this->make(TokenIteratorFactory::class); + $this->staticDoctrineAnnotationParser = $this->make(StaticDoctrineAnnotationParser::class); + } + + /** + * @param CurlyListNode|array $expectedValue + */ + #[DataProvider('provideData')] + public function test(string $docContent, CurlyListNode | array $expectedValue): void + { + $betterTokenIterator = $this->tokenIteratorFactory->create($docContent); + + $string = new String_('some_node'); + $value = $this->staticDoctrineAnnotationParser->resolveAnnotationValue($betterTokenIterator, $string); + + // "equals" on purpose to compare 2 object with same content + $this->assertEquals($expectedValue, $value); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + $curlyListNode = new CurlyListNode([ + new ArrayItemNode(new StringNode('chalet')), + new ArrayItemNode(new StringNode('apartment')), + ]); + yield ['{"chalet", "apartment"}', $curlyListNode]; + + yield [ + 'key={"chalet", "apartment"}', [ + 'key' => $curlyListNode, + ], + ]; + } +} diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnEntityWithAnEmbedded.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnEntityWithAnEmbedded.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnEntityWithAnEmbedded.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnEntityWithAnEmbedded.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnEntityWithAnEmbeddedAndAColumnPrefix.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnEntityWithAnEmbeddedAndAColumnPrefix.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnEntityWithAnEmbeddedAndAColumnPrefix.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnEntityWithAnEmbeddedAndAColumnPrefix.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithComma.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithComma.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithComma.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithComma.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithCommaAndAnnotation.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithCommaAndAnnotation.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithCommaAndAnnotation.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithCommaAndAnnotation.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithCommaAndAnnotationWithoutSpaces.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithCommaAndAnnotationWithoutSpaces.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithCommaAndAnnotationWithoutSpaces.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AnnotationWithCommaAndAnnotationWithoutSpaces.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertArrayType.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertArrayType.php.inc similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertArrayType.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertArrayType.php.inc index f9201b6bfcc..f5489f4fd86 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertArrayType.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertArrayType.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertType; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoice.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoice.php.inc similarity index 94% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoice.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoice.php.inc index f9c3bd67a94..96e1c724969 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoice.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoice.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertChoice; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceNonQuoteValues.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceNonQuoteValues.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceNonQuoteValues.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceNonQuoteValues.php.inc index 14ca181f109..9e9304ab36f 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceNonQuoteValues.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceNonQuoteValues.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertChoice; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceQuoteValues.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceQuoteValues.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceQuoteValues.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceQuoteValues.php.inc index 2483f9d2b8d..f27358eeb73 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceQuoteValues.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceQuoteValues.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertChoice; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithCeroOnOptions.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithCeroOnOptions.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithCeroOnOptions.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithCeroOnOptions.php.inc index c11119f089f..059053eebe6 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithCeroOnOptions.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithCeroOnOptions.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertChoice; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithManyGroups.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithManyGroups.php.inc similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithManyGroups.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithManyGroups.php.inc index 9f339038477..6fc49e93868 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithManyGroups.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithManyGroups.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertChoice; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithMessage.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithMessage.php.inc similarity index 93% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithMessage.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithMessage.php.inc index a3c2009a3ae..b6310e90ad1 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithMessage.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertChoiceWithMessage.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertChoice; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertQuoteChoice.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertQuoteChoice.php.inc similarity index 93% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertQuoteChoice.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertQuoteChoice.php.inc index 25898760342..21ded2b155f 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertQuoteChoice.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertQuoteChoice.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertChoice; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringQuotedType.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringQuotedType.php.inc similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringQuotedType.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringQuotedType.php.inc index 1d5991cd993..bd00398756a 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringQuotedType.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringQuotedType.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertType; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringType.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringType.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringType.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringType.php.inc index 7f5d17c9c4d..c94809b73fd 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringType.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertStringType.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertType; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertType.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertType.php.inc similarity index 93% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertType.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertType.php.inc index eb5b37ebf56..8be443a9d3d 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertType.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertType.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertType; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Doctrine\Common\Collections\Collection; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertTypeWithMessage.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertTypeWithMessage.php.inc similarity index 93% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertTypeWithMessage.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertTypeWithMessage.php.inc index 587e6f433aa..e00ac55f604 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertTypeWithMessage.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/AssertTypeWithMessage.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\AssertType; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Validator\Constraints as Assert; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/BlameableTag.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/BlameableTag.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/BlameableTag.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/BlameableTag.php.inc index d25c4a27441..d32234f61f4 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/BlameableTag.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/BlameableTag.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\Blameable; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Gedmo\Mapping\Annotation as Gedmo; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/Book.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/Book.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/Book.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/Book.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/ConstantTable.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/ConstantTable.php.inc similarity index 90% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/ConstantTable.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/ConstantTable.php.inc index 21e8428bcb7..e97420240a6 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/ConstantTable.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/ConstantTable.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\DoctrineTable; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Doctrine\ORM\Mapping as ORM; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/CustomIdGenerator.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/CustomIdGenerator.php.inc similarity index 87% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/CustomIdGenerator.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/CustomIdGenerator.php.inc index 5a30242de45..0f786d33413 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/CustomIdGenerator.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/CustomIdGenerator.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\DoctrineCustomIdGenerator; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Doctrine\ORM\Mapping as ORM; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/DoctrineColumnWithColon.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/DoctrineColumnWithColon.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/DoctrineColumnWithColon.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/DoctrineColumnWithColon.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/DoctrineColumnWithEqual.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/DoctrineColumnWithEqual.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/DoctrineColumnWithEqual.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/DoctrineColumnWithEqual.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/EntityRepositoryConstant.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/EntityRepositoryConstant.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/EntityRepositoryConstant.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/EntityRepositoryConstant.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/FromOfficialDocs.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/FromOfficialDocs.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/FromOfficialDocs.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/FromOfficialDocs.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValue.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValue.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValue.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValue.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategy.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategy.php.inc similarity index 90% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategy.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategy.php.inc index d58bf15ff52..b72faaeefc9 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategy.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategy.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\DoctrineGeneratedValue; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Doctrine\ORM\Mapping as ORM; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategyString.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategyString.php.inc similarity index 88% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategyString.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategyString.php.inc index 6a1073b4f73..ab869fb8b23 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategyString.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/GeneratedValueWithStrategyString.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\DoctrineGeneratedValue; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Doctrine\ORM\Mapping as ORM; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/InlinedColumn.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/InlinedColumn.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/InlinedColumn.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/InlinedColumn.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/Mixture.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/Mixture.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/Mixture.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/Mixture.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/PropertyWithName.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/PropertyWithName.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/PropertyWithName.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/PropertyWithName.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/QuotedKeysAndValues.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/QuotedKeysAndValues.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/QuotedKeysAndValues.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/QuotedKeysAndValues.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/QuotesInNestedArray.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/QuotesInNestedArray.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/QuotesInNestedArray.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/QuotesInNestedArray.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteName.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteName.php.inc similarity index 93% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteName.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteName.php.inc index c722a068333..0bd2894ba35 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteName.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteName.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SymfonyRoute; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Routing\Annotation\Route; use Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Source\TestController; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteNameWithMethodAndClassConstant.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteNameWithMethodAndClassConstant.php.inc similarity index 93% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteNameWithMethodAndClassConstant.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteNameWithMethodAndClassConstant.php.inc index 82b487c3588..4c7f370f6e7 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteNameWithMethodAndClassConstant.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteNameWithMethodAndClassConstant.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SymfonyRoute; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Routing\Annotation\Route; use Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Source\MyController; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteSomeClassMethod.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteSomeClassMethod.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteSomeClassMethod.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteSomeClassMethod.php.inc index afd5e3ef9d7..d4ae4e59f5a 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteSomeClassMethod.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteSomeClassMethod.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SymfonyRoute; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Routing\Annotation\Route; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithCondition.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithCondition.php.inc similarity index 94% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithCondition.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithCondition.php.inc index 3699017a2ef..74594f229d7 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithCondition.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithCondition.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SymfonyRoute; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Routing\Annotation\Route; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithExtraNewline.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithExtraNewline.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithExtraNewline.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithExtraNewline.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHost.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHost.php.inc similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHost.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHost.php.inc index 75ef716a2c9..fb81c9ac5b8 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHost.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHost.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SymfonyRoute; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Routing\Annotation\Route; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHostWithPath.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHostWithPath.php.inc similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHostWithPath.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHostWithPath.php.inc index 25cfa1853c0..11613a4068e 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHostWithPath.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithHostWithPath.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SymfonyRoute; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Routing\Annotation\Route; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithPrefixAndEmptyName.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithPrefixAndEmptyName.php.inc similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithPrefixAndEmptyName.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithPrefixAndEmptyName.php.inc index beeac24969b..fad302487ef 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithPrefixAndEmptyName.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithPrefixAndEmptyName.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SymfonyRoute; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Symfony\Component\Routing\Annotation\Route; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithSpacesOnItem.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithSpacesOnItem.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithSpacesOnItem.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/RouteWithSpacesOnItem.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioMethodSingle.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioMethodSingle.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioMethodSingle.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioMethodSingle.php.inc index 87805e04947..39341173e52 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioMethodSingle.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioMethodSingle.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SensioMethod; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioTemplateSimilarToConstant.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioTemplateSimilarToConstant.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioTemplateSimilarToConstant.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioTemplateSimilarToConstant.php.inc index 4b375e7e482..184b7f18f93 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioTemplateSimilarToConstant.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SensioTemplateSimilarToConstant.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\SensioTemplate; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeClassMethod.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeClassMethod.php.inc similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeClassMethod.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeClassMethod.php.inc index 5338b487857..0e80e9b75ea 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeClassMethod.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeClassMethod.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\Gedmo\Slug; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Gedmo\Mapping\Annotation as Gedmo; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntity.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntity.php.inc similarity index 92% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntity.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntity.php.inc index 53cf835f44a..6f0ca63fa9d 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntity.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntity.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\DoctrineEntity; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Doctrine\ORM\Mapping as ORM; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntityBrackets.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntityBrackets.php.inc similarity index 88% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntityBrackets.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntityBrackets.php.inc index 414e736a899..0f247afe408 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntityBrackets.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntityBrackets.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\DoctrineEntity; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; use Doctrine\ORM\Mapping as ORM; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntitySimple.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntitySimple.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntitySimple.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeEntitySimple.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeProperty.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeProperty.php.inc similarity index 100% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeProperty.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/Fixture/SomeProperty.php.inc diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureModify/route_with_extra_methods.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureModify/route_with_extra_methods.php.inc similarity index 94% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureModify/route_with_extra_methods.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureModify/route_with_extra_methods.php.inc index 00f4aaf4598..70833bcdd64 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureModify/route_with_extra_methods.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureModify/route_with_extra_methods.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\FixtureModif; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\FixtureModify; use Symfony\Component\Routing\Annotation\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/FormattingDoctrineEntity.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/FormattingDoctrineEntity.php.inc similarity index 91% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/FormattingDoctrineEntity.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/FormattingDoctrineEntity.php.inc index c5da1e79d0d..e2557ead72e 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/FormattingDoctrineEntity.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/FormattingDoctrineEntity.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture\DoctrineTable; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\FixtureNested; use Doctrine\ORM\Mapping as ORM; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTable.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTable.php.inc similarity index 95% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTable.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTable.php.inc index bde92978981..5cf482ef35f 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTable.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTable.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\Fixture; +namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\FixtureNested; use Doctrine\ORM\Mapping as ORM; diff --git a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTableWithJoinColumns.php.inc b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTableWithJoinColumns.php.inc similarity index 79% rename from packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTableWithJoinColumns.php.inc rename to tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTableWithJoinColumns.php.inc index 0231ff23d82..80d424f0737 100644 --- a/packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTableWithJoinColumns.php.inc +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/FixtureNested/JoinTableWithJoinColumns.php.inc @@ -1,5 +1,7 @@ testingParser = $this->make(TestingParser::class); + $this->filePathHelper = $this->make(FilePathHelper::class); + $this->betterNodeFinder = $this->make(BetterNodeFinder::class); + $this->phpDocInfoPrinter = $this->make(PhpDocInfoPrinter::class); + $this->phpDocInfoFactory = $this->make(PhpDocInfoFactory::class); + } + + #[DataProvider('provideData')] + #[DataProvider('provideDataNested')] + public function test(string $filePath): void + { + [$fileContents, $nodeClass, $tagValueNodeClasses] = FixtureSplitter::split($filePath); + + /** @var class-string $nodeClass */ + $nodeClass = trim($nodeClass); + $tagValueNodeClasses = $this->splitListByEOL($tagValueNodeClasses); + + $fixtureFilePath = FixtureTempFileDumper::dump($fileContents); + + foreach ($tagValueNodeClasses as $tagValueNodeClass) { + $this->doTestPrintedPhpDocInfo($fixtureFilePath, $tagValueNodeClass, $nodeClass); + } + + FileSystem::delete($fixtureFilePath); + } + + public static function provideData(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/Fixture'); + } + + public static function provideDataNested(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/FixtureNested'); + } + + /** + * @param class-string $annotationClass + * @param class-string $nodeClass + */ + private function doTestPrintedPhpDocInfo(string $filePath, string $annotationClass, string $nodeClass): void + { + $nodeWithPhpDocInfo = $this->parseFileAndGetFirstNodeOfType($filePath, $nodeClass); + + $docComment = $nodeWithPhpDocInfo->getDocComment(); + if (! $docComment instanceof Doc) { + throw new ShouldNotHappenException(sprintf( + 'Doc comments for "%s" file cannot not be empty', + $filePath + )); + } + + $originalDocCommentText = $docComment->getText(); + $printedPhpDocInfo = $this->printNodePhpDocInfoToString($nodeWithPhpDocInfo); + + $this->assertSame($originalDocCommentText, $printedPhpDocInfo); + $this->doTestContainsTagValueNodeType($nodeWithPhpDocInfo, $annotationClass, $filePath); + } + + /** + * @return string[] + */ + private function splitListByEOL(string $content): array + { + $trimmedContent = trim($content); + return explode("\n", $trimmedContent); + } + + /** + * @template TNode as Node + * + * @param class-string $nodeType + * @return TNode + */ + private function parseFileAndGetFirstNodeOfType(string $filePath, string $nodeType): Node + { + $nodes = $this->testingParser->parseFileToDecoratedNodes($filePath); + + $node = $this->betterNodeFinder->findFirstInstanceOf($nodes, $nodeType); + if (! $node instanceof Node) { + throw new ShouldNotHappenException($filePath); + } + + return $node; + } + + private function printNodePhpDocInfoToString(Node $node): string + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + return $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); + } + + /** + * @param class-string $annotationClass + */ + private function doTestContainsTagValueNodeType(Node $node, string $annotationClass, string $filePath): void + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $hasByAnnotationClass = $phpDocInfo->hasByAnnotationClass($annotationClass); + + $relativeFilePath = $this->filePathHelper->relativePath($filePath); + $this->assertTrue($hasByAnnotationClass, $relativeFilePath); + } +} diff --git a/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TestModifyReprintTest.php b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TestModifyReprintTest.php new file mode 100644 index 00000000000..680bc8729bb --- /dev/null +++ b/tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TestModifyReprintTest.php @@ -0,0 +1,87 @@ +testingParser = $this->make(TestingParser::class); + $this->betterNodeFinder = $this->make(BetterNodeFinder::class); + $this->phpDocInfoPrinter = $this->make(PhpDocInfoPrinter::class); + $this->phpDocInfoFactory = $this->make(PhpDocInfoFactory::class); + } + + public function test(): void + { + [$inputContent, $expectedContent] = FixtureSplitter::split( + __DIR__ . '/FixtureModify/route_with_extra_methods.php.inc' + ); + + $phpDocInfo = $this->parseFileAndGetFirstNodeOfType($inputContent, ClassMethod::class); + + $doctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass( + 'Symfony\Component\Routing\Annotation\Route' + ); + $this->assertInstanceOf(DoctrineAnnotationTagValueNode::class, $doctrineAnnotationTagValueNode); + + // this will extended tokens of first node + $methodsCurlyListNode = new CurlyListNode([ + new ArrayItemNode(new StringNode('GET')), + new ArrayItemNode(new StringNode('HEAD')), + ]); + $doctrineAnnotationTagValueNode->values[] = new ArrayItemNode($methodsCurlyListNode, 'methods'); + + $expectedDocContent = trim($expectedContent); + + $printedPhpDocInfo = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo); + $this->assertSame($expectedDocContent, $printedPhpDocInfo); + } + + /** + * @param class-string $nodeType + */ + private function parseFileAndGetFirstNodeOfType(string $fileContents, string $nodeType): PhpDocInfo + { + $fixtureFilePath = FixtureTempFileDumper::dump($fileContents); + $nodes = $this->testingParser->parseFileToDecoratedNodes($fixtureFilePath); + + FileSystem::delete($fixtureFilePath); + + $node = $this->betterNodeFinder->findFirstInstanceOf($nodes, $nodeType); + if (! $node instanceof Node) { + throw new ShouldNotHappenException($fileContents); + } + + return $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + } +} diff --git a/tests/Bridge/Fixture/some-composer.json b/tests/Bridge/Fixture/some-composer.json new file mode 100644 index 00000000000..7f681ca9f0d --- /dev/null +++ b/tests/Bridge/Fixture/some-composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "php": "^7.3" + } +} diff --git a/tests/Bridge/SetRectorsResolverTest.php b/tests/Bridge/SetRectorsResolverTest.php new file mode 100644 index 00000000000..5aed8f87991 --- /dev/null +++ b/tests/Bridge/SetRectorsResolverTest.php @@ -0,0 +1,74 @@ +setRectorsResolver = new SetRectorsResolver(); + } + + public function testResolveFromFilePathForPhpVersion(): void + { + $configFilePaths = PhpLevelSetResolver::resolveFromPhpVersion(PhpVersion::PHP_70); + $this->assertCount(6, $configFilePaths); + $this->assertContainsOnlyString($configFilePaths); + + foreach ($configFilePaths as $configFilePath) { + $this->assertFileExists($configFilePath); + } + } + + public function testResolveFromFilePathForPhpLevel(): void + { + $projectPhpVersion = ComposerJsonPhpVersionResolver::resolve(__DIR__ . '/Fixture/some-composer.json'); + + $this->assertIsInt($projectPhpVersion); + $this->assertSame(PhpVersion::PHP_73, $projectPhpVersion); + + $configFilePaths = PhpLevelSetResolver::resolveFromPhpVersion($projectPhpVersion); + $this->assertCount(9, $configFilePaths); + + $rectorRulesWithConfiguration = $this->setRectorsResolver->resolveFromFilePathsIncludingConfiguration( + $configFilePaths + ); + $this->assertCount(62, $rectorRulesWithConfiguration); + } + + public function testResolveWithConfiguration(): void + { + $rectorRulesWithConfiguration = $this->setRectorsResolver->resolveFromFilePathIncludingConfiguration( + SetList::PHP_73 + ); + $this->assertCount(9, $rectorRulesWithConfiguration); + + $this->assertArrayHasKey(0, $rectorRulesWithConfiguration); + $this->assertArrayHasKey(8, $rectorRulesWithConfiguration); + + foreach ($rectorRulesWithConfiguration as $rectorRuleWithConfiguration) { + if (is_string($rectorRuleWithConfiguration)) { + $this->assertTrue(is_a($rectorRuleWithConfiguration, RectorInterface::class, true)); + } + + if (is_array($rectorRuleWithConfiguration)) { + foreach ($rectorRuleWithConfiguration as $rectorRule => $rectorRuleConfiguration) { + $this->assertTrue(is_a($rectorRule, RectorInterface::class, true)); + $this->assertIsArray($rectorRuleConfiguration); + } + } + } + } +} diff --git a/tests/Caching/Config/FileHashComputer/FileHashComputerTest.php b/tests/Caching/Config/FileHashComputer/FileHashComputerTest.php new file mode 100644 index 00000000000..2d06d77d60b --- /dev/null +++ b/tests/Caching/Config/FileHashComputer/FileHashComputerTest.php @@ -0,0 +1,64 @@ +fileHashComputer = $this->make(FileHashComputer::class); + } + + public function testRectorPhpChanged(): void + { + SimpleParameterProvider::setParameter(Option::REGISTERED_RECTOR_RULES, null); + + $this->bootFromConfigFiles([__DIR__ . '/Fixture/rector.php']); + + $hashedFile = $this->fileHashComputer->compute(__DIR__ . '/Fixture/rector.php'); + + copy(__DIR__ . '/Fixture/rector.php', __DIR__ . '/Fixture/rector_temp.php'); + copy(__DIR__ . '/Fixture/updated_rector_rule.php', __DIR__ . '/Fixture/rector.php'); + + SimpleParameterProvider::setParameter(Option::REGISTERED_RECTOR_RULES, null); + + $this->bootFromConfigFiles([__DIR__ . '/Fixture/rector.php']); + + $newHashedFile = $this->fileHashComputer->compute(__DIR__ . '/Fixture/rector.php'); + rename(__DIR__ . '/Fixture/rector_temp.php', __DIR__ . '/Fixture/rector.php'); + + $this->assertNotSame($newHashedFile, $hashedFile); + } + + public function testRectorPhpNotChanged(): void + { + SimpleParameterProvider::setParameter(Option::REGISTERED_RECTOR_RULES, null); + + $this->bootFromConfigFiles([__DIR__ . '/Fixture/rector.php']); + + $hashedFile = $this->fileHashComputer->compute(__DIR__ . '/Fixture/rector.php'); + + copy(__DIR__ . '/Fixture/rector.php', __DIR__ . '/Fixture/rector_temp_equal.php'); + copy(__DIR__ . '/Fixture/rector_rule_equals.php', __DIR__ . '/Fixture/rector.php'); + + SimpleParameterProvider::setParameter(Option::REGISTERED_RECTOR_RULES, null); + + $this->bootFromConfigFiles([__DIR__ . '/Fixture/rector.php']); + + $newHashedFile = $this->fileHashComputer->compute(__DIR__ . '/Fixture/rector.php'); + rename(__DIR__ . '/Fixture/rector_temp_equal.php', __DIR__ . '/Fixture/rector.php'); + + $this->assertSame($newHashedFile, $hashedFile); + } +} diff --git a/tests/Caching/Config/FileHashComputer/Fixture/rector.php b/tests/Caching/Config/FileHashComputer/Fixture/rector.php new file mode 100644 index 00000000000..76975c2f816 --- /dev/null +++ b/tests/Caching/Config/FileHashComputer/Fixture/rector.php @@ -0,0 +1,10 @@ +rules([DeclareStrictTypesRector::class]); +}; diff --git a/tests/Caching/Config/FileHashComputer/Fixture/rector_rule_equals.php b/tests/Caching/Config/FileHashComputer/Fixture/rector_rule_equals.php new file mode 100644 index 00000000000..545b045cddc --- /dev/null +++ b/tests/Caching/Config/FileHashComputer/Fixture/rector_rule_equals.php @@ -0,0 +1,15 @@ +rules([ + + // only spaced/comment added, no need to clear cache + DeclareStrictTypesRector::class + + ]); +}; diff --git a/tests/Caching/Config/FileHashComputer/Fixture/updated_rector_rule.php b/tests/Caching/Config/FileHashComputer/Fixture/updated_rector_rule.php new file mode 100644 index 00000000000..06f08d7ead7 --- /dev/null +++ b/tests/Caching/Config/FileHashComputer/Fixture/updated_rector_rule.php @@ -0,0 +1,14 @@ +rules([ + DeclareStrictTypesRector::class, + RemoveDeadStmtRector::class, + ]); +}; diff --git a/packages-tests/Caching/Config/Source/Import/import_a.yaml b/tests/Caching/Config/Source/Import/import_a.yaml similarity index 100% rename from packages-tests/Caching/Config/Source/Import/import_a.yaml rename to tests/Caching/Config/Source/Import/import_a.yaml diff --git a/packages-tests/Caching/Config/Source/Import/import_b.yaml b/tests/Caching/Config/Source/Import/import_b.yaml similarity index 100% rename from packages-tests/Caching/Config/Source/Import/import_b.yaml rename to tests/Caching/Config/Source/Import/import_b.yaml diff --git a/packages-tests/Caching/Config/Source/Import/imported_file_a.yaml b/tests/Caching/Config/Source/Import/imported_file_a.yaml similarity index 100% rename from packages-tests/Caching/Config/Source/Import/imported_file_a.yaml rename to tests/Caching/Config/Source/Import/imported_file_a.yaml diff --git a/packages-tests/Caching/Config/Source/Import/imported_file_b.yaml b/tests/Caching/Config/Source/Import/imported_file_b.yaml similarity index 100% rename from packages-tests/Caching/Config/Source/Import/imported_file_b.yaml rename to tests/Caching/Config/Source/Import/imported_file_b.yaml diff --git a/packages-tests/Caching/Config/Source/config_content_a.yaml b/tests/Caching/Config/Source/config_content_a.yaml similarity index 100% rename from packages-tests/Caching/Config/Source/config_content_a.yaml rename to tests/Caching/Config/Source/config_content_a.yaml diff --git a/packages-tests/Caching/Config/Source/config_content_b.yaml b/tests/Caching/Config/Source/config_content_b.yaml similarity index 100% rename from packages-tests/Caching/Config/Source/config_content_b.yaml rename to tests/Caching/Config/Source/config_content_b.yaml diff --git a/packages-tests/Caching/Config/Source/file.php b/tests/Caching/Config/Source/file.php similarity index 100% rename from packages-tests/Caching/Config/Source/file.php rename to tests/Caching/Config/Source/file.php diff --git a/packages-tests/Caching/Config/Source/file.xml b/tests/Caching/Config/Source/file.xml similarity index 100% rename from packages-tests/Caching/Config/Source/file.xml rename to tests/Caching/Config/Source/file.xml diff --git a/tests/Caching/Detector/ChangedFilesDetectorTest.php b/tests/Caching/Detector/ChangedFilesDetectorTest.php new file mode 100644 index 00000000000..dfc95a3176a --- /dev/null +++ b/tests/Caching/Detector/ChangedFilesDetectorTest.php @@ -0,0 +1,44 @@ +changedFilesDetector = $this->make(ChangedFilesDetector::class); + } + + protected function tearDown(): void + { + $this->changedFilesDetector->clear(); + } + + public function testHasFileChanged(): void + { + $filePath = __DIR__ . '/Source/file.php'; + + $this->assertTrue($this->changedFilesDetector->hasFileChanged($filePath)); + $this->changedFilesDetector->addCacheableFile($filePath); + $this->changedFilesDetector->cacheFile($filePath); + + $this->assertFalse($this->changedFilesDetector->hasFileChanged($filePath)); + $this->changedFilesDetector->invalidateFile($filePath); + + $this->assertTrue($this->changedFilesDetector->hasFileChanged($filePath)); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config.php'; + } +} diff --git a/packages-tests/Caching/Detector/Source/file.php b/tests/Caching/Detector/Source/file.php similarity index 100% rename from packages-tests/Caching/Detector/Source/file.php rename to tests/Caching/Detector/Source/file.php diff --git a/packages-tests/Caching/Detector/Source/file2.php b/tests/Caching/Detector/Source/file2.php similarity index 100% rename from packages-tests/Caching/Detector/Source/file2.php rename to tests/Caching/Detector/Source/file2.php diff --git a/packages-tests/Caching/Detector/Source/file3.php b/tests/Caching/Detector/Source/file3.php similarity index 100% rename from packages-tests/Caching/Detector/Source/file3.php rename to tests/Caching/Detector/Source/file3.php diff --git a/tests/Caching/Detector/config.php b/tests/Caching/Detector/config.php new file mode 100644 index 00000000000..5e535c343a8 --- /dev/null +++ b/tests/Caching/Detector/config.php @@ -0,0 +1,11 @@ +cacheDirectory(sys_get_temp_dir() . '/_rector_cached_files_test'); + $rectorConfig->cacheClass(MemoryCacheStorage::class); +}; diff --git a/tests/Caching/ValueObject/Storage/FileCacheStorageTest.php b/tests/Caching/ValueObject/Storage/FileCacheStorageTest.php new file mode 100644 index 00000000000..bfb4281c216 --- /dev/null +++ b/tests/Caching/ValueObject/Storage/FileCacheStorageTest.php @@ -0,0 +1,56 @@ +fileCacheStorage = new FileCacheStorage(__DIR__ . '/Source', new Filesystem()); + } + + #[DoesNotPerformAssertions] + public function testCleanNonExistingFile(): void + { + $this->fileCacheStorage->clean('inexistant/file'); + } + + public function testClean(): void + { + $this->fileCacheStorage->save('aaK1STfY', 'TEST', 'file cached'); + $file1 = __DIR__ . '/Source/0e/76/0e76658526655756207688271159624026011393.php'; + + $this->fileCacheStorage->save('aaO8zKZF', 'TEST', 'file cached with the same two first characters'); + $file2 = __DIR__ . '/Source/0e/89/0e89257456677279068558073954252716165668.php'; + + $this->fileCacheStorage->clean('aaK1STfY'); + + $this->assertFileDoesNotExist($file1); + $this->assertDirectoryDoesNotExist(__DIR__ . '/Source/0e/76'); + + $this->assertFileExists($file2); + $this->assertDirectoryExists(__DIR__ . '/Source/0e/89'); + + $this->fileCacheStorage->clean('aaO8zKZF'); + + $this->assertFileDoesNotExist($file2); + $this->assertDirectoryDoesNotExist(__DIR__ . '/Source/0e/89'); + $this->assertDirectoryDoesNotExist(__DIR__ . '/Source/0e'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config.php'; + } +} diff --git a/tests/Caching/ValueObject/Storage/config.php b/tests/Caching/ValueObject/Storage/config.php new file mode 100644 index 00000000000..5e535c343a8 --- /dev/null +++ b/tests/Caching/ValueObject/Storage/config.php @@ -0,0 +1,11 @@ +cacheDirectory(sys_get_temp_dir() . '/_rector_cached_files_test'); + $rectorConfig->cacheClass(MemoryCacheStorage::class); +}; diff --git a/tests/ChangesReporting/Output/Factory/JsonOutputFactoryTest.php b/tests/ChangesReporting/Output/Factory/JsonOutputFactoryTest.php new file mode 100644 index 00000000000..5f7a467b910 --- /dev/null +++ b/tests/ChangesReporting/Output/Factory/JsonOutputFactoryTest.php @@ -0,0 +1,47 @@ +assertSame($expectedOutput, $actualOutput); + } +} diff --git a/tests/ChangesReporting/Output/Fixtures/without_diffs.json b/tests/ChangesReporting/Output/Fixtures/without_diffs.json new file mode 100644 index 00000000000..d15f37f36de --- /dev/null +++ b/tests/ChangesReporting/Output/Fixtures/without_diffs.json @@ -0,0 +1,17 @@ +{ + "totals": { + "changed_files": 2, + "errors": 1 + }, + "changed_files": [ + "some/file.php", + "some/file_foo.php" + ], + "errors": [ + { + "message": "Some error message", + "file": "some/file.php", + "line": 1 + } + ] +} \ No newline at end of file diff --git a/tests/ChangesReporting/Output/GitHubOutputFormatterTest.php b/tests/ChangesReporting/Output/GitHubOutputFormatterTest.php new file mode 100644 index 00000000000..98a1d8ad1a8 --- /dev/null +++ b/tests/ChangesReporting/Output/GitHubOutputFormatterTest.php @@ -0,0 +1,113 @@ +gitHubOutputFormatter = new GitHubOutputFormatter(); + + parent::setUp(); + } + + public function testGetName(): void + { + $this->assertSame('github', $this->gitHubOutputFormatter->getName()); + } + + public function testReportShouldOutputErrorMessagesGrouped(): void + { + + $this->expectOsOutputString( + '::group::Rector report' . PHP_EOL . + '::error file=some/file.php,line=1::Some error message' . PHP_EOL . + '::error file=some/file.php,line=38::StrStartsWithRector%0A%0A--- Original%0A+++ New%0A@@ -38,5 +39,6 @@%0Areturn true;%0A}%0A' . PHP_EOL . + '::endgroup::' . PHP_EOL + ); + + $this->gitHubOutputFormatter->report( + new ProcessResult( + [new SystemError('Some error message', 'some/file.php', 1)], + [ + new FileDiff( + 'some/file.php', + '--- Original' . PHP_EOL . '+++ New' . PHP_EOL . + '@@ -38,5 +39,6 @@' . PHP_EOL . + 'return true;' . PHP_EOL . '}' . PHP_EOL, + 'diff console formatted', + [new RectorWithLineChange(StrStartsWithRector::class, 38)] + ), + ], + 1 + ), + new Configuration() + ); + } + + public function testReportShouldOutputErrorMessagesGroupedWithNoErrors(): void + { + $this->expectOsOutputString('::group::Rector report' . PHP_EOL . '::endgroup::' . PHP_EOL); + + $this->gitHubOutputFormatter->report(new ProcessResult([], [], 1), new Configuration()); + } + + public function testReportShouldOutputErrorMessagesGroupedWithMultipleDiffs(): void + { + $this->expectOsOutputString( + '::group::Rector report' . PHP_EOL . + '::error file=some/file.php,line=38::StrStartsWithRector / NullToStrictStringFuncCallArgRector%0A%0A--- Original%0A+++ New%0A@@ -38,5 +39,6 @@%0Areturn true;%0A}%0A' . PHP_EOL . + '::error file=some/another-file.php,line=54::StrStartsWithRector%0A%0A--- Original%0A+++ New%0A@@ -54,10 +54,10 @@%0Areturn true;%0A}%0A' . PHP_EOL . + '::endgroup::' . PHP_EOL + ); + + $this->gitHubOutputFormatter->report( + new ProcessResult([], [ + new FileDiff( + 'some/file.php', + '--- Original' . PHP_EOL . '+++ New' . PHP_EOL . + '@@ -38,5 +39,6 @@' . PHP_EOL . + 'return true;' . PHP_EOL . '}' . PHP_EOL, + 'diff console formatted', + [ + new RectorWithLineChange(StrStartsWithRector::class, 38), + new RectorWithLineChange(NullToStrictStringFuncCallArgRector::class, 38), + ] + ), + new FileDiff( + 'some/another-file.php', + '--- Original' . PHP_EOL . '+++ New' . PHP_EOL . + '@@ -54,10 +54,10 @@' . PHP_EOL . + 'return true;' . PHP_EOL . '}' . PHP_EOL, + 'diff console formatted', + [new RectorWithLineChange(StrStartsWithRector::class, 54)] + ), + ], 2), + new Configuration() + ); + } + + protected function expectOsOutputString(string $expectedOutput): void + { + $isWindows = strncasecmp(PHP_OS, 'WIN', 3) === 0; + if ($isWindows) { + $expectedOutput = str_replace('%0A', '%0D%0A', $expectedOutput); + } + + parent::expectOutputString($expectedOutput); + } +} diff --git a/tests/Comments/CommentRemover/CommentRemoverTest.php b/tests/Comments/CommentRemover/CommentRemoverTest.php new file mode 100644 index 00000000000..eb5e857bbec --- /dev/null +++ b/tests/Comments/CommentRemover/CommentRemoverTest.php @@ -0,0 +1,63 @@ +commentRemover = $this->make(CommentRemover::class); + $this->testingParser = $this->make(TestingParser::class); + + $this->betterStandardPrinter = $this->make(BetterStandardPrinter::class); + } + + #[DataProvider('provideData')] + public function test(string $filePath): void + { + [$inputContents, $expectedOutputContents] = FixtureSplitter::split($filePath); + $inputFilePath = FixtureTempFileDumper::dump($inputContents); + + $nodes = $this->testingParser->parseFileToDecoratedNodes($inputFilePath); + + FileSystem::delete($inputFilePath); + + $nodesWithoutComments = $this->commentRemover->removeFromNode($nodes); + + $printedFileContent = $this->betterStandardPrinter->print($nodesWithoutComments); + $printedFileContent = trim($printedFileContent); + + $expectedContent = trim($expectedOutputContents); + $this->assertSame($printedFileContent, $expectedContent); + + // original nodes are not touched + $originalContent = $this->betterStandardPrinter->print($nodes); + $this->assertNotSame($expectedContent, $originalContent); + } + + public static function provideData(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/Fixture'); + } +} diff --git a/tests/Comments/CommentRemover/Fixture/another_comment.php.inc b/tests/Comments/CommentRemover/Fixture/another_comment.php.inc new file mode 100644 index 00000000000..4c0b367cba9 --- /dev/null +++ b/tests/Comments/CommentRemover/Fixture/another_comment.php.inc @@ -0,0 +1,31 @@ + +----- +namespace Rector\Tests\Comments\CommentRemover\Fixture; + +final class AnotherComment +{ + public function run($value) + { + switch ($value) { + case 'key': + return 'https://some_very_long_link.cz'; + } + } +} diff --git a/packages-tests/Comments/CommentRemover/Fixture/asterisk_comment.php.inc b/tests/Comments/CommentRemover/Fixture/asterisk_comment.php.inc similarity index 100% rename from packages-tests/Comments/CommentRemover/Fixture/asterisk_comment.php.inc rename to tests/Comments/CommentRemover/Fixture/asterisk_comment.php.inc diff --git a/packages-tests/Comments/CommentRemover/Fixture/behind_value.php.inc b/tests/Comments/CommentRemover/Fixture/behind_value.php.inc similarity index 100% rename from packages-tests/Comments/CommentRemover/Fixture/behind_value.php.inc rename to tests/Comments/CommentRemover/Fixture/behind_value.php.inc diff --git a/packages-tests/Comments/CommentRemover/Fixture/cross_bracket_comment.php.inc b/tests/Comments/CommentRemover/Fixture/cross_bracket_comment.php.inc similarity index 100% rename from packages-tests/Comments/CommentRemover/Fixture/cross_bracket_comment.php.inc rename to tests/Comments/CommentRemover/Fixture/cross_bracket_comment.php.inc diff --git a/tests/Composer/InstalledPackageResolverTest.php b/tests/Composer/InstalledPackageResolverTest.php new file mode 100644 index 00000000000..8b26d761324 --- /dev/null +++ b/tests/Composer/InstalledPackageResolverTest.php @@ -0,0 +1,21 @@ +resolve(); + + $this->assertContainsOnlyInstancesOf(InstalledPackage::class, $installedPackages); + $this->assertGreaterThan(77, count($installedPackages)); + } +} diff --git a/tests/Config/RectorConfigTest.php b/tests/Config/RectorConfigTest.php new file mode 100644 index 00000000000..cdee28e9358 --- /dev/null +++ b/tests/Config/RectorConfigTest.php @@ -0,0 +1,32 @@ +getContainer(); + + $rectorConfig->configure() + ->withSets([TwigSetList::TWIG_134]) + ->withRules([ReturnTypeFromReturnNewRector::class])($rectorConfig); + + // only collect root withRules() + $this->assertCount(1, SimpleParameterProvider::provideArrayParameter(Option::ROOT_STANDALONE_REGISTERED_RULES)); + } +} diff --git a/tests/Configuration/ConfigurationFactoryTest.php b/tests/Configuration/ConfigurationFactoryTest.php new file mode 100644 index 00000000000..9602940f0a2 --- /dev/null +++ b/tests/Configuration/ConfigurationFactoryTest.php @@ -0,0 +1,45 @@ +make(ConfigurationFactory::class); + $configuration = $configurationFactory->createForTests([ + __DIR__ . '/../../tests-paths/path/*/some_directory/*', + __DIR__ . '/../../tests-paths/path/NoExtensionFile', + ]); + + $filesFinder = $this->make(FilesFinder::class); + + $filePaths = $filesFinder->findInDirectoriesAndFiles($configuration->getPaths()); + $this->assertCount(3, $filePaths); + + $firstFilePath = $filePaths[0]; + $secondFilePath = $filePaths[1]; + $thirdFilePath = $filePaths[2]; + + $this->assertSame( + realpath(__DIR__ . '/../../tests-paths/path/wildcard-nested/some_directory/AnotherFile.php'), + realpath($firstFilePath) + ); + + $this->assertSame( + realpath(__DIR__ . '/../../tests-paths/path/wildcard-next/some_directory/YetAnotherFile.php'), + realpath($secondFilePath), + ); + + $this->assertSame( + realpath(__DIR__ . '/../../tests-paths/path/NoExtensionFile'), + realpath($thirdFilePath), + ); + } +} diff --git a/tests/Configuration/OnlyRuleResolverTest.php b/tests/Configuration/OnlyRuleResolverTest.php new file mode 100644 index 00000000000..e835f8e0e3d --- /dev/null +++ b/tests/Configuration/OnlyRuleResolverTest.php @@ -0,0 +1,114 @@ +bootFromConfigFiles([__DIR__ . '/config/only_rule_resolver_config.php']); + $rectorConfig = self::getContainer(); + + $this->onlyRuleResolver = new OnlyRuleResolver(iterator_to_array( + $rectorConfig->tagged(RectorInterface::class) + )); + } + + public function testResolveOk(): void + { + $this->assertSame( + RemoveDoubleAssignRector::class, + $this->onlyRuleResolver->resolve('Rector\\DeadCode\\Rector\\Assign\\RemoveDoubleAssignRector') + ); + } + + public function testResolveOkLeadingBackslash(): void + { + $this->assertSame( + RemoveDoubleAssignRector::class, + $this->onlyRuleResolver->resolve('\\Rector\\DeadCode\\Rector\\Assign\\RemoveDoubleAssignRector') + ); + } + + public function testResolveOkDoubleBackslashes(): void + { + $this->assertSame( + RemoveDoubleAssignRector::class, + $this->onlyRuleResolver->resolve('\\\\Rector\\\\DeadCode\\\\Rector\\\\Assign\\\\RemoveDoubleAssignRector'), + 'We want to fix wrongly double-quoted backslashes automatically' + ); + } + + public function testResolveOkSingleQuotes(): void + { + $this->assertSame( + RemoveDoubleAssignRector::class, + $this->onlyRuleResolver->resolve("'Rector\\DeadCode\\Rector\\Assign\\RemoveDoubleAssignRector'"), + 'Remove stray single quotes on Windows systems' + ); + } + + public function testResolveMissingBackslash(): void + { + $this->expectExceptionMessage( + 'Rule "RectorDeadCodeRectorAssignRemoveDoubleAssignRector" was not found.' . PHP_EOL + . 'The rule has no namespace. Make sure to escape the backslashes, and add quotes around the rule name: --only="My\\Rector\\Rule"' + ); + $this->expectException(RectorRuleNotFoundException::class); + + $this->onlyRuleResolver->resolve('RectorDeadCodeRectorAssignRemoveDoubleAssignRector'); + } + + public function testResolveNotFound(): void + { + $this->expectExceptionMessage( + 'Rule "This\Rule\Does\Not\Exist" was not found.' . PHP_EOL + . 'Make sure it is registered in your config or in one of the sets' + ); + $this->expectException(RectorRuleNotFoundException::class); + + $this->onlyRuleResolver->resolve('This\\Rule\\Does\\Not\\Exist'); + } + + public function testResolveShortOk(): void + { + $this->assertSame( + RemoveUnusedPrivateMethodRector::class, + $this->onlyRuleResolver->resolve('RemoveUnusedPrivateMethodRector'), + ); + } + + public function testResolveShortOkTwoLevels(): void + { + $this->assertSame( + RemoveDoubleAssignRector::class, + $this->onlyRuleResolver->resolve('Assign\\RemoveDoubleAssignRector'), + ); + } + + public function testResolveShortAmbiguous(): void + { + $this->expectExceptionMessage( + 'Short rule name "RemoveDoubleAssignRector" is ambiguous. Specify the full rule name:' . PHP_EOL + . '- Rector\\DeadCode\\Rector\\Assign\\RemoveDoubleAssignRector' . PHP_EOL + . '- Rector\\Tests\\Configuration\\Source\\RemoveDoubleAssignRector' + ); + $this->expectException(RectorRuleNameAmbiguousException::class); + + $this->onlyRuleResolver->resolve('RemoveDoubleAssignRector'); + } +} diff --git a/tests/Configuration/PhpLevelSetResolverTest.php b/tests/Configuration/PhpLevelSetResolverTest.php new file mode 100644 index 00000000000..af129f2fe30 --- /dev/null +++ b/tests/Configuration/PhpLevelSetResolverTest.php @@ -0,0 +1,27 @@ +assertCount(5, $phpSetFiles); + + $this->assertSame([ + SetList::PHP_52, + SetList::PHP_53, + SetList::PHP_54, + SetList::PHP_55, + SetList::PHP_56, + ], $phpSetFiles); + } +} diff --git a/tests/Configuration/Source/RemoveDoubleAssignRector.php b/tests/Configuration/Source/RemoveDoubleAssignRector.php new file mode 100644 index 00000000000..4d6c05bdc49 --- /dev/null +++ b/tests/Configuration/Source/RemoveDoubleAssignRector.php @@ -0,0 +1,28 @@ +withRules( + [RemoveDoubleAssignRector::class, RemoveDoubleAssignRectorTest::class, RemoveUnusedPrivateMethodRector::class] + ); diff --git a/tests/Console/Formatter/ColorConsoleDiffFormatterTest.php b/tests/Console/Formatter/ColorConsoleDiffFormatterTest.php new file mode 100644 index 00000000000..e4fd7d4f7c1 --- /dev/null +++ b/tests/Console/Formatter/ColorConsoleDiffFormatterTest.php @@ -0,0 +1,44 @@ +colorConsoleDiffFormatter = new ColorConsoleDiffFormatter(); + } + + #[DataProvider('provideData')] + public function test(string $content, string $expectedFormattedFileContent): void + { + $formattedContent = $this->colorConsoleDiffFormatter->format($content); + $this->assertNotEmpty($expectedFormattedFileContent); + + $this->assertStringEqualsFile($expectedFormattedFileContent, $formattedContent); + } + + /** + * @return Iterator> + */ + public static function provideData(): Iterator + { + yield ['...', __DIR__ . '/Source/expected/expected.txt']; + yield ["-old\n+new", __DIR__ . '/Source/expected/expected_old_new.txt']; + + yield [ + str_replace("\r\n", "\n", FileSystem::read(__DIR__ . '/Fixture/with_full_diff_by_phpunit.diff')), + __DIR__ . '/Fixture/expected_with_full_diff_by_phpunit.diff', + ]; + } +} diff --git a/tests/Console/Formatter/Fixture/expected_with_full_diff_by_phpunit.diff b/tests/Console/Formatter/Fixture/expected_with_full_diff_by_phpunit.diff new file mode 100644 index 00000000000..b2b41e62e78 --- /dev/null +++ b/tests/Console/Formatter/Fixture/expected_with_full_diff_by_phpunit.diff @@ -0,0 +1,3 @@ + ---------- begin diff ---------- +... + ----------- end diff ----------- diff --git a/tests/Console/Formatter/Fixture/with_full_diff_by_phpunit.diff b/tests/Console/Formatter/Fixture/with_full_diff_by_phpunit.diff new file mode 100644 index 00000000000..58d1e099586 --- /dev/null +++ b/tests/Console/Formatter/Fixture/with_full_diff_by_phpunit.diff @@ -0,0 +1,3 @@ +--- Original ++++ New +... diff --git a/tests/Console/Formatter/Source/expected/expected.txt b/tests/Console/Formatter/Source/expected/expected.txt new file mode 100644 index 00000000000..b2b41e62e78 --- /dev/null +++ b/tests/Console/Formatter/Source/expected/expected.txt @@ -0,0 +1,3 @@ + ---------- begin diff ---------- +... + ----------- end diff ----------- diff --git a/tests/Console/Formatter/Source/expected/expected_old_new.txt b/tests/Console/Formatter/Source/expected/expected_old_new.txt new file mode 100644 index 00000000000..20fc7471b0b --- /dev/null +++ b/tests/Console/Formatter/Source/expected/expected_old_new.txt @@ -0,0 +1,4 @@ + ---------- begin diff ---------- +-old ++new + ----------- end diff ----------- diff --git a/tests/DependencyInjection/ConfigurableRectorImportConfigCallsMergeTest.php b/tests/DependencyInjection/ConfigurableRectorImportConfigCallsMergeTest.php index 9870a7a10a1..671b7f2ed04 100644 --- a/tests/DependencyInjection/ConfigurableRectorImportConfigCallsMergeTest.php +++ b/tests/DependencyInjection/ConfigurableRectorImportConfigCallsMergeTest.php @@ -2,109 +2,44 @@ declare(strict_types=1); -namespace Rector\Core\Tests\DependencyInjection; +namespace Rector\Tests\DependencyInjection; use Iterator; -use Rector\Core\Configuration\RenamedClassesDataCollector; +use PHPUnit\Framework\Attributes\DataProvider; +use Rector\Configuration\RenamedClassesDataCollector; use Rector\Renaming\Rector\Name\RenameClassRector; -use Rector\Testing\PHPUnit\AbstractTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class ConfigurableRectorImportConfigCallsMergeTest extends AbstractTestCase +final class ConfigurableRectorImportConfigCallsMergeTest extends AbstractLazyTestCase { /** - * @dataProvider provideData() * @param array $expectedConfiguration */ - public function testMainConfigValues(string $config, array $expectedConfiguration): void + #[DataProvider('provideData')] + public function testMainConfigValues(string $configFile, array $expectedConfiguration): void { - $configFileInfos = [new SmartFileInfo($config)]; - $this->bootFromConfigFileInfos($configFileInfos); + $this->bootFromConfigFiles([$configFile]); // to invoke configure() method call - $this->getService(RenameClassRector::class); + $renameClassRector = $this->make(RenameClassRector::class); + $this->assertInstanceOf(RenameClassRector::class, $renameClassRector); /** @var RenamedClassesDataCollector $renamedClassesDataCollector */ - $renamedClassesDataCollector = $this->getService(RenamedClassesDataCollector::class); + $renamedClassesDataCollector = $this->make(RenamedClassesDataCollector::class); $this->assertSame($expectedConfiguration, $renamedClassesDataCollector->getOldToNewClasses()); } - public function provideData(): Iterator + /** + * @return Iterator<(array>|array)> + */ + public static function provideData(): Iterator { - yield [ - __DIR__ . '/config/main_config_with_only_imports.php', [ - 'old_2' => 'new_2', - 'old_1' => 'new_1', - ], - ]; - yield [ __DIR__ . '/config/main_config_with_override_value.php', [ 'old_2' => 'new_2', - 'old_1' => 'new_1', 'old_4' => 'new_4', - ], - ]; - - yield [ - __DIR__ . '/config/main_config_with_own_value.php', [ - 'old_2' => 'new_2', 'old_1' => 'new_1', - 'old_4' => 'new_4', - 'old_3' => 'new_3', - ], - ]; - - yield [ - __DIR__ . '/config/one_set.php', [ - 'PHPUnit_Framework_MockObject_Stub' => 'PHPUnit\Framework\MockObject\Stub', - 'PHPUnit_Framework_MockObject_Stub_Return' => 'PHPUnit\Framework\MockObject\Stub\ReturnStub', - 'PHPUnit_Framework_MockObject_Matcher_Parameters' => 'PHPUnit\Framework\MockObject\Matcher\Parameters', - 'PHPUnit_Framework_MockObject_Matcher_Invocation' => 'PHPUnit\Framework\MockObject\Matcher\Invocation', - 'PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject', - 'PHPUnit_Framework_MockObject_Invocation_Object' => 'PHPUnit\Framework\MockObject\Invocation\ObjectInvocation', - ], - ]; - - yield [ - __DIR__ . '/config/one_set_with_own_rename.php', [ - 'Old' => 'New', - 'PHPUnit_Framework_MockObject_Stub' => 'PHPUnit\Framework\MockObject\Stub', - 'PHPUnit_Framework_MockObject_Stub_Return' => 'PHPUnit\Framework\MockObject\Stub\ReturnStub', - 'PHPUnit_Framework_MockObject_Matcher_Parameters' => 'PHPUnit\Framework\MockObject\Matcher\Parameters', - 'PHPUnit_Framework_MockObject_Matcher_Invocation' => 'PHPUnit\Framework\MockObject\Matcher\Invocation', - 'PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject', - 'PHPUnit_Framework_MockObject_Invocation_Object' => 'PHPUnit\Framework\MockObject\Invocation\ObjectInvocation', - ], - ]; - - yield [ - __DIR__ . '/config/two_sets.php', [ - 'Twig_SimpleFilter' => 'Twig_Filter', - 'Twig_SimpleFunction' => 'Twig_Function', - 'Twig_SimpleTest' => 'Twig_Test', - 'PHPUnit_Framework_MockObject_Stub' => 'PHPUnit\Framework\MockObject\Stub', - 'PHPUnit_Framework_MockObject_Stub_Return' => 'PHPUnit\Framework\MockObject\Stub\ReturnStub', - 'PHPUnit_Framework_MockObject_Matcher_Parameters' => 'PHPUnit\Framework\MockObject\Matcher\Parameters', - 'PHPUnit_Framework_MockObject_Matcher_Invocation' => 'PHPUnit\Framework\MockObject\Matcher\Invocation', - 'PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject', - 'PHPUnit_Framework_MockObject_Invocation_Object' => 'PHPUnit\Framework\MockObject\Invocation\ObjectInvocation', - ], - ]; - - yield [ - __DIR__ . '/config/two_sets_with_own_rename.php', [ - 'Old' => 'New', - 'Twig_SimpleFilter' => 'Twig_Filter', - 'Twig_SimpleFunction' => 'Twig_Function', - 'Twig_SimpleTest' => 'Twig_Test', - 'PHPUnit_Framework_MockObject_Stub' => 'PHPUnit\Framework\MockObject\Stub', - 'PHPUnit_Framework_MockObject_Stub_Return' => 'PHPUnit\Framework\MockObject\Stub\ReturnStub', - 'PHPUnit_Framework_MockObject_Matcher_Parameters' => 'PHPUnit\Framework\MockObject\Matcher\Parameters', - 'PHPUnit_Framework_MockObject_Matcher_Invocation' => 'PHPUnit\Framework\MockObject\Matcher\Invocation', - 'PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject', - 'PHPUnit_Framework_MockObject_Invocation_Object' => 'PHPUnit\Framework\MockObject\Invocation\ObjectInvocation', ], ]; } diff --git a/tests/DependencyInjection/config/first_config.php b/tests/DependencyInjection/config/first_config.php index 09f767d5d37..aa273175767 100644 --- a/tests/DependencyInjection/config/first_config.php +++ b/tests/DependencyInjection/config/first_config.php @@ -2,15 +2,11 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'old_1' => 'new_1', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'old_1' => 'new_1', + ]); }; diff --git a/tests/DependencyInjection/config/main_config_with_only_imports.php b/tests/DependencyInjection/config/main_config_with_only_imports.php deleted file mode 100644 index d473384108b..00000000000 --- a/tests/DependencyInjection/config/main_config_with_only_imports.php +++ /dev/null @@ -1,14 +0,0 @@ -services(); - $services->set(RenameClassRector::class); - - $containerConfigurator->import(__DIR__ . '/first_config.php'); - $containerConfigurator->import(__DIR__ . '/second_config.php'); -}; diff --git a/tests/DependencyInjection/config/main_config_with_override_value.php b/tests/DependencyInjection/config/main_config_with_override_value.php index 3b64c0c23a5..542b2dad70b 100644 --- a/tests/DependencyInjection/config/main_config_with_override_value.php +++ b/tests/DependencyInjection/config/main_config_with_override_value.php @@ -2,23 +2,15 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'old_2' => 'new_2', - ], - ]]) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'old_4' => 'new_4', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'old_2' => 'new_2', + 'old_4' => 'new_4', + ]); - $containerConfigurator->import(__DIR__ . '/first_config.php'); - $containerConfigurator->import(__DIR__ . '/second_config.php'); + $rectorConfig->import(__DIR__ . '/first_config.php'); + $rectorConfig->import(__DIR__ . '/second_config.php'); }; diff --git a/tests/DependencyInjection/config/main_config_with_own_value.php b/tests/DependencyInjection/config/main_config_with_own_value.php deleted file mode 100644 index 3ede9d5a454..00000000000 --- a/tests/DependencyInjection/config/main_config_with_own_value.php +++ /dev/null @@ -1,24 +0,0 @@ -services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'old_3' => 'new_3', - ], - ]]) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'old_4' => 'new_4', - ], - ]]); - - $containerConfigurator->import(__DIR__ . '/first_config.php'); - $containerConfigurator->import(__DIR__ . '/second_config.php'); -}; diff --git a/tests/DependencyInjection/config/one_set.php b/tests/DependencyInjection/config/one_set.php deleted file mode 100644 index d9f6b72ac22..00000000000 --- a/tests/DependencyInjection/config/one_set.php +++ /dev/null @@ -1,10 +0,0 @@ -import(PHPUnitSetList::PHPUNIT_60); -}; diff --git a/tests/DependencyInjection/config/one_set_with_own_rename.php b/tests/DependencyInjection/config/one_set_with_own_rename.php deleted file mode 100644 index d89c2dbd08e..00000000000 --- a/tests/DependencyInjection/config/one_set_with_own_rename.php +++ /dev/null @@ -1,19 +0,0 @@ -import(PHPUnitSetList::PHPUNIT_60); - - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'Old' => 'New', - ], - ]]); -}; diff --git a/tests/DependencyInjection/config/second_config.php b/tests/DependencyInjection/config/second_config.php index 1ac65783ffc..7c4b9e0db96 100644 --- a/tests/DependencyInjection/config/second_config.php +++ b/tests/DependencyInjection/config/second_config.php @@ -2,16 +2,11 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'old_2' => 'new_2', - ], - ], - ]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'old_2' => 'new_2', + ]); }; diff --git a/tests/DependencyInjection/config/two_sets.php b/tests/DependencyInjection/config/two_sets.php deleted file mode 100644 index 6cc66fdcfb1..00000000000 --- a/tests/DependencyInjection/config/two_sets.php +++ /dev/null @@ -1,12 +0,0 @@ -import(PHPUnitSetList::PHPUNIT_60); - $containerConfigurator->import(TwigSetList::TWIG_20); -}; diff --git a/tests/DependencyInjection/config/two_sets_with_own_rename.php b/tests/DependencyInjection/config/two_sets_with_own_rename.php deleted file mode 100644 index 00256684f66..00000000000 --- a/tests/DependencyInjection/config/two_sets_with_own_rename.php +++ /dev/null @@ -1,21 +0,0 @@ -import(PHPUnitSetList::PHPUNIT_60); - $containerConfigurator->import(TwigSetList::TWIG_20); - - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'Old' => 'New', - ], - ]]); -}; diff --git a/tests/Exclusion/ExclusionManagerTest.php b/tests/Exclusion/ExclusionManagerTest.php deleted file mode 100644 index 1492083c423..00000000000 --- a/tests/Exclusion/ExclusionManagerTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/some_config.php'; - } -} diff --git a/tests/Exclusion/Fixture/different_norector.php.inc b/tests/Exclusion/Fixture/different_norector.php.inc deleted file mode 100644 index 0e18e544078..00000000000 --- a/tests/Exclusion/Fixture/different_norector.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/tests/Exclusion/Fixture/skip_comment_with_spaces.php.inc b/tests/Exclusion/Fixture/skip_comment_with_spaces.php.inc deleted file mode 100644 index 3baf77094af..00000000000 --- a/tests/Exclusion/Fixture/skip_comment_with_spaces.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(RemoveEmptyClassMethodRector::class); - $services->set(RemoveDeadZeroAndOneOperationRector::class); -}; diff --git a/tests/FileSystem/FileAndDirectoryFilter/FileAndDirectoryFilterTest.php b/tests/FileSystem/FileAndDirectoryFilter/FileAndDirectoryFilterTest.php new file mode 100644 index 00000000000..26c974c21e9 --- /dev/null +++ b/tests/FileSystem/FileAndDirectoryFilter/FileAndDirectoryFilterTest.php @@ -0,0 +1,32 @@ +fileAndDirectoryFilter = new FileAndDirectoryFilter(); + } + + public function testSeparateFilesAndDirectories(): void + { + $sources = [__DIR__, __DIR__ . '/FileAndDirectoryFilterTest.php']; + + $files = $this->fileAndDirectoryFilter->filterFiles($sources); + $directories = $this->fileAndDirectoryFilter->filterDirectories($sources); + + $this->assertCount(1, $files); + $this->assertCount(1, $directories); + + $this->assertSame($files, [__DIR__ . '/FileAndDirectoryFilterTest.php']); + $this->assertSame($directories, [__DIR__]); + } +} diff --git a/tests/FileSystem/FilePathHelperTest.php b/tests/FileSystem/FilePathHelperTest.php new file mode 100644 index 00000000000..1fc3431a7de --- /dev/null +++ b/tests/FileSystem/FilePathHelperTest.php @@ -0,0 +1,38 @@ +filePathHelper = new FilePathHelper(new Filesystem()); + } + + #[DataProvider('provideData')] + public function test(string $inputPath, string $expectedNormalizedPath): void + { + $normalizedPath = $this->filePathHelper->normalizePathAndSchema($inputPath); + $this->assertSame($expectedNormalizedPath, $normalizedPath); + } + + /** + * @return Iterator> + */ + public static function provideData(): Iterator + { + // based on Linux + yield ['/any/path', '/any/path']; + yield ['\any\path', '/any/path']; + } +} diff --git a/tests/FileSystem/FilesFinder/ExcludePaths/ExcludePathsTest.php b/tests/FileSystem/FilesFinder/ExcludePaths/ExcludePathsTest.php index 06a8b097ff9..bbd2d9f0453 100644 --- a/tests/FileSystem/FilesFinder/ExcludePaths/ExcludePathsTest.php +++ b/tests/FileSystem/FilesFinder/ExcludePaths/ExcludePathsTest.php @@ -2,19 +2,20 @@ declare(strict_types=1); -namespace Rector\Core\Tests\FileSystem\FilesFinder\ExcludePaths; +namespace Rector\Tests\FileSystem\FilesFinder\ExcludePaths; -use Rector\Core\FileSystem\FilesFinder; -use Rector\Testing\PHPUnit\AbstractTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\FileSystem\FilesFinder; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class ExcludePathsTest extends AbstractTestCase +final class ExcludePathsTest extends AbstractLazyTestCase { - public function testShouldFail(): void + public function test(): void { - $this->bootFromConfigFileInfos([new SmartFileInfo(__DIR__ . '/config/config-with-excluded-paths.php')]); + SimpleParameterProvider::setParameter(Option::SKIP, ['*/ShouldBeExcluded/*']); - $filesFinder = $this->getService(FilesFinder::class); + $filesFinder = $this->make(FilesFinder::class); $foundFileInfos = $filesFinder->findInDirectoriesAndFiles([__DIR__ . '/Source'], ['php']); $this->assertCount(1, $foundFileInfos); diff --git a/tests/FileSystem/FilesFinder/ExcludePaths/Source/ShouldBeExcluded/FileWithMissingClass.php b/tests/FileSystem/FilesFinder/ExcludePaths/Source/ShouldBeExcluded/FileWithMissingClass.php index d5079a07cbb..3ed3942b513 100644 --- a/tests/FileSystem/FilesFinder/ExcludePaths/Source/ShouldBeExcluded/FileWithMissingClass.php +++ b/tests/FileSystem/FilesFinder/ExcludePaths/Source/ShouldBeExcluded/FileWithMissingClass.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Core\Tests\FileSystem\FilesFinder\ExcludePaths\Source\ShouldBeExcluded; +namespace Rector\Tests\FileSystem\FilesFinder\ExcludePaths\Source\ShouldBeExcluded; final class FileWithMissingClass extends ThisClassIsNotHere { diff --git a/tests/FileSystem/FilesFinder/ExcludePaths/Source/ThisShouldBeHere.php b/tests/FileSystem/FilesFinder/ExcludePaths/Source/ThisShouldBeHere.php index b48daac4dc4..3d205157ade 100644 --- a/tests/FileSystem/FilesFinder/ExcludePaths/Source/ThisShouldBeHere.php +++ b/tests/FileSystem/FilesFinder/ExcludePaths/Source/ThisShouldBeHere.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Core\Tests\FileSystem\FilesFinder\ExcludePaths\Source; +namespace Rector\Tests\FileSystem\FilesFinder\ExcludePaths\Source; final class ThisShouldBeHere { diff --git a/tests/FileSystem/FilesFinder/ExcludePaths/config/config-with-excluded-paths.php b/tests/FileSystem/FilesFinder/ExcludePaths/config/config-with-excluded-paths.php deleted file mode 100644 index a9b478f09ec..00000000000 --- a/tests/FileSystem/FilesFinder/ExcludePaths/config/config-with-excluded-paths.php +++ /dev/null @@ -1,11 +0,0 @@ -parameters(); - $parameters->set(Option::SKIP, ['*/ShouldBeExcluded/*']); -}; diff --git a/tests/FileSystem/FilesFinder/FilesFinderTest.php b/tests/FileSystem/FilesFinder/FilesFinderTest.php index 40b41860bb0..419ef0c5d9e 100644 --- a/tests/FileSystem/FilesFinder/FilesFinderTest.php +++ b/tests/FileSystem/FilesFinder/FilesFinderTest.php @@ -2,40 +2,92 @@ declare(strict_types=1); -namespace Rector\Core\Tests\FileSystem\FilesFinder; +namespace Rector\Tests\FileSystem\FilesFinder; use Iterator; -use Rector\Core\FileSystem\FilesFinder; -use Rector\Testing\PHPUnit\AbstractTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; +use PHPUnit\Framework\Attributes\DataProvider; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\FileSystem\FilesFinder; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class FilesFinderTest extends AbstractTestCase +final class FilesFinderTest extends AbstractLazyTestCase { private FilesFinder $filesFinder; protected function setUp(): void { - $this->boot(); - $this->filesFinder = $this->getService(FilesFinder::class); + parent::setUp(); + + $this->filesFinder = $this->make(FilesFinder::class); + } + + public function test(): void + { + $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/SourceWithSymlinks'], ['txt']); + $this->assertCount(1, $foundFiles); + + $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/SourceWithShortEchoes'], ['php']); + $this->assertEmpty($foundFiles); + } + + #[DataProvider('alwaysReturnsAbsolutePathDataProvider')] + public function testAlwaysReturnsAbsolutePath(string $relativePath): void + { + $absolutePath = str_replace('/', DIRECTORY_SEPARATOR, getcwd() . '/' . $relativePath); + $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([$absolutePath], ['php']); + $this->assertStringStartsWith( + $absolutePath, + $foundFiles[0], + 'should return absolute path if absolute is given' + ); + + $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([$relativePath], ['php']); + $this->assertStringStartsWith( + $absolutePath, + $foundFiles[0], + 'should return absolute path if relative is given' + ); } /** - * @dataProvider provideData() + * @return Iterator> */ + public static function alwaysReturnsAbsolutePathDataProvider(): Iterator + { + yield ['tests/FileSystem/FilesFinder/Source/']; + yield ['tests/FileSystem/FilesFinder/Source/SomeFile.php']; + } + + public function testWithFollowingBrokenSymlinks(): void + { + if ($this->isWindows()) { + $this->markTestSkipped('Symlinks test is not reliable on Windows'); + } + + SimpleParameterProvider::setParameter(Option::SKIP, [__DIR__ . '/../SourceWithBrokenSymlinks/folder1']); + + $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/SourceWithBrokenSymlinks']); + $this->assertEmpty($foundFiles); + } + + #[DataProvider('provideData')] public function testSingleSuffix(string $suffix, int $count, string $expectedFileName): void { $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/Source'], [$suffix]); $this->assertCount($count, $foundFiles); - /** @var SmartFileInfo $foundFile */ + /** @var string $foundFile */ $foundFile = array_pop($foundFiles); - $this->assertSame($expectedFileName, $foundFile->getBasename()); + $fileBasename = $this->getFileBasename($foundFile); + + $this->assertSame($expectedFileName, $fileBasename); } /** * @return Iterator> */ - public function provideData(): Iterator + public static function provideData(): Iterator { yield ['php', 1, 'SomeFile.php']; yield ['yml', 1, 'some_config.yml']; @@ -45,19 +97,18 @@ public function provideData(): Iterator public function testMultipleSuffixes(): void { - $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/Source'], ['yaml', 'yml']); - $this->assertCount(2, $foundFiles); + $foundFilePaths = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/Source'], ['yaml', 'yml']); + $this->assertCount(2, $foundFilePaths); - $foundFileNames = []; - foreach ($foundFiles as $foundFile) { - $foundFileNames[] = $foundFile->getFilename(); - } + $expectedFoundFilePath = [ + __DIR__ . DIRECTORY_SEPARATOR . 'Source' . DIRECTORY_SEPARATOR . 'some_config.yml', + __DIR__ . DIRECTORY_SEPARATOR . 'Source' . DIRECTORY_SEPARATOR . 'other_config.yaml', + ]; - $expectedFoundFileNames = ['some_config.yml', 'other_config.yaml']; + sort($foundFilePaths); + sort($expectedFoundFilePath); - sort($foundFileNames); - sort($expectedFoundFileNames); - $this->assertSame($expectedFoundFileNames, $foundFileNames); + $this->assertSame($expectedFoundFilePath, $foundFilePaths); } public function testDirectoriesWithGlobPattern(): void @@ -71,8 +122,42 @@ public function testFilesWithGlobPattern(): void $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/Source/**/foo.txt'], ['txt']); $this->assertCount(2, $foundFiles); - /** @var SmartFileInfo $foundFile */ + /** @var string $foundFile */ $foundFile = array_pop($foundFiles); - $this->assertSame('foo.txt', $foundFile->getBasename()); + + $fileBasename = $this->getFileBasename($foundFile); + $this->assertSame('foo.txt', $fileBasename); + } + + public function testFilterBySuffix(): void + { + // no suffix filter + $foundNoFilterFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/SourceWithSuffix']); + $this->assertCount(3, $foundNoFilterFiles); + + $foundFiles = $this->filesFinder->findInDirectoriesAndFiles( + [__DIR__ . '/SourceWithSuffix', __DIR__ . '/SourceWithSuffix/other_unrelated_file.php'], + ['php'], + true, + 'Controller' + ); + $this->assertCount(1, $foundFiles); + + $this->assertSame('SomeController.php', $this->getFileBasename($foundFiles[0])); + + $foundFullSuffixFiles = $this->filesFinder->findInDirectoriesAndFiles( + [__DIR__ . '/SourceWithSuffix'], + ['php'], + true, + 'Controller.php' + ); + $this->assertCount(1, $foundFullSuffixFiles); + + $this->assertSame($foundFiles, $foundFullSuffixFiles); + } + + private function getFileBasename(string $foundFile): string + { + return pathinfo($foundFile, PATHINFO_BASENAME); } } diff --git a/tests/FileSystem/FilesFinder/Source/folder1/foo.txt b/tests/FileSystem/FilesFinder/Source/folder1/foo.txt index e69de29bb2d..d00491fd7e5 100644 --- a/tests/FileSystem/FilesFinder/Source/folder1/foo.txt +++ b/tests/FileSystem/FilesFinder/Source/folder1/foo.txt @@ -0,0 +1 @@ +1 diff --git a/packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after.txt b/tests/FileSystem/FilesFinder/SourceWithBrokenSymlinks/folder1/foo.txt similarity index 100% rename from packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after.txt rename to tests/FileSystem/FilesFinder/SourceWithBrokenSymlinks/folder1/foo.txt diff --git a/tests/FileSystem/FilesFinder/SourceWithBrokenSymlinks/folder2/folder3 b/tests/FileSystem/FilesFinder/SourceWithBrokenSymlinks/folder2/folder3 new file mode 120000 index 00000000000..2d1db2ad3a6 --- /dev/null +++ b/tests/FileSystem/FilesFinder/SourceWithBrokenSymlinks/folder2/folder3 @@ -0,0 +1 @@ +folderBroken \ No newline at end of file diff --git a/tests/FileSystem/FilesFinder/SourceWithShortEchoes/SomeFileWithShortOpen.php b/tests/FileSystem/FilesFinder/SourceWithShortEchoes/SomeFileWithShortOpen.php new file mode 100644 index 00000000000..1e0b589a91a --- /dev/null +++ b/tests/FileSystem/FilesFinder/SourceWithShortEchoes/SomeFileWithShortOpen.php @@ -0,0 +1,2 @@ + +hello diff --git a/tests/FileSystem/FilesFinder/SourceWithSuffix/SomeController.php b/tests/FileSystem/FilesFinder/SourceWithSuffix/SomeController.php new file mode 100644 index 00000000000..94bc266f596 --- /dev/null +++ b/tests/FileSystem/FilesFinder/SourceWithSuffix/SomeController.php @@ -0,0 +1,7 @@ +initFilePathsResolver = new InitFilePathsResolver(); + } + + public function test(): void + { + $phpDirectoryPaths = $this->initFilePathsResolver->resolve(__DIR__ . '/Fixture/first-project'); + + $this->assertSame(['src', 'tests'], $phpDirectoryPaths); + } +} diff --git a/tests/Issues/AddClassDependency/AddClassDependencyTest.php b/tests/Issues/AddClassDependency/AddClassDependencyTest.php new file mode 100644 index 00000000000..6cc977287ad --- /dev/null +++ b/tests/Issues/AddClassDependency/AddClassDependencyTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/AddClassDependency/Fixture/fixture.php.inc b/tests/Issues/AddClassDependency/Fixture/fixture.php.inc new file mode 100644 index 00000000000..a57efc90db8 --- /dev/null +++ b/tests/Issues/AddClassDependency/Fixture/fixture.php.inc @@ -0,0 +1,57 @@ +someAutowiredService = $someAutowiredService; + } + + public function configure() + { + $someType = $this->get('validator'); + } +} + +?> +----- +someAutowiredService = $someAutowiredService; + $this->validator = $validator; + } + + public function configure() + { + $someType = $this->validator; + } +} + +?> diff --git a/tests/Issues/AddClassDependency/Fixture/strick_with_constructor.php.inc b/tests/Issues/AddClassDependency/Fixture/strick_with_constructor.php.inc new file mode 100644 index 00000000000..11e65dfb871 --- /dev/null +++ b/tests/Issues/AddClassDependency/Fixture/strick_with_constructor.php.inc @@ -0,0 +1,43 @@ +get('validator'); + } +} + +?> +----- +validator; + } +} + +?> diff --git a/tests/Issues/AddClassDependency/Source/SomeAutowiredService.php b/tests/Issues/AddClassDependency/Source/SomeAutowiredService.php new file mode 100644 index 00000000000..8c4ca1776e4 --- /dev/null +++ b/tests/Issues/AddClassDependency/Source/SomeAutowiredService.php @@ -0,0 +1,9 @@ +withRules([GetBySymfonyStringToConstructorInjectionRector::class]); diff --git a/tests/Issues/AliasedImportDouble/AliasedImportDoubleTest.php b/tests/Issues/AliasedImportDouble/AliasedImportDoubleTest.php deleted file mode 100644 index ddb0cb0f150..00000000000 --- a/tests/Issues/AliasedImportDouble/AliasedImportDoubleTest.php +++ /dev/null @@ -1,36 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/Issues/AliasedImportDouble/Fixture/fixture.php.inc b/tests/Issues/AliasedImportDouble/Fixture/fixture.php.inc deleted file mode 100644 index cfad933e386..00000000000 --- a/tests/Issues/AliasedImportDouble/Fixture/fixture.php.inc +++ /dev/null @@ -1,26 +0,0 @@ - ------ - diff --git a/tests/Issues/AliasedImportDouble/Rector/ClassMethod/AddAliasImportRector.php b/tests/Issues/AliasedImportDouble/Rector/ClassMethod/AddAliasImportRector.php deleted file mode 100644 index 1347716531e..00000000000 --- a/tests/Issues/AliasedImportDouble/Rector/ClassMethod/AddAliasImportRector.php +++ /dev/null @@ -1,48 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [ClassMethod::class]; - } - - /** - * @param ClassMethod $node - */ - public function refactor(Node $node): ClassMethod - { - $node->name = new Identifier('go'); - - $this->useNodesToAddCollector->addUseImport( - new AliasedObjectType('Extbase', 'TYPO3\CMS\Extbase\Annotation') - ); - - return $node; - } -} diff --git a/tests/Issues/AliasedImportDouble/config/configured_rule.php b/tests/Issues/AliasedImportDouble/config/configured_rule.php deleted file mode 100644 index d6cd347513e..00000000000 --- a/tests/Issues/AliasedImportDouble/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(AddAliasImportRector::class); -}; diff --git a/tests/Issues/AlwaysTrueIfInDeadConstructor/AlwaysTrueIfInDeadConstructorTest.php b/tests/Issues/AlwaysTrueIfInDeadConstructor/AlwaysTrueIfInDeadConstructorTest.php new file mode 100644 index 00000000000..bbb287c1470 --- /dev/null +++ b/tests/Issues/AlwaysTrueIfInDeadConstructor/AlwaysTrueIfInDeadConstructorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/AlwaysTrueIfInDeadConstructor/Fixture/fixture.php.inc b/tests/Issues/AlwaysTrueIfInDeadConstructor/Fixture/fixture.php.inc new file mode 100644 index 00000000000..e75472b2c95 --- /dev/null +++ b/tests/Issues/AlwaysTrueIfInDeadConstructor/Fixture/fixture.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/tests/Issues/AlwaysTrueIfInDeadConstructor/config/configured_rule.php b/tests/Issues/AlwaysTrueIfInDeadConstructor/config/configured_rule.php new file mode 100644 index 00000000000..28b1cce9c1c --- /dev/null +++ b/tests/Issues/AlwaysTrueIfInDeadConstructor/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveAlwaysTrueIfConditionRector::class, RemoveEmptyClassMethodRector::class]); diff --git a/tests/Issues/AnnotationToAttributeFirstClassCallable/AnnotationToAttributeFirstClassCallableTest.php b/tests/Issues/AnnotationToAttributeFirstClassCallable/AnnotationToAttributeFirstClassCallableTest.php new file mode 100644 index 00000000000..4a9b1d1b7d5 --- /dev/null +++ b/tests/Issues/AnnotationToAttributeFirstClassCallable/AnnotationToAttributeFirstClassCallableTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/AnnotationToAttributeFirstClassCallable/Fixture/change_annotation_to_attribute.php.inc b/tests/Issues/AnnotationToAttributeFirstClassCallable/Fixture/change_annotation_to_attribute.php.inc new file mode 100644 index 00000000000..0ae8bc8b94d --- /dev/null +++ b/tests/Issues/AnnotationToAttributeFirstClassCallable/Fixture/change_annotation_to_attribute.php.inc @@ -0,0 +1,45 @@ + +----- + 'public'])] + public array $images, + ) { + } + } + +} + +?> \ No newline at end of file diff --git a/tests/Issues/AnnotationToAttributeFirstClassCallable/config/configured_rule.php b/tests/Issues/AnnotationToAttributeFirstClassCallable/config/configured_rule.php new file mode 100644 index 00000000000..6dd284d96e8 --- /dev/null +++ b/tests/Issues/AnnotationToAttributeFirstClassCallable/config/configured_rule.php @@ -0,0 +1,14 @@ +withConfiguredRule(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Symfony\Component\Serializer\Annotation\Context'), + ]) + ->withRules([ArrayToFirstClassCallableRector::class]); diff --git a/tests/Issues/AnnotationToAttributeRenameAutoImport/AnnotationToAttributeRenameAutoImportTest.php b/tests/Issues/AnnotationToAttributeRenameAutoImport/AnnotationToAttributeRenameAutoImportTest.php new file mode 100644 index 00000000000..77e1c2f7fc9 --- /dev/null +++ b/tests/Issues/AnnotationToAttributeRenameAutoImport/AnnotationToAttributeRenameAutoImportTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/already_attributed.php.inc b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/already_attributed.php.inc new file mode 100644 index 00000000000..8c8bd2ac920 --- /dev/null +++ b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/already_attributed.php.inc @@ -0,0 +1,30 @@ + '\d+', 'networkId' => '\d+'])] +#[\Symfony\Component\Security\Http\Attribute\IsGranted('TEST')] +class WithExistingAttribute extends AbstractController +{ +} + +?> +----- + '\d+', 'networkId' => '\d+'])] +#[IsGranted('TEST')] +class WithExistingAttribute extends AbstractController +{ +} + +?> diff --git a/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/fixture.php.inc b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/fixture.php.inc new file mode 100644 index 00000000000..b542d20b12c --- /dev/null +++ b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/fixture.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/not_imported_yet.php.inc b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/not_imported_yet.php.inc new file mode 100644 index 00000000000..40264756c5f --- /dev/null +++ b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/not_imported_yet.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/not_imported_yet2.php.inc b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/not_imported_yet2.php.inc new file mode 100644 index 00000000000..3d327963a09 --- /dev/null +++ b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/not_imported_yet2.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/with_existing_attribute.php.inc b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/with_existing_attribute.php.inc new file mode 100644 index 00000000000..12573959bca --- /dev/null +++ b/tests/Issues/AnnotationToAttributeRenameAutoImport/Fixture/with_existing_attribute.php.inc @@ -0,0 +1,33 @@ + '\d+', 'networkId' => '\d+'])] +class WithExistingAttribute extends AbstractController +{ +} + +?> +----- + '\d+', 'networkId' => '\d+'])] +#[IsGranted('TEST')] +class WithExistingAttribute extends AbstractController +{ +} + +?> diff --git a/tests/Issues/AnnotationToAttributeRenameAutoImport/config/configured_rule.php b/tests/Issues/AnnotationToAttributeRenameAutoImport/config/configured_rule.php new file mode 100644 index 00000000000..c634e1a6ed9 --- /dev/null +++ b/tests/Issues/AnnotationToAttributeRenameAutoImport/config/configured_rule.php @@ -0,0 +1,24 @@ +importNames(); + + $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted'), + ]); + + $rectorConfig->ruleWithConfiguration( + RenameClassRector::class, + [ + 'Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted' => 'Symfony\Component\Security\Http\Attribute\IsGranted', + ], + ); +}; diff --git a/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/ArrowFunctionToAnonymousFunctionSuperglobalsTest.php b/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/ArrowFunctionToAnonymousFunctionSuperglobalsTest.php new file mode 100644 index 00000000000..364c6338397 --- /dev/null +++ b/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/ArrowFunctionToAnonymousFunctionSuperglobalsTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/Fixture/do_not_allow_superglobals_in_anonymous_function_use.php.inc b/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/Fixture/do_not_allow_superglobals_in_anonymous_function_use.php.inc new file mode 100644 index 00000000000..bd28c1f1f8f --- /dev/null +++ b/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/Fixture/do_not_allow_superglobals_in_anonymous_function_use.php.inc @@ -0,0 +1,27 @@ + $_SERVER['destroy']['retrieved'][] = $model->my_id); + } +} +?> +----- +my_id; + }); + } +} +?> diff --git a/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/config/configured_rule.php b/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/config/configured_rule.php new file mode 100644 index 00000000000..328dcdc77e6 --- /dev/null +++ b/tests/Issues/ArrowFunctionToAnonymousFunctionSuperglobals/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ArrowFunctionToAnonymousFunctionRector::class]); diff --git a/tests/Issues/AttributeAndArgValueRefresh/AttributeAndArgValueRefreshTest.php b/tests/Issues/AttributeAndArgValueRefresh/AttributeAndArgValueRefreshTest.php new file mode 100644 index 00000000000..a216b53da16 --- /dev/null +++ b/tests/Issues/AttributeAndArgValueRefresh/AttributeAndArgValueRefreshTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/AttributeAndArgValueRefresh/Fixture/replace_arg_value.php.inc b/tests/Issues/AttributeAndArgValueRefresh/Fixture/replace_arg_value.php.inc new file mode 100644 index 00000000000..b5197671db7 --- /dev/null +++ b/tests/Issues/AttributeAndArgValueRefresh/Fixture/replace_arg_value.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/AttributeAndArgValueRefresh/Fixture/replace_attribute_value.php.inc b/tests/Issues/AttributeAndArgValueRefresh/Fixture/replace_attribute_value.php.inc new file mode 100644 index 00000000000..f19f83f1d17 --- /dev/null +++ b/tests/Issues/AttributeAndArgValueRefresh/Fixture/replace_attribute_value.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/AttributeAndArgValueRefresh/Source/SetArrayArgValueRector.php b/tests/Issues/AttributeAndArgValueRefresh/Source/SetArrayArgValueRector.php new file mode 100644 index 00000000000..e5600327425 --- /dev/null +++ b/tests/Issues/AttributeAndArgValueRefresh/Source/SetArrayArgValueRector.php @@ -0,0 +1,39 @@ +> + */ + public function getNodeTypes(): array + { + return [Arg::class]; + } + + /** + * @param Arg $node + */ + public function refactor(Node $node): Arg + { + $node->value = new Array_([new ArrayItem(new String_('value'))]); + + return $node; + } +} \ No newline at end of file diff --git a/tests/Issues/AttributeAndArgValueRefresh/Source/SetArrayAttributeValueRector.php b/tests/Issues/AttributeAndArgValueRefresh/Source/SetArrayAttributeValueRector.php new file mode 100644 index 00000000000..76d1b21d0a6 --- /dev/null +++ b/tests/Issues/AttributeAndArgValueRefresh/Source/SetArrayAttributeValueRector.php @@ -0,0 +1,39 @@ +> + */ + public function getNodeTypes(): array + { + return [Attribute::class]; + } + + /** + * @param Attribute $node + */ + public function refactor(Node $node): Attribute + { + $node->args = [new Arg(new Array_([new ArrayItem(new String_('value'))]))]; + return $node; + } +} \ No newline at end of file diff --git a/tests/Issues/AttributeAndArgValueRefresh/config/configured_rule.php b/tests/Issues/AttributeAndArgValueRefresh/config/configured_rule.php new file mode 100644 index 00000000000..a709ea2c910 --- /dev/null +++ b/tests/Issues/AttributeAndArgValueRefresh/config/configured_rule.php @@ -0,0 +1,15 @@ +withRules([ + SetArrayArgValueRector::class, + SetArrayAttributeValueRector::class, + ArrayToFirstClassCallableRector::class, + ]); diff --git a/tests/Issues/AutoImport/AutoImportTest.php b/tests/Issues/AutoImport/AutoImportTest.php new file mode 100644 index 00000000000..8b9ffa016cd --- /dev/null +++ b/tests/Issues/AutoImport/AutoImportTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/annotation_to_attribute.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/annotation_to_attribute.php.inc new file mode 100644 index 00000000000..f095a8ec738 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/annotation_to_attribute.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/conflict_aliased_with_docblock.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/conflict_aliased_with_docblock.php.inc new file mode 100644 index 00000000000..e39b90149c1 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/conflict_aliased_with_docblock.php.inc @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/doctrine_deep_annotation.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/doctrine_deep_annotation.php.inc new file mode 100644 index 00000000000..b249e129572 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/doctrine_deep_annotation.php.inc @@ -0,0 +1,30 @@ + +----- + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/rename_conflict_docblock_name.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/rename_conflict_docblock_name.php.inc new file mode 100644 index 00000000000..330e9275447 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/rename_conflict_docblock_name.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/short_name_renamed_imported.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/short_name_renamed_imported.php.inc new file mode 100644 index 00000000000..33babfbc359 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/short_name_renamed_imported.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/skip_doctrine_aliased.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/skip_doctrine_aliased.php.inc new file mode 100644 index 00000000000..05e92700c0a --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/skip_doctrine_aliased.php.inc @@ -0,0 +1,17 @@ + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes.php.inc new file mode 100644 index 00000000000..666217130fb --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_after_generic.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_after_generic.php.inc new file mode 100644 index 00000000000..d5c4e69229d --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_after_generic.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_after_generic_with_string_key_numeric.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_after_generic_with_string_key_numeric.php.inc new file mode 100644 index 00000000000..97fb761a684 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_after_generic_with_string_key_numeric.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_before_generic.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_before_generic.php.inc new file mode 100644 index 00000000000..b5845d474e5 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_before_generic.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_comment_before.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_comment_before.php.inc new file mode 100644 index 00000000000..a177f80421e --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_comment_before.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_next_doc.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_next_doc.php.inc new file mode 100644 index 00000000000..e0d3c187c39 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_next_doc.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_next_doc_other_routes.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_next_doc_other_routes.php.inc new file mode 100644 index 00000000000..762cac0b882 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_next_doc_other_routes.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_prev_doc.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_prev_doc.php.inc new file mode 100644 index 00000000000..cb7b006c554 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_prev_doc.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_prev_doc_with_description_multiline.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_prev_doc_with_description_multiline.php.inc new file mode 100644 index 00000000000..164c1ab3c61 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/two_routes_with_prev_doc_with_description_multiline.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/union_docblock_space.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/union_docblock_space.php.inc new file mode 100644 index 00000000000..704ff10f871 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/union_docblock_space.php.inc @@ -0,0 +1,35 @@ + $param + */ + public function some($param) + { + } +} + +?> +----- + $param + */ + public function some($param) + { + } +} + +?> diff --git a/tests/Issues/AutoImport/Fixture/DocBlock/union_equal_last_name_class_exists.php.inc b/tests/Issues/AutoImport/Fixture/DocBlock/union_equal_last_name_class_exists.php.inc new file mode 100644 index 00000000000..a275b813dfa --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/DocBlock/union_equal_last_name_class_exists.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/auto_import_in_alias.php.inc b/tests/Issues/AutoImport/Fixture/auto_import_in_alias.php.inc new file mode 100644 index 00000000000..63ebc82c5e6 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/auto_import_in_alias.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/auto_import_in_alias_same_last_name.php.inc b/tests/Issues/AutoImport/Fixture/auto_import_in_alias_same_last_name.php.inc new file mode 100644 index 00000000000..32799ee2275 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/auto_import_in_alias_same_last_name.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/auto_import_in_groupuse.php.inc b/tests/Issues/AutoImport/Fixture/auto_import_in_groupuse.php.inc new file mode 100644 index 00000000000..6a3322de144 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/auto_import_in_groupuse.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/case_insensitive_name_collide.php.inc b/tests/Issues/AutoImport/Fixture/case_insensitive_name_collide.php.inc new file mode 100644 index 00000000000..97167f9d06e --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/case_insensitive_name_collide.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/case_insensitive_name_collide_function.php.inc b/tests/Issues/AutoImport/Fixture/case_insensitive_name_collide_function.php.inc new file mode 100644 index 00000000000..3b3553e2896 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/case_insensitive_name_collide_function.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name.php.inc b/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name.php.inc new file mode 100644 index 00000000000..b82d873be85 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name2.php.inc b/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name2.php.inc new file mode 100644 index 00000000000..51198e84279 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name2.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name3.php.inc b/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name3.php.inc new file mode 100644 index 00000000000..f76f41be64b --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name3.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name_aliased.php.inc b/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name_aliased.php.inc new file mode 100644 index 00000000000..867ce85b0cb --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/do_not_add_cast_valid_int_type_from_name_aliased.php.inc @@ -0,0 +1,41 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/do_not_rename_different_namespaced_function.php.inc b/tests/Issues/AutoImport/Fixture/do_not_rename_different_namespaced_function.php.inc new file mode 100644 index 00000000000..96ca045cb02 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/do_not_rename_different_namespaced_function.php.inc @@ -0,0 +1,23 @@ + +----- + +----- + +----- + diff --git a/tests/Issues/AutoImport/Fixture/fixture.php.inc b/tests/Issues/AutoImport/Fixture/fixture.php.inc new file mode 100644 index 00000000000..062bf6c523d --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/fixture.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/fqcn_current_same_class.php.inc b/tests/Issues/AutoImport/Fixture/fqcn_current_same_class.php.inc new file mode 100644 index 00000000000..bdab86555ff --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/fqcn_current_same_class.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/fqcn_with_subnamespace.php.inc b/tests/Issues/AutoImport/Fixture/fqcn_with_subnamespace.php.inc new file mode 100644 index 00000000000..4d6c7ae0765 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/fqcn_with_subnamespace.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/inner_with_subnamespace.php.inc b/tests/Issues/AutoImport/Fixture/inner_with_subnamespace.php.inc new file mode 100644 index 00000000000..cb8a9c24e6e --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/inner_with_subnamespace.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/inner_with_subnamespace2.php.inc b/tests/Issues/AutoImport/Fixture/inner_with_subnamespace2.php.inc new file mode 100644 index 00000000000..3bbd9928c55 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/inner_with_subnamespace2.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/long_namespace_in_class_name.php.inc b/tests/Issues/AutoImport/Fixture/long_namespace_in_class_name.php.inc new file mode 100644 index 00000000000..64f4228de8c --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/long_namespace_in_class_name.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/namespaced_after_ternary_to_null_coalesce.php.inc b/tests/Issues/AutoImport/Fixture/namespaced_after_ternary_to_null_coalesce.php.inc new file mode 100644 index 00000000000..31075952279 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/namespaced_after_ternary_to_null_coalesce.php.inc @@ -0,0 +1,37 @@ +brands = Hersteller::getListForProducts(new \ProductFilter()); + } + +} + +?> +----- +brands = Hersteller::getListForProducts(new \ProductFilter()); + } + +} + +?> diff --git a/tests/Issues/AutoImport/Fixture/no_namespace_after_ternary_to_null_coalesce.php.inc b/tests/Issues/AutoImport/Fixture/no_namespace_after_ternary_to_null_coalesce.php.inc new file mode 100644 index 00000000000..7406f2f0b55 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/no_namespace_after_ternary_to_null_coalesce.php.inc @@ -0,0 +1,33 @@ +brands = Hersteller::getListForProducts(new \ProductFilter()); + } + +} + +?> +----- +brands = Hersteller::getListForProducts(new \ProductFilter()); + } + +} + +?> diff --git a/tests/Issues/AutoImport/Fixture/no_namespace_after_ternary_to_null_coalesce_long_name.php.inc b/tests/Issues/AutoImport/Fixture/no_namespace_after_ternary_to_null_coalesce_long_name.php.inc new file mode 100644 index 00000000000..5c38f936863 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/no_namespace_after_ternary_to_null_coalesce_long_name.php.inc @@ -0,0 +1,33 @@ +brands = Hersteller::getListForProducts(new \some\different\ProductFilter()); + } + +} + +?> +----- +brands = Hersteller::getListForProducts(new \some\different\ProductFilter()); + } + +} + +?> diff --git a/tests/Issues/AutoImport/Fixture/no_namespace_used_class_renamed.php.inc b/tests/Issues/AutoImport/Fixture/no_namespace_used_class_renamed.php.inc new file mode 100644 index 00000000000..d08b46226f2 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/no_namespace_used_class_renamed.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/no_namespace_with_strict_types.php.inc b/tests/Issues/AutoImport/Fixture/no_namespace_with_strict_types.php.inc new file mode 100644 index 00000000000..1d1db6c87cb --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/no_namespace_with_strict_types.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/no_namespace_with_strict_types_no_existing_use.php.inc b/tests/Issues/AutoImport/Fixture/no_namespace_with_strict_types_no_existing_use.php.inc new file mode 100644 index 00000000000..3e70b81e939 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/no_namespace_with_strict_types_no_existing_use.php.inc @@ -0,0 +1,37 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/same_namespace_with_sub_use.php.inc b/tests/Issues/AutoImport/Fixture/same_namespace_with_sub_use.php.inc new file mode 100644 index 00000000000..3feb0622dfd --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/same_namespace_with_sub_use.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/short_name_import_in_class_name.php.inc b/tests/Issues/AutoImport/Fixture/short_name_import_in_class_name.php.inc new file mode 100644 index 00000000000..5b0d07b3a1c --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/short_name_import_in_class_name.php.inc @@ -0,0 +1,6 @@ +json([ + 'message' => 'Welcome to your new controller!', + 'path' => 'src/Controller/MainController.php', + ]); + } +} + +?> diff --git a/tests/Issues/AutoImport/Fixture/skip_auto_import_in_alias_same_last_name_different_fqcn.php.inc b/tests/Issues/AutoImport/Fixture/skip_auto_import_in_alias_same_last_name_different_fqcn.php.inc new file mode 100644 index 00000000000..f45e91f8757 --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/skip_auto_import_in_alias_same_last_name_different_fqcn.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/tests/Issues/AutoImport/Fixture/with_namespace_used_class_renamed.php.inc b/tests/Issues/AutoImport/Fixture/with_namespace_used_class_renamed.php.inc new file mode 100644 index 00000000000..13f3f6e461c --- /dev/null +++ b/tests/Issues/AutoImport/Fixture/with_namespace_used_class_renamed.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/tests/Issues/AutoImport/Source/Annotation/SomeEnum.php b/tests/Issues/AutoImport/Source/Annotation/SomeEnum.php new file mode 100644 index 00000000000..dd1321dc900 --- /dev/null +++ b/tests/Issues/AutoImport/Source/Annotation/SomeEnum.php @@ -0,0 +1,16 @@ +importNames(); + + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'Some\Exception' => 'Some\Target\Exception', + 'DateTime' => 'DateTimeInterface', + 'Phalcon\Logger' => 'Phalcon\Logger\Logger', + ]); + + $rectorConfig->rule(TernaryToNullCoalescingRector::class); + + $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Doctrine\ORM\Mapping\Entity'), + ]); + + $rectorConfig->rules([ConsoleExecuteReturnIntRector::class, RemoveUnusedPrivatePropertyRector::class]); + + $rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, [ + 'split' => 'explode', + ]); +}; diff --git a/tests/Issues/AutoImportBackslashed/AutoImportBackslashedTest.php b/tests/Issues/AutoImportBackslashed/AutoImportBackslashedTest.php deleted file mode 100644 index 476e01e0812..00000000000 --- a/tests/Issues/AutoImportBackslashed/AutoImportBackslashedTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/Issues/AutoImportBackslashed/Fixture/coalesce.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/coalesce.php.inc deleted file mode 100644 index a79310bc29e..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/coalesce.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/tests/Issues/AutoImportBackslashed/Fixture/complex_return.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/complex_return.php.inc deleted file mode 100644 index a8b13ba500e..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/complex_return.php.inc +++ /dev/null @@ -1,59 +0,0 @@ - ------ - diff --git a/tests/Issues/AutoImportBackslashed/Fixture/direct_return.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/direct_return.php.inc deleted file mode 100644 index 75c12399a84..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/direct_return.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/tests/Issues/AutoImportBackslashed/Fixture/else_ternary_union.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/else_ternary_union.php.inc deleted file mode 100644 index 6f8954e5d33..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/else_ternary_union.php.inc +++ /dev/null @@ -1,45 +0,0 @@ -getOther(); - } - - public function getOther(): string|int - { - return rand(0, 1) - ? 'a' - : 1; - } -} - -?> ------ -getOther(); - } - - public function getOther(): string|int - { - return rand(0, 1) !== 0 - ? 'a' - : 1; - } -} - -?> \ No newline at end of file diff --git a/tests/Issues/AutoImportBackslashed/Fixture/elvis_exact_value.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/elvis_exact_value.php.inc deleted file mode 100644 index 6f3521c744e..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/elvis_exact_value.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/tests/Issues/AutoImportBackslashed/Fixture/elvis_nullable.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/elvis_nullable.php.inc deleted file mode 100644 index da2f24591ab..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/elvis_nullable.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/tests/Issues/AutoImportBackslashed/Fixture/ternary_return.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/ternary_return.php.inc deleted file mode 100644 index d50d849cef8..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/ternary_return.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/tests/Issues/AutoImportBackslashed/Fixture/ternary_return3.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/ternary_return3.php.inc deleted file mode 100644 index f956d4eab37..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/ternary_return3.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/tests/Issues/AutoImportBackslashed/Fixture/ternary_return4.php.inc b/tests/Issues/AutoImportBackslashed/Fixture/ternary_return4.php.inc deleted file mode 100644 index 2584463c65c..00000000000 --- a/tests/Issues/AutoImportBackslashed/Fixture/ternary_return4.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/tests/Issues/AutoImportBackslashed/config/configured_rule.php b/tests/Issues/AutoImportBackslashed/config/configured_rule.php deleted file mode 100644 index fb3166108ba..00000000000 --- a/tests/Issues/AutoImportBackslashed/config/configured_rule.php +++ /dev/null @@ -1,17 +0,0 @@ -services(); - $services->set(ExplicitBoolCompareRector::class); - $services->set(ReturnTypeDeclarationRector::class); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); -}; diff --git a/tests/Issues/AutoImportShortName/AutoImportShortNameTest.php b/tests/Issues/AutoImportShortName/AutoImportShortNameTest.php new file mode 100644 index 00000000000..3aed5f9828a --- /dev/null +++ b/tests/Issues/AutoImportShortName/AutoImportShortNameTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/AutoImportShortName/DisableShortClassNameAutoImportTest.php b/tests/Issues/AutoImportShortName/DisableShortClassNameAutoImportTest.php new file mode 100644 index 00000000000..c583a9d761c --- /dev/null +++ b/tests/Issues/AutoImportShortName/DisableShortClassNameAutoImportTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureDisableShortClassNameAutoImport'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/disable_short_name_configured_rule.php'; + } +} diff --git a/tests/Issues/AutoImportShortName/Fixture/import_docblock_shortname_fqcn.php.inc b/tests/Issues/AutoImportShortName/Fixture/import_docblock_shortname_fqcn.php.inc new file mode 100644 index 00000000000..302692f9ecc --- /dev/null +++ b/tests/Issues/AutoImportShortName/Fixture/import_docblock_shortname_fqcn.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/tests/Issues/AutoImportShortName/Fixture/import_short_name.php.inc b/tests/Issues/AutoImportShortName/Fixture/import_short_name.php.inc new file mode 100644 index 00000000000..b8bd82b49ef --- /dev/null +++ b/tests/Issues/AutoImportShortName/Fixture/import_short_name.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/AutoImportShortName/Fixture/in_no_namespace_short_name.php.inc b/tests/Issues/AutoImportShortName/Fixture/in_no_namespace_short_name.php.inc new file mode 100644 index 00000000000..4a5cc59ef51 --- /dev/null +++ b/tests/Issues/AutoImportShortName/Fixture/in_no_namespace_short_name.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/tests/Issues/AutoImportShortName/Fixture/same_last_name_not_imported.php.inc b/tests/Issues/AutoImportShortName/Fixture/same_last_name_not_imported.php.inc new file mode 100644 index 00000000000..2b1e5881462 --- /dev/null +++ b/tests/Issues/AutoImportShortName/Fixture/same_last_name_not_imported.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/tests/Issues/AutoImportShortName/Fixture/skip_imported_shortname.php.inc b/tests/Issues/AutoImportShortName/Fixture/skip_imported_shortname.php.inc new file mode 100644 index 00000000000..e4d82530d86 --- /dev/null +++ b/tests/Issues/AutoImportShortName/Fixture/skip_imported_shortname.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/tests/Issues/AutoImportShortName/FixtureDisableShortClassNameAutoImport/skip_import_disabled_short_class_name.php.inc b/tests/Issues/AutoImportShortName/FixtureDisableShortClassNameAutoImport/skip_import_disabled_short_class_name.php.inc new file mode 100644 index 00000000000..2527ada732b --- /dev/null +++ b/tests/Issues/AutoImportShortName/FixtureDisableShortClassNameAutoImport/skip_import_disabled_short_class_name.php.inc @@ -0,0 +1,10 @@ +importNames(); +}; diff --git a/tests/Issues/AutoImportShortName/config/disable_short_name_configured_rule.php b/tests/Issues/AutoImportShortName/config/disable_short_name_configured_rule.php new file mode 100644 index 00000000000..eea28358ddb --- /dev/null +++ b/tests/Issues/AutoImportShortName/config/disable_short_name_configured_rule.php @@ -0,0 +1,10 @@ +importNames(); + $rectorConfig->importShortClasses(false); +}; diff --git a/tests/Issues/ChangeSwitchTernary/ChangeSwitchTernaryTest.php b/tests/Issues/ChangeSwitchTernary/ChangeSwitchTernaryTest.php new file mode 100644 index 00000000000..df2f0fe6432 --- /dev/null +++ b/tests/Issues/ChangeSwitchTernary/ChangeSwitchTernaryTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/ChangeSwitchTernary/Fixture/fixture.php.inc b/tests/Issues/ChangeSwitchTernary/Fixture/fixture.php.inc new file mode 100644 index 00000000000..8a0f73b220a --- /dev/null +++ b/tests/Issues/ChangeSwitchTernary/Fixture/fixture.php.inc @@ -0,0 +1,56 @@ + +----- + 'one', + 2 => 'two', + default => 'other', + }; + } + } +} + +?> diff --git a/tests/Issues/ChangeSwitchTernary/Source/AnotherExpressionRector.php b/tests/Issues/ChangeSwitchTernary/Source/AnotherExpressionRector.php new file mode 100644 index 00000000000..dbe930548b5 --- /dev/null +++ b/tests/Issues/ChangeSwitchTernary/Source/AnotherExpressionRector.php @@ -0,0 +1,32 @@ +withRules( + [ChangeSwitchToMatchRector::class, TernaryFalseExpressionToIfRector::class, AnotherExpressionRector::class] + ); diff --git a/tests/Issues/ClassOnObjectGetDebugType/ClassOnObjectGetDebugTypeTest.php b/tests/Issues/ClassOnObjectGetDebugType/ClassOnObjectGetDebugTypeTest.php new file mode 100644 index 00000000000..51f5aafcbcc --- /dev/null +++ b/tests/Issues/ClassOnObjectGetDebugType/ClassOnObjectGetDebugTypeTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/ClassOnObjectGetDebugType/Fixture/fixture.php.inc b/tests/Issues/ClassOnObjectGetDebugType/Fixture/fixture.php.inc new file mode 100644 index 00000000000..93a06b4c503 --- /dev/null +++ b/tests/Issues/ClassOnObjectGetDebugType/Fixture/fixture.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/tests/Issues/ClassOnObjectGetDebugType/config/configured_rule.php b/tests/Issues/ClassOnObjectGetDebugType/config/configured_rule.php new file mode 100644 index 00000000000..02fbf5362d1 --- /dev/null +++ b/tests/Issues/ClassOnObjectGetDebugType/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([ClassOnObjectRector::class, GetDebugTypeRector::class]); diff --git a/tests/Issues/ConstructorPromoAnnotationToAttribute/ConstructorPromoAnnotationToAttributeTest.php b/tests/Issues/ConstructorPromoAnnotationToAttribute/ConstructorPromoAnnotationToAttributeTest.php new file mode 100644 index 00000000000..9842ae4271c --- /dev/null +++ b/tests/Issues/ConstructorPromoAnnotationToAttribute/ConstructorPromoAnnotationToAttributeTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/ConstructorPromoAnnotationToAttribute/Fixture/fixture.php.inc b/tests/Issues/ConstructorPromoAnnotationToAttribute/Fixture/fixture.php.inc new file mode 100644 index 00000000000..0d50e53ab52 --- /dev/null +++ b/tests/Issues/ConstructorPromoAnnotationToAttribute/Fixture/fixture.php.inc @@ -0,0 +1,44 @@ +items = $items; + } +} +?> +----- + diff --git a/tests/Issues/ConstructorPromoAnnotationToAttribute/config/configured_rule.php b/tests/Issues/ConstructorPromoAnnotationToAttribute/config/configured_rule.php new file mode 100644 index 00000000000..3d2b7a54895 --- /dev/null +++ b/tests/Issues/ConstructorPromoAnnotationToAttribute/config/configured_rule.php @@ -0,0 +1,15 @@ +rule(ClassPropertyAssignToConstructorPromotionRector::class); + $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('OldTag', 'NewAttribute'), + ]); +}; diff --git a/tests/Issues/ContinueToBreakSwitch/ContinueToBreakSwitchTest.php b/tests/Issues/ContinueToBreakSwitch/ContinueToBreakSwitchTest.php new file mode 100644 index 00000000000..c08351c8a56 --- /dev/null +++ b/tests/Issues/ContinueToBreakSwitch/ContinueToBreakSwitchTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/ContinueToBreakSwitch/Fixture/fixture.php.inc b/tests/Issues/ContinueToBreakSwitch/Fixture/fixture.php.inc new file mode 100644 index 00000000000..c23f642cad5 --- /dev/null +++ b/tests/Issues/ContinueToBreakSwitch/Fixture/fixture.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/tests/Issues/ContinueToBreakSwitch/config/configured_rule.php b/tests/Issues/ContinueToBreakSwitch/config/configured_rule.php new file mode 100644 index 00000000000..64043fdec48 --- /dev/null +++ b/tests/Issues/ContinueToBreakSwitch/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([BreakNotInLoopOrSwitchToReturnRector::class, ContinueToBreakInSwitchRector::class]); diff --git a/tests/Issues/CountArrayLongToShort/CountArrayLongToShortTest.php b/tests/Issues/CountArrayLongToShort/CountArrayLongToShortTest.php new file mode 100644 index 00000000000..f7377e4a126 --- /dev/null +++ b/tests/Issues/CountArrayLongToShort/CountArrayLongToShortTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/CountArrayLongToShort/Fixture/count_to_empty_array_compare.php.inc b/tests/Issues/CountArrayLongToShort/Fixture/count_to_empty_array_compare.php.inc new file mode 100644 index 00000000000..7008101cb70 --- /dev/null +++ b/tests/Issues/CountArrayLongToShort/Fixture/count_to_empty_array_compare.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/tests/Issues/CountArrayLongToShort/config/configured_rule.php b/tests/Issues/CountArrayLongToShort/config/configured_rule.php new file mode 100644 index 00000000000..7bbf55deaa2 --- /dev/null +++ b/tests/Issues/CountArrayLongToShort/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([CountArrayToEmptyArrayComparisonRector::class, LongArrayToShortArrayRector::class]); diff --git a/tests/Issues/CountOnNullExactlyArray/CountOnNullExactlyArrayTest.php b/tests/Issues/CountOnNullExactlyArray/CountOnNullExactlyArrayTest.php deleted file mode 100644 index 23752b6b341..00000000000 --- a/tests/Issues/CountOnNullExactlyArray/CountOnNullExactlyArrayTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/Issues/CountOnNullExactlyArray/Fixture/fixture.php.inc b/tests/Issues/CountOnNullExactlyArray/Fixture/fixture.php.inc deleted file mode 100644 index 6354413ab0b..00000000000 --- a/tests/Issues/CountOnNullExactlyArray/Fixture/fixture.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - 0) { - } - } -} - -?> ------ - 0) { - } - } -} - -?> diff --git a/tests/Issues/CountOnNullExactlyArray/config/configured_rule.php b/tests/Issues/CountOnNullExactlyArray/config/configured_rule.php deleted file mode 100644 index e0ea31cb483..00000000000 --- a/tests/Issues/CountOnNullExactlyArray/config/configured_rule.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(ChangeReadOnlyVariableWithDefaultValueToConstantRector::class); - $services->set(CountOnNullRector::class); -}; diff --git a/tests/Issues/CovariantTrio/CovariantTrioTest.php b/tests/Issues/CovariantTrio/CovariantTrioTest.php deleted file mode 100644 index c18a4a5889f..00000000000 --- a/tests/Issues/CovariantTrio/CovariantTrioTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/covariant_trio.php'; - } -} diff --git a/tests/Issues/CovariantTrio/Fixture/coariant_mixture.php.inc b/tests/Issues/CovariantTrio/Fixture/coariant_mixture.php.inc deleted file mode 100644 index a994b9d6269..00000000000 --- a/tests/Issues/CovariantTrio/Fixture/coariant_mixture.php.inc +++ /dev/null @@ -1,34 +0,0 @@ -getName()) { - } - } -} - -?> ------ -getName()) { - } - } -} - -?> diff --git a/tests/Issues/CovariantTrio/Fixture/input_interface.php.inc b/tests/Issues/CovariantTrio/Fixture/input_interface.php.inc deleted file mode 100644 index 2483b3de305..00000000000 --- a/tests/Issues/CovariantTrio/Fixture/input_interface.php.inc +++ /dev/null @@ -1,95 +0,0 @@ -getName()) { - } - } - - /** - * {@inheritdoc} - */ - public function hasParameterOption($values, bool $onlyParams = false) - { - } -} - -abstract class Input implements InputInterface -{ - -} - -class ArrayInput extends Input -{ - public function hasParameterOption($values, bool $onlyParams = false) - { - - } -} - -interface InputInterface -{ - public function hasParameterOption($values, bool $onlyParams = false); -} - -?> ------ -getName()) { - } - } - - /** - * {@inheritdoc} - * @param bool $onlyParams - */ - public function hasParameterOption($values, $onlyParams = false) - { - } -} - -abstract class Input implements InputInterface -{ - -} - -class ArrayInput extends Input -{ - /** - * @param bool $onlyParams - */ - public function hasParameterOption($values, $onlyParams = false) - { - - } -} - -interface InputInterface -{ - /** - * @param bool $onlyParams - */ - public function hasParameterOption($values, $onlyParams = false); -} - -?> diff --git a/tests/Issues/CovariantTrio/config/covariant_trio.php b/tests/Issues/CovariantTrio/config/covariant_trio.php deleted file mode 100644 index 6b09458785d..00000000000 --- a/tests/Issues/CovariantTrio/config/covariant_trio.php +++ /dev/null @@ -1,20 +0,0 @@ -parameters(); - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_70); - - $services = $containerConfigurator->services(); - $services->set(DowngradeArrayKeyFirstLastRector::class); - $services->set(DowngradeScalarTypeDeclarationRector::class); - $services->set(DowngradeNullCoalesceRector::class); -}; diff --git a/tests/Issues/DeadInstanceFlip/DeadInstanceFlipTest.php b/tests/Issues/DeadInstanceFlip/DeadInstanceFlipTest.php new file mode 100644 index 00000000000..8b8c343df45 --- /dev/null +++ b/tests/Issues/DeadInstanceFlip/DeadInstanceFlipTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/DeadInstanceFlip/Fixture/fixture.php.inc b/tests/Issues/DeadInstanceFlip/Fixture/fixture.php.inc new file mode 100644 index 00000000000..b911733a56d --- /dev/null +++ b/tests/Issues/DeadInstanceFlip/Fixture/fixture.php.inc @@ -0,0 +1,34 @@ +getSomeType()) { + $class->result; + } + } +} + +?> +----- +getSomeType(); + $class->result; + } +} + +?> diff --git a/tests/Issues/DeadInstanceFlip/Source/SomeEvent.php b/tests/Issues/DeadInstanceFlip/Source/SomeEvent.php new file mode 100644 index 00000000000..4b466bc5ec6 --- /dev/null +++ b/tests/Issues/DeadInstanceFlip/Source/SomeEvent.php @@ -0,0 +1,11 @@ +withRules([RemoveDeadInstanceOfRector::class, FlipTypeControlToUseExclusiveTypeRector::class]); diff --git a/tests/Issues/DoubleImportedTraitUse/DoubleImportedTraitUseTest.php b/tests/Issues/DoubleImportedTraitUse/DoubleImportedTraitUseTest.php deleted file mode 100644 index e133e8b2758..00000000000 --- a/tests/Issues/DoubleImportedTraitUse/DoubleImportedTraitUseTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/double_imported_trait_use.php'; - } -} diff --git a/tests/Issues/DoubleImportedTraitUse/Fixture/add_array_trait.php.inc b/tests/Issues/DoubleImportedTraitUse/Fixture/add_array_trait.php.inc deleted file mode 100644 index d008071cf7a..00000000000 --- a/tests/Issues/DoubleImportedTraitUse/Fixture/add_array_trait.php.inc +++ /dev/null @@ -1,74 +0,0 @@ -client = $this->prophesize(stdClass::class); - } - - /** - * @dataProvider invalidValues - */ - public function testInvalidValues(int | array $value): void - { - } - - public function invalidValues() - { - yield 'Class without __toString not possible' => [1000]; - } -} - -?> ------ -client = $this->prophesize(stdClass::class); - } - - /** - * @dataProvider invalidValues - * @param int $value - */ - public function testInvalidValues(int | array $value): void - { - } - - public function invalidValues() - { - yield 'Class without __toString not possible' => [1000]; - } -} - -?> diff --git a/tests/Issues/DoubleImportedTraitUse/config/double_imported_trait_use.php b/tests/Issues/DoubleImportedTraitUse/config/double_imported_trait_use.php deleted file mode 100644 index ba0e6dfccd1..00000000000 --- a/tests/Issues/DoubleImportedTraitUse/config/double_imported_trait_use.php +++ /dev/null @@ -1,17 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(AddProphecyTraitRector::class); - $services->set(AddArrayParamDocTypeRector::class); -}; diff --git a/tests/Issues/DoubleRun/DoubleRunTest.php b/tests/Issues/DoubleRun/DoubleRunTest.php index 41bd4285359..2bd6de35aa5 100644 --- a/tests/Issues/DoubleRun/DoubleRunTest.php +++ b/tests/Issues/DoubleRun/DoubleRunTest.php @@ -2,28 +2,23 @@ declare(strict_types=1); -namespace Rector\Core\Tests\Issues\DoubleRun; +namespace Rector\Tests\Issues\DoubleRun; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class DoubleRunTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/Issues/DoubleRun/Fixture/fixture.php.inc b/tests/Issues/DoubleRun/Fixture/fixture.php.inc index a31ea4e09e9..1eae42bb94a 100644 --- a/tests/Issues/DoubleRun/Fixture/fixture.php.inc +++ b/tests/Issues/DoubleRun/Fixture/fixture.php.inc @@ -1,11 +1,11 @@ import(SetList::DEAD_CODE); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->sets([SetList::DEAD_CODE]); }; diff --git a/tests/Issues/DowngradeCombo/CallableInterfaceDowngradeTest.php b/tests/Issues/DowngradeCombo/CallableInterfaceDowngradeTest.php deleted file mode 100644 index 8528afa555c..00000000000 --- a/tests/Issues/DowngradeCombo/CallableInterfaceDowngradeTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/callable_interface_downgrade.php'; - } -} diff --git a/tests/Issues/DowngradeCombo/Fixture/class_with_nullable_callable.php.inc b/tests/Issues/DowngradeCombo/Fixture/class_with_nullable_callable.php.inc deleted file mode 100644 index 6b3a1ee1c96..00000000000 --- a/tests/Issues/DowngradeCombo/Fixture/class_with_nullable_callable.php.inc +++ /dev/null @@ -1,38 +0,0 @@ - ------ - diff --git a/tests/Issues/DowngradeCombo/config/callable_interface_downgrade.php b/tests/Issues/DowngradeCombo/config/callable_interface_downgrade.php deleted file mode 100644 index cd3a0ac370b..00000000000 --- a/tests/Issues/DowngradeCombo/config/callable_interface_downgrade.php +++ /dev/null @@ -1,14 +0,0 @@ -import(DowngradeSetList::PHP_71); - $containerConfigurator->import(DowngradeSetList::PHP_72); - $containerConfigurator->import(DowngradeSetList::PHP_73); - $containerConfigurator->import(DowngradeSetList::PHP_74); - $containerConfigurator->import(DowngradeSetList::PHP_80); -}; diff --git a/tests/Issues/DynamicDocblockRename/DynamicDocblockRenameTest.php b/tests/Issues/DynamicDocblockRename/DynamicDocblockRenameTest.php new file mode 100644 index 00000000000..c1980963f2b --- /dev/null +++ b/tests/Issues/DynamicDocblockRename/DynamicDocblockRenameTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/DynamicDocblockRename/Fixture/fixture.php.inc b/tests/Issues/DynamicDocblockRename/Fixture/fixture.php.inc new file mode 100644 index 00000000000..c89d3711f52 --- /dev/null +++ b/tests/Issues/DynamicDocblockRename/Fixture/fixture.php.inc @@ -0,0 +1,36 @@ +someDependency = new SomeSource\SomeDependency(); + } +} + +?> +----- +someDependency = new \stdClass(); + } +} + +?> diff --git a/tests/Issues/DynamicDocblockRename/config/configured_rule.php b/tests/Issues/DynamicDocblockRename/config/configured_rule.php new file mode 100644 index 00000000000..a132a15f1ae --- /dev/null +++ b/tests/Issues/DynamicDocblockRename/config/configured_rule.php @@ -0,0 +1,18 @@ +rule(DynamicDocBlockPropertyToNativePropertyRector::class); + $rectorConfig->ruleWithConfiguration( + RenameClassRector::class, + [ + 'Rector\Tests\CodeQuality\Rector\Class_\DynamicDocBlockPropertyToNativePropertyRector\Source\SomeDependency' + => 'stdClass', + ] + ); +}; diff --git a/tests/Issues/EmptyLineNestedAnnotationToAttribute/EmptyLineNestedAnnotationToAttributeTest.php b/tests/Issues/EmptyLineNestedAnnotationToAttribute/EmptyLineNestedAnnotationToAttributeTest.php new file mode 100644 index 00000000000..17236080701 --- /dev/null +++ b/tests/Issues/EmptyLineNestedAnnotationToAttribute/EmptyLineNestedAnnotationToAttributeTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/EmptyLineNestedAnnotationToAttribute/Fixture/fixture.php.inc b/tests/Issues/EmptyLineNestedAnnotationToAttribute/Fixture/fixture.php.inc new file mode 100644 index 00000000000..c380503573b --- /dev/null +++ b/tests/Issues/EmptyLineNestedAnnotationToAttribute/Fixture/fixture.php.inc @@ -0,0 +1,46 @@ + +----- + diff --git a/tests/Issues/EmptyLineNestedAnnotationToAttribute/config/configured_rule.php b/tests/Issues/EmptyLineNestedAnnotationToAttribute/config/configured_rule.php new file mode 100644 index 00000000000..33eb0ba8455 --- /dev/null +++ b/tests/Issues/EmptyLineNestedAnnotationToAttribute/config/configured_rule.php @@ -0,0 +1,10 @@ +sets([DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES]); +}; diff --git a/tests/Issues/EmptyLongArraySyntax/EmptyLongArraySyntaxTest.php b/tests/Issues/EmptyLongArraySyntax/EmptyLongArraySyntaxTest.php new file mode 100644 index 00000000000..6dd6e36f39e --- /dev/null +++ b/tests/Issues/EmptyLongArraySyntax/EmptyLongArraySyntaxTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/EmptyLongArraySyntax/Fixture/empty_long_array.php.inc b/tests/Issues/EmptyLongArraySyntax/Fixture/empty_long_array.php.inc new file mode 100644 index 00000000000..ecf782bedac --- /dev/null +++ b/tests/Issues/EmptyLongArraySyntax/Fixture/empty_long_array.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/tests/Issues/EmptyLongArraySyntax/Source/ParentWithEmptyLongArray.php b/tests/Issues/EmptyLongArraySyntax/Source/ParentWithEmptyLongArray.php new file mode 100644 index 00000000000..32db379c6a5 --- /dev/null +++ b/tests/Issues/EmptyLongArraySyntax/Source/ParentWithEmptyLongArray.php @@ -0,0 +1,12 @@ +withRules([SensitiveConstantNameRector::class, AddParamBasedOnParentClassMethodRector::class]); diff --git a/tests/Issues/FileWithoutNamespaceCompat/FileWithoutNamespaceCompatTest.php b/tests/Issues/FileWithoutNamespaceCompat/FileWithoutNamespaceCompatTest.php new file mode 100644 index 00000000000..f4f57be3b9e --- /dev/null +++ b/tests/Issues/FileWithoutNamespaceCompat/FileWithoutNamespaceCompatTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/FileWithoutNamespaceCompat/Fixture/namespace_less_file.php.inc b/tests/Issues/FileWithoutNamespaceCompat/Fixture/namespace_less_file.php.inc new file mode 100644 index 00000000000..c6a2f6dbc4d --- /dev/null +++ b/tests/Issues/FileWithoutNamespaceCompat/Fixture/namespace_less_file.php.inc @@ -0,0 +1,16 @@ + +----- +namespacedName = new Name('someFunction'); + + $node->stmts[] = $function; + + return $node; + } +} diff --git a/tests/Issues/FileWithoutNamespaceCompat/config/configured_rule.php b/tests/Issues/FileWithoutNamespaceCompat/config/configured_rule.php new file mode 100644 index 00000000000..ddceb900caf --- /dev/null +++ b/tests/Issues/FileWithoutNamespaceCompat/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([SubscribedToFileWithoutNamespaceRector::class]); diff --git a/tests/Issues/GetCalledClassToSelfAndStatic/Fixture/on_final_class.php.inc b/tests/Issues/GetCalledClassToSelfAndStatic/Fixture/on_final_class.php.inc new file mode 100644 index 00000000000..8f1b92f069b --- /dev/null +++ b/tests/Issues/GetCalledClassToSelfAndStatic/Fixture/on_final_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/GetCalledClassToSelfAndStatic/Fixture/on_non_final_class.php.inc b/tests/Issues/GetCalledClassToSelfAndStatic/Fixture/on_non_final_class.php.inc new file mode 100644 index 00000000000..f52bed5fda1 --- /dev/null +++ b/tests/Issues/GetCalledClassToSelfAndStatic/Fixture/on_non_final_class.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/GetCalledClassToSelfAndStatic/GetCalledClassToSelfAndStaticTest.php b/tests/Issues/GetCalledClassToSelfAndStatic/GetCalledClassToSelfAndStaticTest.php new file mode 100644 index 00000000000..6e5579ba730 --- /dev/null +++ b/tests/Issues/GetCalledClassToSelfAndStatic/GetCalledClassToSelfAndStaticTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/GetCalledClassToSelfAndStatic/config/configured_rule.php b/tests/Issues/GetCalledClassToSelfAndStatic/config/configured_rule.php new file mode 100644 index 00000000000..ee2e08c8276 --- /dev/null +++ b/tests/Issues/GetCalledClassToSelfAndStatic/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([GetCalledClassToSelfClassRector::class, GetCalledClassToStaticClassRector::class]); diff --git a/tests/Issues/GetValueMagicDir/Fixture/fixture.php.inc b/tests/Issues/GetValueMagicDir/Fixture/fixture.php.inc new file mode 100644 index 00000000000..1e189224851 --- /dev/null +++ b/tests/Issues/GetValueMagicDir/Fixture/fixture.php.inc @@ -0,0 +1,33 @@ + ['foo' => __DIR__ . '/bar']]); + } +} + +?> +----- + ['foo' => 'tests/Issues/GetValueMagicDir/Fixture/bar']]; + } +} + +?> diff --git a/tests/Issues/GetValueMagicDir/GetValueMagicDirTest.php b/tests/Issues/GetValueMagicDir/GetValueMagicDirTest.php new file mode 100644 index 00000000000..adcb498718b --- /dev/null +++ b/tests/Issues/GetValueMagicDir/GetValueMagicDirTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/GetValueMagicDir/Source/GetValueMagicDirRector.php b/tests/Issues/GetValueMagicDir/Source/GetValueMagicDirRector.php new file mode 100644 index 00000000000..2827fe448ed --- /dev/null +++ b/tests/Issues/GetValueMagicDir/Source/GetValueMagicDirRector.php @@ -0,0 +1,49 @@ +valueResolver->getValue($node->args[0]->value); + + if ($node instanceof FuncCall) { + return new String_($this->filePathHelper->relativePath($value)); + } + + $value['bar']['foo'] = $this->filePathHelper->relativePath($value['bar']['foo']); + return $this->nodeFactory->createArray($value); + } +} diff --git a/tests/Issues/GetValueMagicDir/config/configured_rule.php b/tests/Issues/GetValueMagicDir/config/configured_rule.php new file mode 100644 index 00000000000..70c2ddb94b8 --- /dev/null +++ b/tests/Issues/GetValueMagicDir/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([GetValueMagicDirRector::class]); diff --git a/tests/Issues/IfElseAssignReturnUsed/Fixture/use_on_return_after_if_else_assign.php.inc b/tests/Issues/IfElseAssignReturnUsed/Fixture/use_on_return_after_if_else_assign.php.inc new file mode 100644 index 00000000000..626a775465c --- /dev/null +++ b/tests/Issues/IfElseAssignReturnUsed/Fixture/use_on_return_after_if_else_assign.php.inc @@ -0,0 +1,41 @@ +toRawArray(); + } else { + $properties = (array) $data; + } + + return $properties; + } +} + +?> +----- +toRawArray(); + } + + return (array) $data; + } +} + +?> diff --git a/tests/Issues/IfElseAssignReturnUsed/IfElseAssignReturnUsedTest.php b/tests/Issues/IfElseAssignReturnUsed/IfElseAssignReturnUsedTest.php new file mode 100644 index 00000000000..d00bd1bbc48 --- /dev/null +++ b/tests/Issues/IfElseAssignReturnUsed/IfElseAssignReturnUsedTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/IfElseAssignReturnUsed/config/configured_rule.php b/tests/Issues/IfElseAssignReturnUsed/config/configured_rule.php new file mode 100644 index 00000000000..89b4036772d --- /dev/null +++ b/tests/Issues/IfElseAssignReturnUsed/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([ChangeIfElseValueAssignToEarlyReturnRector::class, SimplifyIfElseToTernaryRector::class]); diff --git a/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var.php.inc b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var.php.inc new file mode 100644 index 00000000000..38665794946 --- /dev/null +++ b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var2.php.inc b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var2.php.inc new file mode 100644 index 00000000000..7f866a23bc9 --- /dev/null +++ b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var2.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var3.php.inc b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var3.php.inc new file mode 100644 index 00000000000..6f00574817e --- /dev/null +++ b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Fixture/add_property_var3.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/tests/Issues/ImportFullyQualifiedIdentifierDocblock/ImportFullyQualifiedIdentifierDocblockTest.php b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/ImportFullyQualifiedIdentifierDocblockTest.php new file mode 100644 index 00000000000..394df97328c --- /dev/null +++ b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/ImportFullyQualifiedIdentifierDocblockTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Source/AddFullyQualifiedIdentifierDocblockRector.php b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Source/AddFullyQualifiedIdentifierDocblockRector.php new file mode 100644 index 00000000000..a8dae6cd299 --- /dev/null +++ b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Source/AddFullyQualifiedIdentifierDocblockRector.php @@ -0,0 +1,46 @@ +phpDocInfoFactory->createFromNodeOrEmpty($node); + $varTagValueNode = new VarTagValueNode(new FullyQualifiedIdentifierTypeNode('DateTime'), '', ''); + + $phpDocInfo->addTagValueNode($varTagValueNode); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; + } +} diff --git a/tests/Issues/ImportFullyQualifiedIdentifierDocblock/config/configured_rule.php b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/config/configured_rule.php new file mode 100644 index 00000000000..f145ce3083e --- /dev/null +++ b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([AddFullyQualifiedIdentifierDocblockRector::class]) + ->withImportNames(); diff --git a/tests/Issues/IndentationCrash/CodeQualityCodingStyleIndentationCrashTest.php b/tests/Issues/IndentationCrash/CodeQualityCodingStyleIndentationCrashTest.php new file mode 100644 index 00000000000..055944b3b8f --- /dev/null +++ b/tests/Issues/IndentationCrash/CodeQualityCodingStyleIndentationCrashTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureCodeQualityCodingStyle'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/code_quality_coding_style_configured_rule.php'; + } +} diff --git a/tests/Issues/IndentationCrash/FixtureCodeQualityCodingStyle/namespace_equal_prefix_class_string.php.inc b/tests/Issues/IndentationCrash/FixtureCodeQualityCodingStyle/namespace_equal_prefix_class_string.php.inc new file mode 100644 index 00000000000..d70ba4a813c --- /dev/null +++ b/tests/Issues/IndentationCrash/FixtureCodeQualityCodingStyle/namespace_equal_prefix_class_string.php.inc @@ -0,0 +1,42 @@ +getName() !== 'App\Foo') { + return false; + } + + if (! str_starts_with($methodName, 'with')) { + return false; + } + + return true; + } +} + +?> +----- +getName() !== \App\Foo::class) { + return false; + } + return str_starts_with($methodName, 'with'); + } +} + +?> diff --git a/tests/Issues/IndentationCrash/FixtureTab/if_else_tab_indentation.php.inc b/tests/Issues/IndentationCrash/FixtureTab/if_else_tab_indentation.php.inc new file mode 100644 index 00000000000..2c0148c073f --- /dev/null +++ b/tests/Issues/IndentationCrash/FixtureTab/if_else_tab_indentation.php.inc @@ -0,0 +1,21 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureTab'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/tab_configured_rule.php'; + } +} diff --git a/tests/Issues/IndentationCrash/config/code_quality_coding_style_configured_rule.php b/tests/Issues/IndentationCrash/config/code_quality_coding_style_configured_rule.php new file mode 100644 index 00000000000..d34d07d60ef --- /dev/null +++ b/tests/Issues/IndentationCrash/config/code_quality_coding_style_configured_rule.php @@ -0,0 +1,17 @@ +withRules( + [ + SimplifyIfReturnBoolRector::class, + NewlineAfterStatementRector::class, + StringClassNameToClassConstantRector::class, + ] + ); diff --git a/tests/Issues/IndentationCrash/config/tab_configured_rule.php b/tests/Issues/IndentationCrash/config/tab_configured_rule.php new file mode 100644 index 00000000000..9f184946f18 --- /dev/null +++ b/tests/Issues/IndentationCrash/config/tab_configured_rule.php @@ -0,0 +1,11 @@ +indent("\t", 1); + $rectorConfig->rule(SimplifyIfElseWithSameContentRector::class); +}; diff --git a/tests/Issues/IndexedStmt/Fixture/fixture.php.inc b/tests/Issues/IndexedStmt/Fixture/fixture.php.inc new file mode 100644 index 00000000000..ad2d646c37e --- /dev/null +++ b/tests/Issues/IndexedStmt/Fixture/fixture.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/tests/Issues/IndexedStmt/IndexedStmtTest.php b/tests/Issues/IndexedStmt/IndexedStmtTest.php new file mode 100644 index 00000000000..989b8e1b21f --- /dev/null +++ b/tests/Issues/IndexedStmt/IndexedStmtTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/IndexedStmt/Source/ChangeLastIndex1Rector.php b/tests/Issues/IndexedStmt/Source/ChangeLastIndex1Rector.php new file mode 100644 index 00000000000..4214a02e4bc --- /dev/null +++ b/tests/Issues/IndexedStmt/Source/ChangeLastIndex1Rector.php @@ -0,0 +1,44 @@ +stmts === null) { + return null; + } + + foreach ($node->stmts as $key => $stmt) { + if ($key === 1 && $stmt instanceof Expression && $stmt->expr instanceof String_ && $stmt->expr->value === 'with index 2') { + $stmt->expr->value = 'final index'; + return $node; + } + } + + return null; + } +} diff --git a/tests/Issues/IndexedStmt/Source/RemoveIndex1Rector.php b/tests/Issues/IndexedStmt/Source/RemoveIndex1Rector.php new file mode 100644 index 00000000000..4697df3afc2 --- /dev/null +++ b/tests/Issues/IndexedStmt/Source/RemoveIndex1Rector.php @@ -0,0 +1,37 @@ +expr instanceof String_ && $node->expr->value === 'with index 1') { + return NodeVisitor::REMOVE_NODE; + } + + return null; + } +} \ No newline at end of file diff --git a/tests/Issues/IndexedStmt/config/configured_rule.php b/tests/Issues/IndexedStmt/config/configured_rule.php new file mode 100644 index 00000000000..2a6cabae20c --- /dev/null +++ b/tests/Issues/IndexedStmt/config/configured_rule.php @@ -0,0 +1,11 @@ +rules([RemoveIndex1Rector::class, ChangeLastIndex1Rector::class]); +}; diff --git a/tests/Issues/InfiniteLoop/DeadStmtUnusedVariableTest.php b/tests/Issues/InfiniteLoop/DeadStmtUnusedVariableTest.php new file mode 100644 index 00000000000..c14ad1fc059 --- /dev/null +++ b/tests/Issues/InfiniteLoop/DeadStmtUnusedVariableTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureDeadStmtUnusedVariable'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/dead_stmt_unused_variable.php'; + } +} diff --git a/tests/Issues/InfiniteLoop/DeclareStrictTypesParseStrTest.php b/tests/Issues/InfiniteLoop/DeclareStrictTypesParseStrTest.php new file mode 100644 index 00000000000..9a2d398d192 --- /dev/null +++ b/tests/Issues/InfiniteLoop/DeclareStrictTypesParseStrTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureDeclareStrictTypesParseStr'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/declare_strict_types_parsestr.php'; + } +} diff --git a/tests/Issues/InfiniteLoop/Fixture/de_morgan.php.inc b/tests/Issues/InfiniteLoop/Fixture/de_morgan.php.inc deleted file mode 100644 index 4684d291975..00000000000 --- a/tests/Issues/InfiniteLoop/Fixture/de_morgan.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/tests/Issues/InfiniteLoop/Fixture/some_method_call_infinity.php.inc b/tests/Issues/InfiniteLoop/Fixture/some_method_call_infinity.php.inc deleted file mode 100644 index 0390127e94c..00000000000 --- a/tests/Issues/InfiniteLoop/Fixture/some_method_call_infinity.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -modify('+1'); - -?> ------ -modify('+1'); - -?> diff --git a/tests/Issues/InfiniteLoop/FixtureDeadStmtUnusedVariable/fixture.php.inc b/tests/Issues/InfiniteLoop/FixtureDeadStmtUnusedVariable/fixture.php.inc new file mode 100644 index 00000000000..e84fc8c5ab7 --- /dev/null +++ b/tests/Issues/InfiniteLoop/FixtureDeadStmtUnusedVariable/fixture.php.inc @@ -0,0 +1,28 @@ +a() . $object->b(); + } +} + +?> +----- +b(); + $object->a(); + } +} + +?> diff --git a/tests/Issues/InfiniteLoop/FixtureDeclareStrictTypesParseStr/namespaced_same_function.php.inc b/tests/Issues/InfiniteLoop/FixtureDeclareStrictTypesParseStr/namespaced_same_function.php.inc new file mode 100644 index 00000000000..8ac6174c63d --- /dev/null +++ b/tests/Issues/InfiniteLoop/FixtureDeclareStrictTypesParseStr/namespaced_same_function.php.inc @@ -0,0 +1,25 @@ + +----- +expectException(InfiniteLoopTraversingException::class); - - $fixtureFileInfo = new SmartFileInfo(__DIR__ . '/Fixture/some_method_call_infinity.php.inc'); - $this->doTestFileInfo($fixtureFileInfo); - } - - public function testPass(): void - { - $fixtureFileInfo = new SmartFileInfo(__DIR__ . '/Fixture/de_morgan.php.inc'); - $this->doTestFileInfo($fixtureFileInfo); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/infinite_loop.php'; - } -} diff --git a/tests/Issues/InfiniteLoop/Rector/MethodCall/InfinityLoopRector.php b/tests/Issues/InfiniteLoop/Rector/MethodCall/InfinityLoopRector.php deleted file mode 100644 index 58a6b9de76e..00000000000 --- a/tests/Issues/InfiniteLoop/Rector/MethodCall/InfinityLoopRector.php +++ /dev/null @@ -1,43 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [MethodCall::class]; - } - - /** - * @param MethodCall $node - * @return Assign|null - */ - public function refactor(Node $node): ?Node - { - if (! $this->isName($node->name, 'modify')) { - return null; - } - - return new Assign($node->var, $node); - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Road to left... to left... to lefthell..', []); - } -} diff --git a/tests/Issues/InfiniteLoop/config/dead_stmt_unused_variable.php b/tests/Issues/InfiniteLoop/config/dead_stmt_unused_variable.php new file mode 100644 index 00000000000..6187c4c6a4f --- /dev/null +++ b/tests/Issues/InfiniteLoop/config/dead_stmt_unused_variable.php @@ -0,0 +1,10 @@ +withRules([RemoveDeadStmtRector::class, RemoveUnusedVariableAssignRector::class]); diff --git a/tests/Issues/InfiniteLoop/config/declare_strict_types_parsestr.php b/tests/Issues/InfiniteLoop/config/declare_strict_types_parsestr.php new file mode 100644 index 00000000000..8d9fc4c05bd --- /dev/null +++ b/tests/Issues/InfiniteLoop/config/declare_strict_types_parsestr.php @@ -0,0 +1,10 @@ +withRules([DeclareStrictTypesRector::class, ParseStrWithResultArgumentRector::class]); diff --git a/tests/Issues/InfiniteLoop/config/infinite_loop.php b/tests/Issues/InfiniteLoop/config/infinite_loop.php deleted file mode 100644 index a933da2a86a..00000000000 --- a/tests/Issues/InfiniteLoop/config/infinite_loop.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(InfinityLoopRector::class); - $services->set(SimplifyDeMorganBinaryRector::class); -}; diff --git a/tests/Issues/InfiniteSwapParams/Fixture/fixture.php.inc b/tests/Issues/InfiniteSwapParams/Fixture/fixture.php.inc new file mode 100644 index 00000000000..ab69a721d80 --- /dev/null +++ b/tests/Issues/InfiniteSwapParams/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/InfiniteSwapParams/InfiniteSwapParamsTest.php b/tests/Issues/InfiniteSwapParams/InfiniteSwapParamsTest.php new file mode 100644 index 00000000000..d3739bb8f76 --- /dev/null +++ b/tests/Issues/InfiniteSwapParams/InfiniteSwapParamsTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/InfiniteSwapParams/config/configured_rule.php b/tests/Issues/InfiniteSwapParams/config/configured_rule.php new file mode 100644 index 00000000000..66d3671e6e2 --- /dev/null +++ b/tests/Issues/InfiniteSwapParams/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveUnusedVariableInCatchRector::class, OptionalParametersAfterRequiredRector::class]); diff --git a/tests/Issues/InlineIfReplaceBlock/Fixture/fixture.php.inc b/tests/Issues/InlineIfReplaceBlock/Fixture/fixture.php.inc new file mode 100644 index 00000000000..5b885c93241 --- /dev/null +++ b/tests/Issues/InlineIfReplaceBlock/Fixture/fixture.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/tests/Issues/InlineIfReplaceBlock/InlineIfReplaceBlockTest.php b/tests/Issues/InlineIfReplaceBlock/InlineIfReplaceBlockTest.php new file mode 100644 index 00000000000..b8c554fada0 --- /dev/null +++ b/tests/Issues/InlineIfReplaceBlock/InlineIfReplaceBlockTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/InlineIfReplaceBlock/config/configured_rule.php b/tests/Issues/InlineIfReplaceBlock/config/configured_rule.php new file mode 100644 index 00000000000..46ff9c42981 --- /dev/null +++ b/tests/Issues/InlineIfReplaceBlock/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([InlineIfToExplicitIfRector::class, ReplaceBlockToItsStmtsRector::class]); diff --git a/tests/Issues/InlineTags/Fixture/fixture.php.inc b/tests/Issues/InlineTags/Fixture/fixture.php.inc new file mode 100644 index 00000000000..42fbd35b531 --- /dev/null +++ b/tests/Issues/InlineTags/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/InlineTags/Fixture/with_description.php.inc b/tests/Issues/InlineTags/Fixture/with_description.php.inc new file mode 100644 index 00000000000..3c01d41c715 --- /dev/null +++ b/tests/Issues/InlineTags/Fixture/with_description.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/tests/Issues/InlineTags/Fixture/with_punctuation.inc b/tests/Issues/InlineTags/Fixture/with_punctuation.inc new file mode 100644 index 00000000000..0f6e4f70631 --- /dev/null +++ b/tests/Issues/InlineTags/Fixture/with_punctuation.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/InlineTags/InlineTagsTest.php b/tests/Issues/InlineTags/InlineTagsTest.php new file mode 100644 index 00000000000..d6720685002 --- /dev/null +++ b/tests/Issues/InlineTags/InlineTagsTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/InlineTags/config/configured_rule.php b/tests/Issues/InlineTags/config/configured_rule.php new file mode 100644 index 00000000000..ac138f68ec7 --- /dev/null +++ b/tests/Issues/InlineTags/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([CoversAnnotationWithValueToAttributeRector::class]); diff --git a/tests/Issues/InlineTypedFromConstructorDefaultValue/Fixture/inlined_typed_property.php.inc b/tests/Issues/InlineTypedFromConstructorDefaultValue/Fixture/inlined_typed_property.php.inc new file mode 100644 index 00000000000..599c53ef1b6 --- /dev/null +++ b/tests/Issues/InlineTypedFromConstructorDefaultValue/Fixture/inlined_typed_property.php.inc @@ -0,0 +1,34 @@ +url = 'https://website.tld'; + } +} + +?> +----- + diff --git a/tests/Issues/InlineTypedFromConstructorDefaultValue/InlineTypedFromConstructorDefaultValueTest.php b/tests/Issues/InlineTypedFromConstructorDefaultValue/InlineTypedFromConstructorDefaultValueTest.php new file mode 100644 index 00000000000..6703cc5280e --- /dev/null +++ b/tests/Issues/InlineTypedFromConstructorDefaultValue/InlineTypedFromConstructorDefaultValueTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/InlineTypedFromConstructorDefaultValue/config/configured_rule.php b/tests/Issues/InlineTypedFromConstructorDefaultValue/config/configured_rule.php new file mode 100644 index 00000000000..b503ac09973 --- /dev/null +++ b/tests/Issues/InlineTypedFromConstructorDefaultValue/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([TypedPropertyFromStrictConstructorRector::class, InlineConstructorDefaultToPropertyRector::class]); diff --git a/tests/Issues/Issue6420/Fixture/fixture.php.inc b/tests/Issues/Issue6420/Fixture/fixture.php.inc index f91b8b98156..3afa09ada7a 100644 --- a/tests/Issues/Issue6420/Fixture/fixture.php.inc +++ b/tests/Issues/Issue6420/Fixture/fixture.php.inc @@ -1,6 +1,6 @@ doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/Issues/Issue6420/config/configured_rule.php b/tests/Issues/Issue6420/config/configured_rule.php index 5ae517a2f5a..c1c5b84d400 100644 --- a/tests/Issues/Issue6420/config/configured_rule.php +++ b/tests/Issues/Issue6420/config/configured_rule.php @@ -2,17 +2,15 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\Expression\RemoveDeadStmtRector; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RemoveDeadStmtRector::class); - $services->set(RenameFunctionRector::class) - ->call('configure', [[ - RenameFunctionRector::OLD_FUNCTION_TO_NEW_FUNCTION => [ - 'preg_replace' => 'Safe\preg_replace', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->rule(RemoveDeadStmtRector::class); + + $rectorConfig + ->ruleWithConfiguration(RenameFunctionRector::class, [ + 'preg_replace' => 'Safe\preg_replace', + ]); }; diff --git a/tests/Issues/Issue6480/Fixture/fixture.php.inc b/tests/Issues/Issue6480/Fixture/fixture.php.inc index 3b8cc13fd26..2cdf0985bdf 100644 --- a/tests/Issues/Issue6480/Fixture/fixture.php.inc +++ b/tests/Issues/Issue6480/Fixture/fixture.php.inc @@ -1,11 +1,13 @@ doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/Issues/Issue6480/Source/ExistingClass.php b/tests/Issues/Issue6480/Source/ExistingClass.php new file mode 100644 index 00000000000..1b1cea4afcd --- /dev/null +++ b/tests/Issues/Issue6480/Source/ExistingClass.php @@ -0,0 +1,10 @@ +services(); - $services->set(RemoveDeadInstanceOfRector::class); - $services->set(SwitchNegatedTernaryRector::class); -}; +return RectorConfig::configure() + ->withRules([RemoveDeadInstanceOfRector::class, SwitchNegatedTernaryRector::class]); diff --git a/tests/Issues/Issue6481/EncapsedStringsInsideDeadInstanceTest.php b/tests/Issues/Issue6481/EncapsedStringsInsideDeadInstanceTest.php index 0f0882b373a..2ca647fc427 100644 --- a/tests/Issues/Issue6481/EncapsedStringsInsideDeadInstanceTest.php +++ b/tests/Issues/Issue6481/EncapsedStringsInsideDeadInstanceTest.php @@ -2,31 +2,26 @@ declare(strict_types=1); -namespace Rector\Core\Tests\Issues\Issue6481; +namespace Rector\Tests\Issues\Issue6481; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; /** * @see https://github.com/rectorphp/rector/issues/6481 */ final class EncapsedStringsInsideDeadInstanceTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/Issues/Issue6481/Fixture/fixture.php.inc b/tests/Issues/Issue6481/Fixture/fixture.php.inc index caed4a2c0b5..2b725a6caca 100644 --- a/tests/Issues/Issue6481/Fixture/fixture.php.inc +++ b/tests/Issues/Issue6481/Fixture/fixture.php.inc @@ -1,6 +1,6 @@ services(); - $services->set(EncapsedStringsToSprintfRector::class); - $services->set(RemoveDeadInstanceOfRector::class); -}; +return RectorConfig::configure() + ->withRules([EncapsedStringsToSprintfRector::class, RemoveDeadInstanceOfRector::class]); diff --git a/tests/Issues/Issue6496/AutoImportDocBlockTest.php b/tests/Issues/Issue6496/AutoImportDocBlockTest.php deleted file mode 100644 index 11476733f99..00000000000 --- a/tests/Issues/Issue6496/AutoImportDocBlockTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configure_rule.php'; - } -} diff --git a/tests/Issues/Issue6496/Fixture/double_import_same_suffix.php.inc b/tests/Issues/Issue6496/Fixture/double_import_same_suffix.php.inc deleted file mode 100644 index 298f07a059c..00000000000 --- a/tests/Issues/Issue6496/Fixture/double_import_same_suffix.php.inc +++ /dev/null @@ -1,34 +0,0 @@ - ------ - diff --git a/tests/Issues/Issue6496/Fixture/import_doc_after_rename.php.inc b/tests/Issues/Issue6496/Fixture/import_doc_after_rename.php.inc deleted file mode 100644 index d2f0a61fa73..00000000000 --- a/tests/Issues/Issue6496/Fixture/import_doc_after_rename.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/tests/Issues/Issue6496/Fixture/skip_import_of_aliased.php.inc b/tests/Issues/Issue6496/Fixture/skip_import_of_aliased.php.inc deleted file mode 100644 index bd52a496d53..00000000000 --- a/tests/Issues/Issue6496/Fixture/skip_import_of_aliased.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - $parameters->set(Option::IMPORT_DOC_BLOCKS, true); - - $services = $containerConfigurator->services(); - $services->set(ReplaceSensioRouteAnnotationWithSymfonyRector::class); -}; diff --git a/tests/Issues/Issue6557/Fixture/fixture.php.inc b/tests/Issues/Issue6557/Fixture/fixture.php.inc deleted file mode 100644 index f6e6b083965..00000000000 --- a/tests/Issues/Issue6557/Fixture/fixture.php.inc +++ /dev/null @@ -1,51 +0,0 @@ -doSomething(); - return; - }; - } -} - -final class SomeClass -{ - public function doSomething(): void - { - - } -} - -?> ------ -doSomething(); - return; - }; - } -} - -final class SomeClass -{ - public function doSomething(): void - { - - } -} - -?> diff --git a/tests/Issues/Issue6557/NullStartTokenTest.php b/tests/Issues/Issue6557/NullStartTokenTest.php deleted file mode 100644 index 82d61f5727c..00000000000 --- a/tests/Issues/Issue6557/NullStartTokenTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/Issues/Issue6557/config/configured_rule.php b/tests/Issues/Issue6557/config/configured_rule.php deleted file mode 100644 index 23344e00a2a..00000000000 --- a/tests/Issues/Issue6557/config/configured_rule.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(RemoveOverriddenValuesRector::class); - $services->set(RenameVariableToMatchNewTypeRector::class); -}; diff --git a/tests/Issues/Issue6561/CountArrayOnTruthyEmptyArrayCompareTest.php b/tests/Issues/Issue6561/CountArrayOnTruthyEmptyArrayCompareTest.php index 3813322bf3a..75dcfc9cfca 100644 --- a/tests/Issues/Issue6561/CountArrayOnTruthyEmptyArrayCompareTest.php +++ b/tests/Issues/Issue6561/CountArrayOnTruthyEmptyArrayCompareTest.php @@ -2,28 +2,23 @@ declare(strict_types=1); -namespace Rector\Core\Tests\Issues\Issue6561; +namespace Rector\Tests\Issues\Issue6561; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CountArrayOnTruthyEmptyArrayCompareTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/Issues/Issue6561/Fixture/fixture.php.inc b/tests/Issues/Issue6561/Fixture/fixture.php.inc index 1307d618c76..d422a973104 100644 --- a/tests/Issues/Issue6561/Fixture/fixture.php.inc +++ b/tests/Issues/Issue6561/Fixture/fixture.php.inc @@ -1,6 +1,6 @@ get(); + + if (count($array)) { + } + } + + /** + * @return string[] + */ + private function get(): array + { + return ['a', 'b']; + } +} + +?> +----- +get(); + + if ($array !== []) { + } + } + + /** + * @return string[] + */ + private function get(): array + { + return ['a', 'b']; + } +} + +?> diff --git a/tests/Issues/Issue6561/config/configured_rule.php b/tests/Issues/Issue6561/config/configured_rule.php index 1020a911d10..02c16dd5993 100644 --- a/tests/Issues/Issue6561/config/configured_rule.php +++ b/tests/Issues/Issue6561/config/configured_rule.php @@ -4,10 +4,7 @@ use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector; use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Rector\Config\RectorConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(ExplicitBoolCompareRector::class); - $services->set(CountArrayToEmptyArrayComparisonRector::class); -}; +return RectorConfig::configure() + ->withRules([ExplicitBoolCompareRector::class, CountArrayToEmptyArrayComparisonRector::class]); diff --git a/tests/Issues/Issue6670/Fixture/do_not_remove_used_class_method.php.inc b/tests/Issues/Issue6670/Fixture/do_not_remove_used_class_method.php.inc new file mode 100644 index 00000000000..053db2bc39f --- /dev/null +++ b/tests/Issues/Issue6670/Fixture/do_not_remove_used_class_method.php.inc @@ -0,0 +1,44 @@ +notUnused(); + } + } + + private function notUnused(): int + { + // This is some code that is very important + } +} +?> +----- +notUnused(); + } + + private function notUnused(): int + { + // This is some code that is very important + } +} +?> diff --git a/tests/Issues/Issue6670/RemoveAlwaysElseAndUnusedPrivateMethodsRectorTest.php b/tests/Issues/Issue6670/RemoveAlwaysElseAndUnusedPrivateMethodsRectorTest.php new file mode 100644 index 00000000000..971de373bd6 --- /dev/null +++ b/tests/Issues/Issue6670/RemoveAlwaysElseAndUnusedPrivateMethodsRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/Issue6670/config/configured_rule.php b/tests/Issues/Issue6670/config/configured_rule.php new file mode 100644 index 00000000000..31bd8abc534 --- /dev/null +++ b/tests/Issues/Issue6670/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveUnusedPrivateMethodRector::class, RemoveAlwaysElseRector::class]); diff --git a/tests/Issues/Issue6840/ClassPropertyPromotionAndNewLineAfterStatementRectorTest.php b/tests/Issues/Issue6840/ClassPropertyPromotionAndNewLineAfterStatementRectorTest.php new file mode 100644 index 00000000000..624221fb049 --- /dev/null +++ b/tests/Issues/Issue6840/ClassPropertyPromotionAndNewLineAfterStatementRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/Issue6840/Fixture/fixture.php.inc b/tests/Issues/Issue6840/Fixture/fixture.php.inc new file mode 100644 index 00000000000..2354e4b656c --- /dev/null +++ b/tests/Issues/Issue6840/Fixture/fixture.php.inc @@ -0,0 +1,31 @@ +imageId = $imageId; + $this->imageSize = []; + } +} +?> +----- +imageSize = []; + } +} +?> diff --git a/tests/Issues/Issue6840/config/configured_rule.php b/tests/Issues/Issue6840/config/configured_rule.php new file mode 100644 index 00000000000..260da73479f --- /dev/null +++ b/tests/Issues/Issue6840/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([ClassPropertyAssignToConstructorPromotionRector::class, NewlineAfterStatementRector::class]); diff --git a/tests/Issues/Issue7112/Fixture/fixture.php.inc b/tests/Issues/Issue7112/Fixture/fixture.php.inc new file mode 100644 index 00000000000..7c4d314cd2b --- /dev/null +++ b/tests/Issues/Issue7112/Fixture/fixture.php.inc @@ -0,0 +1,26 @@ +something()->value(); + $model = $this->something( + function ($query) { + return $query; + } + )->value(); + } + + private function value(): string + { + return 'anything'; + } +} diff --git a/tests/Issues/Issue7112/RuleCombinationShouldNotAddVoidReturnTypeWhereReturnExistsRectorTest.php b/tests/Issues/Issue7112/RuleCombinationShouldNotAddVoidReturnTypeWhereReturnExistsRectorTest.php new file mode 100644 index 00000000000..c358e0aa2b9 --- /dev/null +++ b/tests/Issues/Issue7112/RuleCombinationShouldNotAddVoidReturnTypeWhereReturnExistsRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/Issue7112/config/configured_rule.php b/tests/Issues/Issue7112/config/configured_rule.php new file mode 100644 index 00000000000..f6f3617e439 --- /dev/null +++ b/tests/Issues/Issue7112/config/configured_rule.php @@ -0,0 +1,12 @@ +withRules( + [RenameVariableToMatchMethodCallReturnTypeRector::class, AddVoidReturnTypeWhereNoReturnRector::class] + ); diff --git a/tests/Issues/Issue7306/Fixture/fixture.php.inc b/tests/Issues/Issue7306/Fixture/fixture.php.inc new file mode 100644 index 00000000000..a6a91ea55f3 --- /dev/null +++ b/tests/Issues/Issue7306/Fixture/fixture.php.inc @@ -0,0 +1,38 @@ + +----- + diff --git a/tests/Issues/Issue7306/RuleCombinationShouldNotSimplifyIfNotNullReturnTest.php b/tests/Issues/Issue7306/RuleCombinationShouldNotSimplifyIfNotNullReturnTest.php new file mode 100644 index 00000000000..72aae2f279b --- /dev/null +++ b/tests/Issues/Issue7306/RuleCombinationShouldNotSimplifyIfNotNullReturnTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/Issue7306/config/configured_rule.php b/tests/Issues/Issue7306/config/configured_rule.php new file mode 100644 index 00000000000..05a75d33ea1 --- /dev/null +++ b/tests/Issues/Issue7306/config/configured_rule.php @@ -0,0 +1,17 @@ +withRules( + [ + SimplifyIfNotNullReturnRector::class, + ExplicitBoolCompareRector::class, + NewlineBetweenClassLikeStmtsRector::class, + ] + ); diff --git a/tests/Issues/Issue7374/Fixture/fixture.php.inc b/tests/Issues/Issue7374/Fixture/fixture.php.inc new file mode 100644 index 00000000000..1d22fc1e29f --- /dev/null +++ b/tests/Issues/Issue7374/Fixture/fixture.php.inc @@ -0,0 +1,40 @@ +displayTree("Equity", $equity, array( + array("name" => "NET INCOME", "amount" => $net_income), + array("name" => "RETAINED EARNINGS", "amount" => $retained_earnings) + )); + } +} +----- +displayTree($equity); + } +} diff --git a/tests/Issues/Issue7374/RuleCombinationShouldNotReturnGetAttributeOnNullReturnTest.php b/tests/Issues/Issue7374/RuleCombinationShouldNotReturnGetAttributeOnNullReturnTest.php new file mode 100644 index 00000000000..d9700ad63cd --- /dev/null +++ b/tests/Issues/Issue7374/RuleCombinationShouldNotReturnGetAttributeOnNullReturnTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/Issue7374/config/configured_rule.php b/tests/Issues/Issue7374/config/configured_rule.php new file mode 100644 index 00000000000..657871fb018 --- /dev/null +++ b/tests/Issues/Issue7374/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveUnusedPrivateMethodParameterRector::class, RenameVariableToMatchNewTypeRector::class]); diff --git a/tests/Issues/Issue9388/Fixture/fixture.php.inc b/tests/Issues/Issue9388/Fixture/fixture.php.inc new file mode 100644 index 00000000000..a6ebb285b9b --- /dev/null +++ b/tests/Issues/Issue9388/Fixture/fixture.php.inc @@ -0,0 +1,33 @@ + 'NotEmpty'])] + protected $name = ''; + + #[\TYPO3\CMS\Extbase\Annotation\Validate(['validator' => 'NotEmpty'])] + protected $thisWorks = ''; +} diff --git a/tests/Issues/Issue9388/Issue9388Test.php b/tests/Issues/Issue9388/Issue9388Test.php new file mode 100644 index 00000000000..07b6c0a3445 --- /dev/null +++ b/tests/Issues/Issue9388/Issue9388Test.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/Issue9388/Source/AnnotationToAttribute/AttributeDecorator.php b/tests/Issues/Issue9388/Source/AnnotationToAttribute/AttributeDecorator.php new file mode 100644 index 00000000000..69355db3c5c --- /dev/null +++ b/tests/Issues/Issue9388/Source/AnnotationToAttribute/AttributeDecorator.php @@ -0,0 +1,26 @@ +decorators as $decorator) { + if ($decorator->supports($phpAttributeName)) { + $decorator->decorate($attribute); + } + } + } +} diff --git a/tests/Issues/Issue9388/Source/AnnotationToAttribute/AttributeDecoratorInterface.php b/tests/Issues/Issue9388/Source/AnnotationToAttribute/AttributeDecoratorInterface.php new file mode 100644 index 00000000000..77af25249fa --- /dev/null +++ b/tests/Issues/Issue9388/Source/AnnotationToAttribute/AttributeDecoratorInterface.php @@ -0,0 +1,14 @@ +args as $arg) { + $key = $arg->name instanceof Identifier ? new String_($arg->name->toString()) : new String_('validator'); + + if ($this->valueResolver->isValue($key, 'validator')) { + $classNameString = $this->valueResolver->getValue($arg->value); + if (! is_string($classNameString)) { + continue; + } + + $className = ltrim($classNameString, '\\'); + $classConstant = $this->stringClassNameToClassConstantRector->refactor(new String_($className)); + $value = $classConstant instanceof ClassConstFetch ? $classConstant : $arg->value; + } else { + $value = $arg->value; + } + + $array->items[] = new ArrayItem($value, $key); + } + + $attribute->args = [new Arg($array)]; + } +} diff --git a/tests/Issues/Issue9388/Source/Rule/ExtbaseAnnotationToAttributeRector.php b/tests/Issues/Issue9388/Source/Rule/ExtbaseAnnotationToAttributeRector.php new file mode 100644 index 00000000000..0f145bbeefe --- /dev/null +++ b/tests/Issues/Issue9388/Source/Rule/ExtbaseAnnotationToAttributeRector.php @@ -0,0 +1,185 @@ +annotationsToAttributes = [ + new AnnotationToAttribute('TYPO3\CMS\Extbase\Annotation\Validate'), + ]; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Change annotation to attribute', [new CodeSample( + <<<'CODE_SAMPLE' +use TYPO3\CMS\Extbase\Annotation as Extbase; + +class MyEntity +{ + /** + * @Extbase\ORM\Transient() + */ + protected string $myProperty; +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use TYPO3\CMS\Extbase\Annotation as Extbase; + +class MyEntity +{ + #[Extbase\ORM\Transient()] + protected string $myProperty; +} +CODE_SAMPLE + )]); + } + + public function getNodeTypes(): array + { + return [Property::class]; + } + + /** + * @param Property $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $phpDocInfo instanceof PhpDocInfo) { + return null; + } + + $uses = $this->useImportsResolver->resolveBareUses(); + $annotationAttributeGroups = $this->processDoctrineAnnotationClasses($phpDocInfo, $uses); + if ($annotationAttributeGroups === []) { + return null; + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + foreach ($annotationAttributeGroups as $annotationAttributeGroup) { + foreach ($annotationAttributeGroup->attrs as $attr) { + $phpAttributeName = $attr->name->getAttribute(AttributeKey::PHP_ATTRIBUTE_NAME); + $this->attributeDecorator->decorate($phpAttributeName, $attr); + } + } + + $node->attrGroups = \array_merge($node->attrGroups, $annotationAttributeGroups); + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ATTRIBUTES; + } + + /** + * @param Use_[] $uses + * @return AttributeGroup[] + */ + private function processDoctrineAnnotationClasses(PhpDocInfo $phpDocInfo, array $uses): array + { + if ($phpDocInfo->getPhpDocNode()->children === []) { + return []; + } + + $doctrineTagAndAnnotationToAttributes = []; + $doctrineTagValueNodes = []; + foreach ($phpDocInfo->getPhpDocNode()->children as $phpDocChildNode) { + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if (! $phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) { + continue; + } + + $doctrineTagValueNode = $phpDocChildNode->value; + $annotationToAttribute = $this->matchAnnotationToAttribute($doctrineTagValueNode); + if (! $annotationToAttribute instanceof AnnotationToAttribute) { + continue; + } + + // Fix the missing leading slash in most of the wild use cases + if (str_starts_with($doctrineTagValueNode->identifierTypeNode->name, '@TYPO3\CMS')) { + $doctrineTagValueNode->identifierTypeNode->name = str_replace( + '@TYPO3\CMS', + '@\\TYPO3\CMS', + $doctrineTagValueNode->identifierTypeNode->name + ); + } + + $doctrineTagAndAnnotationToAttributes[] = new DoctrineTagAndAnnotationToAttribute( + $doctrineTagValueNode, + $annotationToAttribute + ); + $doctrineTagValueNodes[] = $doctrineTagValueNode; + } + + $attributeGroups = $this->attrGroupsFactory->create($doctrineTagAndAnnotationToAttributes, $uses); + if ($this->phpAttributeAnalyzer->hasRemoveArrayState($attributeGroups)) { + return []; + } + + foreach ($doctrineTagValueNodes as $doctrineTagValueNode) { + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineTagValueNode); + } + + return $attributeGroups; + } + + private function matchAnnotationToAttribute( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode + ): ?AnnotationToAttribute { + foreach ($this->annotationsToAttributes as $annotationToAttribute) { + if (! $doctrineAnnotationTagValueNode->hasClassName($annotationToAttribute->getTag())) { + continue; + } + + return $annotationToAttribute; + } + + return null; + } +} diff --git a/tests/Issues/Issue9388/config/configured_rule.php b/tests/Issues/Issue9388/config/configured_rule.php new file mode 100644 index 00000000000..9f59172b0bd --- /dev/null +++ b/tests/Issues/Issue9388/config/configured_rule.php @@ -0,0 +1,28 @@ +autotagInterface(AttributeDecoratorInterface::class); + $rectorConfig->singleton(ValidateAttributeDecorator::class); + $rectorConfig->when(AttributeDecorator::class)->needs('$decorators')->giveTagged( + AttributeDecoratorInterface::class + ); + + $rectorConfig->importNames(false, false); + $rectorConfig->phpVersion(PhpVersionFeature::ATTRIBUTES); + + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'TYPO3\CMS\Extbase\Mvc\Web\Request' => 'TYPO3\CMS\Extbase\Mvc\Request', + ]); + $rectorConfig->rule(ExtbaseAnnotationToAttributeRector::class); + $rectorConfig->phpVersion(PhpVersionFeature::ATTRIBUTES); +}; diff --git a/tests/Issues/IssueAnnotation/Fixture/fixture.php.inc b/tests/Issues/IssueAnnotation/Fixture/fixture.php.inc index e50e0991594..4f33ab76a32 100644 --- a/tests/Issues/IssueAnnotation/Fixture/fixture.php.inc +++ b/tests/Issues/IssueAnnotation/Fixture/fixture.php.inc @@ -1,10 +1,10 @@ doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/Issues/IssueAnnotation/Source/SomeAttributes.php b/tests/Issues/IssueAnnotation/Source/SomeAttributes.php new file mode 100644 index 00000000000..88a2f336f6b --- /dev/null +++ b/tests/Issues/IssueAnnotation/Source/SomeAttributes.php @@ -0,0 +1,10 @@ +services(); - $services->set(DowngradeNamedArgumentRector::class); -}; +return RectorConfig::configure() + ->withRules([DowngradeNamedArgumentRector::class]); diff --git a/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc b/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc new file mode 100644 index 00000000000..e4d817629fa --- /dev/null +++ b/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ +|scalar + * + * @return (TValue is scalar ? array|scalar : array) + */ + public function resolveValue(): mixed + { + } +} + +?> +----- +|scalar + * + * @return (TValue is scalar ? array|scalar : array) + */ + public function resolveValue() + { + } +} + +?> diff --git a/tests/Issues/IssueConditionalType/IssueConditionalTypeTest.php b/tests/Issues/IssueConditionalType/IssueConditionalTypeTest.php new file mode 100644 index 00000000000..dfdae2aad38 --- /dev/null +++ b/tests/Issues/IssueConditionalType/IssueConditionalTypeTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/IssueConditionalType/config/configured_rule.php b/tests/Issues/IssueConditionalType/config/configured_rule.php new file mode 100644 index 00000000000..d6852edfa72 --- /dev/null +++ b/tests/Issues/IssueConditionalType/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([DowngradeMixedTypeDeclarationRector::class]); diff --git a/tests/Issues/IssueConstructorPromotionRename/Fixture/fixture.php.inc b/tests/Issues/IssueConstructorPromotionRename/Fixture/fixture.php.inc new file mode 100644 index 00000000000..ea1dd0ab5bd --- /dev/null +++ b/tests/Issues/IssueConstructorPromotionRename/Fixture/fixture.php.inc @@ -0,0 +1,40 @@ +a = $a; + dump($a); + dump($a->someCall('Y-m-d')); + } +} + +?> +----- +promotedPropertyObject); + dump($this->promotedPropertyObject->someCall('Y-m-d')); + } +} + +?> diff --git a/tests/Issues/IssueConstructorPromotionRename/IssueConstructorPromotionRenameTest.php b/tests/Issues/IssueConstructorPromotionRename/IssueConstructorPromotionRenameTest.php new file mode 100644 index 00000000000..292daf14e3b --- /dev/null +++ b/tests/Issues/IssueConstructorPromotionRename/IssueConstructorPromotionRenameTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/IssueConstructorPromotionRename/Source/PromotedPropertyObject.php b/tests/Issues/IssueConstructorPromotionRename/Source/PromotedPropertyObject.php new file mode 100644 index 00000000000..ba7619b6264 --- /dev/null +++ b/tests/Issues/IssueConstructorPromotionRename/Source/PromotedPropertyObject.php @@ -0,0 +1,10 @@ +withRules([ClassPropertyAssignToConstructorPromotionRector::class, RenamePropertyToMatchTypeRector::class]); diff --git a/tests/Issues/IssueCreatePublicPropertyOnRename/CreatePublicPropertyOnRenameTest.php b/tests/Issues/IssueCreatePublicPropertyOnRename/CreatePublicPropertyOnRenameTest.php index bc51ced7812..39ef3b5b075 100644 --- a/tests/Issues/IssueCreatePublicPropertyOnRename/CreatePublicPropertyOnRenameTest.php +++ b/tests/Issues/IssueCreatePublicPropertyOnRename/CreatePublicPropertyOnRenameTest.php @@ -2,28 +2,23 @@ declare(strict_types=1); -namespace Rector\Core\Tests\Issues\IssueCreatePublicPropertyOnRename; +namespace Rector\Tests\Issues\IssueCreatePublicPropertyOnRename; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CreatePublicPropertyOnRenameTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/Issues/IssueCreatePublicPropertyOnRename/Fixture/fixture.php.inc b/tests/Issues/IssueCreatePublicPropertyOnRename/Fixture/fixture.php.inc index 16505cdf187..1c0f3054483 100644 --- a/tests/Issues/IssueCreatePublicPropertyOnRename/Fixture/fixture.php.inc +++ b/tests/Issues/IssueCreatePublicPropertyOnRename/Fixture/fixture.php.inc @@ -2,11 +2,13 @@ declare(strict_types=1); -namespace Rector\Core\Tests\Issues\IssueCreatePublicPropertyOnRename\Fixture; +namespace Rector\Tests\Issues\IssueCreatePublicPropertyOnRename\Fixture; -final class DemoFile +use Rector\Tests\Issues\IssueCreatePublicPropertyOnRename\Source\SomePropertyObject; + +final class Fixture { - public function __construct(private \DateTimeImmutable $date) + public function __construct(private SomePropertyObject $date) { } @@ -21,17 +23,19 @@ final class DemoFile declare(strict_types=1); -namespace Rector\Core\Tests\Issues\IssueCreatePublicPropertyOnRename\Fixture; +namespace Rector\Tests\Issues\IssueCreatePublicPropertyOnRename\Fixture; + +use Rector\Tests\Issues\IssueCreatePublicPropertyOnRename\Source\SomePropertyObject; -final class DemoFile +final class Fixture { - public function __construct(private \DateTimeImmutable $dateTimeImmutable) + public function __construct(private SomePropertyObject $somePropertyObject) { } public function run(): string { - return $this->dateTimeImmutable->format('l, F j, Y'); + return $this->somePropertyObject->format('l, F j, Y'); } } ?> diff --git a/tests/Issues/IssueCreatePublicPropertyOnRename/Source/SomePropertyObject.php b/tests/Issues/IssueCreatePublicPropertyOnRename/Source/SomePropertyObject.php new file mode 100644 index 00000000000..2446836153c --- /dev/null +++ b/tests/Issues/IssueCreatePublicPropertyOnRename/Source/SomePropertyObject.php @@ -0,0 +1,10 @@ +services(); - $services->set(RenamePropertyToMatchTypeRector::class); - $services->set(CompleteDynamicPropertiesRector::class); -}; +return RectorConfig::configure() + ->withRules([RenamePropertyToMatchTypeRector::class, CompleteDynamicPropertiesRector::class]); diff --git a/tests/Issues/IssueDoubleNestedAnnotationDocBlock/Fixture/some_fixture.php.inc b/tests/Issues/IssueDoubleNestedAnnotationDocBlock/Fixture/some_fixture.php.inc new file mode 100644 index 00000000000..64e18bd0bc0 --- /dev/null +++ b/tests/Issues/IssueDoubleNestedAnnotationDocBlock/Fixture/some_fixture.php.inc @@ -0,0 +1,14 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configure_rule.php'; + } +} diff --git a/tests/Issues/IssueDoubleNestedAnnotationDocBlock/Source/SomeAnnotation.php b/tests/Issues/IssueDoubleNestedAnnotationDocBlock/Source/SomeAnnotation.php new file mode 100644 index 00000000000..ea2beb2bb7d --- /dev/null +++ b/tests/Issues/IssueDoubleNestedAnnotationDocBlock/Source/SomeAnnotation.php @@ -0,0 +1,12 @@ +importNames(); +}; diff --git a/tests/Issues/IssueDoubleNestedAnnotatoinDocBlock/Fixture/some_fixture.php.inc b/tests/Issues/IssueDoubleNestedAnnotatoinDocBlock/Fixture/some_fixture.php.inc deleted file mode 100644 index fa729cd7a9a..00000000000 --- a/tests/Issues/IssueDoubleNestedAnnotatoinDocBlock/Fixture/some_fixture.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configure_rule.php'; - } -} diff --git a/tests/Issues/IssueDoubleNestedAnnotatoinDocBlock/Source/SomeAnnotation.php b/tests/Issues/IssueDoubleNestedAnnotatoinDocBlock/Source/SomeAnnotation.php deleted file mode 100644 index fe273a50cb0..00000000000 --- a/tests/Issues/IssueDoubleNestedAnnotatoinDocBlock/Source/SomeAnnotation.php +++ /dev/null @@ -1,12 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); -}; diff --git a/tests/Issues/IssueDowngradeMixedType/DowngradeMixedTypeTest.php b/tests/Issues/IssueDowngradeMixedType/DowngradeMixedTypeTest.php deleted file mode 100644 index 58342661c21..00000000000 --- a/tests/Issues/IssueDowngradeMixedType/DowngradeMixedTypeTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/Issues/IssueDowngradeMixedType/Fixture/fixture.php.inc b/tests/Issues/IssueDowngradeMixedType/Fixture/fixture.php.inc deleted file mode 100644 index 6199359f4b1..00000000000 --- a/tests/Issues/IssueDowngradeMixedType/Fixture/fixture.php.inc +++ /dev/null @@ -1,40 +0,0 @@ - ------ - diff --git a/tests/Issues/IssueDowngradeMixedType/Source/SomeClass.php b/tests/Issues/IssueDowngradeMixedType/Source/SomeClass.php deleted file mode 100644 index c678c737abb..00000000000 --- a/tests/Issues/IssueDowngradeMixedType/Source/SomeClass.php +++ /dev/null @@ -1,9 +0,0 @@ -parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); - - $services = $containerConfigurator->services(); - $services->set(DowngradeTypedPropertyRector::class); - $services->set(DowngradeMixedTypeDeclarationRector::class); -}; diff --git a/tests/Issues/IssueEarlyReturnAndOr/Fixture/fixture.php.inc b/tests/Issues/IssueEarlyReturnAndOr/Fixture/fixture.php.inc deleted file mode 100644 index 33d799f2e29..00000000000 --- a/tests/Issues/IssueEarlyReturnAndOr/Fixture/fixture.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/tests/Issues/IssueEarlyReturnAndOr/IssueEarlyReturnAndOrTest.php b/tests/Issues/IssueEarlyReturnAndOr/IssueEarlyReturnAndOrTest.php deleted file mode 100644 index dbc6be3d3a6..00000000000 --- a/tests/Issues/IssueEarlyReturnAndOr/IssueEarlyReturnAndOrTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/Issues/IssueEarlyReturnAndOr/config/configured_rule.php b/tests/Issues/IssueEarlyReturnAndOr/config/configured_rule.php deleted file mode 100644 index a766c141ba8..00000000000 --- a/tests/Issues/IssueEarlyReturnAndOr/config/configured_rule.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(ChangeOrIfReturnToEarlyReturnRector::class); - $services->set(ChangeAndIfToEarlyReturnRector::class); -}; diff --git a/tests/Issues/IssueImportedReturn/Fixture/fixture.php.inc b/tests/Issues/IssueImportedReturn/Fixture/fixture.php.inc deleted file mode 100644 index b569b4863f5..00000000000 --- a/tests/Issues/IssueImportedReturn/Fixture/fixture.php.inc +++ /dev/null @@ -1,59 +0,0 @@ -config['config']) { - - } - - if (! $this->config['config']) { - - } - - if (! $this->config['config']) { - - } - - return new Logging(); - } -} - -?> ------ -config[self::CONFIG]) { - - } - - if (! $this->config[self::CONFIG]) { - - } - - if (! $this->config[self::CONFIG]) { - - } - - return new Logging(); - } -} - -?> diff --git a/tests/Issues/IssueImportedReturn/Fixture/namespaced.php.inc b/tests/Issues/IssueImportedReturn/Fixture/namespaced.php.inc deleted file mode 100644 index 7a60aa3e9f9..00000000000 --- a/tests/Issues/IssueImportedReturn/Fixture/namespaced.php.inc +++ /dev/null @@ -1,59 +0,0 @@ -config['config']) { - - } - - if (! $this->config['config']) { - - } - - if (! $this->config['config']) { - - } - - return new Logging(); - } -} - -?> ------ -config[self::CONFIG]) { - - } - - if (! $this->config[self::CONFIG]) { - - } - - if (! $this->config[self::CONFIG]) { - - } - - return new Logging(); - } -} - -?> diff --git a/tests/Issues/IssueImportedReturn/Fixture/namespaced_union.php.inc b/tests/Issues/IssueImportedReturn/Fixture/namespaced_union.php.inc deleted file mode 100644 index 6dfbaeb89be..00000000000 --- a/tests/Issues/IssueImportedReturn/Fixture/namespaced_union.php.inc +++ /dev/null @@ -1,67 +0,0 @@ -config['config']) { - - } - - if (! $this->config['config']) { - - } - - if (! $this->config['config']) { - - } - - if (rand(0, 1)) { - return new Logging(); - } - - return new Logging2(); - } -} - -?> ------ -config[self::CONFIG]) { - - } - - if (! $this->config[self::CONFIG]) { - - } - - if (! $this->config[self::CONFIG]) { - - } - - if (rand(0, 1)) { - return new Logging(); - } - - return new Logging2(); - } -} - -?> diff --git a/tests/Issues/IssueImportedReturn/Fixture/repeated_trait_use.php.inc b/tests/Issues/IssueImportedReturn/Fixture/repeated_trait_use.php.inc deleted file mode 100644 index 4acffb60005..00000000000 --- a/tests/Issues/IssueImportedReturn/Fixture/repeated_trait_use.php.inc +++ /dev/null @@ -1,10 +0,0 @@ -config['config']) { - - } - - if (! $this->config['config']) { - - } - - if (! $this->config['config']) { - - } - - if (rand(0, 1)) { - return new Logging(); - } - - return new Logging2(); - } -} - -?> ------ -config[self::CONFIG]) { - - } - - if (! $this->config[self::CONFIG]) { - - } - - if (! $this->config[self::CONFIG]) { - - } - - if (rand(0, 1)) { - return new Logging(); - } - - return new Logging2(); - } -} - -?> diff --git a/tests/Issues/IssueImportedReturn/ImportedReturnTest.php b/tests/Issues/IssueImportedReturn/ImportedReturnTest.php deleted file mode 100644 index 670debb1e14..00000000000 --- a/tests/Issues/IssueImportedReturn/ImportedReturnTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/Issues/IssueImportedReturn/Source/Logging.php b/tests/Issues/IssueImportedReturn/Source/Logging.php deleted file mode 100644 index c632f85a7d1..00000000000 --- a/tests/Issues/IssueImportedReturn/Source/Logging.php +++ /dev/null @@ -1,10 +0,0 @@ -services(); - $services->set(RepeatedLiteralToClassConstantRector::class); - $services->set(ReturnTypeDeclarationRector::class); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::AUTO_IMPORT_NAMES, true); -}; diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Fixture/fixture.php.inc b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Fixture/fixture.php.inc new file mode 100644 index 00000000000..41d5f1d887c --- /dev/null +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Fixture/fixture.php.inc @@ -0,0 +1,34 @@ +prop = $prop; + parent::__construct(); + } +} + +?> +----- + diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/IssuePropertyPromoRemoveDelegatingParentTest.php b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/IssuePropertyPromoRemoveDelegatingParentTest.php new file mode 100644 index 00000000000..0231e5064e3 --- /dev/null +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/IssuePropertyPromoRemoveDelegatingParentTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php new file mode 100644 index 00000000000..05e4ddb58ae --- /dev/null +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php @@ -0,0 +1,18 @@ +init(); + } + + private function init(): void + { + echo 'A init'; + } +} \ No newline at end of file diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/config/configured_rule.php b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/config/configured_rule.php new file mode 100644 index 00000000000..454a89aec1a --- /dev/null +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/config/configured_rule.php @@ -0,0 +1,13 @@ +withRules([ + ClassPropertyAssignToConstructorPromotionRector::class, + RemoveParentDelegatingConstructorRector::class, + ]); diff --git a/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/Fixture/fixture.php.inc b/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/Fixture/fixture.php.inc new file mode 100644 index 00000000000..eda3489ffc1 --- /dev/null +++ b/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/Fixture/fixture.php.inc @@ -0,0 +1,50 @@ + +----- + diff --git a/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/IssueRemoveAlwaysElseReturnEarlyIfVariableTest.php b/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/IssueRemoveAlwaysElseReturnEarlyIfVariableTest.php new file mode 100644 index 00000000000..77cb43a0d73 --- /dev/null +++ b/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/IssueRemoveAlwaysElseReturnEarlyIfVariableTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/config/configured_rule.php b/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/config/configured_rule.php new file mode 100644 index 00000000000..3a2bcf62ef8 --- /dev/null +++ b/tests/Issues/IssueRemoveAlwaysElseReturnEarlyIfVariable/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveAlwaysElseRector::class, ReturnEarlyIfVariableRector::class]); diff --git a/tests/Issues/IssueReturnBeforeElseIf/Fixture/complex_if_cond_or_without_elseif.php.inc b/tests/Issues/IssueReturnBeforeElseIf/Fixture/complex_if_cond_or_without_elseif.php.inc deleted file mode 100644 index ebc16f0e5db..00000000000 --- a/tests/Issues/IssueReturnBeforeElseIf/Fixture/complex_if_cond_or_without_elseif.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - ------ - diff --git a/tests/Issues/IssueReturnBeforeElseIf/Fixture/skip_complex_if_cond_or.php.inc b/tests/Issues/IssueReturnBeforeElseIf/Fixture/skip_complex_if_cond_or.php.inc deleted file mode 100644 index 33e76d89f76..00000000000 --- a/tests/Issues/IssueReturnBeforeElseIf/Fixture/skip_complex_if_cond_or.php.inc +++ /dev/null @@ -1,20 +0,0 @@ - diff --git a/tests/Issues/IssueReturnBeforeElseIf/FixtureAnd/skip_complex_if_cond_and.php.inc b/tests/Issues/IssueReturnBeforeElseIf/FixtureAnd/skip_complex_if_cond_and.php.inc deleted file mode 100644 index b14659b3b83..00000000000 --- a/tests/Issues/IssueReturnBeforeElseIf/FixtureAnd/skip_complex_if_cond_and.php.inc +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/tests/Issues/IssueReturnBeforeElseIf/IssueReturnBeforeElseIfAndTest.php b/tests/Issues/IssueReturnBeforeElseIf/IssueReturnBeforeElseIfAndTest.php deleted file mode 100644 index eb4d3c6580e..00000000000 --- a/tests/Issues/IssueReturnBeforeElseIf/IssueReturnBeforeElseIfAndTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureAnd'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule_and.php'; - } -} diff --git a/tests/Issues/IssueReturnBeforeElseIf/IssueReturnBeforeElseIfOrTest.php b/tests/Issues/IssueReturnBeforeElseIf/IssueReturnBeforeElseIfOrTest.php deleted file mode 100644 index 31e8b00770d..00000000000 --- a/tests/Issues/IssueReturnBeforeElseIf/IssueReturnBeforeElseIfOrTest.php +++ /dev/null @@ -1,33 +0,0 @@ -doTestFileInfo($fileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule_or.php'; - } -} diff --git a/tests/Issues/IssueReturnBeforeElseIf/config/configured_rule_and.php b/tests/Issues/IssueReturnBeforeElseIf/config/configured_rule_and.php deleted file mode 100644 index 10e4120034e..00000000000 --- a/tests/Issues/IssueReturnBeforeElseIf/config/configured_rule_and.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(ChangeAndIfToEarlyReturnRector::class); - $services->set(RemoveAlwaysElseRector::class); -}; diff --git a/tests/Issues/IssueReturnBeforeElseIf/config/configured_rule_or.php b/tests/Issues/IssueReturnBeforeElseIf/config/configured_rule_or.php deleted file mode 100644 index 5284d321756..00000000000 --- a/tests/Issues/IssueReturnBeforeElseIf/config/configured_rule_or.php +++ /dev/null @@ -1,13 +0,0 @@ -services(); - $services->set(ChangeOrIfReturnToEarlyReturnRector::class); - $services->set(RemoveAlwaysElseRector::class); -}; diff --git a/tests/Issues/KeepDoubleAssignParam/Fixture/fixture.php.inc b/tests/Issues/KeepDoubleAssignParam/Fixture/fixture.php.inc new file mode 100644 index 00000000000..1e994546ab5 --- /dev/null +++ b/tests/Issues/KeepDoubleAssignParam/Fixture/fixture.php.inc @@ -0,0 +1,49 @@ +items = [$input]; + } else { + $this->items = $input; + } + + $this->items = $this->getItems(); + } + + public function getItems() + { + return sort($this->items); + } +} + +?> +----- +items = ! \is_array($input) ? [$input] : $input; + + $this->items = $this->getItems(); + } + + public function getItems() + { + return sort($this->items); + } +} + +?> diff --git a/tests/Issues/KeepDoubleAssignParam/KeepDoubleAssignParamTest.php b/tests/Issues/KeepDoubleAssignParam/KeepDoubleAssignParamTest.php new file mode 100644 index 00000000000..4cbe0f9c96e --- /dev/null +++ b/tests/Issues/KeepDoubleAssignParam/KeepDoubleAssignParamTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/KeepDoubleAssignParam/config/configured_rule.php b/tests/Issues/KeepDoubleAssignParam/config/configured_rule.php new file mode 100644 index 00000000000..0c0f164e715 --- /dev/null +++ b/tests/Issues/KeepDoubleAssignParam/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveDoubleAssignRector::class, SimplifyIfElseToTernaryRector::class]); diff --git a/tests/Issues/LocallyThis/Fixture/fixture.php.inc b/tests/Issues/LocallyThis/Fixture/fixture.php.inc new file mode 100644 index 00000000000..7cab62dcae0 --- /dev/null +++ b/tests/Issues/LocallyThis/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ + +----- +someStatic(); + } + + private function someStatic() + { + } +} + +?> diff --git a/tests/Issues/LocallyThis/LocallyThisTest.php b/tests/Issues/LocallyThis/LocallyThisTest.php new file mode 100644 index 00000000000..3ea4d934410 --- /dev/null +++ b/tests/Issues/LocallyThis/LocallyThisTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/LocallyThis/config/configured_rule.php b/tests/Issues/LocallyThis/config/configured_rule.php new file mode 100644 index 00000000000..9d6cff856a5 --- /dev/null +++ b/tests/Issues/LocallyThis/config/configured_rule.php @@ -0,0 +1,12 @@ +withRules( + [LocallyCalledStaticMethodToNonStaticRector::class, ThisCallOnStaticMethodToStaticCallRector::class] + ); diff --git a/tests/Issues/NamespacedUse/Fixture/end_with_name_backslash.php.inc b/tests/Issues/NamespacedUse/Fixture/end_with_name_backslash.php.inc new file mode 100644 index 00000000000..f50caceb10e --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/end_with_name_backslash.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/multiple_use_const_namespace.php.inc b/tests/Issues/NamespacedUse/Fixture/multiple_use_const_namespace.php.inc new file mode 100644 index 00000000000..005a63a99c6 --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/multiple_use_const_namespace.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/multiple_use_namespace.php.inc b/tests/Issues/NamespacedUse/Fixture/multiple_use_namespace.php.inc new file mode 100644 index 00000000000..47ad790121e --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/multiple_use_namespace.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased.php.inc b/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased.php.inc new file mode 100644 index 00000000000..336b8f3d0d4 --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_docblock.php.inc b/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_docblock.php.inc new file mode 100644 index 00000000000..1fb22678e0d --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_docblock.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_part.php.inc b/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_part.php.inc new file mode 100644 index 00000000000..cdb2436ceb9 --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_part.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_part_docblock.php.inc b/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_part_docblock.php.inc new file mode 100644 index 00000000000..b533df4058e --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/namespaced_class_aliased_part_docblock.php.inc @@ -0,0 +1,36 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/namespaced_constant_different_letter_case.php.inc b/tests/Issues/NamespacedUse/Fixture/namespaced_constant_different_letter_case.php.inc new file mode 100644 index 00000000000..588364d8035 --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/namespaced_constant_different_letter_case.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/namespaced_constant_with_mixed_letter_case.php.inc b/tests/Issues/NamespacedUse/Fixture/namespaced_constant_with_mixed_letter_case.php.inc new file mode 100644 index 00000000000..e3d6dcecc5f --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/namespaced_constant_with_mixed_letter_case.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/namespaced_open_tag_spaced.php.inc b/tests/Issues/NamespacedUse/Fixture/namespaced_open_tag_spaced.php.inc new file mode 100644 index 00000000000..69386ec661b --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/namespaced_open_tag_spaced.php.inc @@ -0,0 +1,17 @@ + + + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/skip_conflict_last_name_insensitive.php.inc b/tests/Issues/NamespacedUse/Fixture/skip_conflict_last_name_insensitive.php.inc new file mode 100644 index 00000000000..9e54133cd80 --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/skip_conflict_last_name_insensitive.php.inc @@ -0,0 +1,17 @@ + diff --git a/tests/Issues/NamespacedUse/Fixture/skip_namespaced_use_class.php.inc b/tests/Issues/NamespacedUse/Fixture/skip_namespaced_use_class.php.inc new file mode 100644 index 00000000000..d80fcdc4a16 --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/skip_namespaced_use_class.php.inc @@ -0,0 +1,9 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/subnamespace_from_namespace.php.inc b/tests/Issues/NamespacedUse/Fixture/subnamespace_from_namespace.php.inc new file mode 100644 index 00000000000..66737d67e33 --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/subnamespace_from_namespace.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/subnamespace_from_namespace2.php.inc b/tests/Issues/NamespacedUse/Fixture/subnamespace_from_namespace2.php.inc new file mode 100644 index 00000000000..68bf0171f56 --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/subnamespace_from_namespace2.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/use_without_usage.php.inc b/tests/Issues/NamespacedUse/Fixture/use_without_usage.php.inc new file mode 100644 index 00000000000..beeb25e143d --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/use_without_usage.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/tests/Issues/NamespacedUse/Fixture/use_without_usage_namespaced.php.inc b/tests/Issues/NamespacedUse/Fixture/use_without_usage_namespaced.php.inc new file mode 100644 index 00000000000..96d259cbece --- /dev/null +++ b/tests/Issues/NamespacedUse/Fixture/use_without_usage_namespaced.php.inc @@ -0,0 +1,14 @@ + +----- + +----- + diff --git a/tests/Issues/NamespacedUse/NamespacedUseTest.php b/tests/Issues/NamespacedUse/NamespacedUseTest.php new file mode 100644 index 00000000000..389e7382be3 --- /dev/null +++ b/tests/Issues/NamespacedUse/NamespacedUseTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/NamespacedUse/Source/SomeClass.php b/tests/Issues/NamespacedUse/Source/SomeClass.php new file mode 100644 index 00000000000..d9a03d89fd4 --- /dev/null +++ b/tests/Issues/NamespacedUse/Source/SomeClass.php @@ -0,0 +1,9 @@ +removeUnusedImports(); +}; diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased.php.inc new file mode 100644 index 00000000000..a1d555abc5f --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased2.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased2.php.inc new file mode 100644 index 00000000000..f044a82ea47 --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased2.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased_used.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased_used.php.inc new file mode 100644 index 00000000000..7c50b5f9d9f --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_aliased_used.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_unused.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_unused.php.inc new file mode 100644 index 00000000000..6688db50cb4 --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/conflict_last_name_unused.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/in_use_fqcn_docblock.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/in_use_fqcn_docblock.php.inc new file mode 100644 index 00000000000..db4171110cd --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/in_use_fqcn_docblock.php.inc @@ -0,0 +1,49 @@ + +----- + \ No newline at end of file diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/namespace_use_single_imported.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/namespace_use_single_imported.php.inc new file mode 100644 index 00000000000..2404a8ac0da --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/namespace_use_single_imported.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/namespaced_use_class.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/namespaced_use_class.php.inc new file mode 100644 index 00000000000..21e92a6d9fa --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/namespaced_use_class.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/namespaced_use_single_namespace.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/namespaced_use_single_namespace.php.inc new file mode 100644 index 00000000000..e21b79a1171 --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/namespaced_use_single_namespace.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/non_docblock_conflict.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/non_docblock_conflict.php.inc new file mode 100644 index 00000000000..fcc64bacd00 --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/non_docblock_conflict.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/not_in_use_fqcn_docblock_2.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/not_in_use_fqcn_docblock_2.php.inc new file mode 100644 index 00000000000..37a1d152b0d --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/not_in_use_fqcn_docblock_2.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/tests/Issues/NamespacedUseAutoImport/Fixture/skip_conflict_last_name_aliased_used.php.inc b/tests/Issues/NamespacedUseAutoImport/Fixture/skip_conflict_last_name_aliased_used.php.inc new file mode 100644 index 00000000000..96ae9018e76 --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Fixture/skip_conflict_last_name_aliased_used.php.inc @@ -0,0 +1,17 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/NamespacedUseAutoImport/Source/SomeClass.php b/tests/Issues/NamespacedUseAutoImport/Source/SomeClass.php new file mode 100644 index 00000000000..f06d960afbc --- /dev/null +++ b/tests/Issues/NamespacedUseAutoImport/Source/SomeClass.php @@ -0,0 +1,9 @@ +removeUnusedImports(); + $rectorConfig->importNames(); +}; diff --git a/tests/Issues/NoNamespaced/Fixture/keep_first_comment.php.inc b/tests/Issues/NoNamespaced/Fixture/keep_first_comment.php.inc new file mode 100644 index 00000000000..58b3fe666e1 --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/keep_first_comment.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/tests/Issues/NoNamespaced/Fixture/keep_first_comment_after_declare.php.inc b/tests/Issues/NoNamespaced/Fixture/keep_first_comment_after_declare.php.inc new file mode 100644 index 00000000000..925280b9c41 --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/keep_first_comment_after_declare.php.inc @@ -0,0 +1,40 @@ + +----- + diff --git a/tests/Issues/NoNamespaced/Fixture/no_namespaced_class.php.inc b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class.php.inc new file mode 100644 index 00000000000..ffb14785a74 --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased.php.inc b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased.php.inc new file mode 100644 index 00000000000..bb118cba98c --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_docblock.php.inc b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_docblock.php.inc new file mode 100644 index 00000000000..3b49d6455be --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_docblock.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_part.php.inc b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_part.php.inc new file mode 100644 index 00000000000..6e84d97ac99 --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_part.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_part_docblock.php.inc b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_part_docblock.php.inc new file mode 100644 index 00000000000..7728d47827a --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/no_namespaced_class_aliased_part_docblock.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/tests/Issues/NoNamespaced/Fixture/open_tag_spaced.php.inc b/tests/Issues/NoNamespaced/Fixture/open_tag_spaced.php.inc new file mode 100644 index 00000000000..ccafe6b0f8c --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/open_tag_spaced.php.inc @@ -0,0 +1,10 @@ + +
+ +
+ + +
  • test
  • + + diff --git a/tests/Issues/NoNamespaced/Fixture/skip_used_in_annotation.php.inc b/tests/Issues/NoNamespaced/Fixture/skip_used_in_annotation.php.inc new file mode 100644 index 00000000000..681a04a374b --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/skip_used_in_annotation.php.inc @@ -0,0 +1,16 @@ +locations as $location) { + if ($location->isTestLocation()) { + $testLocation = $location; + break; + } +} diff --git a/tests/Issues/NoNamespaced/Fixture/skip_used_in_see.php.inc b/tests/Issues/NoNamespaced/Fixture/skip_used_in_see.php.inc new file mode 100644 index 00000000000..cd199f3649e --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/skip_used_in_see.php.inc @@ -0,0 +1,23 @@ + $types + */ + public function __construct( + public array $types, + ) {} + + /** + * Returns true if the location is a city + * + * @see Place::isCity() + */ + public function isCity() : bool + { + return in_array('locality', $this->types, true); + } +} diff --git a/tests/Issues/NoNamespaced/Fixture/skip_used_in_used_by.php.inc b/tests/Issues/NoNamespaced/Fixture/skip_used_in_used_by.php.inc new file mode 100644 index 00000000000..53dc9960fe4 --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/skip_used_in_used_by.php.inc @@ -0,0 +1,8 @@ +setLocations('locations'); \ No newline at end of file diff --git a/tests/Issues/NoNamespaced/Fixture/space_before_open_tag.php.inc b/tests/Issues/NoNamespaced/Fixture/space_before_open_tag.php.inc new file mode 100644 index 00000000000..53a1e9a1302 --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/space_before_open_tag.php.inc @@ -0,0 +1,5 @@ + + +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/NoNamespaced/Source/SomeClass.php b/tests/Issues/NoNamespaced/Source/SomeClass.php new file mode 100644 index 00000000000..374c0e2afc0 --- /dev/null +++ b/tests/Issues/NoNamespaced/Source/SomeClass.php @@ -0,0 +1,9 @@ +removeUnusedImports(); +}; diff --git a/tests/Issues/PartialValueDocblockUpdate/Fixture/add_default_value_to_route.php.inc b/tests/Issues/PartialValueDocblockUpdate/Fixture/add_default_value_to_route.php.inc new file mode 100644 index 00000000000..78336da1d9b --- /dev/null +++ b/tests/Issues/PartialValueDocblockUpdate/Fixture/add_default_value_to_route.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/tests/Issues/PartialValueDocblockUpdate/Fixture/add_default_value_to_route2.php.inc b/tests/Issues/PartialValueDocblockUpdate/Fixture/add_default_value_to_route2.php.inc new file mode 100644 index 00000000000..a60d13078c3 --- /dev/null +++ b/tests/Issues/PartialValueDocblockUpdate/Fixture/add_default_value_to_route2.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/tests/Issues/PartialValueDocblockUpdate/PartialValueDocblockUpdateTest.php b/tests/Issues/PartialValueDocblockUpdate/PartialValueDocblockUpdateTest.php new file mode 100644 index 00000000000..534ec46b8ac --- /dev/null +++ b/tests/Issues/PartialValueDocblockUpdate/PartialValueDocblockUpdateTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/PartialValueDocblockUpdate/Source/PartialUpdateTestRector.php b/tests/Issues/PartialValueDocblockUpdate/Source/PartialUpdateTestRector.php new file mode 100644 index 00000000000..d5aeed8a266 --- /dev/null +++ b/tests/Issues/PartialValueDocblockUpdate/Source/PartialUpdateTestRector.php @@ -0,0 +1,60 @@ +phpDocInfoFactory->createFromNodeOrEmpty($node); + $routeDoctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClass('Symfony\Component\Routing\Annotation\Route'); + + if (! $routeDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { + return null; + } + + $defaultsArrayItem = $routeDoctrineAnnotationTagValueNode->getValue('defaults'); + if (! $defaultsArrayItem instanceof ArrayItemNode) { + $routeDoctrineAnnotationTagValueNode->values[] = new ArrayItemNode( + new CurlyListNode(), + 'defaults', + ); + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; + } + + return null; + } +} diff --git a/tests/Issues/PartialValueDocblockUpdate/config/configured_rule.php b/tests/Issues/PartialValueDocblockUpdate/config/configured_rule.php new file mode 100644 index 00000000000..b67db0bd501 --- /dev/null +++ b/tests/Issues/PartialValueDocblockUpdate/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([PartialUpdateTestRector::class]); diff --git a/tests/Issues/PhpTagsAddedToBlade/Fixture/php_tags_added_to_blade.expected.php b/tests/Issues/PhpTagsAddedToBlade/Fixture/php_tags_added_to_blade.expected.php deleted file mode 100644 index f64c9fffec9..00000000000 --- a/tests/Issues/PhpTagsAddedToBlade/Fixture/php_tags_added_to_blade.expected.php +++ /dev/null @@ -1,10 +0,0 @@ -@extends('layout') - - - -@section('content') - The value is -@endsection diff --git a/tests/Issues/PhpTagsAddedToBlade/Fixture/php_tags_added_to_blade.input.php b/tests/Issues/PhpTagsAddedToBlade/Fixture/php_tags_added_to_blade.input.php deleted file mode 100644 index be4892eb6a7..00000000000 --- a/tests/Issues/PhpTagsAddedToBlade/Fixture/php_tags_added_to_blade.input.php +++ /dev/null @@ -1,10 +0,0 @@ -@extends('layout') - - - -@section('content') - The value is -@endsection diff --git a/tests/Issues/PhpTagsAddedToBlade/PhpTagsAddedToBladeTest.php b/tests/Issues/PhpTagsAddedToBlade/PhpTagsAddedToBladeTest.php deleted file mode 100644 index 62b035584ca..00000000000 --- a/tests/Issues/PhpTagsAddedToBlade/PhpTagsAddedToBladeTest.php +++ /dev/null @@ -1,38 +0,0 @@ -getContents(); - $expectedFileInfo = new SmartFileInfo(__DIR__ . '/Fixture/php_tags_added_to_blade.expected.php'); - - $configuration = new Configuration(isDryRun: false); - $file = new File($inputFileInfo, $inputFileInfo->getContents()); - - $applicationFileProcessor = $this->getService(ApplicationFileProcessor::class); - $applicationFileProcessor->run([$file], $configuration); - - $this->assertStringEqualsFile($expectedFileInfo->getRealPath(), $file->getFileContent()); - - $smartFileSystem = new SmartFileSystem(); - $smartFileSystem->dumpFile($inputFileInfo->getRealPath(), $inputFileInfoContent); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/php_tags_added_to_blade.php'; - } -} diff --git a/tests/Issues/PhpTagsAddedToBlade/config/php_tags_added_to_blade.php b/tests/Issues/PhpTagsAddedToBlade/config/php_tags_added_to_blade.php deleted file mode 100644 index eb7f46ddbde..00000000000 --- a/tests/Issues/PhpTagsAddedToBlade/config/php_tags_added_to_blade.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(TernaryToNullCoalescingRector::class); -}; diff --git a/tests/Issues/PlainValueParser/Fixture/fixture.php.inc b/tests/Issues/PlainValueParser/Fixture/fixture.php.inc new file mode 100644 index 00000000000..c64045ccbf3 --- /dev/null +++ b/tests/Issues/PlainValueParser/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ +TRY: To try + * - TEST: to test (Default if no parameters given)") + */ + public function test() + {} +} + +?> +----- +TRY: To try + - TEST: to test (Default if no parameters given)')] + public function test() + {} +} + +?> diff --git a/tests/Issues/PlainValueParser/PlainValueParserTest.php b/tests/Issues/PlainValueParser/PlainValueParserTest.php new file mode 100644 index 00000000000..fe177552d45 --- /dev/null +++ b/tests/Issues/PlainValueParser/PlainValueParserTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/PlainValueParser/Source/CustomAnnotation.php b/tests/Issues/PlainValueParser/Source/CustomAnnotation.php new file mode 100644 index 00000000000..c9bd86664a8 --- /dev/null +++ b/tests/Issues/PlainValueParser/Source/CustomAnnotation.php @@ -0,0 +1,11 @@ +ruleWithConfiguration(AnnotationToAttributeRectorAlias::class, [ + new AnnotationToAttribute(CustomAnnotation::class), + ]); +}; diff --git a/tests/Issues/PrintAnonymousClassSpaced/Fixture/skip_space_changed.php.inc b/tests/Issues/PrintAnonymousClassSpaced/Fixture/skip_space_changed.php.inc new file mode 100644 index 00000000000..48db7016f56 --- /dev/null +++ b/tests/Issues/PrintAnonymousClassSpaced/Fixture/skip_space_changed.php.inc @@ -0,0 +1,17 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/PrintAnonymousClassSpaced/config/configured_rule.php b/tests/Issues/PrintAnonymousClassSpaced/config/configured_rule.php new file mode 100644 index 00000000000..d5530c76c28 --- /dev/null +++ b/tests/Issues/PrintAnonymousClassSpaced/config/configured_rule.php @@ -0,0 +1,7 @@ + $checkInput) { + echo "Input#{$index}: Wrong tokenTime"; + } + } +} + diff --git a/tests/Issues/PrintCrash/PrintCrashTest.php b/tests/Issues/PrintCrash/PrintCrashTest.php new file mode 100644 index 00000000000..8c6f0d25efb --- /dev/null +++ b/tests/Issues/PrintCrash/PrintCrashTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/PrintCrash/config/configured_rule.php b/tests/Issues/PrintCrash/config/configured_rule.php new file mode 100644 index 00000000000..033e63d7939 --- /dev/null +++ b/tests/Issues/PrintCrash/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(RemoveUnusedForeachKeyRector::class); +}; diff --git a/tests/Issues/PrintMatchPhpstan/Fixture/fixture.php.inc b/tests/Issues/PrintMatchPhpstan/Fixture/fixture.php.inc new file mode 100644 index 00000000000..8ae0bf24139 --- /dev/null +++ b/tests/Issues/PrintMatchPhpstan/Fixture/fixture.php.inc @@ -0,0 +1,26 @@ +getType(); + + $arguments[] = match ($reflectionType->getName()) { + DefinitionConfigurator::class => $configurator, + FileLoader::class, self::class => $this, + }; + } + } +} diff --git a/tests/Issues/PrintMatchPhpstan/PrintMatchPhpstanTest.php b/tests/Issues/PrintMatchPhpstan/PrintMatchPhpstanTest.php new file mode 100644 index 00000000000..9fe9ebe208b --- /dev/null +++ b/tests/Issues/PrintMatchPhpstan/PrintMatchPhpstanTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/PrintMatchPhpstan/config/configured_rule.php b/tests/Issues/PrintMatchPhpstan/config/configured_rule.php new file mode 100644 index 00000000000..f48c064b5d4 --- /dev/null +++ b/tests/Issues/PrintMatchPhpstan/config/configured_rule.php @@ -0,0 +1,8 @@ +withDowngradeSets(php81: true); diff --git a/tests/Issues/PrintStringNowDocUnderAttributeTarget/Fixture/fixture.php.inc b/tests/Issues/PrintStringNowDocUnderAttributeTarget/Fixture/fixture.php.inc new file mode 100644 index 00000000000..09cae7dd7e7 --- /dev/null +++ b/tests/Issues/PrintStringNowDocUnderAttributeTarget/Fixture/fixture.php.inc @@ -0,0 +1,51 @@ + +----- + \ No newline at end of file diff --git a/tests/Issues/PrintStringNowDocUnderAttributeTarget/PrintStringNowDocUnderAttributeTargetTest.php b/tests/Issues/PrintStringNowDocUnderAttributeTarget/PrintStringNowDocUnderAttributeTargetTest.php new file mode 100644 index 00000000000..9dc43a047e6 --- /dev/null +++ b/tests/Issues/PrintStringNowDocUnderAttributeTarget/PrintStringNowDocUnderAttributeTargetTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/PrintStringNowDocUnderAttributeTarget/Source/SomeAttributeTargetRector.php b/tests/Issues/PrintStringNowDocUnderAttributeTarget/Source/SomeAttributeTargetRector.php new file mode 100644 index 00000000000..6f89467ac4f --- /dev/null +++ b/tests/Issues/PrintStringNowDocUnderAttributeTarget/Source/SomeAttributeTargetRector.php @@ -0,0 +1,37 @@ +> + */ + public function getNodeTypes(): array + { + return [Attribute::class]; + } + + /** + * @param Attribute $node + */ + public function refactor(Node $node): Node + { + $node->name = new FullyQualified('SomeNewAttribute'); + + return $node; + } +} \ No newline at end of file diff --git a/tests/Issues/PrintStringNowDocUnderAttributeTarget/config/configured_rule.php b/tests/Issues/PrintStringNowDocUnderAttributeTarget/config/configured_rule.php new file mode 100644 index 00000000000..82b3f20e842 --- /dev/null +++ b/tests/Issues/PrintStringNowDocUnderAttributeTarget/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(SomeAttributeTargetRector::class); +}; diff --git a/tests/Issues/PropertyPromoNeverReturn/Fixture/fixture.php.inc b/tests/Issues/PropertyPromoNeverReturn/Fixture/fixture.php.inc new file mode 100644 index 00000000000..9aa18fbf0ad --- /dev/null +++ b/tests/Issues/PropertyPromoNeverReturn/Fixture/fixture.php.inc @@ -0,0 +1,42 @@ +value = $value; + + $this->rules = [ + 'chave' => function ($value) { + return is_string($value) ? true : 'not string'; + }, + ]; + } +} + +?> +----- +rules = [ + 'chave' => function ($value) { + return is_string($value) ? true : 'not string'; + }, + ]; + } +} + +?> diff --git a/tests/Issues/PropertyPromoNeverReturn/PropertyPromoNeverReturnTest.php b/tests/Issues/PropertyPromoNeverReturn/PropertyPromoNeverReturnTest.php new file mode 100644 index 00000000000..ff7dc5f48cf --- /dev/null +++ b/tests/Issues/PropertyPromoNeverReturn/PropertyPromoNeverReturnTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/PropertyPromoNeverReturn/config/configured_rule.php b/tests/Issues/PropertyPromoNeverReturn/config/configured_rule.php new file mode 100644 index 00000000000..bb5d11488bb --- /dev/null +++ b/tests/Issues/PropertyPromoNeverReturn/config/configured_rule.php @@ -0,0 +1,11 @@ +rules([ClassPropertyAssignToConstructorPromotionRector::class, ReturnNeverTypeRector::class]); +}; diff --git a/tests/Issues/RemoveUnusedParamInMiddle/Fixture/do_not_remove_parameter_in_middle.php.inc b/tests/Issues/RemoveUnusedParamInMiddle/Fixture/do_not_remove_parameter_in_middle.php.inc new file mode 100644 index 00000000000..d68e2e12dbd --- /dev/null +++ b/tests/Issues/RemoveUnusedParamInMiddle/Fixture/do_not_remove_parameter_in_middle.php.inc @@ -0,0 +1,53 @@ +propertyA = $propertyA; + $this->propertyB = $propertyB; + $this->propertyC = $propertyC; + } + + public function run() + { + echo $this->propertyA; + echo $this->propertyC; + } +} + +?> +----- +propertyA = $propertyA; + $this->propertyC = $propertyC; + } + + public function run() + { + echo $this->propertyA; + echo $this->propertyC; + } +} + +?> diff --git a/tests/Issues/RemoveUnusedParamInMiddle/RemoveUnusedParamInMiddleTest.php b/tests/Issues/RemoveUnusedParamInMiddle/RemoveUnusedParamInMiddleTest.php new file mode 100644 index 00000000000..3ad91626889 --- /dev/null +++ b/tests/Issues/RemoveUnusedParamInMiddle/RemoveUnusedParamInMiddleTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/RemoveUnusedParamInMiddle/config/configured_rule.php b/tests/Issues/RemoveUnusedParamInMiddle/config/configured_rule.php new file mode 100644 index 00000000000..6474a97fd4e --- /dev/null +++ b/tests/Issues/RemoveUnusedParamInMiddle/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveUnusedConstructorParamRector::class, RemoveUnusedPrivatePropertyRector::class]); diff --git a/tests/Issues/RemoveUnusedVariableAlwaysElse/Fixture/fixture.php.inc b/tests/Issues/RemoveUnusedVariableAlwaysElse/Fixture/fixture.php.inc new file mode 100644 index 00000000000..4a4840e119b --- /dev/null +++ b/tests/Issues/RemoveUnusedVariableAlwaysElse/Fixture/fixture.php.inc @@ -0,0 +1,46 @@ +get('path', null); + $url = $request->get('url', null); + if (null !== $path) { + return response()->file($path); + } elseif (null !== $url) { + return response()->getFile($url); + } + throw new \Exception('Error'); + } +} + +?> +----- +get('path', null); + $url = $request->get('url', null); + if (null !== $path) { + return response()->file($path); + } + if (null !== $url) { + return response()->getFile($url); + } + throw new \Exception('Error'); + } +} + +?> diff --git a/tests/Issues/RemoveUnusedVariableAlwaysElse/Fixture/next_node_changed.php.inc b/tests/Issues/RemoveUnusedVariableAlwaysElse/Fixture/next_node_changed.php.inc new file mode 100644 index 00000000000..abda5a74605 --- /dev/null +++ b/tests/Issues/RemoveUnusedVariableAlwaysElse/Fixture/next_node_changed.php.inc @@ -0,0 +1,50 @@ +dummyProperty = 1; + } + + private function getLowestPriceItem($value): int + { + $newValue = $value + 1; + if ($value > 100) { + return $value; + } else { + return $newValue; + } + } +} + +?> +----- +dummyProperty = 1; + } + + private function getLowestPriceItem($value): int + { + $newValue = $value + 1; + if ($value > 100) { + return $value; + } + return $newValue; + } +} + +?> diff --git a/tests/Issues/RemoveUnusedVariableAlwaysElse/RemoveUnusedVariableAlwaysElseTest.php b/tests/Issues/RemoveUnusedVariableAlwaysElse/RemoveUnusedVariableAlwaysElseTest.php new file mode 100644 index 00000000000..9f76b4acdc7 --- /dev/null +++ b/tests/Issues/RemoveUnusedVariableAlwaysElse/RemoveUnusedVariableAlwaysElseTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/RemoveUnusedVariableAlwaysElse/config/configured_rule.php b/tests/Issues/RemoveUnusedVariableAlwaysElse/config/configured_rule.php new file mode 100644 index 00000000000..faabf91118c --- /dev/null +++ b/tests/Issues/RemoveUnusedVariableAlwaysElse/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveUnusedVariableAssignRector::class, RemoveAlwaysElseRector::class]); diff --git a/tests/Issues/RemoveUnusedVariableDouble/Fixture/fixture.php.inc b/tests/Issues/RemoveUnusedVariableDouble/Fixture/fixture.php.inc new file mode 100644 index 00000000000..30c36d9d9e2 --- /dev/null +++ b/tests/Issues/RemoveUnusedVariableDouble/Fixture/fixture.php.inc @@ -0,0 +1,45 @@ + ['name' => 'John']]; + $objects = [1, 2, 3]; + $groups = []; + $defaultGroup = 'default'; + + foreach ($objects as $aco) { + $groups['aco'] = $aco; + } + + return ['groups' => $groups, 'user' => $user]; + } +} + +?> +----- + ['name' => 'John']]; + $objects = [1, 2, 3]; + $groups = []; + + foreach ($objects as $aco) { + $groups['aco'] = $aco; + } + + return ['groups' => $groups, 'user' => $user]; + } +} + +?> diff --git a/tests/Issues/RemoveUnusedVariableDouble/RemoveUnusedVariableDoubleTest.php b/tests/Issues/RemoveUnusedVariableDouble/RemoveUnusedVariableDoubleTest.php new file mode 100644 index 00000000000..40e563c13eb --- /dev/null +++ b/tests/Issues/RemoveUnusedVariableDouble/RemoveUnusedVariableDoubleTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/RemoveUnusedVariableDouble/config/configured_rule.php b/tests/Issues/RemoveUnusedVariableDouble/config/configured_rule.php new file mode 100644 index 00000000000..a79435a4dd2 --- /dev/null +++ b/tests/Issues/RemoveUnusedVariableDouble/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveDoubleAssignRector::class, RemoveUnusedVariableAssignRector::class]); diff --git a/tests/Issues/RenameClassInCallbackFromAssertAnnotation/Fixture/callback_with_curly_braces.php.inc b/tests/Issues/RenameClassInCallbackFromAssertAnnotation/Fixture/callback_with_curly_braces.php.inc index 1980f039f71..8511394bfa2 100644 --- a/tests/Issues/RenameClassInCallbackFromAssertAnnotation/Fixture/callback_with_curly_braces.php.inc +++ b/tests/Issues/RenameClassInCallbackFromAssertAnnotation/Fixture/callback_with_curly_braces.php.inc @@ -1,10 +1,10 @@ doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/Issues/RenameClassInCallbackFromAssertAnnotation/config/configured_rule.php b/tests/Issues/RenameClassInCallbackFromAssertAnnotation/config/configured_rule.php index e9a053ea426..1a424d8bad9 100644 --- a/tests/Issues/RenameClassInCallbackFromAssertAnnotation/config/configured_rule.php +++ b/tests/Issues/RenameClassInCallbackFromAssertAnnotation/config/configured_rule.php @@ -2,15 +2,11 @@ declare(strict_types=1); +use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -return static function (ContainerConfigurator $containerConfigurator): void { - $services = $containerConfigurator->services(); - $services->set(RenameClassRector::class) - ->call('configure', [[ - RenameClassRector::OLD_TO_NEW_CLASSES => [ - 'Some\\Random\\Class_' => 'Some\\Other\\Random\\Class_', - ], - ]]); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'Some\Random\Class_' => 'Some\Other\Random\Class_', + ]); }; diff --git a/tests/Issues/RenameString/Fixture/rename_string.php.inc b/tests/Issues/RenameString/Fixture/rename_string.php.inc new file mode 100644 index 00000000000..ecaa78e2cc8 --- /dev/null +++ b/tests/Issues/RenameString/Fixture/rename_string.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/tests/Issues/RenameString/RenameStringTest.php b/tests/Issues/RenameString/RenameStringTest.php new file mode 100644 index 00000000000..e432141129d --- /dev/null +++ b/tests/Issues/RenameString/RenameStringTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/RenameString/config/configured_rule.php b/tests/Issues/RenameString/config/configured_rule.php new file mode 100644 index 00000000000..a5835ec442a --- /dev/null +++ b/tests/Issues/RenameString/config/configured_rule.php @@ -0,0 +1,15 @@ +rule(StringClassNameToClassConstantRector::class); + + $rectorConfig->ruleWithConfiguration(RenameStringRector::class, [ + 'Rector\Tests\Issues\RenameString\Fixture\RenameString' => 'new test', + ]); +}; diff --git a/tests/Issues/ReplaceStmtToExpr/Fixture/some_fixture.php.inc b/tests/Issues/ReplaceStmtToExpr/Fixture/some_fixture.php.inc new file mode 100644 index 00000000000..f35cdd7e106 --- /dev/null +++ b/tests/Issues/ReplaceStmtToExpr/Fixture/some_fixture.php.inc @@ -0,0 +1,63 @@ +getUser($user); + + if (!$user) { + return false; + } + + return $user->isFoo() || $user->isBar(); + } + +} + +?> +----- +getUser($user); + + if (!$user instanceof \Rector\Tests\Issues\ReplaceStmtToExpr\Source\SomeUser) { + return false; + } + if ($user->isFoo()) { + return true; + } + return (bool) $user->isBar(); + } + +} + +?> diff --git a/tests/Issues/ReplaceStmtToExpr/ReplaceStmtToExprTest.php b/tests/Issues/ReplaceStmtToExpr/ReplaceStmtToExprTest.php new file mode 100644 index 00000000000..3d1c6b6fe8f --- /dev/null +++ b/tests/Issues/ReplaceStmtToExpr/ReplaceStmtToExprTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/ReplaceStmtToExpr/Source/SomeUser.php b/tests/Issues/ReplaceStmtToExpr/Source/SomeUser.php new file mode 100644 index 00000000000..8e2bc3d6d1f --- /dev/null +++ b/tests/Issues/ReplaceStmtToExpr/Source/SomeUser.php @@ -0,0 +1,8 @@ +withRules( + [ + ExplicitBoolCompareRector::class, + FlipTypeControlToUseExclusiveTypeRector::class, + ReturnBinaryOrToEarlyReturnRector::class, + ] + ); diff --git a/tests/Issues/ReturnEmptyNodes/Fixture/empty_if_stmts.php.inc b/tests/Issues/ReturnEmptyNodes/Fixture/empty_if_stmts.php.inc new file mode 100644 index 00000000000..ceb9d8feac2 --- /dev/null +++ b/tests/Issues/ReturnEmptyNodes/Fixture/empty_if_stmts.php.inc @@ -0,0 +1,14 @@ +expectExceptionMessage('Array of nodes cannot be empty'); + $this->expectException(ShouldNotHappenException::class); + + $this->doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/ReturnEmptyNodes/Source/ReturnEmptyStmtsRector.php b/tests/Issues/ReturnEmptyNodes/Source/ReturnEmptyStmtsRector.php new file mode 100644 index 00000000000..25390ce97bf --- /dev/null +++ b/tests/Issues/ReturnEmptyNodes/Source/ReturnEmptyStmtsRector.php @@ -0,0 +1,31 @@ +stmts; + } +} diff --git a/tests/Issues/ReturnEmptyNodes/config/configured_rule.php b/tests/Issues/ReturnEmptyNodes/config/configured_rule.php new file mode 100644 index 00000000000..3d9d03f3bc3 --- /dev/null +++ b/tests/Issues/ReturnEmptyNodes/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([ReturnEmptyStmtsRector::class]); diff --git a/tests/Issues/RuleWithConfigurationAfterSet/Fixture/skip_doctrine_class_string.php.inc b/tests/Issues/RuleWithConfigurationAfterSet/Fixture/skip_doctrine_class_string.php.inc new file mode 100644 index 00000000000..2f49214a717 --- /dev/null +++ b/tests/Issues/RuleWithConfigurationAfterSet/Fixture/skip_doctrine_class_string.php.inc @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/RuleWithConfigurationAfterSet/config/configured_rule.php b/tests/Issues/RuleWithConfigurationAfterSet/config/configured_rule.php new file mode 100644 index 00000000000..caa5195fc87 --- /dev/null +++ b/tests/Issues/RuleWithConfigurationAfterSet/config/configured_rule.php @@ -0,0 +1,13 @@ +sets([LevelSetList::UP_TO_PHP_81]); + + $rectorConfig->ruleWithConfiguration(StringClassNameToClassConstantRector::class, ['Doctrine\*']); +}; diff --git a/tests/Issues/ScopeNotAvailable/ArrayAnnotationToAttributeTest.php b/tests/Issues/ScopeNotAvailable/ArrayAnnotationToAttributeTest.php new file mode 100644 index 00000000000..8dc9bb75306 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/ArrayAnnotationToAttributeTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureArrayAnnotationToAttribute'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/array_annotation_to_attribute.php'; + } +} diff --git a/tests/Issues/ScopeNotAvailable/FirstClassCallableTest.php b/tests/Issues/ScopeNotAvailable/FirstClassCallableTest.php new file mode 100644 index 00000000000..8591d5b0b3f --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FirstClassCallableTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureFirstClassCallable'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/first_class_callable_configured_rule.php'; + } +} diff --git a/tests/Issues/ScopeNotAvailable/Fixture/callable_this.php.inc b/tests/Issues/ScopeNotAvailable/Fixture/callable_this.php.inc new file mode 100644 index 00000000000..c6ae4af1e13 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/Fixture/callable_this.php.inc @@ -0,0 +1,27 @@ +getRoles(), ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN'])) > 0; + } +} + +?> +----- +getRoles(), ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN']) !== []; + } +} + +?> diff --git a/tests/Issues/ScopeNotAvailable/Fixture/count_array.php.inc b/tests/Issues/ScopeNotAvailable/Fixture/count_array.php.inc new file mode 100644 index 00000000000..8b022be401f --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/Fixture/count_array.php.inc @@ -0,0 +1,47 @@ +decode($hash); + if (count($values) === 0) { + return null; + } + + return $values[0]; + } +} + +?> +----- +decode($hash); + if ($values === []) { + return null; + } + + return $values[0]; + } +} + +?> diff --git a/tests/Issues/ScopeNotAvailable/FixtureArrayAnnotationToAttribute/array_in_attribute.php.inc b/tests/Issues/ScopeNotAvailable/FixtureArrayAnnotationToAttribute/array_in_attribute.php.inc new file mode 100644 index 00000000000..f12e92610d7 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureArrayAnnotationToAttribute/array_in_attribute.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/tests/Issues/ScopeNotAvailable/FixtureDowngradePregUnmatched/preg_unmatched.php.inc b/tests/Issues/ScopeNotAvailable/FixtureDowngradePregUnmatched/preg_unmatched.php.inc new file mode 100644 index 00000000000..8d14864b9b7 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureDowngradePregUnmatched/preg_unmatched.php.inc @@ -0,0 +1,28 @@ +string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset); +} + +?> +----- +string, $matches, $flags, $offset); + array_walk_recursive($matches, function (&$value) { + if ($value === '') { + $value = null; + } + }); +} + +?> diff --git a/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/first_class_callable_as_key.php.inc b/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/first_class_callable_as_key.php.inc new file mode 100644 index 00000000000..04ba069b4a9 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/first_class_callable_as_key.php.inc @@ -0,0 +1,31 @@ +opposite(...) => 'test', + ]; + } +} + +?> +----- + 'test', + ]; + } +} + +?> diff --git a/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/first_class_callable_as_key2.php.inc b/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/first_class_callable_as_key2.php.inc new file mode 100644 index 00000000000..53ca699107c --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/first_class_callable_as_key2.php.inc @@ -0,0 +1,31 @@ +opposite(...)() => 'test', + ]; + } +} + +?> +----- + 'test', + ]; + } +} + +?> diff --git a/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/fixture.php.inc b/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/fixture.php.inc new file mode 100644 index 00000000000..c4aed1e8af0 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureFirstClassCallable/fixture.php.inc @@ -0,0 +1,31 @@ +opposite(...), + ]; + } +} + +?> +----- + diff --git a/tests/Issues/ScopeNotAvailable/FixtureForeachToArrayParam/fixture.php.inc b/tests/Issues/ScopeNotAvailable/FixtureForeachToArrayParam/fixture.php.inc new file mode 100644 index 00000000000..4be843282f5 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureForeachToArrayParam/fixture.php.inc @@ -0,0 +1,55 @@ + $urls + */ + public function create(array $urls, string $hash): ?Url + { + if (0 === \count($urls)) { + $url = new Url(); + } else { + $url = null; + foreach ($urls as $urlToCheck) { + if ($urlToCheck->getPathHash() === $hash) { + $url = $urlToCheck; + break; + } + } + } + + return $url; + } +} + +?> +----- + $urls + */ + public function create(array $urls, string $hash): ?Url + { + if (0 === \count($urls)) { + $url = new Url(); + } else { + $url = array_find($urls, fn($urlToCheck) => $urlToCheck->getPathHash() === $hash); + } + + return $url; + } +} + +?> \ No newline at end of file diff --git a/tests/Issues/ScopeNotAvailable/FixtureForeachValue/fixture.php.inc b/tests/Issues/ScopeNotAvailable/FixtureForeachValue/fixture.php.inc new file mode 100644 index 00000000000..4a1e3d01a05 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureForeachValue/fixture.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts.php.inc b/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts.php.inc new file mode 100644 index 00000000000..f3ccd3a5aae --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts2.php.inc b/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts2.php.inc new file mode 100644 index 00000000000..1ead7870b06 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts2.php.inc @@ -0,0 +1,51 @@ + +----- + diff --git a/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts_on_top_level.php.inc b/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts_on_top_level.php.inc new file mode 100644 index 00000000000..400ae26a00e --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureJsonThrowCaseSensitiveConstFetch/non_exact_value_after_unreachable_stmts_on_top_level.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/tests/Issues/ScopeNotAvailable/FixtureThrowOptionalParams/fixture.php.inc b/tests/Issues/ScopeNotAvailable/FixtureThrowOptionalParams/fixture.php.inc new file mode 100644 index 00000000000..f51661a5eb0 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/FixtureThrowOptionalParams/fixture.php.inc @@ -0,0 +1,47 @@ +writeln('Hello'); + } catch (\Doctrine\DBAL\Exception $e) { + throw new \Exception((string)$e); + } + } +} + +?> +----- +writeln('Hello'); + } catch (\Doctrine\DBAL\Exception $e) { + throw new \Exception((string)$e, $e->getCode(), $e); + } + } +} + +?> diff --git a/tests/Issues/ScopeNotAvailable/ForeachToArrayParamTest.php b/tests/Issues/ScopeNotAvailable/ForeachToArrayParamTest.php new file mode 100644 index 00000000000..450c85e9cc6 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/ForeachToArrayParamTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureForeachToArrayParam'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/foreach_to_array_param.php'; + } +} diff --git a/tests/Issues/ScopeNotAvailable/ForeachValueScopeTest.php b/tests/Issues/ScopeNotAvailable/ForeachValueScopeTest.php new file mode 100644 index 00000000000..d5d378758bf --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/ForeachValueScopeTest.php @@ -0,0 +1,29 @@ +doTestFileExpectingWarningAboutRuleApplied($filePath, ArrayItemForeachValueRector::class); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureForeachValue'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/foreach_value_configured.php'; + } +} diff --git a/tests/Issues/ScopeNotAvailable/JsonThrowCaseSensitiveConstFetchTest.php b/tests/Issues/ScopeNotAvailable/JsonThrowCaseSensitiveConstFetchTest.php new file mode 100644 index 00000000000..b0440ad0685 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/JsonThrowCaseSensitiveConstFetchTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureJsonThrowCaseSensitiveConstFetch'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/json_throw_case_sensitive_const_fetch.php'; + } +} diff --git a/tests/Issues/ScopeNotAvailable/PregUnmatchedTest.php b/tests/Issues/ScopeNotAvailable/PregUnmatchedTest.php new file mode 100644 index 00000000000..50189e4ba2c --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/PregUnmatchedTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureDowngradePregUnmatched'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/callable_this_downgrade_preg_unmatched_configured_rule.php'; + } +} diff --git a/tests/Issues/ScopeNotAvailable/ThrowOptionalParamsTest.php b/tests/Issues/ScopeNotAvailable/ThrowOptionalParamsTest.php new file mode 100644 index 00000000000..bd27e11b600 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/ThrowOptionalParamsTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureThrowOptionalParams'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/throw_optional_params_rule.php'; + } +} diff --git a/tests/Issues/ScopeNotAvailable/Variable/ArrayItemForeachValueRector.php b/tests/Issues/ScopeNotAvailable/Variable/ArrayItemForeachValueRector.php new file mode 100644 index 00000000000..2b0bb0a9a87 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/Variable/ArrayItemForeachValueRector.php @@ -0,0 +1,33 @@ +> + */ + public function getNodeTypes(): array + { + return [Variable::class]; + } + + public function refactor(Node $node): Node + { + return $node; + } +} diff --git a/tests/Issues/ScopeNotAvailable/config/array_annotation_to_attribute.php b/tests/Issues/ScopeNotAvailable/config/array_annotation_to_attribute.php new file mode 100644 index 00000000000..fbdf4379995 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/config/array_annotation_to_attribute.php @@ -0,0 +1,19 @@ +phpVersion(PhpVersionFeature::ATTRIBUTES); + + $rectorConfig->rule(LongArrayToShortArrayRector::class); + + $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ + new AnnotationToAttribute('Doctrine\ORM\Mapping\ManyToOne'), + ]); +}; diff --git a/tests/Issues/ScopeNotAvailable/config/callable_this_downgrade_preg_unmatched_configured_rule.php b/tests/Issues/ScopeNotAvailable/config/callable_this_downgrade_preg_unmatched_configured_rule.php new file mode 100644 index 00000000000..77ef4607b8c --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/config/callable_this_downgrade_preg_unmatched_configured_rule.php @@ -0,0 +1,10 @@ +withRules([DowngradeArrayIsListRector::class, DowngradePregUnmatchedAsNullConstantRector::class]); diff --git a/tests/Issues/ScopeNotAvailable/config/first_class_callable_configured_rule.php b/tests/Issues/ScopeNotAvailable/config/first_class_callable_configured_rule.php new file mode 100644 index 00000000000..407b969ce63 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/config/first_class_callable_configured_rule.php @@ -0,0 +1,9 @@ +withRules([DowngradeFirstClassCallableSyntaxRector::class]); diff --git a/tests/Issues/ScopeNotAvailable/config/foreach_to_array_param.php b/tests/Issues/ScopeNotAvailable/config/foreach_to_array_param.php new file mode 100644 index 00000000000..aa929214558 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/config/foreach_to_array_param.php @@ -0,0 +1,14 @@ +rules([ForeachToArrayFindRector::class, OptionalParametersAfterRequiredRector::class]); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/tests/Issues/ScopeNotAvailable/config/foreach_value_configured.php b/tests/Issues/ScopeNotAvailable/config/foreach_value_configured.php new file mode 100644 index 00000000000..61b348afbe5 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/config/foreach_value_configured.php @@ -0,0 +1,9 @@ +withRules([ArrayItemForeachValueRector::class]); diff --git a/tests/Issues/ScopeNotAvailable/config/json_throw_case_sensitive_const_fetch.php b/tests/Issues/ScopeNotAvailable/config/json_throw_case_sensitive_const_fetch.php new file mode 100644 index 00000000000..681b3c03f57 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/config/json_throw_case_sensitive_const_fetch.php @@ -0,0 +1,10 @@ +withRules([JsonThrowOnErrorRector::class, SensitiveConstantNameRector::class]); diff --git a/tests/Issues/ScopeNotAvailable/config/throw_optional_params_rule.php b/tests/Issues/ScopeNotAvailable/config/throw_optional_params_rule.php new file mode 100644 index 00000000000..bac2497ad49 --- /dev/null +++ b/tests/Issues/ScopeNotAvailable/config/throw_optional_params_rule.php @@ -0,0 +1,10 @@ +withRules([ThrowWithPreviousExceptionRector::class, OptionalParametersAfterRequiredRector::class]); diff --git a/tests/Issues/SimplifyEmpty/Fixture/fixture.php.inc b/tests/Issues/SimplifyEmpty/Fixture/fixture.php.inc new file mode 100644 index 00000000000..eed7ea51454 --- /dev/null +++ b/tests/Issues/SimplifyEmpty/Fixture/fixture.php.inc @@ -0,0 +1,61 @@ +string; + } + + public function getString2(): ?string + { + return $this->string2; + } + + public function check(): bool + { + if (empty($this->getString()) || (empty($this->getString2()) && !is_numeric($this->getString2()))) { + return false; + } + + return true; + } +} + +?> +----- +string; + } + + public function getString2(): ?string + { + return $this->string2; + } + + public function check(): bool + { + return !in_array($this->getString(), [null, '', '0'], true) && !(in_array($this->getString2(), [null, '', '0'], true) && !is_numeric($this->getString2())); + } +} + +?> diff --git a/tests/Issues/SimplifyEmpty/SimplifyEmptyTest.php b/tests/Issues/SimplifyEmpty/SimplifyEmptyTest.php new file mode 100644 index 00000000000..7c81bc83a0b --- /dev/null +++ b/tests/Issues/SimplifyEmpty/SimplifyEmptyTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/SimplifyEmpty/config/configured_rule.php b/tests/Issues/SimplifyEmpty/config/configured_rule.php new file mode 100644 index 00000000000..5b3d54394a5 --- /dev/null +++ b/tests/Issues/SimplifyEmpty/config/configured_rule.php @@ -0,0 +1,13 @@ +withRules( + [SimplifyIfReturnBoolRector::class, SimplifyDeMorganBinaryRector::class, DisallowedEmptyRuleFixerRector::class] + ); diff --git a/tests/Issues/SimplifyVariableIfElseTernary/Fixture/do_not_duplicated_expr.php.inc b/tests/Issues/SimplifyVariableIfElseTernary/Fixture/do_not_duplicated_expr.php.inc new file mode 100644 index 00000000000..97b1eb8d3fa --- /dev/null +++ b/tests/Issues/SimplifyVariableIfElseTernary/Fixture/do_not_duplicated_expr.php.inc @@ -0,0 +1,37 @@ + 0) { + $baz = 'a'; + } else { + $baz = 'b'; + } + + return $baz; + } +} + +?> +----- + 0 ? 'a' : 'b'; + } +} + +?> diff --git a/tests/Issues/SimplifyVariableIfElseTernary/SimplifyVariableIfElseTernaryTest.php b/tests/Issues/SimplifyVariableIfElseTernary/SimplifyVariableIfElseTernaryTest.php new file mode 100644 index 00000000000..ed5c1539eab --- /dev/null +++ b/tests/Issues/SimplifyVariableIfElseTernary/SimplifyVariableIfElseTernaryTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/SimplifyVariableIfElseTernary/config/configured_rule.php b/tests/Issues/SimplifyVariableIfElseTernary/config/configured_rule.php new file mode 100644 index 00000000000..a49cb9a6ad7 --- /dev/null +++ b/tests/Issues/SimplifyVariableIfElseTernary/config/configured_rule.php @@ -0,0 +1,17 @@ +withRules( + [ + SimplifyIfElseToTernaryRector::class, + SimplifyUselessVariableRector::class, + CompleteDynamicPropertiesRector::class, + ] + ); diff --git a/tests/Issues/SplitMultiAssignRemovePrivate/Fixture/extracted_assign.php.inc b/tests/Issues/SplitMultiAssignRemovePrivate/Fixture/extracted_assign.php.inc new file mode 100644 index 00000000000..ef5c382ae7a --- /dev/null +++ b/tests/Issues/SplitMultiAssignRemovePrivate/Fixture/extracted_assign.php.inc @@ -0,0 +1,38 @@ +content_type = "Something"; + $content_type = "Something"; + + return $content_type; + } +} + +?> +----- + diff --git a/tests/Issues/SplitMultiAssignRemovePrivate/Fixture/multi_assign_extract.php.inc b/tests/Issues/SplitMultiAssignRemovePrivate/Fixture/multi_assign_extract.php.inc new file mode 100644 index 00000000000..3a95478aeb0 --- /dev/null +++ b/tests/Issues/SplitMultiAssignRemovePrivate/Fixture/multi_assign_extract.php.inc @@ -0,0 +1,39 @@ +content_type = $content_type = "Something"; + + return $content_type; + } +} + +?> +----- +content_type = "Something"; + $content_type = "Something"; + return $content_type; + } +} + +?> diff --git a/tests/Issues/SplitMultiAssignRemovePrivate/SplitMultiAssignRemovePrivateTest.php b/tests/Issues/SplitMultiAssignRemovePrivate/SplitMultiAssignRemovePrivateTest.php new file mode 100644 index 00000000000..55c16a9fead --- /dev/null +++ b/tests/Issues/SplitMultiAssignRemovePrivate/SplitMultiAssignRemovePrivateTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/SplitMultiAssignRemovePrivate/config/configured_rule.php b/tests/Issues/SplitMultiAssignRemovePrivate/config/configured_rule.php new file mode 100644 index 00000000000..33324a1997c --- /dev/null +++ b/tests/Issues/SplitMultiAssignRemovePrivate/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([SplitDoubleAssignRector::class, RemoveUnusedPrivatePropertyRector::class]); diff --git a/tests/Issues/TemplatedTypeOnParamAndReturn/Fixture/skip_templated_type_on_param_and_return.php.inc b/tests/Issues/TemplatedTypeOnParamAndReturn/Fixture/skip_templated_type_on_param_and_return.php.inc new file mode 100644 index 00000000000..6d87ea91fe9 --- /dev/null +++ b/tests/Issues/TemplatedTypeOnParamAndReturn/Fixture/skip_templated_type_on_param_and_return.php.inc @@ -0,0 +1,18 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/TemplatedTypeOnParamAndReturn/config/configured_rule.php b/tests/Issues/TemplatedTypeOnParamAndReturn/config/configured_rule.php new file mode 100644 index 00000000000..3cb97cdb0d6 --- /dev/null +++ b/tests/Issues/TemplatedTypeOnParamAndReturn/config/configured_rule.php @@ -0,0 +1,10 @@ +withRules([RemoveUselessParamTagRector::class, RemoveUselessReturnTagRector::class]); diff --git a/tests/Issues/TestDocAnnotation/Fixture/fixture.php.inc b/tests/Issues/TestDocAnnotation/Fixture/fixture.php.inc new file mode 100644 index 00000000000..aa094791d2d --- /dev/null +++ b/tests/Issues/TestDocAnnotation/Fixture/fixture.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/tests/Issues/TestDocAnnotation/Fixture/multi_double_colon.php.inc b/tests/Issues/TestDocAnnotation/Fixture/multi_double_colon.php.inc new file mode 100644 index 00000000000..f4fb4a70618 --- /dev/null +++ b/tests/Issues/TestDocAnnotation/Fixture/multi_double_colon.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/tests/Issues/TestDocAnnotation/TestDocAnnotationTest.php b/tests/Issues/TestDocAnnotation/TestDocAnnotationTest.php new file mode 100644 index 00000000000..4aed7d1afb1 --- /dev/null +++ b/tests/Issues/TestDocAnnotation/TestDocAnnotationTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/TestDocAnnotation/config/configured_rule.php b/tests/Issues/TestDocAnnotation/config/configured_rule.php new file mode 100644 index 00000000000..e66ada2ce93 --- /dev/null +++ b/tests/Issues/TestDocAnnotation/config/configured_rule.php @@ -0,0 +1,13 @@ +ruleWithConfiguration(AnnotationWithValueToAttributeRector::class, [ + new AnnotationWithValueToAttribute('testdox', 'PHPUnit\Framework\Attributes\TestDox'), + ]); +}; diff --git a/tests/Issues/TestWithAttribute/Fixture/keep_string_as_is.php.inc b/tests/Issues/TestWithAttribute/Fixture/keep_string_as_is.php.inc new file mode 100644 index 00000000000..fd238ca262a --- /dev/null +++ b/tests/Issues/TestWithAttribute/Fixture/keep_string_as_is.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/tests/Issues/TestWithAttribute/TestWithAttributeTest.php b/tests/Issues/TestWithAttribute/TestWithAttributeTest.php new file mode 100644 index 00000000000..35ad441ae75 --- /dev/null +++ b/tests/Issues/TestWithAttribute/TestWithAttributeTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/TestWithAttribute/config/configured_rule.php b/tests/Issues/TestWithAttribute/config/configured_rule.php new file mode 100644 index 00000000000..4d4acca6e86 --- /dev/null +++ b/tests/Issues/TestWithAttribute/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(TestWithAnnotationToAttributeRector::class); +}; diff --git a/tests/Issues/WithPhpSets/Fixture/fixture.php.inc b/tests/Issues/WithPhpSets/Fixture/fixture.php.inc new file mode 100644 index 00000000000..bfa3ec8764e --- /dev/null +++ b/tests/Issues/WithPhpSets/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/Issues/WithPhpSets/WithPhpSetsTest.php b/tests/Issues/WithPhpSets/WithPhpSetsTest.php new file mode 100644 index 00000000000..0798a2fd054 --- /dev/null +++ b/tests/Issues/WithPhpSets/WithPhpSetsTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/WithPhpSets/config/configured_rule.php b/tests/Issues/WithPhpSets/config/configured_rule.php new file mode 100644 index 00000000000..abb32da9a73 --- /dev/null +++ b/tests/Issues/WithPhpSets/config/configured_rule.php @@ -0,0 +1,10 @@ +withPhpSets(php85: true) + ->withPhpVersion(PhpVersion::PHP_85); diff --git a/tests/Naming/ExpectedNameResolver/InflectorSingularResolverTest.php b/tests/Naming/ExpectedNameResolver/InflectorSingularResolverTest.php new file mode 100644 index 00000000000..b3410dff31e --- /dev/null +++ b/tests/Naming/ExpectedNameResolver/InflectorSingularResolverTest.php @@ -0,0 +1,46 @@ +inflectorSingularResolver = $this->make(InflectorSingularResolver::class); + } + + #[DataProvider('provideData')] + public function testResolveForForeach(string $currentName, string $expectedSingularName): void + { + $singularValue = $this->inflectorSingularResolver->resolve($currentName); + $this->assertSame($expectedSingularName, $singularValue); + } + + /** + * @return Iterator> + */ + public static function provideData(): Iterator + { + yield ['psr4NamespacesToPaths', 'psr4NamespaceToPath']; + yield ['nestedNews', 'nestedNew']; + yield ['news', 'new']; + yield ['property', 'property']; + yield ['argsOrOptions', 'argOrOption']; + + // news and plural + yield ['staticCallsToNews', 'staticCallToNew']; + yield ['newsToMethodCalls', 'newToMethodCall']; + yield ['hasFilters', 'hasFilter']; + } +} diff --git a/tests/NodeCollector/BinaryOpConditionsCollectorTest.php b/tests/NodeCollector/BinaryOpConditionsCollectorTest.php new file mode 100644 index 00000000000..c7c2487bd6c --- /dev/null +++ b/tests/NodeCollector/BinaryOpConditionsCollectorTest.php @@ -0,0 +1,105 @@ +findConditions($abcPlus, Plus::class); + + $this->assertSame([ + 2 => $firstVariable, + 1 => $secondVariable, + 0 => $thirdVariable, + ], $result); + } + + public function testRightAssociative(): void + { + $binaryOpConditionsCollector = new BinaryOpConditionsCollector(); + + // (Plus a (Plus b c)) + $firstVariable = new Variable('a'); + $secondVariable = new Variable('b'); + $thirdVariable = new Variable('c'); + + $bcPlus = new Plus($secondVariable, $thirdVariable); + $abcPlus = new Plus($firstVariable, $bcPlus); + + $result = $binaryOpConditionsCollector->findConditions($abcPlus, Plus::class); + + $this->assertSame([ + 1 => $firstVariable, + 0 => $bcPlus, + ], $result); + } + + public function testWrongRootOp(): void + { + $binaryOpConditionsCollector = new BinaryOpConditionsCollector(); + + // (Minus (Plus a b) c) + $firstVariable = new Variable('a'); + $secondVariable = new Variable('b'); + $thirdVariable = new Variable('c'); + + $abcMinus = new Minus(new Plus($firstVariable, $secondVariable), $thirdVariable); + + $result = $binaryOpConditionsCollector->findConditions($abcMinus, Plus::class); + + $this->assertSame([ + 0 => $abcMinus, + ], $result); + } + + public function testTrivialCase(): void + { + $binaryOpConditionsCollector = new BinaryOpConditionsCollector(); + + $variable = new Variable('a'); + + $result = $binaryOpConditionsCollector->findConditions($variable, Plus::class); + + $this->assertSame([ + 0 => $variable, + ], $result); + } + + public function testInnerNodeDifferentOp(): void + { + $binaryOpConditionsCollector = new BinaryOpConditionsCollector(); + + // (Plus (Minus a b) c) + $firstVariable = new Variable('a'); + $secondVariable = new Variable('b'); + $thirdVariable = new Variable('c'); + + $abMinus = new Minus($firstVariable, $secondVariable); + $abcPlus = new Plus($abMinus, $thirdVariable); + + $result = $binaryOpConditionsCollector->findConditions($abcPlus, Plus::class); + + $this->assertSame([ + 1 => $abMinus, + 0 => $thirdVariable, + ], $result); + } +} diff --git a/tests/NodeFactory/ClassWithPublicPropertiesFactory/ClassWithPublicPropertiesFactoryTest.php b/tests/NodeFactory/ClassWithPublicPropertiesFactory/ClassWithPublicPropertiesFactoryTest.php deleted file mode 100644 index bcb165508cd..00000000000 --- a/tests/NodeFactory/ClassWithPublicPropertiesFactory/ClassWithPublicPropertiesFactoryTest.php +++ /dev/null @@ -1,61 +0,0 @@ -bootFromConfigFileInfos([new SmartFileInfo(__DIR__ . '/../../../config/config.php')]); - $this->classWithPublicPropertiesFactory = $this->getService(ClassWithPublicPropertiesFactory::class); - $this->betterStandardPrinter = $this->getService(BetterStandardPrinter::class); - } - - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fixtureFileInfo): void - { - $contents = $fixtureFileInfo->getContents(); - // normalize for windows compat - $contents = str_replace("\r\n", "\n", $contents); - [$content, $expected] = explode("-----\n", $contents, 2); - - $classSettings = Json::decode($content, Json::FORCE_ARRAY); - - $node = $this->classWithPublicPropertiesFactory->createNode( - $classSettings['fullyQualifiedName'], - $classSettings['properties'], - $classSettings['parent'] ?? null, - $classSettings['traits'] ?? [] - ); - - $output = "betterStandardPrinter->print($node) . "\n"; - $this->assertSame($expected, $output); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectoryExclusively(__DIR__ . '/Fixture'); - } -} diff --git a/tests/NodeFactory/ClassWithPublicPropertiesFactory/Fixture/empty_class_no_namespace.php.inc b/tests/NodeFactory/ClassWithPublicPropertiesFactory/Fixture/empty_class_no_namespace.php.inc deleted file mode 100644 index 6a9097efca6..00000000000 --- a/tests/NodeFactory/ClassWithPublicPropertiesFactory/Fixture/empty_class_no_namespace.php.inc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "fullyQualifiedName": "EmptyClassNoNamespace", - "properties": [], - "parent": null, - "traits": [] -} ------ -classDependencyManipulator = $this->make(ClassDependencyManipulator::class); + + $this->printerStandard = new Standard(); + + // use at least readonly property + SimpleParameterProvider::setParameter(Option::PHP_VERSION_FEATURES, PhpVersionFeature::READONLY_PROPERTY); + } + + public function testEmptyClass(): void + { + $someClass = new Class_(new Identifier('EmptyClass')); + + $this->setNamespacedName($someClass); + + $this->addSingleDependency($someClass); + $this->assertClassEqualsFile($someClass, __DIR__ . '/Fixture/expected_empty_class.php.inc'); + } + + public function testSingleMethod(): void + { + $someClass = new Class_(new Identifier('SingleMethodClass')); + $this->setNamespacedName($someClass); + + $someClass->stmts[] = new ClassMethod('firstMethod'); + + $this->addSingleDependency($someClass); + + $this->assertClassEqualsFile($someClass, __DIR__ . '/Fixture/expected_single_method.php.inc'); + } + + public function testWithProperty(): void + { + $someClass = new Class_(new Identifier('ClassWithSingleProperty')); + + $this->setNamespacedName($someClass); + + $someClass->stmts[] = new Property(Modifiers::PRIVATE, [new PropertyItem('someProperty')]); + + $this->addSingleDependency($someClass); + + $this->assertClassEqualsFile($someClass, __DIR__ . '/Fixture/expected_single_property.php.inc'); + + } + + public function testWithMethodAndProperty(): void + { + $someClass = new Class_(new Identifier('ClassWithMethodAndProperty')); + + $this->setNamespacedName($someClass); + + $someClass->stmts[] = new Property(Modifiers::PRIVATE, [new PropertyItem('someProperty')]); + $someClass->stmts[] = new ClassMethod(new Identifier('someMethod')); + + $this->addSingleDependency($someClass); + + $this->assertClassEqualsFile($someClass, __DIR__ . '/Fixture/expected_method_and_property.php.inc'); + } + + public function testConstantProperties(): void + { + $someClass = new Class_(new Identifier('ConstantProperties')); + + $this->setNamespacedName($someClass); + + $someClass->stmts[] = new ClassConst([new Const_('SOME_CONST', new String_('value'))]); + $someClass->stmts[] = new Property(Modifiers::PUBLIC, [new PropertyItem('someProperty')]); + $someClass->stmts[] = new Property(Modifiers::PUBLIC, [new PropertyItem('anotherProperty')]); + + $this->addSingleDependency($someClass); + + $this->assertClassEqualsFile($someClass, __DIR__ . '/Fixture/expected_class_const_property.php.inc'); + } + + private function setNamespacedName(Class_ $class): void + { + $nameResolver = new NameResolver(); + $nodeTraverser = new NodeTraverser($nameResolver); + + $nodeTraverser->traverse([$class]); + } + + private function addSingleDependency(Class_ $class): void + { + $this->classDependencyManipulator->addConstructorDependency($class, new PropertyMetadata( + 'eventDispatcher', + new ObjectType('EventDispatcherInterface') + )); + } + + private function assertClassEqualsFile(Class_ $class, string $expectedFilePath): void + { + $printedClass = $this->printerStandard->prettyPrintFile([$class]); + + // normalize newline in Windows + $printedClass = str_replace("\n", PHP_EOL, $printedClass . "\n"); + + $this->assertStringEqualsFile($expectedFilePath, $printedClass); + } +} diff --git a/tests/NodeManipulator/Fixture/expected_class_const_property.php.inc b/tests/NodeManipulator/Fixture/expected_class_const_property.php.inc new file mode 100644 index 00000000000..92cae65cfb6 --- /dev/null +++ b/tests/NodeManipulator/Fixture/expected_class_const_property.php.inc @@ -0,0 +1,11 @@ +make(PHPStanServicesFactory::class); + + $phpstanParser = $phpStanServicesFactory->createPHPStanParser(); + $this->assertInstanceOf(Parser::class, $phpstanParser); + } +} diff --git a/tests/NodeTypeResolver/PerNodeTypeResolver/AbstractNodeTypeResolverTestCase.php b/tests/NodeTypeResolver/PerNodeTypeResolver/AbstractNodeTypeResolverTestCase.php new file mode 100644 index 00000000000..501c4f04872 --- /dev/null +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/AbstractNodeTypeResolverTestCase.php @@ -0,0 +1,40 @@ +betterNodeFinder = $this->make(BetterNodeFinder::class); + $this->testingParser = $this->make(TestingParser::class); + $this->nodeTypeResolver = $this->make(NodeTypeResolver::class); + } + + /** + * @template T as Node + * @param class-string $type + * @return T[] + */ + protected function getNodesForFileOfType(string $filePath, string $type): array + { + $nodes = $this->testingParser->parseFileToDecoratedNodes($filePath); + return $this->betterNodeFinder->findInstanceOf($nodes, $type); + } +} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/ClassTypeResolverTest.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/ClassTypeResolverTest.php similarity index 83% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/ClassTypeResolverTest.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/ClassTypeResolverTest.php index 36ad82cdcfb..fd6673a5f1b 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/ClassTypeResolverTest.php +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/ClassTypeResolverTest.php @@ -8,7 +8,8 @@ use PhpParser\Node\Stmt\Class_; use PHPStan\Type\ObjectType; use PHPStan\Type\TypeWithClassName; -use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\AbstractNodeTypeResolverTest; +use PHPUnit\Framework\Attributes\DataProvider; +use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\AbstractNodeTypeResolverTestCase; use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\ClassWithParentClass; use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\ClassWithParentInterface; use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\ClassWithParentTrait; @@ -17,23 +18,24 @@ /** * @see \Rector\NodeTypeResolver\NodeTypeResolver\ClassAndInterfaceTypeResolver */ -final class ClassTypeResolverTest extends AbstractNodeTypeResolverTest +final class ClassTypeResolverTest extends AbstractNodeTypeResolverTestCase { - /** - * @dataProvider dataProvider() - */ + #[DataProvider('dataProvider')] public function test(string $file, int $nodePosition, ObjectType $expectedObjectType): void { $variableNodes = $this->getNodesForFileOfType($file, Class_::class); - $resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]); + $resolvedType = $this->nodeTypeResolver->getType($variableNodes[$nodePosition]); $this->assertInstanceOf(TypeWithClassName::class, $resolvedType); /** @var TypeWithClassName $resolvedType */ - $this->assertEquals($expectedObjectType->getClassName(), $resolvedType->getClassName()); + $this->assertSame($expectedObjectType->getClassName(), $resolvedType->getClassName()); } - public function dataProvider(): Iterator + /** + * @return Iterator> + */ + public static function dataProvider(): Iterator { yield [ __DIR__ . '/Source/ClassWithParentInterface.php', @@ -54,7 +56,7 @@ public function testAnonymousClass(): void $variableNodes = $this->getNodesForFileOfType($file, Class_::class); - $resolvedType = $this->nodeTypeResolver->resolve($variableNodes[0]); + $resolvedType = $this->nodeTypeResolver->getType($variableNodes[0]); $this->assertInstanceOf(TypeWithClassName::class, $resolvedType); /** @var TypeWithClassName $resolvedType */ diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/InterfaceTypeResolverTest.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/InterfaceTypeResolverTest.php similarity index 77% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/InterfaceTypeResolverTest.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/InterfaceTypeResolverTest.php index 54cd306be95..2c8d7587c78 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/InterfaceTypeResolverTest.php +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/InterfaceTypeResolverTest.php @@ -8,32 +8,31 @@ use PhpParser\Node\Stmt\Interface_; use PHPStan\Type\ObjectType; use PHPStan\Type\TypeWithClassName; -use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\AbstractNodeTypeResolverTest; +use PHPUnit\Framework\Attributes\DataProvider; +use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\AbstractNodeTypeResolverTestCase; use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\SomeInterfaceWithParentInterface; /** * @see \Rector\NodeTypeResolver\NodeTypeResolver\ClassAndInterfaceTypeResolver */ -final class InterfaceTypeResolverTest extends AbstractNodeTypeResolverTest +final class InterfaceTypeResolverTest extends AbstractNodeTypeResolverTestCase { - /** - * @dataProvider dataProvider() - */ + #[DataProvider('dataProvider')] public function test(string $file, int $nodePosition, TypeWithClassName $expectedTypeWithClassName): void { $variableNodes = $this->getNodesForFileOfType($file, Interface_::class); - $resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]); + $resolvedType = $this->nodeTypeResolver->getType($variableNodes[$nodePosition]); $this->assertInstanceOf(TypeWithClassName::class, $resolvedType); /** @var TypeWithClassName $resolvedType */ - $this->assertEquals($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName()); + $this->assertSame($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName()); } /** * @return Iterator */ - public function dataProvider(): Iterator + public static function dataProvider(): Iterator { yield [ __DIR__ . '/Source/SomeInterfaceWithParentInterface.php', diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/AnonymousClass.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/AnonymousClass.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/AnonymousClass.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/AnonymousClass.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/AnotherTrait.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/AnotherTrait.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/AnotherTrait.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/AnotherTrait.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentClass.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentClass.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentClass.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentClass.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentInterface.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentInterface.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentInterface.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentInterface.php diff --git a/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentTrait.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentTrait.php new file mode 100644 index 00000000000..86aa21676ad --- /dev/null +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/ClassAndInterfaceTypeResolver/Source/ClassWithParentTrait.php @@ -0,0 +1,9 @@ +getNodesForFileOfType($file, Name::class); + + $resolvedType = $this->nodeTypeResolver->getType($nameNodes[$nodePosition]); + $this->assertEquals($expectedType, $resolvedType); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + $expectedFullyQualifiedObjectType = new FullyQualifiedObjectType(AnotherClass::class); + + # test new + yield [__DIR__ . '/Source/ParentCall.php', 2, $expectedFullyQualifiedObjectType]; + } +} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/NameTypeResolver/Source/ParentCall.php b/tests/NodeTypeResolver/PerNodeTypeResolver/NameTypeResolver/Source/ParentCall.php similarity index 84% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/NameTypeResolver/Source/ParentCall.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/NameTypeResolver/Source/ParentCall.php index 50a81a97d97..ec593258e17 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/NameTypeResolver/Source/ParentCall.php +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/NameTypeResolver/Source/ParentCall.php @@ -4,7 +4,7 @@ use Rector\Tests\NodeTypeResolver\Source\AnotherClass; -class ParentCall extends AnotherClass +final class ParentCall extends AnotherClass { public function getParameters() { diff --git a/tests/NodeTypeResolver/PerNodeTypeResolver/NewTypeResolver/NewTypeResolverTest.php b/tests/NodeTypeResolver/PerNodeTypeResolver/NewTypeResolver/NewTypeResolverTest.php new file mode 100644 index 00000000000..6cd754433cd --- /dev/null +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/NewTypeResolver/NewTypeResolverTest.php @@ -0,0 +1,54 @@ +getNodesForFileOfType($file, New_::class); + + $resolvedType = $this->nodeTypeResolver->getType($newNodes[$nodePosition]); + $this->assertEquals($expectedType, $resolvedType); + + $this->assertSame( + $isObjectType, + $this->nodeTypeResolver->isObjectType( + $newNodes[$nodePosition], + new ObjectType('Symfony\Bundle\TwigBundle\Loader\FilesystemLoader') + ) + ); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + $objectWithoutClassType = new ObjectWithoutClassType(); + + # test new + yield [__DIR__ . '/Source/NewDynamicNew.php', 0, $objectWithoutClassType, false]; + + $objectWithoutClassTypeWithParentTypes = new ObjectWithoutClassTypeWithParentTypes( + [new FullyQualifiedObjectType('Symfony\Bundle\TwigBundle\Loader\FilesystemLoader')] + ); + yield [__DIR__ . '/Source/NewDynamicNewExtends.php', 0, $objectWithoutClassTypeWithParentTypes, true]; + } +} diff --git a/tests/NodeTypeResolver/PerNodeTypeResolver/NewTypeResolver/Source/NewDynamicNew.php b/tests/NodeTypeResolver/PerNodeTypeResolver/NewTypeResolver/Source/NewDynamicNew.php new file mode 100644 index 00000000000..9c36f8cb1db --- /dev/null +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/NewTypeResolver/Source/NewDynamicNew.php @@ -0,0 +1,11 @@ +getNodesForFileOfType($file, Param::class); + + $resolvedType = $this->nodeTypeResolver->getType($variableNodes[$nodePosition]); + $this->assertSame($resolvedType::class, $expectedType); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Source/MethodParamTypeHint.php', 0, FullyQualifiedObjectType::class]; + yield [__DIR__ . '/Source/MethodParamDocBlock.php', 0, MixedType::class]; + } +} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/Html.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/Html.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/Html.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/Html.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/MethodParamDocBlock.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/MethodParamDocBlock.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/MethodParamDocBlock.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/MethodParamDocBlock.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/MethodParamTypeHint.php b/tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/MethodParamTypeHint.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/MethodParamTypeHint.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/ParamTypeResolver/Source/MethodParamTypeHint.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/explicit_mixed.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/explicit_mixed.php.inc similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/explicit_mixed.php.inc rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/explicit_mixed.php.inc diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/implicit_mixed.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/implicit_mixed.php.inc similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/implicit_mixed.php.inc rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/implicit_mixed.php.inc diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/non_existing_property.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/non_existing_property.php.inc similarity index 92% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/non_existing_property.php.inc rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/non_existing_property.php.inc index 74fdab00fec..f7675798d40 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/non_existing_property.php.inc +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/non_existing_property.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\FixturePhp74; +namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\Fixture; use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\Source\ClassWithTypedPropertyTypes; diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/nullable_number.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_number.php.inc similarity index 93% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/nullable_number.php.inc rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_number.php.inc index d93793ea1d3..42041e3cf26 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/nullable_number.php.inc +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_number.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\FixturePhp74; +namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\Fixture; use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\Source\ClassWithTypedPropertyTypes; diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/nullable_object_type.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_object_type.php.inc similarity index 94% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/nullable_object_type.php.inc rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_object_type.php.inc index 57e01c3a851..f4879cc1674 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/FixturePhp74/nullable_object_type.php.inc +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_object_type.php.inc @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\FixturePhp74; +namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\Fixture; use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\Source\ClassWithTypedPropertyTypes; diff --git a/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_string.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_string.php.inc new file mode 100644 index 00000000000..8dda238ec47 --- /dev/null +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Fixture/nullable_string.php.inc @@ -0,0 +1,25 @@ +textNullable->xxx(); + } +} + +?> +----- +nonexistent->xxx(); + } +} + +?> +----- +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return FixtureFileFinder::yieldDirectory(__DIR__ . '/Fixture'); + } + + private function doTestFile(string $filePath): void + { + [$inputFileContents, $expectedType] = FixtureSplitter::split($filePath); + $inputFilePath = FixtureTempFileDumper::dump($inputFileContents); + + $propertyFetchNodes = $this->getNodesForFileOfType($inputFilePath, PropertyFetch::class); + $resolvedType = $this->nodeTypeResolver->getType($propertyFetchNodes[0]); + + // this file actually contains PHP for type + $typeFilePath = FixtureTempFileDumper::dump($expectedType); + $expectedType = include $typeFilePath; + + $expectedTypeAsString = $this->getStringFromType($expectedType); + $resolvedTypeAsString = $this->getStringFromType($resolvedType); + + $this->assertSame($expectedTypeAsString, $resolvedTypeAsString); + + FileSystem::delete($inputFilePath); + FileSystem::delete($typeFilePath); + } + + private function getStringFromType(Type $type): string + { + return $type->describe(VerbosityLevel::precise()); + } +} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/Abc.php b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/Abc.php similarity index 89% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/Abc.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/Abc.php index 564ab800ea6..4b6fb64a8ac 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/Abc.php +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/Abc.php @@ -4,6 +4,6 @@ namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyFetchTypeResolver\Source; -class Abc +final class Abc { } diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativePropsPhp80.php b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativePropsPhp80.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativePropsPhp80.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativePropsPhp80.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithTypedPropertyTypes.php b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithTypedPropertyTypes.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithTypedPropertyTypes.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithTypedPropertyTypes.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/DocBlockProperties.php b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/DocBlockProperties.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/DocBlockProperties.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/DocBlockProperties.php diff --git a/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/PropertyTypeResolverTest.php b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/PropertyTypeResolverTest.php new file mode 100644 index 00000000000..55a3072d36c --- /dev/null +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/PropertyTypeResolverTest.php @@ -0,0 +1,68 @@ +getNodesForFileOfType($file, Property::class); + + $resolvedType = $this->nodeTypeResolver->getType($propertyNodes[$nodePosition]); + + // type is as expected + $expectedTypeClass = $expectedType::class; + $this->assertInstanceOf($expectedTypeClass, $resolvedType); + + $expectedTypeAsString = $this->getStringFromType($expectedType); + $resolvedTypeAsString = $this->getStringFromType($resolvedType); + + $this->assertSame($expectedTypeAsString, $resolvedTypeAsString); + } + + /** + * @return Iterator> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Source/MethodParamDocBlock.php', 0, new ObjectType(Html::class)]; + yield [__DIR__ . '/Source/MethodParamDocBlock.php', 1, new ObjectType(ClassThatExtendsHtml::class)]; + + // mimics failing test from DomainDrivenDesign set + $unionType = new UnionType([new ObjectType(SomeChild::class), new NullType()]); + yield [__DIR__ . '/Source/ActionClass.php', 0, $unionType]; + + $unionType = new UnionType([ + new ConstantStringType(Enum::MODE_ADD), + new ConstantStringType(Enum::MODE_EDIT), + new ConstantStringType(Enum::MODE_CLONE), + ]); + yield [__DIR__ . '/Source/Enum.php', 0, $unionType]; + } + + private function getStringFromType(Type $type): string + { + return $type->describe(VerbosityLevel::precise()); + } +} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ActionClass.php b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ActionClass.php similarity index 94% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ActionClass.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ActionClass.php index 5873ed80bf5..55ec3fa635a 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ActionClass.php +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ActionClass.php @@ -4,7 +4,7 @@ use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\PropertyTypeResolver\Source\SomeChild; -class ActionClass +final class ActionClass { /** * @var SomeChild|null diff --git a/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ClassThatExtendsHtml.php b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ClassThatExtendsHtml.php new file mode 100644 index 00000000000..8d61a35eefc --- /dev/null +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/PropertyTypeResolver/Source/ClassThatExtendsHtml.php @@ -0,0 +1,10 @@ +getNodesForFileOfType(__DIR__ . '/Source/TraitWithTrait.php', Trait_::class); + + $resolvedType = $this->nodeTypeResolver->getType($variableNodes[0]); + $expectedUnionType = $this->createExpectedType(); + + $this->assertEquals($expectedUnionType, $resolvedType); + } + + private function createExpectedType(): UnionType + { + $anotherTraitObjectType = new ObjectType(AnotherTrait::class); + $traitWithTraitObjectType = new ObjectType(TraitWithTrait::class); + + return new UnionType([$traitWithTraitObjectType, $anotherTraitObjectType]); + } +} diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/argument_typehint.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/argument_typehint.php.inc similarity index 92% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/argument_typehint.php.inc rename to tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/argument_typehint.php.inc index 9d9a12592da..5aae3925716 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/argument_typehint.php.inc +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/argument_typehint.php.inc @@ -8,4 +8,4 @@ use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\VariableTypeResolver\Sourc array_map(function (AnotherType $useUse) { return $useUse; -}, []); +}, [new AnotherType]); diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/assignment_class.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/assignment_class.php.inc similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/assignment_class.php.inc rename to tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/assignment_class.php.inc diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/new_class.php.inc b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/new_class.php.inc similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/new_class.php.inc rename to tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Fixture/new_class.php.inc diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/AnotherType.php b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/AnotherType.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/AnotherType.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/AnotherType.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/FirstType.php b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/FirstType.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/FirstType.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/FirstType.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/SecondType.php b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/SecondType.php similarity index 100% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/SecondType.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/Source/SecondType.php diff --git a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/VariableTypeResolverTest.php b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/VariableTypeResolverTest.php similarity index 77% rename from packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/VariableTypeResolverTest.php rename to tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/VariableTypeResolverTest.php index d5bd212b95d..1d9e6ff5356 100644 --- a/packages-tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/VariableTypeResolverTest.php +++ b/tests/NodeTypeResolver/PerNodeTypeResolver/VariableTypeResolver/VariableTypeResolverTest.php @@ -8,29 +8,31 @@ use PhpParser\Node\Expr\Variable; use PHPStan\Type\ObjectType; use PHPStan\Type\TypeWithClassName; -use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\AbstractNodeTypeResolverTest; +use PHPUnit\Framework\Attributes\DataProvider; +use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\AbstractNodeTypeResolverTestCase; use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\VariableTypeResolver\Source\AnotherType; /** * @see \Rector\NodeTypeResolver\NodeTypeResolver\VariableTypeResolver */ -final class VariableTypeResolverTest extends AbstractNodeTypeResolverTest +final class VariableTypeResolverTest extends AbstractNodeTypeResolverTestCase { - /** - * @dataProvider provideData() - */ + #[DataProvider('provideData')] public function test(string $file, int $nodePosition, TypeWithClassName $expectedTypeWithClassName): void { $variableNodes = $this->getNodesForFileOfType($file, Variable::class); - $resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]); + $resolvedType = $this->nodeTypeResolver->getType($variableNodes[$nodePosition]); $this->assertInstanceOf(TypeWithClassName::class, $resolvedType); /** @var TypeWithClassName $resolvedType */ - $this->assertEquals($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName()); + $this->assertSame($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName()); } - public function provideData(): Iterator + /** + * @return Iterator> + */ + public static function provideData(): Iterator { $anotherTypeObjectType = new ObjectType(AnotherType::class); yield [__DIR__ . '/Fixture/new_class.php.inc', 1, $anotherTypeObjectType]; diff --git a/tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after.txt b/tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after3.txt b/tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after3.txt similarity index 100% rename from packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after3.txt rename to tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after3.txt diff --git a/packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after4.txt b/tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after4.txt similarity index 100% rename from packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after4.txt rename to tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/after4.txt diff --git a/packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before.txt b/tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before.txt similarity index 100% rename from packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before.txt rename to tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before.txt diff --git a/packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before3.txt b/tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before3.txt similarity index 100% rename from packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before3.txt rename to tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before3.txt diff --git a/packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before4.txt b/tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before4.txt similarity index 100% rename from packages-tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before4.txt rename to tests/NodeTypeResolver/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveSource/before4.txt diff --git a/packages-tests/NodeTypeResolver/Source/AnotherClass.php b/tests/NodeTypeResolver/Source/AnotherClass.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/AnotherClass.php rename to tests/NodeTypeResolver/Source/AnotherClass.php diff --git a/packages-tests/NodeTypeResolver/Source/ClassWithFluentNonSelfReturn.php b/tests/NodeTypeResolver/Source/ClassWithFluentNonSelfReturn.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/ClassWithFluentNonSelfReturn.php rename to tests/NodeTypeResolver/Source/ClassWithFluentNonSelfReturn.php diff --git a/packages-tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel1.php b/tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel1.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel1.php rename to tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel1.php diff --git a/packages-tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel2.php b/tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel2.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel2.php rename to tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel2.php diff --git a/packages-tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel3.php b/tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel3.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel3.php rename to tests/NodeTypeResolver/Source/NestedProperty/ClassWithPropertyLevel3.php diff --git a/packages-tests/NodeTypeResolver/Source/NestedProperty/ParentClass.php b/tests/NodeTypeResolver/Source/NestedProperty/ParentClass.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/NestedProperty/ParentClass.php rename to tests/NodeTypeResolver/Source/NestedProperty/ParentClass.php diff --git a/packages-tests/NodeTypeResolver/Source/SomeClass.php b/tests/NodeTypeResolver/Source/SomeClass.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/SomeClass.php rename to tests/NodeTypeResolver/Source/SomeClass.php diff --git a/packages-tests/NodeTypeResolver/Source/SomeClassWithTrait.php b/tests/NodeTypeResolver/Source/SomeClassWithTrait.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/SomeClassWithTrait.php rename to tests/NodeTypeResolver/Source/SomeClassWithTrait.php diff --git a/packages-tests/NodeTypeResolver/Source/SomeTrait.php b/tests/NodeTypeResolver/Source/SomeTrait.php similarity index 100% rename from packages-tests/NodeTypeResolver/Source/SomeTrait.php rename to tests/NodeTypeResolver/Source/SomeTrait.php diff --git a/tests/NodeTypeResolver/StaticTypeMapper/StaticTypeMapperTest.php b/tests/NodeTypeResolver/StaticTypeMapper/StaticTypeMapperTest.php new file mode 100644 index 00000000000..9b48149da29 --- /dev/null +++ b/tests/NodeTypeResolver/StaticTypeMapper/StaticTypeMapperTest.php @@ -0,0 +1,98 @@ +staticTypeMapper = $this->make(StaticTypeMapper::class); + } + + /** + * @param class-string $expectedType + */ + #[DataProvider('provideData')] + public function testMapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, string $expectedType): void + { + $string = new String_('hey'); + + $phpStanType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, $string); + + $this->assertInstanceOf($expectedType, $phpStanType); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + $genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('Traversable'), []); + yield [$genericTypeNode, GenericObjectType::class]; + + $genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('iterable'), [ + new IdentifierTypeNode('string'), + ]); + + yield [$genericTypeNode, IterableType::class]; + + yield [new IdentifierTypeNode('mixed'), MixedType::class]; + } + + public function testMapPHPStanTypeToPHPStanPhpDocTypeNode(): void + { + $iterableType = new IterableType(new MixedType(), new ClassStringType()); + + $phpStanDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($iterableType); + $this->assertInstanceOf(GenericTypeNode::class, $phpStanDocTypeNode); + $this->assertInstanceOf(IdentifierTypeNode::class, $phpStanDocTypeNode->type); + } + + public function testMixed(): void + { + $mixedType = new MixedType(); + + $phpStanDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($mixedType); + $this->assertInstanceOf(IdentifierTypeNode::class, $phpStanDocTypeNode); + } + + /** + * @param class-string $expectedType + */ + #[DataProvider('provideDataForMapPhpParserNodePHPStanType')] + public function testMapPhpParserNodePHPStanType(Node $node, string $expectedType): void + { + $phpStanType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($node); + $this->assertInstanceOf($expectedType, $phpStanType); + } + + /** + * @return Iterator|Identifier)>> + */ + public static function provideDataForMapPhpParserNodePHPStanType(): Iterator + { + yield [new Identifier('iterable'), IterableType::class]; + } +} diff --git a/tests/NodeTypeResolver/TypeComparator/ArrayTypeComparatorTest.php b/tests/NodeTypeResolver/TypeComparator/ArrayTypeComparatorTest.php new file mode 100644 index 00000000000..e921af6e436 --- /dev/null +++ b/tests/NodeTypeResolver/TypeComparator/ArrayTypeComparatorTest.php @@ -0,0 +1,60 @@ +arrayTypeComparator = $this->make(ArrayTypeComparator::class); + $this->reflectionProvider = $this->make(ReflectionProvider::class); + } + + public function testClassStringSubtype(): void + { + $classStringKeysArrayType = new ArrayType(new StringType(), new ClassStringType()); + $stringArrayType = new ArrayType(new StringType(), new MixedType()); + + $isSubtypeActual = $this->arrayTypeComparator->isSubtype($classStringKeysArrayType, $stringArrayType); + $this->assertTrue($isSubtypeActual); + } + + public function testGenericObjectType(): void + { + $someGenericTypeObjectClassReflection = $this->reflectionProvider->getClass(SomeGenericTypeObject::class); + $objectType = new ObjectType(SomeGenericTypeObject::class, null, $someGenericTypeObjectClassReflection); + $genericClassStringType = new GenericClassStringType($objectType); + + $constantArrayType = new ConstantArrayType( + [new ConstantIntegerType(0)], + [new UnionType([$genericClassStringType, $genericClassStringType])] + ); + + $stringArrayType = new ArrayType(new StringType(), new MixedType()); + + $isSubtypeActual = $this->arrayTypeComparator->isSubtype($constantArrayType, $stringArrayType); + $this->assertFalse($isSubtypeActual); + } +} diff --git a/tests/NodeTypeResolver/TypeComparator/ScalarTypeComparatorTest.php b/tests/NodeTypeResolver/TypeComparator/ScalarTypeComparatorTest.php new file mode 100644 index 00000000000..dc057d8aa16 --- /dev/null +++ b/tests/NodeTypeResolver/TypeComparator/ScalarTypeComparatorTest.php @@ -0,0 +1,47 @@ +scalarTypeComparator = $this->make(ScalarTypeComparator::class); + } + + #[DataProvider('provideData')] + public function test(Type $firstType, Type $secondType, bool $areExpectedEqual): void + { + $areEqual = $this->scalarTypeComparator->areEqualScalar($firstType, $secondType); + $this->assertSame($areExpectedEqual, $areEqual); + } + + /** + * @return Iterator + */ + public static function provideData(): Iterator + { + yield [new StringType(), new BooleanType(), false]; + yield [new StringType(), new StringType(), true]; + yield [new StringType(), new ClassStringType(), false]; + yield [new IntegerType(), new IntegerType(), true]; + yield [new IntegerType(), IntegerRangeType::fromInterval(1, 10), false]; + } +} diff --git a/packages-tests/NodeTypeResolver/TypeComparator/Source/SomeGenericTypeObject.php b/tests/NodeTypeResolver/TypeComparator/Source/SomeGenericTypeObject.php similarity index 100% rename from packages-tests/NodeTypeResolver/TypeComparator/Source/SomeGenericTypeObject.php rename to tests/NodeTypeResolver/TypeComparator/Source/SomeGenericTypeObject.php diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/laravel.blade.php.inc b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/laravel.blade.php.inc deleted file mode 100644 index 350bb824d76..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/laravel.blade.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -
    - -
    ------ -
    - -
    diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/laravel_html_form_mix.blade.php.inc b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/laravel_html_form_mix.blade.php.inc deleted file mode 100644 index f90ba0d779d..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/laravel_html_form_mix.blade.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -
    - -
    ------ -
    - -
    diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/pre_slash_laravel.blade.php.inc b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/pre_slash_laravel.blade.php.inc deleted file mode 100644 index dbd0defbb5a..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/pre_slash_laravel.blade.php.inc +++ /dev/null @@ -1,3 +0,0 @@ -{if \Session::some()} ------ -{if \Illuminate\Support\Facades\Session::some()} diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_already_renamed.blade.php.inc b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_already_renamed.blade.php.inc deleted file mode 100644 index 5e0f12d69de..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_already_renamed.blade.php.inc +++ /dev/null @@ -1,8 +0,0 @@ -
    - -
    diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_dot_or_quote.php.inc b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_dot_or_quote.php.inc deleted file mode 100644 index b5321d30579..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_dot_or_quote.php.inc +++ /dev/null @@ -1,3 +0,0 @@ -'Session' - -Session.some diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_html_or_space.blade.php.inc b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_html_or_space.blade.php.inc deleted file mode 100644 index e8ca39b7a70..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_html_or_space.blade.php.inc +++ /dev/null @@ -1,3 +0,0 @@ -
    Session
    - -First Session diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_not_class_laravel.blade.php.inc b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_not_class_laravel.blade.php.inc deleted file mode 100644 index 7534e7478d3..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/blade-template/skip_not_class_laravel.blade.php.inc +++ /dev/null @@ -1,4 +0,0 @@ -
    -
    -
    -
    diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-neon/local_config.neon b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-neon/local_config.neon deleted file mode 100644 index fccc925fa14..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-neon/local_config.neon +++ /dev/null @@ -1,7 +0,0 @@ -services: - - - class: Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass ------ -services: - - - class: Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-xml/local_config.xml b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-xml/local_config.xml deleted file mode 100644 index e9cbed58aa2..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-xml/local_config.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - ------ - - - diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-yaml/local_config.yaml b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-yaml/local_config.yaml deleted file mode 100644 index e3fdbba9a76..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-yaml/local_config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -services: - Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass: null ------ -services: - Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass: null diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-yaml/local_config.yml b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-yaml/local_config.yml deleted file mode 100644 index e3fdbba9a76..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/config-yaml/local_config.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass: null ------ -services: - Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass: null diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/latte-template/dummy_template.latte b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/latte-template/dummy_template.latte deleted file mode 100644 index 95972902d33..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/latte-template/dummy_template.latte +++ /dev/null @@ -1,5 +0,0 @@ -{if \Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\OldClass::SOME_COSTANT === $value} -{/if} ------ -{if \Rector\Tests\Renaming\Rector\Name\RenameClassRector\Source\NewClass::SOME_COSTANT === $value} -{/if} diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/twig-template/dummy_template.twig b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/twig-template/dummy_template.twig deleted file mode 100644 index 0697998829a..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/Fixture/twig-template/dummy_template.twig +++ /dev/null @@ -1,3 +0,0 @@ -{{ constant('Rector\\Tests\\Renaming\\Rector\\Name\\RenameClassRector\\Source\\OldClass::SOME_COSTANT') }} ------ -{{ constant('Rector\\Tests\\Renaming\\Rector\\Name\\RenameClassRector\\Source\\NewClass::SOME_COSTANT') }} diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/RenameClassNonPhpRectorTest.php b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/RenameClassNonPhpRectorTest.php deleted file mode 100644 index 3e6b83ff0af..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/RenameClassNonPhpRectorTest.php +++ /dev/null @@ -1,34 +0,0 @@ -doTestFileInfo($fixtureFileInfo); - } - - /** - * @return Iterator> - */ - public function provideData(): Iterator - { - return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture', '*'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/config/configured_rule.php b/tests/NonPhpFile/Rector/RenameClassNonPhpRector/config/configured_rule.php deleted file mode 100644 index 53b1b41868a..00000000000 --- a/tests/NonPhpFile/Rector/RenameClassNonPhpRector/config/configured_rule.php +++ /dev/null @@ -1,23 +0,0 @@ -services(); - - $services->set(RenameClassNonPhpRector::class) - ->call('configure', [[ - RenameClassNonPhpRector::RENAME_CLASSES => [ - 'Session' => 'Illuminate\Support\Facades\Session', - OldClass::class => NewClass::class, - // Laravel - 'Form' => 'Collective\Html\FormFacade', - 'Html' => 'Collective\Html\HtmlFacade', - ], - ]]); -}; diff --git a/tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php b/tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php new file mode 100644 index 00000000000..32226689504 --- /dev/null +++ b/tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php @@ -0,0 +1,93 @@ +arrayTypeMapper = $this->make(ArrayTypeMapper::class); + } + + #[DataProvider('provideDataWithoutKeys')] + #[DataProvider('provideDataUnionedWithoutKeys')] + public function testWithoutKeys(ArrayType $arrayType, string $expectedResult): void + { + $actualTypeNode = $this->arrayTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType); + $this->assertSame($expectedResult, (string) $actualTypeNode); + } + + #[DataProvider('provideDataWithKeys')] + public function testWithKeys(ArrayType $arrayType, string $expectedResult): void + { + $actualTypeNode = $this->arrayTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType); + $this->assertSame($expectedResult, (string) $actualTypeNode); + } + + /** + * @return Iterator + */ + public static function provideDataWithoutKeys(): Iterator + { + $arrayType = new ArrayType(new MixedType(), new StringType()); + yield [$arrayType, 'string[]']; + + $stringStringUnionType = new UnionType([new StringType(), new StringType()]); + $arrayType = new ArrayType(new MixedType(), $stringStringUnionType); + yield [$arrayType, 'string[]']; + } + + /** + * @return Iterator> + */ + public static function provideDataUnionedWithoutKeys(): Iterator + { + $stringAndIntegerUnionType = new UnionType([new StringType(), new IntegerType()]); + $unionArrayType = new ArrayType(new MixedType(), $stringAndIntegerUnionType); + yield [$unionArrayType, 'string[]|int[]']; + + $moreNestedUnionArrayType = new ArrayType(new MixedType(), $unionArrayType); + yield [$moreNestedUnionArrayType, 'string[][]|int[][]']; + + $evenMoreNestedUnionArrayType = new ArrayType(new MixedType(), $moreNestedUnionArrayType); + yield [$evenMoreNestedUnionArrayType, 'string[][][]|int[][][]']; + } + + /** + * @return Iterator> + */ + public static function provideDataWithKeys(): Iterator + { + $arrayMixedToStringType = new ArrayType(new MixedType(), new StringType()); + $arrayType = new ArrayType(new StringType(), $arrayMixedToStringType); + yield [$arrayType, 'array']; + + $stringAndIntegerUnionType = new UnionType([new StringType(), new IntegerType()]); + + $stringAndIntegerUnionArrayType = new ArrayType(new MixedType(), $stringAndIntegerUnionType); + $arrayType = new ArrayType(new StringType(), $stringAndIntegerUnionArrayType); + yield [$arrayType, 'array>']; + + $arrayType = new ArrayType(new StringType(), new IntegerType()); + yield [$arrayType, 'array']; + + $arrayType = new ArrayType(new StringType(), $stringAndIntegerUnionType); + yield [$arrayType, 'array']; + } +} diff --git a/tests/Parallel/Command/WorkerCommandLineFactoryTest.php b/tests/Parallel/Command/WorkerCommandLineFactoryTest.php new file mode 100644 index 00000000000..f33fb9bb19d --- /dev/null +++ b/tests/Parallel/Command/WorkerCommandLineFactoryTest.php @@ -0,0 +1,222 @@ +workerCommandLineFactory = $this->make(WorkerCommandLineFactory::class); + $this->processCommand = $this->make(ProcessCommand::class); + } + + /** + * @param array $inputParameters + */ + #[DataProvider('provideDataSpacedMainScript')] + public function testSpacedMainScript(array $inputParameters, string $expectedCommand): void + { + $inputDefinition = $this->prepareProcessCommandDefinition(); + $arrayInput = new ArrayInput($inputParameters, $inputDefinition); + + $workerCommandLine = $this->workerCommandLineFactory->create( + self::SPACED_DUMMY_MAIN_SCRIPT, + ProcessCommand::class, + 'worker', + $arrayInput, + 'identifier', + 2000 + ); + + $expectedCommand = $this->normalizeExpectedCommandOutput($expectedCommand); + $expectedCommand = $this->cleanUpEmptyQuoteExpectedCommandOutput($expectedCommand); + + $this->assertSame($expectedCommand, $workerCommandLine); + } + + /** + * @return Iterator>|string[]> + */ + public static function provideDataSpacedMainScript(): Iterator + { + $cliInputOptions = array_slice($_SERVER['argv'], 1); + $cliInputOptionsAsString = implode("' '", $cliInputOptions); + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + ], + "'" . PHP_BINARY . "' '" . self::SPACED_DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + '--' . Option::OUTPUT_FORMAT => ConsoleOutputFormatter::NAME, + ], + "'" . PHP_BINARY . "' '" . self::SPACED_DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + '--' . Option::OUTPUT_FORMAT => ConsoleOutputFormatter::NAME, + '--' . Option::DEBUG => true, + ], + "'" . PHP_BINARY . "' '" . self::SPACED_DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --debug --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + '--' . Option::OUTPUT_FORMAT => ConsoleOutputFormatter::NAME, + '--' . Option::DEBUG => true, + '--' . Option::XDEBUG => true, + ], + "'" . PHP_BINARY . "' '" . self::SPACED_DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --debug --xdebug --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + } + + /** + * @param array $inputParameters + */ + #[DataProvider('provideData')] + public function test(array $inputParameters, string $expectedCommand): void + { + $inputDefinition = $this->prepareProcessCommandDefinition(); + $arrayInput = new ArrayInput($inputParameters, $inputDefinition); + + $workerCommandLine = $this->workerCommandLineFactory->create( + self::DUMMY_MAIN_SCRIPT, + ProcessCommand::class, + 'worker', + $arrayInput, + 'identifier', + 2000 + ); + + $expectedCommand = $this->normalizeExpectedCommandOutput($expectedCommand); + $expectedCommand = $this->cleanUpEmptyQuoteExpectedCommandOutput($expectedCommand); + + $this->assertSame($expectedCommand, $workerCommandLine); + } + + /** + * @return Iterator>|string[]> + */ + public static function provideData(): Iterator + { + $cliInputOptions = array_slice($_SERVER['argv'], 1); + $cliInputOptionsAsString = implode("' '", $cliInputOptions); + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + ], + "'" . PHP_BINARY . "' '" . self::DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + '--' . Option::OUTPUT_FORMAT => ConsoleOutputFormatter::NAME, + ], + "'" . PHP_BINARY . "' '" . self::DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + '--' . Option::OUTPUT_FORMAT => ConsoleOutputFormatter::NAME, + '--' . Option::DEBUG => true, + ], + "'" . PHP_BINARY . "' '" . self::DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --debug --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + '--' . Option::OUTPUT_FORMAT => ConsoleOutputFormatter::NAME, + '--' . Option::DEBUG => true, + '--' . Option::XDEBUG => true, + ], + "'" . PHP_BINARY . "' '" . self::DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --debug --xdebug --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + + yield [ + [ + self::COMMAND => 'process', + Option::SOURCE => ['src'], + '--' . Option::OUTPUT_FORMAT => ConsoleOutputFormatter::NAME, + '--' . Option::MEMORY_LIMIT => '-1', + ], + "'" . PHP_BINARY . "' '" . self::DUMMY_MAIN_SCRIPT . "' '" . $cliInputOptionsAsString . "' worker --memory-limit='-1' --port 2000 --identifier 'identifier' 'src' --output-format 'json' --no-ansi", + ]; + } + + private function cleanUpEmptyQuoteExpectedCommandOutput(string $result): string + { + if ($this->isWindows()) { + return str_replace(' "" ', ' ', $result); + } + + return str_replace(" '' ", ' ', $result); + } + + private function normalizeExpectedCommandOutput(string $command): string + { + if ($this->isWindows()) { + return str_replace("'", '"', $command); + } + + return $command; + } + + private function prepareProcessCommandDefinition(): InputDefinition + { + // clone the object as we should not modify a object taken from the DI container + $inputDefinition = clone $this->processCommand->getDefinition(); + + // not sure why, but the 1st argument "command" is missing; this is needed for a command name + $arguments = $inputDefinition->getArguments(); + $commandInputArgument = new InputArgument(self::COMMAND, InputArgument::REQUIRED); + $arguments = [$commandInputArgument, ...$arguments]; + + $inputDefinition->setArguments($arguments); + + return $inputDefinition; + } +} diff --git a/tests/Php/PhpVersionProviderTest.php b/tests/Php/PhpVersionProviderTest.php index be8ad5d2490..88c226fca54 100644 --- a/tests/Php/PhpVersionProviderTest.php +++ b/tests/Php/PhpVersionProviderTest.php @@ -2,24 +2,22 @@ declare(strict_types=1); -namespace Rector\Core\Tests\Php; +namespace Rector\Tests\Php; -use Rector\Core\Php\PhpVersionProvider; -use Rector\Testing\PHPUnit\AbstractTestCase; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; +use Rector\Php\PhpVersionProvider; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class PhpVersionProviderTest extends AbstractTestCase +final class PhpVersionProviderTest extends AbstractLazyTestCase { - private PhpVersionProvider $phpVersionProvider; - - protected function setUp(): void - { - $this->boot(); - $this->phpVersionProvider = $this->getService(PhpVersionProvider::class); - } - public function test(): void { - $phpVersion = $this->phpVersionProvider->provide(); + SimpleParameterProvider::setParameter(Option::PHP_VERSION_FEATURES, 100000); + + $phpVersionProvider = $this->make(PhpVersionProvider::class); + $phpVersion = $phpVersionProvider->provide(); + $this->assertSame(100000, $phpVersion); } } diff --git a/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/ComposerJsonPhpVersionResolverTest.php b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/ComposerJsonPhpVersionResolverTest.php new file mode 100644 index 00000000000..f6eb4c629a1 --- /dev/null +++ b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/ComposerJsonPhpVersionResolverTest.php @@ -0,0 +1,31 @@ +assertSame($expectedPhpVersion, $resolvePhpVersion); + } + + /** + * @return Iterator> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/some_composer.json', 70300]; + yield [__DIR__ . '/Fixture/some_composer_with_platform.json', 70400]; + yield [__DIR__ . '/Fixture/no_php_definition_composer_json.json', null]; + yield [__DIR__ . '/Fixture/some_composer_with_64bit-php.json', 80100]; + } +} diff --git a/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/no_php_definition_composer_json.json b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/no_php_definition_composer_json.json new file mode 100644 index 00000000000..a57e5614c37 --- /dev/null +++ b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/no_php_definition_composer_json.json @@ -0,0 +1,3 @@ +{ + "name": "some/repo" +} diff --git a/tests/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver/Fixture/some_composer.json b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/some_composer.json similarity index 100% rename from tests/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver/Fixture/some_composer.json rename to tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/some_composer.json diff --git a/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/some_composer_with_64bit-php.json b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/some_composer_with_64bit-php.json new file mode 100644 index 00000000000..8e1ebf4efad --- /dev/null +++ b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/some_composer_with_64bit-php.json @@ -0,0 +1,5 @@ +{ + "require": { + "php-64bit": "8.1.*" + } +} diff --git a/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/some_composer_with_platform.json b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/some_composer_with_platform.json new file mode 100644 index 00000000000..eabccc338ae --- /dev/null +++ b/tests/Php/PhpVersionResolver/ComposerJsonPhpVersionResolver/Fixture/some_composer_with_platform.json @@ -0,0 +1,10 @@ +{ + "require": { + "php": ">=7.4" + }, + "config": { + "platform": { + "php": "7.3" + } + } +} diff --git a/tests/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver/Fixture/some_composer_with_platform.json b/tests/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver/Fixture/some_composer_with_platform.json deleted file mode 100644 index 69651895768..00000000000 --- a/tests/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver/Fixture/some_composer_with_platform.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "require": { - "php": ">=7.3" - }, - "config": { - "platform": { - "php": "7.4" - } - } -} diff --git a/tests/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver/ProjectComposerJsonPhpVersionResolverTest.php b/tests/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver/ProjectComposerJsonPhpVersionResolverTest.php deleted file mode 100644 index 1aa17243dce..00000000000 --- a/tests/Php/PhpVersionResolver/ProjectComposerJsonPhpVersionResolver/ProjectComposerJsonPhpVersionResolverTest.php +++ /dev/null @@ -1,38 +0,0 @@ -boot(); - $this->projectComposerJsonPhpVersionResolver = $this->getService(ProjectComposerJsonPhpVersionResolver::class); - } - - /** - * @dataProvider provideData() - */ - public function test(string $composerJsonFilePath, int $expectedPhpVersion): void - { - $resolvePhpVersion = $this->projectComposerJsonPhpVersionResolver->resolve($composerJsonFilePath); - $this->assertSame($expectedPhpVersion, $resolvePhpVersion); - } - - /** - * @return Iterator> - */ - public function provideData(): Iterator - { - yield [__DIR__ . '/Fixture/some_composer.json', 70300]; - yield [__DIR__ . '/Fixture/some_composer_with_platform.json', 70400]; - } -} diff --git a/tests/PhpAttribute/AnnotationToAttributeMapper/AnnotationToAttributeMapperTest.php b/tests/PhpAttribute/AnnotationToAttributeMapper/AnnotationToAttributeMapperTest.php new file mode 100644 index 00000000000..387002ee8fc --- /dev/null +++ b/tests/PhpAttribute/AnnotationToAttributeMapper/AnnotationToAttributeMapperTest.php @@ -0,0 +1,57 @@ +annotationToAttributeMapper = $this->make(AnnotationToAttributeMapper::class); + } + + /** + * @param class-string $expectedTypeClass + */ + #[DataProvider('provideData')] + public function test(mixed $input, string $expectedTypeClass): void + { + $mappedExpr = $this->annotationToAttributeMapper->map($input); + $this->assertInstanceOf($expectedTypeClass, $mappedExpr); + + if ($mappedExpr instanceof Array_) { + $arrayItem = $mappedExpr->items[0]; + $this->assertInstanceOf(ArrayItem::class, $arrayItem); + $this->assertInstanceOf(String_::class, $arrayItem->value); + } + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [false, ConstFetch::class]; + yield ['false', String_::class]; + yield ['100', String_::class]; + yield ['hey', String_::class]; + yield [['hey'], Array_::class]; + yield [100, Int_::class]; + } +} diff --git a/tests/PhpAttribute/Printer/PhpAttributeGroupFactoryTest.php b/tests/PhpAttribute/Printer/PhpAttributeGroupFactoryTest.php new file mode 100644 index 00000000000..c6f9df331a8 --- /dev/null +++ b/tests/PhpAttribute/Printer/PhpAttributeGroupFactoryTest.php @@ -0,0 +1,48 @@ +phpAttributeGroupFactory = $this->make(PhpAttributeGroupFactory::class); + } + + public function testCreateFromClassWithItems(): void + { + $attributeGroup = $this->phpAttributeGroupFactory->createFromClassWithItems( + 'Symfony\Component\Routing\Annotation\Route', + [ + 'path' => '/path', + 'name' => 'action', + ] + ); + + $this->assertInstanceOf(AttributeGroup::class, $attributeGroup); + } + + public function testCreateArgsFromItems(): void + { + $args = $this->phpAttributeGroupFactory->createArgsFromItems([ + new ArrayItemNode(new StringNode('/path'), 'path'), + new ArrayItemNode(new StringNode('action'), 'name'), + ]); + + $this->assertCount(2, $args); + $this->assertContainsOnlyInstancesOf(Arg::class, $args); + } +} diff --git a/tests/PhpAttribute/UseAliasNameMatcherTest.php b/tests/PhpAttribute/UseAliasNameMatcherTest.php new file mode 100644 index 00000000000..cdf15e3f348 --- /dev/null +++ b/tests/PhpAttribute/UseAliasNameMatcherTest.php @@ -0,0 +1,98 @@ +useAliasNameMatcher = $this->make(UseAliasNameMatcher::class); + } + + #[DataProvider('provideData')] + public function test( + AnnotationToAttribute $annotationToAttribute, + string $useImportName, + string $useAlias, + string $shortAnnotationName, + // attribute + string $expectedAttributeUseImportName, + string $expectedShortAttributeName, + ): void { + $useItem = new UseItem(new Name($useImportName), $useAlias); + $useItem->setAttribute(AttributeKey::ORIGINAL_NODE, $useItem); + + $uses = [new Use_([$useItem])]; + + // uses + $useAliasMetadata = $this->useAliasNameMatcher->match($uses, $shortAnnotationName, $annotationToAttribute); + $this->assertInstanceOf(UseAliasMetadata::class, $useAliasMetadata); + + // test new use import + $this->assertSame($expectedShortAttributeName, $useAliasMetadata->getShortAttributeName()); + + // test new short attribute name + $this->assertSame($expectedAttributeUseImportName, $useAliasMetadata->getUseImportName()); + } + + /** + * @return Iterator> + */ + public static function provideData(): Iterator + { + yield [ + // configuration + new AnnotationToAttribute(PastAnnotation::class, FutureAttribute::class), + + // use import + 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi', + // use import alias + 'OA', + // short attribute name + '@OA\PastAnnotation', + + // expected attribute import + 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi', + // expected attribute short name + 'OA\FutureAttribute', + ]; + + yield [ + // configuration + new AnnotationToAttribute(NestedPastAnnotation::class, NestedFutureAttribute::class), + + // use import + 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi\Annotation', + // use import alias + 'OA', + // short attribute name + '@OA\NestedPastAnnotation', + + // expected attribute import + 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi\Attribute', + // expected attribute short name + 'OA\NestedFutureAttribute', + ]; + } +} diff --git a/tests/PhpDocParser/NodeValue/Fixture/SomeClassWithConstant.php b/tests/PhpDocParser/NodeValue/Fixture/SomeClassWithConstant.php new file mode 100644 index 00000000000..8b4ed8191af --- /dev/null +++ b/tests/PhpDocParser/NodeValue/Fixture/SomeClassWithConstant.php @@ -0,0 +1,10 @@ +phpDocNodeTraverser = $this->make(PhpDocNodeTraverser::class); + + /** @var ParentConnectingPhpDocNodeVisitor $parentConnectingPhpDocNodeVisitor */ + $parentConnectingPhpDocNodeVisitor = $this->make(ParentConnectingPhpDocNodeVisitor::class); + $this->phpDocNodeTraverser->addPhpDocNodeVisitor($parentConnectingPhpDocNodeVisitor); + } + + public function testTypeNode(): void + { + $phpDocNode = $this->createPhpDocNode(); + $this->phpDocNodeTraverser->traverse($phpDocNode); + + /** @var PhpDocTagNode $phpDocChildNode */ + $phpDocChildNode = $phpDocNode->children[0]; + + $returnTagValueNode = $phpDocChildNode->value; + + $this->assertInstanceOf(ReturnTagValueNode::class, $returnTagValueNode); + + /** @var ReturnTagValueNode $returnTagValueNode */ + $returnParent = $returnTagValueNode->getAttribute(PhpDocAttributeKey::PARENT); + $this->assertSame($phpDocChildNode, $returnParent); + + $returnTypeParent = $returnTagValueNode->type->getAttribute(PhpDocAttributeKey::PARENT); + $this->assertSame($returnTagValueNode, $returnTypeParent); + + // test child + parent node + $phpDocChildNode = $phpDocNode->children[0]; + $this->assertInstanceOf(PhpDocTagNode::class, $phpDocChildNode); + + $childParent = $phpDocChildNode->getAttribute(PhpDocAttributeKey::PARENT); + $this->assertSame($phpDocNode, $childParent); + } + + private function createPhpDocNode(): PhpDocNode + { + $returnTagValueNode = new ReturnTagValueNode(new IdentifierTypeNode('string'), ''); + + return new PhpDocNode([ + new PhpDocTagNode('@return', $returnTagValueNode), + new PhpDocTextNode('some text'), + ]); + } +} diff --git a/tests/PhpDocParser/PhpDocParser/SimplePhpDocNodeTraverser/PhpDocNodeTraverserTest.php b/tests/PhpDocParser/PhpDocParser/SimplePhpDocNodeTraverser/PhpDocNodeTraverserTest.php new file mode 100644 index 00000000000..afb4e205251 --- /dev/null +++ b/tests/PhpDocParser/PhpDocParser/SimplePhpDocNodeTraverser/PhpDocNodeTraverserTest.php @@ -0,0 +1,45 @@ +phpDocNodeTraverser = $this->make(PhpDocNodeTraverser::class); + } + + public function test(): void + { + $varTagValueNode = new VarTagValueNode(new IdentifierTypeNode('string'), '', ''); + $phpDocNode = new PhpDocNode([new PhpDocTagNode('@var', $varTagValueNode)]); + + $this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', static function (Node $node): Node { + if (! $node instanceof VarTagValueNode) { + return $node; + } + + $node->description = self::SOME_DESCRIPTION; + return $node; + }); + + $varTagValueNodes = $phpDocNode->getVarTagValues(); + $this->assertSame(self::SOME_DESCRIPTION, $varTagValueNodes[0]->description); + } +} diff --git a/tests/PhpDocParser/PhpDocParser/SimplePhpDocParser/Fixture/param_string_name.txt b/tests/PhpDocParser/PhpDocParser/SimplePhpDocParser/Fixture/param_string_name.txt new file mode 100644 index 00000000000..99db73e8323 --- /dev/null +++ b/tests/PhpDocParser/PhpDocParser/SimplePhpDocParser/Fixture/param_string_name.txt @@ -0,0 +1,3 @@ +/** + * @param string $name + */ diff --git a/tests/PhpDocParser/PhpDocParser/SimplePhpDocParser/Fixture/var_int.txt b/tests/PhpDocParser/PhpDocParser/SimplePhpDocParser/Fixture/var_int.txt new file mode 100644 index 00000000000..00b065e588d --- /dev/null +++ b/tests/PhpDocParser/PhpDocParser/SimplePhpDocParser/Fixture/var_int.txt @@ -0,0 +1,3 @@ +/** + * @var int + */ diff --git a/tests/PhpParser/Node/BetterNodeFinder/BetterNodeFinderTest.php b/tests/PhpParser/Node/BetterNodeFinder/BetterNodeFinderTest.php index 17c6ba8636b..afb4a42cb95 100644 --- a/tests/PhpParser/Node/BetterNodeFinder/BetterNodeFinderTest.php +++ b/tests/PhpParser/Node/BetterNodeFinder/BetterNodeFinderTest.php @@ -2,18 +2,16 @@ declare(strict_types=1); -namespace Rector\Core\Tests\PhpParser\Node\BetterNodeFinder; +namespace Rector\Tests\PhpParser\Node\BetterNodeFinder; use PhpParser\Node; -use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassLike; -use Rector\Core\PhpParser\Node\BetterNodeFinder; -use Rector\Core\PhpParser\Parser\SimplePhpParser; -use Rector\Testing\PHPUnit\AbstractTestCase; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Parser\SimplePhpParser; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class BetterNodeFinderTest extends AbstractTestCase +final class BetterNodeFinderTest extends AbstractLazyTestCase { /** * @var Node[] @@ -24,12 +22,11 @@ final class BetterNodeFinderTest extends AbstractTestCase protected function setUp(): void { - $this->boot(); + parent::setUp(); - $this->betterNodeFinder = $this->getService(BetterNodeFinder::class); + $this->betterNodeFinder = $this->make(BetterNodeFinder::class); - /** @var SimplePhpParser $simplePhpParser */ - $simplePhpParser = $this->getService(SimplePhpParser::class); + $simplePhpParser = $this->make(SimplePhpParser::class); $this->nodes = $simplePhpParser->parseFile(__DIR__ . '/Source/SomeFile.php.inc'); } @@ -38,22 +35,7 @@ public function testFindFirstAncestorInstanceOf(): void $variable = $this->betterNodeFinder->findFirstInstanceOf($this->nodes, Variable::class); $class = $this->betterNodeFinder->findFirstInstanceOf($this->nodes, Class_::class); - $this->assertNotNull($variable); - $this->assertNotNull($class); - $this->assertInstanceOf(Variable::class, $variable); $this->assertInstanceOf(Class_::class, $class); - - /** @var Variable $variable */ - $classLikeNode = $this->betterNodeFinder->findParentType($variable, ClassLike::class); - $this->assertSame($classLikeNode, $class); - } - - public function testFindMissingFirstAncestorInstanceOf(): void - { - /** @var Variable $variableNode */ - $variableNode = $this->betterNodeFinder->findFirstInstanceOf($this->nodes, Variable::class); - - $this->assertNull($this->betterNodeFinder->findParentType($variableNode, Array_::class)); } } diff --git a/tests/PhpParser/Node/NodeFactoryTest.php b/tests/PhpParser/Node/NodeFactoryTest.php index da0527b9889..6b7a40d821e 100644 --- a/tests/PhpParser/Node/NodeFactoryTest.php +++ b/tests/PhpParser/Node/NodeFactoryTest.php @@ -2,53 +2,80 @@ declare(strict_types=1); -namespace Rector\Core\Tests\PhpParser\Node; +namespace Rector\Tests\PhpParser\Node; use Iterator; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayItem; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Expr\ConstFetch; +use PhpParser\Node\Name; +use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; -use Rector\Core\PhpParser\Node\NodeFactory; -use Rector\Testing\PHPUnit\AbstractTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class NodeFactoryTest extends AbstractTestCase +final class NodeFactoryTest extends AbstractLazyTestCase { private NodeFactory $nodeFactory; protected function setUp(): void { - $this->boot(); + parent::setUp(); - $this->nodeFactory = $this->getService(NodeFactory::class); + $this->nodeFactory = $this->make(NodeFactory::class); } /** * @param int[]|array $inputArray - * @dataProvider provideDataForArray() */ - public function testCreateArray(array $inputArray, Array_ $expectedArrayNode): void + #[DataProvider('provideDataForArray')] + public function testCreateArray(array $inputArray, Array_ $expectedArray): void { $arrayNode = $this->nodeFactory->createArray($inputArray); - $this->assertEquals($expectedArrayNode, $arrayNode); + $this->assertEquals($expectedArray, $arrayNode); } /** - * @return Iterator|Array_[]> + * @return Iterator */ - public function provideDataForArray(): Iterator + public static function provideDataForArray(): Iterator { - $array = new Array_(); - $array->items[] = new ArrayItem(new LNumber(1)); + $int = new Int_(1); + $string = new String_('a'); + $trueConstFetch = new ConstFetch(new Name('true')); + $falseConstFetch = new ConstFetch(new Name('false')); + $nullConstEtch = new ConstFetch(new Name('null')); + $array = new Array_(); + $array->items[] = new ArrayItem($int); yield [[1], $array]; $array = new Array_(); - $array->items[] = new ArrayItem(new LNumber(1), new String_('a')); - + $array->items[] = new ArrayItem($int, $string); yield [[ 'a' => 1, ], $array]; + + $array = new Array_(); + $array->items[] = new ArrayItem($int); + yield [[$int], $array]; + + $array = new Array_(); + $array->items[] = new ArrayItem($string); + yield [[$string], $array]; + + $array = new Array_(); + $array->items[] = new ArrayItem($trueConstFetch); + yield [[$trueConstFetch], $array]; + + $array = new Array_(); + $array->items[] = new ArrayItem($falseConstFetch); + yield [[$falseConstFetch], $array]; + + $array = new Array_(); + $array->items[] = new ArrayItem($nullConstEtch); + yield [[$nullConstEtch], $array]; } } diff --git a/tests/PhpParser/Node/Value/Source/ClassForConstant.php b/tests/PhpParser/Node/Value/Source/ClassForConstant.php new file mode 100644 index 00000000000..a9bc5091fa0 --- /dev/null +++ b/tests/PhpParser/Node/Value/Source/ClassForConstant.php @@ -0,0 +1,9 @@ +boot(); - $this->valueResolver = $this->getService(ValueResolver::class); + parent::setUp(); + + $this->valueResolver = $this->make(ValueResolver::class); } - /** - * @dataProvider dataProvider - */ + #[DataProvider('dataProvider')] public function test(Expr $expr, string | bool | int | float | null $expectedValue): void { $resolvedValue = $this->valueResolver->getValue($expr); @@ -35,17 +34,13 @@ public function test(Expr $expr, string | bool | int | float | null $expectedVal /** * @return Iterator> */ - public function dataProvider(): Iterator + public static function dataProvider(): Iterator { $builderFactory = new BuilderFactory(); - $classConstFetchNode = $builderFactory->classConstFetch('SomeClass', 'SOME_CONSTANT'); - $classConstFetchNode->class->setAttribute( - AttributeKey::RESOLVED_NAME, - new FullyQualified('SomeClassResolveName') - ); + $classConstFetchNode = $builderFactory->classConstFetch(ClassForConstant::class, 'SOME_CONSTANT'); - yield [$classConstFetchNode, 'SomeClassResolveName::SOME_CONSTANT']; + yield [$classConstFetchNode, ClassForConstant::class . '::SOME_CONSTANT']; yield [$builderFactory->val(true), true]; yield [$builderFactory->val(1), 1]; yield [$builderFactory->val(1.0), 1.0]; diff --git a/tests/PhpParser/NodeTraverser/ClassLike/RuleUsingClassLikeRector.php b/tests/PhpParser/NodeTraverser/ClassLike/RuleUsingClassLikeRector.php new file mode 100644 index 00000000000..44361f9432d --- /dev/null +++ b/tests/PhpParser/NodeTraverser/ClassLike/RuleUsingClassLikeRector.php @@ -0,0 +1,35 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassLike::class]; + } + + public function refactor(Node $node): Node + { + return $node; + } +} diff --git a/tests/PhpParser/NodeTraverser/Class_/RuleUsingClassRector.php b/tests/PhpParser/NodeTraverser/Class_/RuleUsingClassRector.php new file mode 100644 index 00000000000..02e8070f83f --- /dev/null +++ b/tests/PhpParser/NodeTraverser/Class_/RuleUsingClassRector.php @@ -0,0 +1,35 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + public function refactor(Node $node): Node + { + return $node; + } +} diff --git a/tests/PhpParser/NodeTraverser/Function_/RuleUsingFunctionRector.php b/tests/PhpParser/NodeTraverser/Function_/RuleUsingFunctionRector.php new file mode 100644 index 00000000000..6c23a37406d --- /dev/null +++ b/tests/PhpParser/NodeTraverser/Function_/RuleUsingFunctionRector.php @@ -0,0 +1,35 @@ +> + */ + public function getNodeTypes(): array + { + return [Function_::class]; + } + + public function refactor(Node $node): Node + { + return $node; + } +} diff --git a/tests/PhpParser/NodeTraverser/RectorNodeTraverserTest.php b/tests/PhpParser/NodeTraverser/RectorNodeTraverserTest.php new file mode 100644 index 00000000000..d7e9208ae7d --- /dev/null +++ b/tests/PhpParser/NodeTraverser/RectorNodeTraverserTest.php @@ -0,0 +1,83 @@ +rectorNodeTraverser = $this->make(RectorNodeTraverser::class); + $this->rectorNodeTraverser->refreshPhpRectors([]); + + $this->ruleUsingFunctionRector = new RuleUsingFunctionRector(); + $this->ruleUsingClassRector = new RuleUsingClassRector(); + $this->ruleUsingClassLikeRector = new RuleUsingClassLikeRector(); + } + + public function testGetVisitorsForNodeWhenNoVisitorsAvailable(): void + { + $class = new Class_('test'); + + $visitors = $this->rectorNodeTraverser->getVisitorsForNode($class); + + $this->assertSame([], $visitors); + } + + public function testGetVisitorsForNodeWhenNoVisitorsMatch(): void + { + $class = new Class_('test'); + $this->rectorNodeTraverser->refreshPhpRectors([$this->ruleUsingFunctionRector]); + + $visitors = $this->rectorNodeTraverser->getVisitorsForNode($class); + + $this->assertSame([], $visitors); + } + + public function testGetVisitorsForNodeWhenSomeVisitorsMatch(): void + { + $class = new Class_('test'); + $this->rectorNodeTraverser->refreshPhpRectors([ + $this->ruleUsingFunctionRector, + $this->ruleUsingClassRector, + ]); + + $visitors = $this->rectorNodeTraverser->getVisitorsForNode($class); + + $this->assertEquals([$this->ruleUsingClassRector], $visitors); + } + + public function testGetVisitorsForNodeWhenAllVisitorsMatch(): void + { + $class = new Class_('test'); + $this->rectorNodeTraverser->refreshPhpRectors([ + $this->ruleUsingClassRector, + $this->ruleUsingClassLikeRector, + ]); + + $visitors = $this->rectorNodeTraverser->getVisitorsForNode($class); + + $this->assertEquals([$this->ruleUsingClassRector, $this->ruleUsingClassLikeRector], $visitors); + } +} diff --git a/tests/PhpParser/NodeTraverser/StopTraverseOnTypeChange/Class_/RuleChangingClassToTraitRector.php b/tests/PhpParser/NodeTraverser/StopTraverseOnTypeChange/Class_/RuleChangingClassToTraitRector.php new file mode 100644 index 00000000000..f85c4212ffa --- /dev/null +++ b/tests/PhpParser/NodeTraverser/StopTraverseOnTypeChange/Class_/RuleChangingClassToTraitRector.php @@ -0,0 +1,36 @@ +namespacedName = new Name('SomeNamespace\SomeTrait'); + + return $trait; + } +} diff --git a/tests/PhpParser/NodeTraverser/StopTraverseOnTypeChange/Class_/RuleCheckingClassRector.php b/tests/PhpParser/NodeTraverser/StopTraverseOnTypeChange/Class_/RuleCheckingClassRector.php new file mode 100644 index 00000000000..def96ad8c80 --- /dev/null +++ b/tests/PhpParser/NodeTraverser/StopTraverseOnTypeChange/Class_/RuleCheckingClassRector.php @@ -0,0 +1,35 @@ +rectorNodeTraverser = $this->make(RectorNodeTraverser::class); + + $this->rectorNodeTraverser->refreshPhpRectors([ + $this->make(RuleChangingClassToTraitRector::class), + $this->make(RuleCheckingClassRector::class), + ]); + + $this->testingParser = $this->make(TestingParser::class); + } + + public function testGetVisitorsForNodeWhenNoVisitorsAvailable(): void + { + // must be cloned + Scope set to allow node replacement + $nodes = $this->testingParser->parseFileToDecoratedNodes(__DIR__ . '/Fixture/SimpleClass.php'); + + $changedNodes = $this->rectorNodeTraverser->traverse($nodes); + + $nodeFinder = new NodeFinder(); + $classes = $nodeFinder->findInstanceOf($changedNodes, Class_::class); + $this->assertCount(0, $classes); + + $traits = $nodeFinder->findInstanceOf($changedNodes, Trait_::class); + $this->assertCount(1, $traits); + } +} diff --git a/tests/PhpParser/Printer/BetterStandardPrinterTest.php b/tests/PhpParser/Printer/BetterStandardPrinterTest.php index dd2b5592f88..582ce593519 100644 --- a/tests/PhpParser/Printer/BetterStandardPrinterTest.php +++ b/tests/PhpParser/Printer/BetterStandardPrinterTest.php @@ -2,28 +2,33 @@ declare(strict_types=1); -namespace Rector\Core\Tests\PhpParser\Printer; +namespace Rector\Tests\PhpParser\Printer; use Iterator; use PhpParser\Comment; +use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Expr\Yield_; use PhpParser\Node\Scalar\String_; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; -use Rector\Core\PhpParser\Printer\BetterStandardPrinter; +use PHPUnit\Framework\Attributes\DataProvider; +use Rector\Configuration\Option; +use Rector\Configuration\Parameter\SimpleParameterProvider; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\Testing\PHPUnit\AbstractTestCase; -use Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder; +use Rector\PhpParser\Printer\BetterStandardPrinter; +use Rector\Testing\PHPUnit\AbstractLazyTestCase; -final class BetterStandardPrinterTest extends AbstractTestCase +final class BetterStandardPrinterTest extends AbstractLazyTestCase { private BetterStandardPrinter $betterStandardPrinter; protected function setUp(): void { - $this->boot(); - $this->betterStandardPrinter = $this->getService(BetterStandardPrinter::class); + parent::setUp(); + + $this->betterStandardPrinter = $this->make(BetterStandardPrinter::class); } public function testAddingCommentOnSomeNodesFail(): void @@ -34,12 +39,10 @@ public function testAddingCommentOnSomeNodesFail(): void $methodCallExpression = new Expression($methodCall); $methodCallExpression->setAttribute(AttributeKey::COMMENTS, [new Comment('// todo: fix')]); - $methodBuilder = new MethodBuilder('run'); - $methodBuilder->addStmt($methodCallExpression); - - $classMethod = $methodBuilder->getNode(); + $classMethod = new ClassMethod('run'); + $classMethod->stmts = [$methodCallExpression]; - $printed = $this->betterStandardPrinter->print($classMethod) . PHP_EOL; + $printed = str_replace("\n", PHP_EOL, $this->betterStandardPrinter->print($classMethod) . "\n"); $this->assertStringEqualsFile( __DIR__ . '/Source/expected_code_with_non_stmt_placed_nested_comment.php.inc', $printed @@ -51,13 +54,11 @@ public function testStringWithAddedComment(): void $string = new String_('hey'); $string->setAttribute(AttributeKey::COMMENTS, [new Comment('// todo: fix')]); - $printed = $this->betterStandardPrinter->print($string) . PHP_EOL; + $printed = str_replace("\n", PHP_EOL, $this->betterStandardPrinter->print($string) . "\n"); $this->assertStringEqualsFile(__DIR__ . '/Source/expected_code_with_comment.php.inc', $printed); } - /** - * @dataProvider provideDataForDoubleSlashEscaping() - */ + #[DataProvider('provideDataForDoubleSlashEscaping')] public function testDoubleSlashEscaping(string $content, string $expectedOutput): void { $printed = $this->betterStandardPrinter->print(new String_($content)); @@ -67,26 +68,58 @@ public function testDoubleSlashEscaping(string $content, string $expectedOutput) /** * @return Iterator */ - public function provideDataForDoubleSlashEscaping(): Iterator + public static function provideDataForDoubleSlashEscaping(): Iterator { yield ['Vendor\Name', "'Vendor\Name'"]; yield ['Vendor\\', "'Vendor\\\\'"]; yield ["Vendor'Name", "'Vendor\'Name'"]; } - public function testYield(): void + #[DataProvider('provideDataForYield')] + public function testYield(Node $node, string $expectedPrintedNode): void { - $yield = new Yield_(new String_('value')); + $printedNode = $this->betterStandardPrinter->print($node); + $this->assertSame($expectedPrintedNode, $printedNode); + } - $printed = $this->betterStandardPrinter->print($yield); - $this->assertSame("(yield 'value')", $printed); + public function testPerNodeNewlineOnFluentCallAttribute(): void + { + SimpleParameterProvider::setParameter(Option::NEW_LINE_ON_FLUENT_CALL, false); + + $innerCall = new MethodCall(new Variable('foo'), 'bar'); + $outerCall = new MethodCall($innerCall, 'baz'); + $outerCall->setAttribute(AttributeKey::NEWLINE_ON_FLUENT_CALL, true); - $printed = $this->betterStandardPrinter->print(new Yield_()); - $this->assertSame('yield', $printed); + $printed = $this->betterStandardPrinter->print($outerCall); + $this->assertSame('$foo->bar()' . "\n ->baz()", $printed); + } + + public function testNoNewlineOnFluentCallWithoutAttribute(): void + { + SimpleParameterProvider::setParameter(Option::NEW_LINE_ON_FLUENT_CALL, false); + + $innerCall = new MethodCall(new Variable('foo'), 'bar'); + $outerCall = new MethodCall($innerCall, 'baz'); + + $printed = $this->betterStandardPrinter->print($outerCall); + $this->assertSame('$foo->bar()->baz()', $printed); + } + + /** + * @return Iterator> + */ + public static function provideDataForYield(): Iterator + { + $yield = new Yield_(new String_('value')); + yield [$yield, "yield 'value'"]; + + yield [new Yield_(), 'yield']; $expression = new Expression($yield); - $yield->setAttribute(AttributeKey::PARENT_NODE, $expression); - $printed = $this->betterStandardPrinter->print($expression); - $this->assertSame("yield 'value';", $printed); + yield [$expression, "yield 'value';"]; + + $assignedToYield = clone $yield; + $assignedToYield->setAttribute(AttributeKey::IS_ASSIGNED_TO, true); + yield [$assignedToYield, "(yield 'value')"]; } } diff --git a/tests/PhpParser/Printer/CommentPreserving/CommentPreservingTest.php b/tests/PhpParser/Printer/CommentPreserving/CommentPreservingTest.php index c4eb09858c3..bbc4e6c447a 100644 --- a/tests/PhpParser/Printer/CommentPreserving/CommentPreservingTest.php +++ b/tests/PhpParser/Printer/CommentPreserving/CommentPreservingTest.php @@ -2,28 +2,23 @@ declare(strict_types=1); -namespace Rector\Core\Tests\PhpParser\Printer\CommentPreserving; +namespace Rector\Tests\PhpParser\Printer\CommentPreserving; use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -use Symplify\SmartFileSystem\SmartFileInfo; final class CommentPreservingTest extends AbstractRectorTestCase { - /** - * @dataProvider provideData() - */ - public function test(SmartFileInfo $fileInfo): void + #[DataProvider('provideData')] + public function test(string $filePath): void { - $this->doTestFileInfo($fileInfo); + $this->doTestFile($filePath); } - /** - * @return Iterator - */ - public function provideData(): Iterator + public static function provideData(): Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } public function provideConfigFilePath(): string diff --git a/tests/PhpParser/Printer/CommentPreserving/Fixture/comments_for_typed_property.php.inc b/tests/PhpParser/Printer/CommentPreserving/Fixture/comments_for_typed_property.php.inc index edf1be4d3e6..c5d35e09b13 100644 --- a/tests/PhpParser/Printer/CommentPreserving/Fixture/comments_for_typed_property.php.inc +++ b/tests/PhpParser/Printer/CommentPreserving/Fixture/comments_for_typed_property.php.inc @@ -1,8 +1,8 @@ services(); - $services->set(TypedPropertyRector::class); -}; +return RectorConfig::configure() + ->withRules([TypedPropertyFromAssignsRector::class]); diff --git a/tests/PhpParser/Printer/Fixture/some_array_map.php b/tests/PhpParser/Printer/Fixture/some_array_map.php new file mode 100644 index 00000000000..c5a50902dd6 --- /dev/null +++ b/tests/PhpParser/Printer/Fixture/some_array_map.php @@ -0,0 +1,3 @@ + $value); diff --git a/tests/PhpParser/Printer/FormatPerservingPrinterTest.php b/tests/PhpParser/Printer/FormatPerservingPrinterTest.php deleted file mode 100644 index f1c59dd787f..00000000000 --- a/tests/PhpParser/Printer/FormatPerservingPrinterTest.php +++ /dev/null @@ -1,51 +0,0 @@ -boot(); - $this->formatPerservingPrinter = $this->getService(FormatPerservingPrinter::class); - $this->smartFileSystem = $this->getService(SmartFileSystem::class); - } - - protected function tearDown(): void - { - $this->smartFileSystem->remove(__DIR__ . '/Fixture'); - } - - public function testFileModeIsPreserved(): void - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('file modes are not supported on windows.'); - } - - mkdir(__DIR__ . '/Fixture'); - touch(__DIR__ . '/Fixture/file.php'); - - chmod(__DIR__ . '/Fixture/file.php', self::EXPECTED_FILEMOD); - - $fileInfo = new SmartFileInfo(__DIR__ . '/Fixture/file.php'); - $this->formatPerservingPrinter->printToFile($fileInfo, [], [], []); - - $this->assertSame(self::EXPECTED_FILEMOD, fileperms(__DIR__ . '/Fixture/file.php') & 0777); - } -} diff --git a/tests/PhpParser/Printer/PHPStanPrinterTest.php b/tests/PhpParser/Printer/PHPStanPrinterTest.php new file mode 100644 index 00000000000..d95bdafaf0d --- /dev/null +++ b/tests/PhpParser/Printer/PHPStanPrinterTest.php @@ -0,0 +1,45 @@ +make(Parser::class); + + PHPStanContainerMemento::removeRichVisitors($phpstanParser); + + $stmts = $phpstanParser->parseFile(__DIR__ . '/Fixture/some_array_map.php'); + + // get private property "parser" + $parserReflectionProperty = new ReflectionProperty(RichParser::class, 'parser'); + + /** @var \PhpParser\Parser $innerParser */ + $innerParser = $parserReflectionProperty->getValue($phpstanParser); + $tokens = $innerParser->getTokens(); + + $standard = new Standard([]); + $printerContents = $standard->printFormatPreserving($stmts, $stmts, $tokens); + + $newlineNormalizedContents = str_replace("\r\n", PHP_EOL, $printerContents); + + $this->assertStringEqualsFile(__DIR__ . '/Fixture/some_array_map.php', $newlineNormalizedContents); + } +} diff --git a/tests/PhpUnit/MultipleFilesChangedTrait/Fixture/fixture.php.inc b/tests/PhpUnit/MultipleFilesChangedTrait/Fixture/fixture.php.inc deleted file mode 100644 index 30128ac8870..00000000000 --- a/tests/PhpUnit/MultipleFilesChangedTrait/Fixture/fixture.php.inc +++ /dev/null @@ -1,19 +0,0 @@ -doTestFileInfoWithAdditionalChanges($smartFileInfo); - } - - /** - * @return Iterator - */ - public function provideData(): Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/PhpUnit/MultipleFilesChangedTrait/Rector/Class_/CreateJsonWithNamesForClassRector.php b/tests/PhpUnit/MultipleFilesChangedTrait/Rector/Class_/CreateJsonWithNamesForClassRector.php deleted file mode 100644 index 0efb02a7ef1..00000000000 --- a/tests/PhpUnit/MultipleFilesChangedTrait/Rector/Class_/CreateJsonWithNamesForClassRector.php +++ /dev/null @@ -1,49 +0,0 @@ -> - */ - public function getNodeTypes(): array - { - return [Class_::class]; - } - - /** - * @param Class_ $node - */ - public function refactor(Node $node) - { - $smartFileInfo = $this->file->getSmartFileInfo(); - $targetFilePath = $smartFileInfo->getRealPathDirectory() . '/names.json'; - - $content = Json::encode([ - 'short' => $this->nodeNameResolver->getShortName($node), - 'fqn' => $this->getName($node), - ], Json::PRETTY); - - $addedFileWithContent = new AddedFileWithContent($targetFilePath, $content); - $this->removedAndAddedFilesCollector->addAddedFile($addedFileWithContent); - return null; - } -} diff --git a/tests/PhpUnit/MultipleFilesChangedTrait/config/configured_rule.php b/tests/PhpUnit/MultipleFilesChangedTrait/config/configured_rule.php deleted file mode 100644 index bd211952b50..00000000000 --- a/tests/PhpUnit/MultipleFilesChangedTrait/config/configured_rule.php +++ /dev/null @@ -1,11 +0,0 @@ -services(); - $services->set(CreateJsonWithNamesForClassRector::class); -}; diff --git a/tests/Set/SetManager/Fixture/project-twig-127/vendor/composer/installed.json b/tests/Set/SetManager/Fixture/project-twig-127/vendor/composer/installed.json new file mode 100644 index 00000000000..b75daeeb383 --- /dev/null +++ b/tests/Set/SetManager/Fixture/project-twig-127/vendor/composer/installed.json @@ -0,0 +1,9 @@ +{ + "packages": [ + { + "name": "twig/twig", + "version": "v1.27.0", + "version_normalized": "1.27.0.0" + } + ] +} diff --git a/tests/Set/SetManager/Fixture/project-twig-20/vendor/composer/installed.json b/tests/Set/SetManager/Fixture/project-twig-20/vendor/composer/installed.json new file mode 100644 index 00000000000..fffd4b3a2b9 --- /dev/null +++ b/tests/Set/SetManager/Fixture/project-twig-20/vendor/composer/installed.json @@ -0,0 +1,9 @@ +{ + "packages": [ + { + "name": "twig/twig", + "version": "v2.0.0", + "version_normalized": "2.0.0.0" + } + ] +} diff --git a/tests/Set/SetManager/Fixture/project-twig-24/vendor/composer/installed.json b/tests/Set/SetManager/Fixture/project-twig-24/vendor/composer/installed.json new file mode 100644 index 00000000000..d29f2edb5ff --- /dev/null +++ b/tests/Set/SetManager/Fixture/project-twig-24/vendor/composer/installed.json @@ -0,0 +1,9 @@ +{ + "packages": [ + { + "name": "twig/twig", + "version": "v2.4.0", + "version_normalized": "2.4.0.0" + } + ] +} diff --git a/tests/Set/SetManager/SetManagerTest.php b/tests/Set/SetManager/SetManagerTest.php new file mode 100644 index 00000000000..9e25d74fc54 --- /dev/null +++ b/tests/Set/SetManager/SetManagerTest.php @@ -0,0 +1,62 @@ +createSetManagerWithProjectDirectory(getcwd()); + + $twigComposerTriggeredSet = $setManager->matchComposerTriggered(SetGroup::TWIG); + $this->assertCount(6, $twigComposerTriggeredSet); + } + + /** + * @param string[] $expectedSets + */ + #[DataProvider('provideInstalledTwigData')] + public function testByVersion(string $projectDirectory, array $expectedSets): void + { + $setManager = $this->createSetManagerWithProjectDirectory($projectDirectory); + + $composerTriggeredSets = $setManager->matchBySetGroups([SetGroup::TWIG]); + + $this->assertCount(count($expectedSets), $composerTriggeredSets); + $this->assertSame($expectedSets, $composerTriggeredSets); + } + + /** + * @return Iterator<(array>|array>|array>|array)> + */ + public static function provideInstalledTwigData(): Iterator + { + // here we cannot used features coming up in 2.4, as we only have 2.0 + yield [__DIR__ . '/Fixture/project-twig-20', [realpath(TwigSetList::TWIG_20)]]; + + yield [__DIR__ . '/Fixture/project-twig-24', [realpath(TwigSetList::TWIG_20), realpath(TwigSetList::TWIG_24)]]; + + yield [ + __DIR__ . '/Fixture/project-twig-127', + [realpath(TwigSetList::TWIG_112), realpath(TwigSetList::TWIG_127)]]; + } + + private function createSetManagerWithProjectDirectory(string $projectDirectory): SetManager + { + $setProviderCollector = new SetProviderCollector(); + $installedPackageResolver = new InstalledPackageResolver($projectDirectory); + + return new SetManager($setProviderCollector, $installedPackageResolver); + } +} diff --git a/tests/Set/SetManager/Source/SomeSetProvider.php b/tests/Set/SetManager/Source/SomeSetProvider.php new file mode 100644 index 00000000000..9999c095677 --- /dev/null +++ b/tests/Set/SetManager/Source/SomeSetProvider.php @@ -0,0 +1,24 @@ +fnMatchPathNormalizer = $this->make(FnMatchPathNormalizer::class); + } + + #[DataProvider('providePaths')] + public function testPaths(string $path, string $expectedNormalizedPath): void + { + $normalizedPath = $this->fnMatchPathNormalizer->normalizeForFnmatch($path); + $this->assertSame($expectedNormalizedPath, $normalizedPath); + } + + /** + * @return Iterator> + */ + public static function providePaths(): Iterator + { + yield ['path/with/no/asterisk', 'path/with/no/asterisk']; + yield ['*path/with/asterisk/begin', '*path/with/asterisk/begin*']; + yield ['path/with/asterisk/end*', '*path/with/asterisk/end*']; + yield ['*path/with/asterisk/begin/and/end*', '*path/with/asterisk/begin/and/end*']; + yield [__DIR__ . '/Fixture/path/with/../in/it', PathNormalizer::normalize(__DIR__ . '/Fixture/path/in/it')]; + yield [__DIR__ . '/Fixture/path/with/../../in/it', PathNormalizer::normalize(__DIR__ . '/Fixture/in/it')]; + } +} diff --git a/tests/Skipper/SkipCriteriaResolver/SkippedPathsResolver/Fixture/existing_paths.txt b/tests/Skipper/SkipCriteriaResolver/SkippedPathsResolver/Fixture/existing_paths.txt new file mode 100644 index 00000000000..a0cde59caf0 --- /dev/null +++ b/tests/Skipper/SkipCriteriaResolver/SkippedPathsResolver/Fixture/existing_paths.txt @@ -0,0 +1 @@ +you diff --git a/tests/Skipper/SkipCriteriaResolver/SkippedPathsResolver/SkippedPathsResolverTest.php b/tests/Skipper/SkipCriteriaResolver/SkippedPathsResolver/SkippedPathsResolverTest.php new file mode 100644 index 00000000000..a284e17514b --- /dev/null +++ b/tests/Skipper/SkipCriteriaResolver/SkippedPathsResolver/SkippedPathsResolverTest.php @@ -0,0 +1,45 @@ +skippedPathsResolver = $this->make(SkippedPathsResolver::class); + } + + protected function tearDown(): void + { + SimpleParameterProvider::setParameter(Option::SKIP, []); + } + + public function test(): void + { + $skippedPaths = $this->skippedPathsResolver->resolve(); + + $this->assertCount(2, $skippedPaths); + + $this->assertSame(PathNormalizer::normalize(__DIR__ . '/Fixture'), $skippedPaths[0]); + $this->assertSame('*/Mask/*', $skippedPaths[1]); + } +} diff --git a/tests/Skipper/Skipper/Fixture/AlwaysSkippedPath/some_file.txt b/tests/Skipper/Skipper/Fixture/AlwaysSkippedPath/some_file.txt new file mode 100644 index 00000000000..2ef267e25bd --- /dev/null +++ b/tests/Skipper/Skipper/Fixture/AlwaysSkippedPath/some_file.txt @@ -0,0 +1 @@ +some content diff --git a/tests/Skipper/Skipper/Fixture/Element/FifthElement.php b/tests/Skipper/Skipper/Fixture/Element/FifthElement.php new file mode 100644 index 00000000000..2b49be13b67 --- /dev/null +++ b/tests/Skipper/Skipper/Fixture/Element/FifthElement.php @@ -0,0 +1,9 @@ +make(SkippedClassResolver::class); + + $this->assertSame([], $skippedClassResolver->resolveDeprecatedSkippedClasses()); + } +} diff --git a/tests/Skipper/Skipper/SkipperRectorRuleTest.php b/tests/Skipper/Skipper/SkipperRectorRuleTest.php new file mode 100644 index 00000000000..414634e9ed3 --- /dev/null +++ b/tests/Skipper/Skipper/SkipperRectorRuleTest.php @@ -0,0 +1,49 @@ +bootFromConfigFiles([__DIR__ . '/config/single_skipped_rule_config.php']); + + $rectorConfig = self::getContainer(); + + // to invoke before resolving + $rectorConfig->make(FileNodesFetcher::class); + + // here 1 rule should be removed and 1 should remain + /** @var RewindableGenerator $rectorsIterator */ + $rectorsIterator = $rectorConfig->tagged(RectorInterface::class); + $this->assertCount(1, $rectorsIterator); + + $rectors = iterator_to_array($rectorsIterator->getIterator()); + $this->assertInstanceOf(RemoveUnusedPromotedPropertyRector::class, $rectors[0]); + } +} diff --git a/tests/Skipper/Skipper/SkipperTest.php b/tests/Skipper/Skipper/SkipperTest.php new file mode 100644 index 00000000000..33dda0fd5a8 --- /dev/null +++ b/tests/Skipper/Skipper/SkipperTest.php @@ -0,0 +1,112 @@ + ['Fixture/someFile', '*/someDirectory/*'], + ]); + + $this->skipper = $this->make(Skipper::class); + } + + protected function tearDown(): void + { + // cleanup configuration + SimpleParameterProvider::setParameter(Option::SKIP, []); + } + + #[DataProvider('provideDataShouldSkipFilePath')] + public function testSkipFilePath(string $filePath, bool $expectedSkip): void + { + $filePathResultSkip = $this->skipper->shouldSkipFilePath($filePath); + $this->assertSame($expectedSkip, $filePathResultSkip); + } + + /** + * @return Iterator + */ + public static function provideDataShouldSkipFilePath(): Iterator + { + yield [__DIR__ . '/Fixture/SomeRandom/file.txt', false]; + yield [__DIR__ . '/Fixture/SomeSkipped/any.txt', true]; + yield ['tests/Skipper/Skipper/Fixture/SomeSkippedPath/any.txt', true]; + yield ['tests/Skipper/Skipper/Fixture/SomeSkippedPathToFile/any.txt', true]; + yield [__DIR__ . '/Fixture/AlwaysSkippedPath/some_file.txt', true]; + yield [__DIR__ . '/Fixture/PathSkippedWithMask/another_file.txt', true]; + } + + /** + * @param object|class-string $element + */ + #[DataProvider('provideDataShouldSkipElement')] + public function testSkipElement(string|object $element, bool $expectedSkip): void + { + $resultSkip = $this->skipper->shouldSkipElement($element); + $this->assertSame($expectedSkip, $resultSkip); + } + + #[DataProvider('provideCheckerAndFile')] + public function testSkipElementAndFilePath(string $element, string $filePath, bool $expectedSkip): void + { + $resolvedSkip = $this->skipper->shouldSkipElementAndFilePath($element, $filePath); + $this->assertSame($expectedSkip, $resolvedSkip); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideCheckerAndFile(): Iterator + { + yield [FifthElement::class, __DIR__ . '/Fixture', true]; + + yield [AnotherClassToSkip::class, __DIR__ . '/Fixture/someFile', true]; + yield [AnotherClassToSkip::class, __DIR__ . '/Fixture/someDirectory/anotherFile.php', true]; + + yield [NotSkippedClass::class, __DIR__ . '/Fixture/someFile', false]; + yield [NotSkippedClass::class, __DIR__ . '/Fixture/someOtherFile', false]; + } + + /** + * @return Iterator, mixed>> + */ + public static function provideDataShouldSkipElement(): Iterator + { + yield [ThreeMan::class, false]; + yield [FifthElement::class, true]; + yield [new FifthElement(), true]; + } +} diff --git a/tests/Skipper/Skipper/Source/AnotherClassToSkip.php b/tests/Skipper/Skipper/Source/AnotherClassToSkip.php new file mode 100644 index 00000000000..150276ff3f3 --- /dev/null +++ b/tests/Skipper/Skipper/Source/AnotherClassToSkip.php @@ -0,0 +1,9 @@ +withSkip([InlineConstructorDefaultToPropertyRector::class]) + ->withRules([InlineConstructorDefaultToPropertyRector::class, RemoveUnusedPromotedPropertyRector::class]); diff --git a/tests/StaticTypeMapper/PhpDoc/PhpDocTypeMapperTest.php b/tests/StaticTypeMapper/PhpDoc/PhpDocTypeMapperTest.php new file mode 100644 index 00000000000..bd7708d61ce --- /dev/null +++ b/tests/StaticTypeMapper/PhpDoc/PhpDocTypeMapperTest.php @@ -0,0 +1,57 @@ +phpDocTypeMapper = $this->make(PhpDocTypeMapper::class); + $this->nameScopeFactory = $this->make(NameScopeFactory::class); + } + + /** + * @param class-string $expectedPHPStanType + */ + #[DataProvider('provideData')] + public function test(TypeNode $typeNode, string $expectedPHPStanType): void + { + $nop = new Nop(); + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($nop); + + $phpStanType = $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $nop, $nameScope); + $this->assertInstanceOf($expectedPHPStanType, $phpStanType); + } + + /** + * @return Iterator<(class-string[] | ArrayShapeNode[])> + */ + public static function provideData(): Iterator + { + $arrayShapeNode = ArrayShapeNode::createSealed([ + new ArrayShapeItemNode(null, true, new IdentifierTypeNode('string')), + ]); + + yield [$arrayShapeNode, ConstantArrayType::class]; + } +} diff --git a/tests/Testing/RectorRuleShouldNotBeApplied/Fixture/no_change.php.inc b/tests/Testing/RectorRuleShouldNotBeApplied/Fixture/no_change.php.inc new file mode 100644 index 00000000000..518910ea0e9 --- /dev/null +++ b/tests/Testing/RectorRuleShouldNotBeApplied/Fixture/no_change.php.inc @@ -0,0 +1,11 @@ + diff --git a/tests/Testing/RectorRuleShouldNotBeApplied/RectorRuleShouldNotBeAppliedTest.php b/tests/Testing/RectorRuleShouldNotBeApplied/RectorRuleShouldNotBeAppliedTest.php new file mode 100644 index 00000000000..374c8007a16 --- /dev/null +++ b/tests/Testing/RectorRuleShouldNotBeApplied/RectorRuleShouldNotBeAppliedTest.php @@ -0,0 +1,29 @@ +doTestFileExpectingWarningAboutRuleApplied($filePath, NoChangeRector::class); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Testing/RectorRuleShouldNotBeApplied/Source/NoChangeRector.php b/tests/Testing/RectorRuleShouldNotBeApplied/Source/NoChangeRector.php new file mode 100644 index 00000000000..041ee9e7976 --- /dev/null +++ b/tests/Testing/RectorRuleShouldNotBeApplied/Source/NoChangeRector.php @@ -0,0 +1,30 @@ +withRules([NoChangeRector::class]); diff --git a/tests/Util/FileHasherTest.php b/tests/Util/FileHasherTest.php new file mode 100644 index 00000000000..c94647469fc --- /dev/null +++ b/tests/Util/FileHasherTest.php @@ -0,0 +1,48 @@ +fileHasher = $this->make(FileHasher::class); + } + + public function testHash(): void + { + $hash = $this->fileHasher->hash('some string'); + $this->assertSame('8df638f91bacc826bf50c04efd7df1b1', $hash); + } + + public function testHashFiles(): void + { + $dir = sys_get_temp_dir(); + $file = $dir . '/FileHasherTest-fixture.txt'; + + try { + FileSystem::write($file, 'some string', null); + + $hash = $this->fileHasher->hashFiles([$file]); + $this->assertSame('8df638f91bacc826bf50c04efd7df1b1', $hash); + } finally { + FileSystem::delete($file); + } + } + + public function testHashFilesWithEmptyArray(): void + { + $hash = $this->fileHasher->hashFiles([]); + $this->assertSame('', $hash); + } +} diff --git a/tests/Util/Reflection/Fixture/AbstractPrivateProperty.php b/tests/Util/Reflection/Fixture/AbstractPrivateProperty.php new file mode 100644 index 00000000000..e7d1b496cc3 --- /dev/null +++ b/tests/Util/Reflection/Fixture/AbstractPrivateProperty.php @@ -0,0 +1,17 @@ +parentValue; + } +} diff --git a/tests/Util/Reflection/Fixture/SomeClassWithPrivateMethods.php b/tests/Util/Reflection/Fixture/SomeClassWithPrivateMethods.php new file mode 100644 index 00000000000..f62538cd661 --- /dev/null +++ b/tests/Util/Reflection/Fixture/SomeClassWithPrivateMethods.php @@ -0,0 +1,23 @@ +object = new stdClass(); + } + + public function getValue(): int + { + return $this->value; + } + + public function getObject() : stdClass + { + return $this->object; + } +} diff --git a/tests/Util/Reflection/PrivatesAccessorTest.php b/tests/Util/Reflection/PrivatesAccessorTest.php new file mode 100644 index 00000000000..418fa7fbb04 --- /dev/null +++ b/tests/Util/Reflection/PrivatesAccessorTest.php @@ -0,0 +1,78 @@ +privatesAccessor = new PrivatesAccessor(); + } + + /** + * @param class-string|SomeClassWithPrivateMethods $object + * @param mixed[]|int[] $arguments + */ + #[DataProvider('provideData')] + public function test( + string | SomeClassWithPrivateMethods $object, + string $methodName, + array $arguments, + int $expectedResult + ): void { + $result = $this->privatesAccessor->callPrivateMethod($object, $methodName, $arguments); + $this->assertSame($expectedResult, $result); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [SomeClassWithPrivateMethods::class, 'getNumber', [], 5]; + yield [new SomeClassWithPrivateMethods(), 'getNumber', [], 5]; + yield [new SomeClassWithPrivateMethods(), 'plus10', [30], 40]; + } + + public function testGetterSetter(): void + { + $privatesAccessor = new PrivatesAccessor(); + $someClassWithPrivateProperty = new SomeClassWithPrivateProperty(); + + $fetchedValue = $privatesAccessor->getPrivateProperty($someClassWithPrivateProperty, 'value'); + $this->assertSame($someClassWithPrivateProperty->getValue(), $fetchedValue); + + $fetchedParentValue = $privatesAccessor->getPrivateProperty($someClassWithPrivateProperty, 'parentValue'); + $this->assertSame($someClassWithPrivateProperty->getParentValue(), $fetchedParentValue); + + $privatesAccessor->setPrivateProperty($someClassWithPrivateProperty, 'value', 25); + $this->assertSame(25, $someClassWithPrivateProperty->getValue()); + } + + public function testGetterSetterTypesafe(): void + { + $privatesAccessor = new PrivatesAccessor(); + $someClassWithPrivateProperty = new SomeClassWithPrivateProperty(); + + $newObject = new stdClass(); + $this->assertNotSame($newObject, $someClassWithPrivateProperty->getObject()); + $privatesAccessor->setPrivateProperty($someClassWithPrivateProperty, 'object', $newObject); + $this->assertSame($newObject, $someClassWithPrivateProperty->getObject()); + + $fetchedValue = $privatesAccessor->getPrivateProperty($someClassWithPrivateProperty, 'object'); + + $this->assertSame($someClassWithPrivateProperty->getObject(), $fetchedValue); + } +} diff --git a/tests/Validation/RectorAssertTest.php b/tests/Validation/RectorAssertTest.php new file mode 100644 index 00000000000..78272b4e867 --- /dev/null +++ b/tests/Validation/RectorAssertTest.php @@ -0,0 +1,102 @@ +expectException(InvalidArgumentException::class); + RectorAssert::className($className); + } + + /** + * @return Iterator + */ + public static function provideDataValidClassNames(): Iterator + { + yield ['App']; + yield ['App\\SomeClass']; + } + + /** + * @return Iterator + */ + public static function provideDataInvalidClassNames(): Iterator + { + yield ['App Some']; + yield ['App$SomeClass']; + yield ['$SomeClass']; + yield ['App\\\\Some']; + yield ['3AppSome']; + } + + #[DataProvider('provideDataValidFunctionNames')] + #[DoesNotPerformAssertions] + public function testValidFunctionName(string $functionName): void + { + RectorAssert::functionName($functionName); + } + + /** + * @return Iterator + */ + public static function provideDataValidFunctionNames(): Iterator + { + yield ['some_function']; + yield ['Namespace\\some_function']; + yield ['Namespace\\so3me_f6n']; + } + + #[DataProvider('provideDataValidMethodNames')] + #[DoesNotPerformAssertions] + public function testValidMethodName(string $methodName): void + { + RectorAssert::methodName($methodName); + } + + /** + * @return Iterator + */ + public static function provideDataValidMethodNames(): Iterator + { + yield ['some_method']; + yield ['__method_magic']; + yield ['__M3th0d']; + } + + #[DataProvider('provideDataInvalidFunctionNames')] + public function testInvalidFunctionName(string $functionName): void + { + $this->expectException(InvalidArgumentException::class); + RectorAssert::functionName($functionName); + } + + /** + * @return Iterator> + */ + public static function provideDataInvalidFunctionNames(): Iterator + { + yield ['35']; + yield ['/function']; + yield ['$function']; + yield ['-key_name']; + } +} diff --git a/tests/ValueObject/Error/SystemErrorTest.php b/tests/ValueObject/Error/SystemErrorTest.php new file mode 100644 index 00000000000..713a1a4c490 --- /dev/null +++ b/tests/ValueObject/Error/SystemErrorTest.php @@ -0,0 +1,36 @@ +assertSame(realpath(__DIR__ . '/SystemErrorTest.php'), $systemError->getAbsoluteFilePath()); + } + + public function testGetAbsoluteFilePathShouldReturnNullWhenRelativeFilePathIsNull(): void + { + $systemError = new SystemError('Some error message'); + $this->assertNull($systemError->getAbsoluteFilePath()); + } + + public function testGetRectorShortClass(): void + { + $systemError = new SystemError('Some error message', null, 1, StrStartsWithRector::class); + $this->assertSame('StrStartsWithRector', $systemError->getRectorShortClass()); + } + + public function testGetRectorShortClassShouldReturnNullWhenRectorClassIsNull(): void + { + $systemError = new SystemError('Some error message'); + $this->assertNull($systemError->getRectorShortClass()); + } +} diff --git a/tests/ValueObject/Reporting/FileDiffTest.php b/tests/ValueObject/Reporting/FileDiffTest.php new file mode 100644 index 00000000000..0cf0269cb1f --- /dev/null +++ b/tests/ValueObject/Reporting/FileDiffTest.php @@ -0,0 +1,61 @@ +assertSame(38, $fileDiff->getFirstLineNumber()); + } + + public function testGetFirstLineNumberShouldBeNullWhenHunkIsInvalid(): void + { + $fileDiff = new FileDiff( + 'some/file.php', + '--- Original\n+++ New\n@@@@\nreturn true;\n}\n', + 'diff console formatted' + ); + $this->assertNull($fileDiff->getFirstLineNumber()); + } + + public function testGetLastLineNumberShouldReturnLastLineNumberRegardingHunk(): void + { + $fileDiff = new FileDiff( + 'some/file.php', + '--- Original\n+++ New\n@@ -38,5 +39,6 @@\nreturn true;\n}\n', + 'diff console formatted' + ); + $this->assertSame(43, $fileDiff->getLastLineNumber()); + } + + public function testGetLastLineNumberShouldBeNullWhenHunkIsInvalid(): void + { + $fileDiff = new FileDiff( + 'some/file.php', + '--- Original\n+++ New\n@@@@\nreturn true;\n}\n', + 'diff console formatted' + ); + $this->assertNull($fileDiff->getLastLineNumber()); + } + + public function testGetLastLineNumberShouldReturnFirstLineWhenUndefinedInHunk(): void + { + $fileDiff = new FileDiff( + 'some/file.php', + '--- Original\n+++ New\n@@ -38 +39 @@\nreturn true;\n}\n', + 'diff console formatted' + ); + $this->assertSame(38, $fileDiff->getLastLineNumber()); + } +} diff --git a/tests/VersionBonding/ComposerPackageConstraintFilterTest.php b/tests/VersionBonding/ComposerPackageConstraintFilterTest.php new file mode 100644 index 00000000000..c762fa64e7b --- /dev/null +++ b/tests/VersionBonding/ComposerPackageConstraintFilterTest.php @@ -0,0 +1,74 @@ +composerPackageConstraintFilter = new ComposerPackageConstraintFilter($installedPackageResolver); + } + + public function testRectorWithoutInterfaceIsIncluded(): void + { + $noInterfaceRector = new NoInterfaceRector(); + $filtered = $this->composerPackageConstraintFilter->filter([$noInterfaceRector]); + + $this->assertCount(1, $filtered); + $this->assertSame($noInterfaceRector, $filtered[0]); + } + + public function testRectorWithSatisfiedConstraintIsIncluded(): void + { + $composerPackageConstraintRector = new ComposerPackageConstraintRector('nikic/php-parser', '>=4.0.0'); + $filtered = $this->composerPackageConstraintFilter->filter([$composerPackageConstraintRector]); + + $this->assertCount(1, $filtered); + $this->assertSame($composerPackageConstraintRector, $filtered[0]); + } + + public function testRectorWithUnsatisfiedConstraintIsExcluded(): void + { + $composerPackageConstraintRector = new ComposerPackageConstraintRector('nikic/php-parser', '>=999.0.0'); + $filtered = $this->composerPackageConstraintFilter->filter([$composerPackageConstraintRector]); + + $this->assertCount(0, $filtered); + } + + public function testRectorWithMissingPackageIsExcluded(): void + { + $composerPackageConstraintRector = new ComposerPackageConstraintRector('non-existent/package', '>=1.0.0'); + $filtered = $this->composerPackageConstraintFilter->filter([$composerPackageConstraintRector]); + + $this->assertCount(0, $filtered); + } + + public function testRectorWithCaretConstraint(): void + { + $composerPackageConstraintRector = new ComposerPackageConstraintRector('nikic/php-parser', '^5.0'); + $filtered = $this->composerPackageConstraintFilter->filter([$composerPackageConstraintRector]); + + $this->assertCount(1, $filtered); + $this->assertSame($composerPackageConstraintRector, $filtered[0]); + } + + public function testRectorWithLessThanConstraintExcludesNewerVersions(): void + { + $composerPackageConstraintRector = new ComposerPackageConstraintRector('nikic/php-parser', '<1.0.0'); + $filtered = $this->composerPackageConstraintFilter->filter([$composerPackageConstraintRector]); + + $this->assertCount(0, $filtered); + } +} diff --git a/tests/VersionBonding/Fixture/ComposerPackageConstraintRector.php b/tests/VersionBonding/Fixture/ComposerPackageConstraintRector.php new file mode 100644 index 00000000000..5dfe01c2425 --- /dev/null +++ b/tests/VersionBonding/Fixture/ComposerPackageConstraintRector.php @@ -0,0 +1,40 @@ +packageName, $this->constraint); + } +} diff --git a/tests/VersionBonding/Fixture/NoInterfaceRector.php b/tests/VersionBonding/Fixture/NoInterfaceRector.php new file mode 100644 index 00000000000..18dd4921276 --- /dev/null +++ b/tests/VersionBonding/Fixture/NoInterfaceRector.php @@ -0,0 +1,27 @@ +phpVersionedFilter = new PhpVersionedFilter($phpVersionProvider, $polyfillPackagesProvider); + } + + public function testRectorWithoutInterfaceIsIncluded(): void + { + $noInterfaceRector = new NoInterfaceRector(); + $filtered = $this->phpVersionedFilter->filter([$noInterfaceRector]); + + $this->assertCount(1, $filtered); + $this->assertSame($noInterfaceRector, $filtered[0]); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 932a0d70f00..800fd015f78 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,10 +2,6 @@ declare(strict_types=1); -use Rector\Core\Stubs\PHPStanStubLoader; - -require_once __DIR__ . '/../src/constants.php'; - // make local php-parser a priority to avoid conflict require_once __DIR__ . '/../preload.php'; require_once __DIR__ . '/../vendor/autoload.php'; @@ -15,6 +11,3 @@ // performance boost gc_disable(); - -$phpStanStubLoader = new PHPStanStubLoader(); -$phpStanStubLoader->loadStubs(); diff --git a/tests/debug_functions.php b/tests/debug_functions.php index 76f031afc2a..0cfcc410927 100644 --- a/tests/debug_functions.php +++ b/tests/debug_functions.php @@ -2,25 +2,23 @@ declare(strict_types=1); -use PhpParser\Node; -use PhpParser\PrettyPrinter\Standard; +use Tracy\Dumper; -require __DIR__ . '/../vendor/autoload.php'; +// helpful functions to create new rector rules -/** - * @param Node|Node[] $node - */ -function print_node(Node | array $node): void -{ - $standard = new Standard(); +if (! function_exists('dd')) { + function dd(mixed $value, int $depth = 2): never + { + d($value, $depth); + die; + } +} - if (is_array($node)) { - foreach ($node as $singleNode) { - $printedContent = $standard->prettyPrint([$singleNode]); - dump($printedContent); - } - } else { - $printedContent = $standard->prettyPrint([$node]); - dump($printedContent); +if (! function_exists('d')) { + function d(mixed $value, int $depth = 2): void + { + Dumper::dump($value, [ + Dumper::DEPTH => $depth, + ]); } } diff --git a/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/Fixture/cover_bare_get_node_types.php.inc b/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/Fixture/cover_bare_get_node_types.php.inc new file mode 100644 index 00000000000..456c6bd025a --- /dev/null +++ b/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/Fixture/cover_bare_get_node_types.php.inc @@ -0,0 +1,54 @@ + +----- + diff --git a/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/Fixture/some_class.php.inc b/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..f4991045b08 --- /dev/null +++ b/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/Fixture/some_class.php.inc @@ -0,0 +1,60 @@ + +----- + diff --git a/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/RemoveRefactorDuplicatedNodeInstanceCheckRectorTest.php b/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/RemoveRefactorDuplicatedNodeInstanceCheckRectorTest.php new file mode 100644 index 00000000000..1ae75654141 --- /dev/null +++ b/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/RemoveRefactorDuplicatedNodeInstanceCheckRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/config/configured_rule.php b/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/config/configured_rule.php new file mode 100644 index 00000000000..f4ae1bb7e60 --- /dev/null +++ b/utils-tests/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector/config/configured_rule.php @@ -0,0 +1,11 @@ +withRules([ + RemoveRefactorDuplicatedNodeInstanceCheckRector::class, + ]); diff --git a/utils/compiler/src/Unprefixer.php b/utils/Compiler/Unprefixer.php similarity index 87% rename from utils/compiler/src/Unprefixer.php rename to utils/Compiler/Unprefixer.php index fc47166efef..b13a09b3165 100644 --- a/utils/compiler/src/Unprefixer.php +++ b/utils/Compiler/Unprefixer.php @@ -2,18 +2,20 @@ declare(strict_types=1); -namespace Rector\Compiler; +namespace Rector\Utils\Compiler; use Nette\Utils\Strings; final class Unprefixer { /** - * @var string * @see https://regex101.com/r/P8sXfr/1 */ - private const QUOTED_VALUE_REGEX = '#\'\\\\(\w|@)#'; + private const string QUOTED_VALUE_REGEX = '#\'\\\\(\w|@)#'; + /** + * @api + */ public static function unprefixQuoted(string $content, string $prefix): string { $match = sprintf('\'%s\\\\r\\\\n\'', $prefix); diff --git a/utils/Rector/MoveAbstractRectorToChildrenRector.php b/utils/Rector/MoveAbstractRectorToChildrenRector.php new file mode 100644 index 00000000000..c5500f4c54f --- /dev/null +++ b/utils/Rector/MoveAbstractRectorToChildrenRector.php @@ -0,0 +1,111 @@ + + */ + private const array PROPERTIES_TO_TYPES = [ + 'phpDocInfoFactory' => PhpDocInfoFactory::class, + 'valueResolver' => ValueResolver::class, + 'betterNodeFinder' => BetterNodeFinder::class, + 'staticTypeMapper' => StaticTypeMapper::class, + ]; + + public function __construct( + private readonly ClassDependencyManipulator $classDependencyManipulator, + ) { + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Move parent class autowired dependency to constructor of children', []); + } + + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isAbstract()) { + return null; + } + + if (! $this->isObjectType($node, new ObjectType('Rector\Rector\AbstractRector'))) { + return null; + } + + $typesToAdd = []; + + // has dependency on X type? + $this->traverseNodesWithCallable($node->stmts, function (Node $node) use (&$typesToAdd) { + if (! $node instanceof PropertyFetch) { + return null; + } + + if (! $this->isName($node->var, 'this')) { + return null; + } + + foreach (self::PROPERTIES_TO_TYPES as $propertyName => $propertyType) { + if (! $this->isName($node->name, $propertyName)) { + continue; + } + + $typesToAdd[$propertyName] = $propertyType; + } + }); + + // remove already added properties + + if ($typesToAdd === []) { + return null; + } + + $hasChanged = false; + + foreach ($typesToAdd as $propertyNameToAdd => $propertyTypeToAdd) { + // skip if property already exists + if ($node->getProperty($propertyNameToAdd) instanceof Property) { + continue; + } + + $this->classDependencyManipulator->addConstructorDependency( + $node, + new PropertyMetadata($propertyNameToAdd, new ObjectType($propertyTypeToAdd)) + ); + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } +} diff --git a/utils/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector.php b/utils/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector.php new file mode 100644 index 00000000000..35bb54987e5 --- /dev/null +++ b/utils/Rector/RemoveRefactorDuplicatedNodeInstanceCheckRector.php @@ -0,0 +1,165 @@ +getClassReflection(); + + if (! $classReflection instanceof ClassReflection) { + return null; + } + + if (! $classReflection->is('Rector\Rector\AbstractRector')) { + return null; + } + + $refactorClassMethod = $node->getMethod('refactor'); + if (! $refactorClassMethod instanceof ClassMethod) { + return null; + } + + $firstStmt = $refactorClassMethod->stmts[0] ?? null; + if (! $firstStmt instanceof If_) { + return null; + } + + $instanceofNodeClass = $this->matchBooleanNotInstanceOfNodeClass($firstStmt->cond); + if (! is_string($instanceofNodeClass)) { + return null; + } + + $nodeParamTypeClass = $this->matchNodeParamType($refactorClassMethod); + + $getNodeTypesClassMethod = $node->getMethod('getNodeTypes'); + if (! $getNodeTypesClassMethod instanceof ClassMethod) { + return null; + } + + $soleReturn = $getNodeTypesClassMethod->stmts[0] ?? null; + + $nodeTypeClass = null; + if ($soleReturn instanceof Return_) { + Assert::isInstanceOf($soleReturn->expr, Expr::class); + + $nodeTypes = $this->valueResolver->getValue($soleReturn->expr); + if (count($nodeTypes) === 1) { + $nodeTypeClass = $nodeTypes[0]; + } + } + + if ($nodeParamTypeClass !== null) { + if ($nodeParamTypeClass !== $instanceofNodeClass) { + return null; + } + } elseif ($nodeTypeClass !== null) { + if ($nodeTypeClass !== $instanceofNodeClass) { + return null; + } + } else { + return null; + } + + unset($refactorClassMethod->stmts[0]); + + return $node; + } + + private function matchBooleanNotInstanceOfNodeClass(Expr $expr): ?string + { + if (! $expr instanceof BooleanNot) { + return null; + } + + $booleanNot = $expr; + if (! $booleanNot->expr instanceof Instanceof_) { + return null; + } + + return $this->getInstanceofNodeClass($booleanNot->expr); + } + + /** + * @return class-string|null + */ + private function getInstanceofNodeClass(Instanceof_ $instanceof): ?string + { + $checkedClassType = $this->getType($instanceof->class); + if (! $checkedClassType instanceof ObjectType) { + return null; + } + + /** @var ClassReflection $classReflection */ + $classReflection = $checkedClassType->getClassReflection(); + + if (! $classReflection->is(Node::class)) { + return null; + } + + return $classReflection->getName(); + } + + private function matchNodeParamType(ClassMethod $classMethod): ?string + { + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + + $paramType = $classMethodPhpDocInfo->getParamType('$node'); + if (! $paramType instanceof ObjectType) { + return null; + } + + if ($paramType instanceof ShortenedObjectType) { + return $paramType->getFullyQualifiedName(); + } + + return $paramType->getClassName(); + } +} diff --git a/utils/compiler/config/config.php b/utils/compiler/config/config.php deleted file mode 100644 index ab73fd8bd8a..00000000000 --- a/utils/compiler/config/config.php +++ /dev/null @@ -1,16 +0,0 @@ -services(); - - $services->defaults() - ->public() - ->autowire() - ->autoconfigure(); - - $services->load('Rector\Compiler\\', __DIR__ . '/../src'); -}; diff --git a/utils/compiler/src/Command/DowngradePathsCommand.php b/utils/compiler/src/Command/DowngradePathsCommand.php deleted file mode 100644 index a36cd642bc9..00000000000 --- a/utils/compiler/src/Command/DowngradePathsCommand.php +++ /dev/null @@ -1,100 +0,0 @@ -setDescription('[DEV] Provide vendor paths that require downgrade to required PHP version'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $downgradePaths = $this->findVendorAndRulePaths(); - - foreach ($downgradePaths as $key => $downgradePath) { - if (in_array( - $downgradePath, - [ - 'vendor/symplify', - 'vendor/symfony', - 'vendor/nikic', - 'vendor/psr', - 'vendor/phpstan', - 'vendor/ssch', - ], - true - )) { - unset($downgradePaths[$key]); - } - } - - $downgradePaths = array_merge([ - // must be separated to cover container get() trait + psr container contract get() - 'config', - 'vendor/phpstan/phpdoc-parser/src', - 'vendor/symfony/error-handler', - 'vendor/symfony/dependency-injection', - 'vendor/symfony/console', - 'vendor/symfony vendor/psr', - 'vendor/symplify vendor/nikic vendor/ssch bin src packages rector.php', - 'rules', - ], $downgradePaths); - - if (file_exists(getcwd() . '/vendor/phpstan/phpstan-extracted/vendor')) { - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/phpstan/phpdoc-parser/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/ondrejmirtes/better-reflection/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/nette/di/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/nette/php-generator/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/nette/utils/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/nette/schema/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/nette/finder/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/nette/robot-loader/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/nette/bootstrap/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/ondram/ci-detector/src'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/symfony/finder'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/symfony/console/Output/OutputInterface.php'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/symfony/console/Output/TrimmedBufferOutput.php'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/symfony/console/Logger/ConsoleLogger.php'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/symfony/console/Style/SymfonyStyle.php'; - $downgradePaths[] = 'vendor/phpstan/phpstan-extracted/vendor/symfony/console'; - $downgradePaths[] = 'vendor/phpstan/phpstan-phpunit/src'; - } - - // bash format - $downgradePathsLine = implode(';', $downgradePaths); - echo $downgradePathsLine . PHP_EOL; - - return Command::SUCCESS; - } - - /** - * @return string[] - */ - private function findVendorAndRulePaths(): array - { - $finder = (new Finder())->directories() - ->in(__DIR__ . '/../../../..') - ->depth(1) - ->path('#(vendor)\/(.*?)#') - ->sortByName(); - - $directoryPaths = []; - foreach ($finder->getIterator() as $fileInfo) { - $directoryPaths[] = $fileInfo->getRelativePathname(); - } - - $directoryPaths = array_unique($directoryPaths); - return array_values($directoryPaths); - } -} diff --git a/utils/compiler/src/PhpScoper/StaticEasyPrefixer.php b/utils/compiler/src/PhpScoper/StaticEasyPrefixer.php deleted file mode 100644 index 401aeda2069..00000000000 --- a/utils/compiler/src/PhpScoper/StaticEasyPrefixer.php +++ /dev/null @@ -1,53 +0,0 @@ -\w+)\\\\#'; - - /** - * @var string - */ - private const CATEGORY = 'category'; - - public function infer(RuleDefinition $ruleDefinition): ?string - { - $matches = Strings::match($ruleDefinition->getRuleClass(), self::RECTOR_CATEGORY_REGEX); - if (! isset($matches[self::CATEGORY])) { - $message = sprintf('Category for "%s" could not be resolved', $ruleDefinition->getRuleClass()); - throw new ShouldNotHappenException($message); - } - - return $matches[self::CATEGORY]; - } -}